原有的事务支持使用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. Mac 下配置php环境

    Mac OS X 内置了Apache 和 PHP,这样使用起来非常方便.本文以Mac OS X 10.6.3为例.主要内容包括: 启动Apache 运行PHP 安装MySQL 使用phpMyAdmin ...

  2. IDEA创建的Web项目配置Tomcat并启动Maven项目

    点击如图所示的地方,进行添加Tomcat配置页面   弹出页面后,按照如图顺序找到,点击+号     tomcat Service -> Local   注意,这里不要选错了哦,还有一个TomE ...

  3. ping命令技巧详解 windows下ping命令知识大全

    windows ping命令对于多数电脑爱好者都不会陌生,通过ping ip可以知道网络是否畅通或者网络传输质量如何等,是网络技术人员常用的检测网络命令,多数朋友对ping命令知道的并不多,接下来本文 ...

  4. Linux命令行操作进程

    参见 12个进程管理命令介绍 Linux如何查看进程.杀死进程.启动进程等常用命令

  5. os.path.join 用法

    写在前面的话:看大家阅读量这么大,也应该在放点干货来了~~ 获取层级路径,直到可以获取文件夹下面的文件,多一个判断就行了: level1_list = [os.path.join(base_path, ...

  6. 利用asynchttpclient开源项目来把数据提交给服务器

    可以通过github去查找asynchttpclient,并下载源代码,并加载到自己的工程中. 1.利用get方法提交 2.利用post方法来提交

  7. CF1096. G. Lucky Tickets(快速幂NTT)

    All bus tickets in Berland have their numbers. A number consists of n digits (n is even). Only k dec ...

  8. mac上textstudio在系统升级以后不能编译

    不能工作的最主要原因为:os x改变了文件存放的路径.因此,需要修改setting中的command的路径,将原来的改为: /usr/local/texlive/2015/bin/x86_64-dar ...

  9. laravel5.3 源码分析 Passport

    laravel5.3,密码模式的授权认证过程.我会通过两部分内容分享以及查看passport的认证流程分享出来 第一部分:根据官方文档,通过Composer安装Passport 文档地址:http:/ ...

  10. (研) int(*p)[10]; int *p[10]; int(*)[10]; 之间的区别

    int *p[10]; 从这个最简单的说起 p先与后面的[4]结合,说明他本质是一个数组 ,“[]”的优先级比“*”要高.p先与“[]”结合,构成一个数组的定义,数组名为p,int *修饰的是数组的内 ...