原有的事务支持使用MemcachedState来进行,现在需要将其迁移至Redis,并且需要记录所有key值列表,因为在redis中虽然可以使用keys *操作,但不是被推荐的方式,所以把所有结果存在Redis中的一个HASH格式字段中。
 
关于Redis与Storm集成的相关文档,可以参考:
 
 
由于Redis中也有着较多种类型的数据结构,这也为我们提供了可能,将所有的key至统一放置到set中,或其他更为合适的数据结构中。
 
搭建启动Redis
 
目前,分配过来的4台服务器,只有135剩余内存较多,分出1G用来作为Redis存储使用,搭建一台单机Redis服务,用于记录所有的查询日志。
 
 
启动该服务:
 
sudo bin/redis-server conf/redis.6388.conf
 
Storm集成Redis
 
添加maven依赖:
 
<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-redis</artifactId>
<version>${storm.version}</version>
</dependency>
 
对于正常的Bolt来说,storm-redis提供了基本的bolt实现,RedisLookupBolt和RedisStoreBolt,
 


 
 
 
其中使用了策略模式,将实际要查询/保存相关的key设置以及策略放到了RedisLookup/StoreMapper中,在LookupBolt和StoreBolt中进行实际的查找、保存操作,根据RedisDataType的不同,支持Redis的各种数据类型:STRING, HASH, LIST, SET, SORTED_SET, HYPER_LOG_LOG。
 
从对应传输过来的Tuple中查找、保存相应字段的值,在RedisLookupBolt中,根据不同的key值,从key值/或者additionalKey中使用不同的方法来get得到对应的值。
 
@Override
public void execute(Tuple input) {
String key = lookupMapper.getKeyFromTuple(input);
Object lookupValue; JedisCommands jedisCommand = null;
try {
jedisCommand = getInstance(); switch (dataType) {
case STRING:
lookupValue = jedisCommand.get(key);
break; case LIST:
lookupValue = jedisCommand.lpop(key);
break; case HASH:
lookupValue = jedisCommand.hget(additionalKey, key);
break; case SET:
lookupValue = jedisCommand.scard(key);
break; case SORTED_SET:
lookupValue = jedisCommand.zscore(additionalKey, key);
break; case HYPER_LOG_LOG:
lookupValue = jedisCommand.pfcount(key);
break; default:
throw new IllegalArgumentException("Cannot process such data type: " + dataType);
} List<Values> values = lookupMapper.toTuple(input, lookupValue);
for (Values value : values) {
collector.emit(input, value);
} collector.ack(input);
} catch (Exception e) {
this.collector.reportError(e);
this.collector.fail(input);
} finally {
returnInstance(jedisCommand);
}
 
 
Redis TridentState支持
 
此外,storm-redis中还支持trident state:
 
RedisState and RedisMapState, which provide Jedis interface just for single redis.

RedisClusterState and RedisClusterMapState, which provide JedisCluster interface, just for redis cluster.
由于我们使用的是single redis模式(非集群),在下面的UML图中会有所体现:
 
 


 
 
 
使用RedisDataTypeDescription来定义保存到Redis的数据类型和额外的key,其中支持两种数据类型:STRING和HASH。如果使用HASH类型,则需要定义额外的key,因为hash属于两层的,我们定义的additionalKey为最外层的key类型。
 
例如我们需要保存结果至Redis的Hash数据结构中,则需要定义RedisDataTypeDescription.RedisDataType.HASH,定义hash的key:"controller:5min”,根据key进行group by操作,当前使用非事务型(对数据正确性敏感度不高)。
            Options<Object> fiveMinitesOptions = new Options<>();
fiveMinitesOptions.dataTypeDescription = new RedisDataTypeDescription(RedisDataTypeDescription.RedisDataType.HASH,
"controller:5min");
logStream.each(new Fields("logObject"), new Log5MinGroupFunction(), new Fields("key"))
.groupBy(new Fields("key"))
.persistentAggregate(RedisMapState.nonTransactional(poolConfig, fiveMinitesOptions), new Fields("logObject"),
new LogCombinerAggregator(), new Fields("statistic"));
 
最后在Redis中保存的值为:
 
controller:5min
Log5MinGroupFunction生成的key,LogCombinerAggregator合并完成后的value;
 
Log5MinGroupFunction生成的key会经过KeyFactory.build(List<Object> key)方法转换,可以考虑自定义生成的key;最终的value会通过Serializer的序列化以及反序列化方法转换成byte[]存放至Redis中,默认是通过JSON的格式。
 
在AbstractRedisMapState中,对于传过来的keys进行统一KeyFactory.get操作,而实际获取值和持久化值是通过 retrieveValuesFromRedis以及updateStatesToRedis两个方法来实现的
@Override public List<T> multiGet(List<List<Object>> keys) {
if (keys.size() == 0) {
return Collections.emptyList();
} List<String> stringKeys = buildKeys(keys);
List<String> values = retrieveValuesFromRedis(stringKeys); return deserializeValues(keys, values);
} private List<String> buildKeys(List<List<Object>> keys) {
List<String> stringKeys = new ArrayList<String>();
for (List<Object> key : keys) {
stringKeys.add(getKeyFactory().build(key));
}
return stringKeys;
} @Override
public void multiPut(List<List<Object>> keys, List<T> vals) {
if (keys.size() == 0) {
return;
} Map<String, String> keyValues = new HashMap<String, String>();
for (int i = 0; i < keys.size(); i++) {
String val = new String(getSerializer().serialize(vals.get(i)));
String redisKey = getKeyFactory().build(keys.get(i));
keyValues.put(redisKey, val);
} updateStatesToRedis(keyValues);
}
 
在RedisMapState中,从Redis中获取值的方法:
 
@Override
protected List<String> retrieveValuesFromRedis(List<String> keys) {
String[] stringKeys = keys.toArray(new String[keys.size()]); Jedis jedis = null;
try {
jedis = jedisPool.getResource(); RedisDataTypeDescription description = this.options.dataTypeDescription;
switch (description.getDataType()) {
case STRING:
return jedis.mget(stringKeys); case HASH:
return jedis.hmget(description.getAdditionalKey(), stringKeys);
 
可以看出,支持两种类型STRING以及HASH,可以通过批量获取的API获取多个keys值,update的过程也比较类似,如果是STRING类型,通过pipeline的方式(分布式不支持)可以极大提高查找效率;如果为hash类型,直接通过hmget即可。
protected void updateStatesToRedis(Map<String, String> keyValues) {
Jedis jedis = null; try {
jedis = jedisPool.getResource(); RedisDataTypeDescription description = this.options.dataTypeDescription;
switch (description.getDataType()) {
case STRING:
String[] keyValue = buildKeyValuesList(keyValues);
jedis.mset(keyValue);
if(this.options.expireIntervalSec > 0){
Pipeline pipe = jedis.pipelined();
for(int i = 0; i < keyValue.length; i += 2){
pipe.expire(keyValue[i], this.options.expireIntervalSec);
}
pipe.sync();
}
break; case HASH:
jedis.hmset(description.getAdditionalKey(), keyValues);
if (this.options.expireIntervalSec > 0) {
jedis.expire(description.getAdditionalKey(), this.options.expireIntervalSec);
}
break;
 
 

Storm存储结果至Redis的更多相关文章

  1. C# Azure 存储-分布式缓存Redis工具类 RedisHelper

    using System; using System.Collections.Generic; using Newtonsoft.Json; using StackExchange.Redis; na ...

  2. C# Azure 存储-分布式缓存Redis在session中的配置

    1. 开始 对于分布式的缓存,平常的session的处理是一个用户对应一台分布式的机器,如果这台机器中途挂机或者不能处理这个用户session的情况发生,则此用户的session会丢失,会发生不可预知 ...

  3. C# Azure 存储-分布式缓存Redis的新建&配置&查看

    1. 介绍 Redis 是一款开源的,基于 BSD 许可的,高级键值 (key-value) 缓存 (cache) 和存储 (store) 系统.由于 Redis 的键包括 string,hash,l ...

  4. 一些应该使用mongodb或者其他文档存储而不是redis或mysql、oracle json的情形(最近更新场景)

    通常来说,我们应该使用应用的特性而不是自己的爱好或者规定而去选择一种合适的组件,选择的标准应该是这个组件最适合或者本身其设计就是为了解决这个问题,而不是这个组件能够做这事情为标准.就拿存储来说,任何时 ...

  5. PHP 更改session存储方式为Redis

    前言: 服务器默认的session存放方式是file.当客户端发送请求带有PHPSESSID时是顺序的去比对session存储文件,如果有5000个session文件,那就有可能需要比对4998次那么 ...

  6. 服务端指南 数据存储篇 | 聊聊 Redis 使用场景(转)

    作者:梁桂钊 本文,是升级版,补充部分实战案例.梳理几个场景下利用 Redis 的特性可以大大提高效率. 随着数据量的增长,MySQL 已经满足不了大型互联网类应用的需求.因此,Redis 基于内存存 ...

  7. 爬虫文件存储-3:Redis

    前提条件: 安装并运行redis服务端程序,安装RedisPy库 说明:Redis 是 StrictRedis 的子类,它的主要功能是用于向后兼容旧版本库里的几个方法,官方推荐使用 StrictRed ...

  8. 【原】thinkphp实现存储session至redis

    Thinkphp\Library\Think\Session\Driver中新建redis缓存文件:Redis.class.php Thinkphp\Common\function.php 中 fun ...

  9. redis数据存储--C++连接redis

    一.下载的是Redis Windows版本:下载地址:https://github.com/microsoftarchive/redis:解压到:E:\Software\redis-3.0: 二.用V ...

随机推荐

  1. 使用Inno Setup Compiler制作安装软件包

    前言 项目开发完成之后,需要程序打包发行,本文使用Inno Setup工具制作安装软件包. 系统环境 系统:win7_x64 工具:Inno Setup Complier 实现步骤 1.下载安装Inn ...

  2. cloneNode

  3. Linux系统部署Web项目

    首先,需要有java环境和tomcat服务器,至于这个怎么安装,可以参考:云服务器 java+tomcat 部署与配置 项目部署 有两个大步骤: Step1 将已经写好的web项目打包成war包,在I ...

  4. Codeforces123E. Maze【树形dp】【概率dp】【证明题】

    LINK 题目大意 一棵树,上面的每个点都有一定概率成为起点和终点 从起点出发,随机游走,并按照下列规则统计count: DFS(x) if x == exit vertex then finish ...

  5. The Suspects 并查集

    Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized ...

  6. 《DSP using MATLAB》Problem 4.2

    用matlab不会证,惭愧.

  7. 《DSP using MATLAB》 Problem 3.22

    代码: %% ------------------------------------------------------------------------ %% Output Info about ...

  8. LeetCode Majority Element Python

    Given an array of size n, find the majority element. The majority element is the element that appear ...

  9. $.inArray方法

  10. 笔记本设置 2K 显示屏 Intel HD Graphics 3000

    收了一台 32英寸的显示器,接到笔记 本只有 1920x1080,心想没得救了,要换笔记本才能用了. 在网上找了一圈,找到一篇说 Intel HD Graphics 3000 可以支持到 2560x1 ...