SODBASE CEP学习进阶篇(五):与分布式缓存集成

对一些历史数据的查询,如果将数据放在存储中如Hbase或RDBMS中,读写性能可能会拖累整个CEP系统的性能。CEP引擎和分布式缓存系统集成是解决这个问题的一种方法。如果CEP Server机器的内存足够大,直接开辟在CEP Server的内存中存数据当然速度最快。以下场合可考虑外接分布式缓存

(1)CEP Server内存不足,想通过外接缓存的方式来存储数据。

(2)已经有了分布式缓存系统,想增加CEP计算和规则管理能力。

本文以Redis集成为例,因为Redis作为一个成熟的分布式缓存系统,业界包括一线互联网企业都有在生产环境的部署。

1.示例操作步骤

例1,在线商城中我们要查询某商品实时UV(Unique Visitor),对于千万级以上用户的系统做这样的实时计算。一种方法是在Redis中建一个<商品ID,独立访问次数>的hash map。为每个用户在Redis中建一个bitmap key,key的bit位置代表商品id。当一条<用户ID,商品ID,timestamp>数据实时传过来时,我们先需要看此用户ID是否第一次访问此商品,如果不是就SETBIT useridvisited productid 1。注意:Redis现在bitmap索引的最大长度是512M,也就是一个bitmap中商品的数目不超过512M。如果超了这个数也不要紧,可多建立几个bitmap,因为bitmap的数量是不限的。

(1)网上下载一个Redis,SODBASE CEP已支持与Redis 3.0以上的Redis Cluster集成。如果觉得麻烦想快速把例子跑起来,可以用这个Windows版,解压,根据自己机器选择32bit或64bit文件夹,运行redis-server.exe,默认端口6379启动。注意若linux版本,防火墙要开启相应端口。

(2)下载SODBASE Studio 2.0.21(sp3)版本以上

下载示例CEP模型redis01.sod,redis02.sod,redis03.sod

(3)运行SODBASE Studio,导入redis01.sod,redis02.sod,redis03.sod

redis01:模拟用户的商品浏览(初始模拟了100个用户,100种商品),并查redis bitmap此用户是否已经浏览过此商品

redis02:计算uv,更新bitmap

redis03:屏幕打印

(4)将三个EPL模型全部测试运行起来

(5)输出结果


例2:还是在线商城中统计商品的独立访问量。如果商品数量很大,那么即使为每个商品建了bitmap索引,占的内存空间也会很大。可以用HyperLogLogs的PFCOUNT命令,单个商品的 HyperLogLogs数据结构占内存小。PFCOUNT 对于一个key值的复杂度为O(1),当然因为是概率算法,统计有一些误差。

例3:如果要求滑动窗口在数小时、或者1天以上,且包含数据量很大,可采用SODBASE CEP新增的sodbase-hexpire等命令结合Redis实现大窗口,并且保证处理性能。

2. 工作原理和注意事项

2.1  模拟用户访问商品输入适配器

/**
 * 
 */
package com.sodbase.inputadaptor.simulation;

import java.util.Date;
import java.util.Random;

import zstreamplus.eventbuffer.PrimitiveEvent;
import zstreamplus.eventbuffer.ValueType;
import zstreamplus.streamsource.StreamSource;

import com.sodbase.inputadaptor.OptimizedInputAdaptorI;

/**
 * 模拟在线商城用户对商品的访问
 *参数列表:
 *params[0] 	streamname
 *params[1] 	period
 *params[2]     user number
 *params[3]     product numbers
 */
public class ProductVisitStreamInput extends OptimizedInputAdaptorI
{

	private boolean running=true;
	private long period=1000;
	private int usernumber=100;
	private int productnumber=100;

	/* (non-Javadoc)
	 * @see com.sodbase.inputadaptor.OptimizedInputAdaptorI#run()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void run()
	{
		
		int count=0;
		while (running)
		{
			//debug
			count++;
			/*if(count>10)
				running=false;*/
			
			try
			{
				Thread.sleep(period);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			
			/**
			 * API: 新建一个Primitive Event基本事件(复杂事件由基本事件组成)
			 * 
			 */
			
			PrimitiveEvent primitiveEvent = new PrimitiveEvent();
			Random random = new Random();
			int random1 = random.nextInt(usernumber);
			int random2 = random.nextInt(productnumber);
			/**
			 * 为primitiveEvent name字段赋值
			 */
			ValueType valueType = new ValueType(String.valueOf(random1),
					"string");
			primitiveEvent.getAttributeMap().put("userid", valueType);

			ValueType valueType2 = new ValueType(String.valueOf(random2),
					"string");
			primitiveEvent.getAttributeMap().put("productid", valueType2);			
			Date d = new Date();			
			/**
			 * API: 为primitiveEvent 设置时间戳,通常每个基本事件必须有时间戳
			 * 每个event都有start time 和end time
			 */
			long time = d.getTime();
			primitiveEvent.setStart_ts(time);
			primitiveEvent.setEnd_ts(time);
			this.putEventToStream(primitiveEvent);
		}
	
	}

	/* (non-Javadoc)
	 * @see com.sodbase.inputadaptor.OptimizedInputAdaptorI#setUp()
	 */
	@Override
	public void setUp()
	{
		if(params[0]!=null)
			this.streamName= params[0];
		if(params.length>1)
			this.period= Long.valueOf(params[1]);
		if(params.length>2)
			this.usernumber= Long.valueOf(params[2]);	
		if(params.length>3)
			this.productnumber= Long.valueOf(params[3]);	
		StreamSource.instance().createDataSource(streamName);
	
		
	}

	/* (non-Javadoc)
	 * @see stream.adaptor.inputadaptor.InputAdaptorI#stopInputStream()
	 */
	@Override
	public void stopInputStream()
	{
		running=false;
		
	}

	/* (non-Javadoc)
	 * @see stream.adaptor.inputadaptor.InputAdaptorI#isRunning()
	 */
	@Override
	public boolean isRunning()
	{
		return running;
	}

}

2.2 操作Redis

CEP的输出适配器也常常作为动作输出,执行动作。redis01的输出适配器就是操作Redis。



命令名 getbit

参数 ProductVisitBM:?{productid} ?{userid}

?{}一般是用来引用字段值的,如果productid=a,userid=20,完整的Redis命令就是 getbit ProductVisitBM:a 20,获取bitmap ProductVisitBM:a的第20位。

返回值赋给了增加的字段visited,并连同其它字段一起接入下一个EPL的输入流redis02.input。


redis02的filter如下图所示



redis02的第一个输出配置如下图所示

redis02的第二个输出配置如下

redis03将结果屏幕打印出来。

当然,使用Redis的读者,也要注意官方文档介绍,单次查询响应时间可能会很慢,达到几十毫秒甚至上百毫秒,因为发给缓存系统的查询要经过网络传输返回结果(所有的分布式缓存都有这个问题)。CEP应用的地方往往需要响应延迟在几十毫秒以内,所以CEP服务器和分布式缓存服务器之间最好是高速网络连接。如果业务允许批量操作,尽量批量操作即pipeline,再将结果返回。和批量入库的原理类似,这样性能整体得到了提升。

在集群管理方面,Redis推出了Redis Cluster正式版,可以动态伸缩集群,相配套的client也趋于完善。之前Redis作为Data Store,集群的数目和数据映射是不能变的。Redis作为Cache用,则不影响,因为Cache没有命中可以到硬盘上去找。

SODBASE CEP用于轻松、高效实施数据监测、监控类、实时交易类项目 微笑 嵌入式方式编程参见运行第一个EPL例子。与Storm集成参见EPL与Storm集成


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值