Esper使用技巧



win:参数
win:length
win:length(10) 滑动窗口,存最近的10条数据

win:length_batch
win:length_batch(10) 间隔窗口,存满10条触发查询

win:time
win:time(5 sec) 滑动窗口,存最近5秒内的数据
单位:年/year  月/month  周/week 天/day 小时/hour 分/min 秒/sec 毫秒/msec
可多项   如:
1 day 2 hour 20 min 15 sec 110 msec
win:ext_timed
win:ext_timed(timestamp,10 sec) 滑动窗口,存最近10秒内的数据,不再基于EPL引擎,而是系统时间戳

win:time_batch
win:time_batch(10 sec) 10秒间隔的窗口
win:time_batch(10 sec,"FORCE_UPDATE, START_EAGER") 加上这两个参数后,会在窗口初始化时就执行查询,并且在没有数据进入和移出时,强制查询出结果

win:time_length_batch
win:time_length_batch(10 sec,5),当时间或数量任意一个满足条件时,触发查询

win:time_accum
select rstream * from userbuy.win:time_accum(15 sec) 阻塞输出,直到15秒内没有新进入的数据时,才输出并触发查询. 这些数据视为已移出窗口

win:keepall
win:keepall() 无参数,记录所有进入的数据,除非使用delete操作,才能从窗口移出数据。

win:firstlength
win:firstlength(10) 保留一批数据的前10条,需配合delete操作

win:firsttime
win:firsttime(10 sec) 保留窗口初始化后10秒内的所有数据


例子:
select avg(int_data) as avgIntData from TestEvent.win:time(1 sec)
解析:查询1秒内的TestEvent类int_data值的平均值,并把值赋给avgIntData(esper后通过这个键获取值)


std
std:unique
std:unique(id) 对不同的id保留其最先执行的一条事件!!!

std:groupwin
std:groupwin(id).win:length(5) 一般与其它窗口组合使用,此例子中将进入的数据按id分组,相同id的数据条目数不大于5
select sum(amount),id from std:groupwin(id).win:length(5) group by id 统计每组id最近5次消费的总金额数

std:size
select size from userbuy.win:time(1 min).std:size() 统计一分钟内事件总数

std:firstunique
std:firstunique(id) 相同id的事件只取第一条

例子:
select int_data as avgIntData from TestEvent.std:firstunique(int_data).win:length(2)
解析:查询TestEvent类中最近两次去掉重复的int_data的值并赋给avgIntData

stat
进行一些统计数学统计,统计项固定
stat:uni, stat:linest, stat:correl,stat:weighted_avg

ext
ext:sort
对指定的字段进行排序,限定个数
select sum(amount) from userbuy.ext:sort(3, amount desc) 将3个最高金额求和
select * from userbuy.ext:sort(3,amount desc,id asc) 找出最高金额的3条事件,并按id排序

output 与 stream
com.espertech.esper.client.EPStatementException: Unexpected exception starting statement: Class

'class org.apache.commons.dbcp.BasicDataSourceFactory' failed in method createDataSource :null
[ select nameDest,beforeTime,nameSrc  from TransferAccount(transferMoney/nameSrcMoney>=0.9).win:time(3 sec)
as ta,sql:testEsperData[' SELECT COUNT(a.ip) AS cnt  FROM(SELECT DISTINCT ip FROM loginlog WHERE name=${nameSrc})
AS a  INNER JOIN (SELECT DISTINCT ip FROM loginlog WHERE name=${nameDest}) AS b  ON a.ip=b.ip'] as ld where ld.cnt>0 ]

 at com.espertech.esper.core.service.StatementLifecycleSvcImpl.startInternal(StatementLifecycleSvcImpl.java:652)


 select nameDest,beforeTime  from TransferAccount.win:time(3 sec) as ta,
 sql:esperdemo[' SELECT COUNT(a.ip) AS cnt FROM(SELECT DISTINCT ip FROM 
 testEsperData.dbo.loginlog WHERE name=${ta.nameSrc})
 AS a  INNER JOIN (SELECT DISTINCT ip FROM 
 testEsperData.dbo.loginlog WHERE name=${ta.nameDest})
 AS b  ON a.ip=b.ip'] as ld where ld.cnt>0

select nameDest,beforeTime  from TransferAccount.std:firstunique(nameDest).win:length(2),
  sql:esperdemo [' select ip from testEsperData.dbo.loginlog where name=${nameSrc} ']

与数据库关联查询
EPL可以与各种关系型及非关系型数据库通讯,进行关联查询,基本语法

select custId, cust_name from CustomerCallEvent,
  sql:MyCustomerDB [' select cust_name from Customer where cust_id = ${custId} ']

name window
在EPL中可以创建命名窗口,此窗口可供很多语句来查询,并且可以对此窗口进行insert update delete操作,一些不自动移出数据的view可以派上用场了,当然,也可以对常用的那些view使用

create
create window USERBUY.win:keepall() select id,amount,lottery from userbuy

创建命名窗口,并声明包含的字段和类型

insert
insert into USERBUY select id,amount,lottery from userbuy

update
update和delete属于触发式操作,当有事件进入时产生

on lotterychange.win:time(5 sec)
update USERBUY set amount = 1
where USERBUY.id = lotterychange.id

delete
on ticket.win:time(10 sec)
delete from USERBUY
where USERBUY.amount != 1

trigger
与标准sql不同,EPL基于数据流,操作都需要事件触发,比如动态窗口的每次查询都是在事件发生的时候。
一个适合的例子是子查询

select (select count(*) from OLDUSERBUY) as oldcount,
       (select sum(amount) from NEWUSERBUY) as newsum    //标准SQL到此为止
from trigger.win:time(5 sec)       //EPL由事件触发子查询

此查询只会在trigger接收到事件时触发

分割与复制
将事件进行条件判断,把数据流划分到不同命名窗口

on userbuy
insert into OLDUSERBUY select id,amount,lottery where id > 100
insert into NEWUSERBUY select id,amount,lottery where amount > 1000
[output all]

当事件进入时,以从上往下的速度去执行,成功则停止,只进入一个命名窗口,实现分割;加上output all后,逐一判断,复制到多个命名窗口

自定义变量
创建一个变量,此值可以用在各种语句中作为条件判断,也可以被触发修改

Pattern
Match Recognize
以正则表达式的形式匹配事件,做出复杂分析,语法如下:

match_recognize (
  [ partition by partition_expression [, partition_expression] [,...]  ]
  measures  measure_expression as col_name [, measure_expression as col_name ] [,...]
  [ all matches ]
  [ after match skip (past last row | to next row | to current row) ]
  pattern ( variable_regular_expr [, variable_regular_expr] [,...] )
  [ interval time_period ]
  define  variable as variable_condition [, variable as variable_condition]  [,...]
)

假定现在有人对单个url逐页进行抓取,我们可以从日志中取出IP、URL、页码,发给cep进行分析

select * from webaccess.win:time_batch(60 sec)
  match_recognize(
     partition by ip,url
     measures A.url as a_url,count(B.page) as b_count,A.ip as a_ip
     pattern (A B+)
     define
     B as B.page - prev(1,B.page) = 1)
having b_count > 10

从webaccess数据流接收事件,并开始匹配,按相同的ip和url来分组,需要获取访问的url、ip及访问次数
接下来要确认什么样的事件符合我们的需求,它在抓取第一页后,抓取第二页时,页码数都要比之前大1位,之后一直是这类情况,于是统计此情况发生的次数,
如果此次数在1分钟的窗口内大于100次,便认为它是抓取


注释:
//   或 /*   */
如:
select field1, // first comment
/* second comment*/ field2
from MyEvent


特殊字符处理:
select * from OrderEvent(name='John\'s')
//改为
select * from OrderEvent(name='John\u0027s')

select * from OrderEvent(description like "Quote \"Hello\"")
//改为
select * from OrderEvent(description like "Quote \u0022Hello\u0022")

注解
EPL可调用java注解

epl声明
epl声明的表达式的参数目前只能是一个流的名字。
声明表达范围以下规则适用于:
1。一个声明表达表达身体的范围不仅包括明确列出的参数。

如:
expression newsSubq(md) {
(select sentiment from NewsEvent.std:unique(symbol) where symbol = md.symbol)
}
select newsSubq(mdstream)  from MarketDataEvent mdstream


epl支持脚本语言
JSR223和MVEL

JSR223和也
MVEL脚本语言,可以指定在英超。

事件方法:
1。声明事件类型的创建模式。
2。声明一个变量创建变量。
3。创建一个名为“窗口的索引,建立索引。
4。更新istream的更新插入流事件。

epl 语句
select *  from  event 
* 包含event中的所有事件,
优点,今后扩展功能强
缺点,查出的事件过多,占内存资源
总结:尽量不用

选择事件:
insert into TickNewStream
select
tick, news, MyLib.compute(news, tick)
as result
from StockTick.win:time(10) as tick, News.win:time(10) as news
where tick.symbol = news.symbol

选择不同的关键字:DISTINCT

事件流过滤器
如:
select * from RfidEvent(zone=1 and category=10)

基于模式的事件流
如:
select tick.price as tickPrice, trade.price as tradePrice,
sum(tick.price) + sum(trade.price) as total
from pattern [every tick=StockTickEvent or every trade=TradeEvent].win:time(30 sec)

事件流计数分组
select cardId, expressway, direction, segment, count(*)
from CarLocEvent.std:groupwin(carId).win:length(4)
group by carId, expressway, direction, segment


win:time(30).std:firstunique(keys)    保留30秒内,键的值是唯一的事件

win:firstlength(3).std:firstunique(keys) 保留了前3个事件,也是每个键唯一

win:time_batch(N sec).std:unique(keys)   每N秒一批,键的值是唯一的事件

win:time_batch(N sec).std:firstunique(keys) 同上

win:length_batch(N).std:unique(keys)  每N个事件一批,键的值是唯一的事件

win:length_batch(N).std:firstunique(keys)  同上

std:firstunique(keys) 与 std:unique(keys) 的异同:
相同点:都是保留键的值是唯一的事件
不同点:
std:firstunique(keys) 是取第一个事件(也就是最晚发送的那个事件)
std:unique(keys)  是取最后一个事件(也就是最早发送的那个事件)


一条EPL语句查询多个流:
select prod.* as product, ord.* as order from ProductEvent as prod, OrderEvent as ord


where 条件 设置
...where fraud.severity = 5 and amount > 500
...where (orderItem.orderId is null) or (orderItem.class != 10)
...where (orderItem.orderId = null) or (orderItem.class <> 10)
...where itemCount / packageCount > 10

EPL 函数  (基本上等同于SQL函数)
select 'IBM stats' as title, avg(price) as avgPrice, sum(price) as sumPrice
from StockTickEvent(symbol='IBM').win:length(10)

group by 示例
select symbol, sum(price) from StockTickEvent group by timestamp

where和having的区别
WHERE语句在GROUP BY语句之前;SQL会在分组之前计算WHERE语句。  
HAVING语句在GROUP BY语句之后;SQL会在分组之后计算HAVING语句。

having  示例
select symbol, price, avg(price)
from StockTickEvent.win:time(30 sec)
having price < avg(price)

过滤器实例
select tickDataFeed, stddev(price)
from StockTickEvent(symbol='IBM').win:length(10)
where volume > 1000
group by tickDataFeed
having stddev(price) > 0.8

output 事件输出
示例:
select * from StockTickEvent.win:length(5) output every 1.5 minutes
1.5 分钟内最近的5个事件

select * from StockTickEvent.win:time(30 sec) output every 5 events
30秒内 每5个事件输出一次

select * from StockTickEvent.win:time(30 sec) output last every 5 events
30秒内 每5个事件输出最后一个事件

select * from StockTickEvent.win:time(30 sec)  output first every 20 sec
30秒内 每间隔20秒输出最早的一个事件

输出事件时间控制
select symbol, sum(price) from StockTickEvent group by symbol output at (*/15, 8:17, *, *, *)

使用输出表达式
select sum(price) from StockTickEvent
output when OutputTriggerVar = true
then set OutputTriggerVar = false

epl语句顺序
select * from 事件流.std:XX.win:XX  where  XX>0  group by XX having XX output XX


select symbol, sum(price) from StockTickEvent.std:groupwin(id).win:length(5) where id>5
group by symbol output first every 10 seconds

除 output  不用缓存之外 其他关键字都会用缓存

order by 排序用法示例
order by 是一行一行的找数据,所以比较慢
select symbol from StockTickEvent.win:time(60 sec)
output every 5 events
order by price, volume

limit 选取行
select propertyOne,propertyTwo from MyEvent.win:length(10) limit 5
最早的5个事件

limit 5 offset 1
最早的第2个事件到第6个事件
注:两个参数不能出现0,否则会报错

limit 5,3
最早的第6个事件到第8个事件
注:第一个参数不能出现0,否则会报错,
第二个参数可以出现0,不会报错,会返回一个没有事件的流

插入和合并事件流
1。在select子句中的元素的数量必须匹配的元素插入到
如果子句子句指定事件属性名称列表
2。如果事件流的名称已经事先声明或配置,事件定义
属性名称和/或事件类型不匹配,抛出异常语句创建时间。

例子:
insert into CombinedEvent
select A.customerId as custId, A.timestamp - B.timestamp as latency
from EventA.win:time(30 min) A, EventB.win:time(30 min) B
where A.txnId = B.txnId

rstream删除流事件里的值
insert rstream into CombinedEvent
select A.customerId as custId, A.timestamp - B.timestamp as latency
from EventA.win:time(30 min) A, EventB.win:time(30 min) B
where A.txnId = B.txnId

事件流转移数据
注:只支持javaBean事件流,不支持Map和XML事件流
例子:
insert into MyBidStream select bid.* from Summary


合并事件类型流

insert into MergedStream select * from OrderEvent

insert into MergedStream select ord.* from ItemScanEvent, OrderEvent as ord

子查询:
1。子查询流的定义,必须定义一个数据窗口或其他限制子查询结果,减少
子查询的执行
2。子查询只能由一个SELECT子句,FROM子句和WHERE子句,通过组连接,外连接和output Limit限制不会允许在子查询。
3。如果在子查询中使用聚合函数,请注意以下限制:
一个没有相关流的属性,可用于在聚合函数。
子查询流的属性都必须在聚合函数。

例子:
select * from A(a_val in
(select b_val from DNamedWindow as d where a.a_id = d.d_id)) as a


exists 关键字 (true or false ,当为true时epl语句至少返回一个事件)
例子:
select assetId from RFIDEvent as RFID
where exists (select * from Asset.std:unique(assetId) where assetId = RFID.assetId)

in 例子:
select assetId from RFIDEvent
where zone in (select zone from ZoneUpdate(status = 'closed').win:time(10 min))

any 例子:
select * from ProductOrder as ord
where quantity < any
(select minimumQuantity from MinimumQuantity.win:keepall())

all 例子:
select * from ProductOrder as ord
where quantity < all
(select minimumQuantity from MinimumQuantity.win:keepall())

下面查询的输出事件包含的原事件流的所有属性。此外,每个输出
事件包含1个的OrderNamedWindow属性
select *,
(select sum(qty) as sumPrice, count(*) as countRows
from OrderNamedWindow as onw
where onw.client = oe.client) as pastOrderTotals
from OrderEvent as oe

多行选择 例子:
select price,
(select window(orderId) as winorders
from OrderNamedWindow onw
where onw.symbol = md.symbol) as orderIds
from MarketData md


@HintHint 优化方法,指定工作方式

两个或多个事件流组合
select * from TickEvent.std:unique(symbol) as t, NewsEvent.std:unique(symbol) as n
where t.symbol = n.symbol


保留FraudWarningEvent中所有事件和20秒内PINChangeEvent的事件
select * from FraudWarningEvent.win:keepall() as fraud, PINChangeEvent.win:time(20 sec) as pin
where fraud.accountNumber = pin.accountNumber

左联接  例子
select * from RfidEvent.win:time(30 sec) as rfid
left outer join
OrderList.win:length(10000) as orderlist
on rfid.itemId = orderList.itemId


单向连接:

1。单向关键字只能指定一个在FROM子句中的单个流。
2。通过拉API(迭代法)从单向加入接收数据是不允许的。这是因为
引擎认为没有国家提供的事件,执行联接的单流。
3。流的声明单向关键字不能声明一个数据窗口视图或其他视图
流,删除流事件以来的单流处理。

例子:
select count(*) from TickEvent unidirectional, NewsEvent.win:time(10 sec)
where tick.symbol = news.symbol


1、esper 的监听模式
listener和Subscriber 区别:

listener必须实现UpdateListener或StatementUpdateListener 接口,
Subscriber不用实现任何接口和继承任何类;

listener 监听获取到的是EventBean
Subscriber 是自己指定(如Object、String  等)

listener添加方法是 addListener
Subscriber是 addSubscriber

Subscriber获取事件的速度比listeners快(实测)

一个EPStatement只能设置一个Subscriber 监听器
可以设置多个listener监听器
可以设置一个Subscriber 监听器加多个listeners监听器

EPStatement发送事件给Subscriber 监听器是一对一发送
EPStatement发送事件给listeners监听器是一对多

EPStatement发送事件给Subscriber 监听器是 强类型 即有类型
EPStatement发送事件给listeners 监听器是 弱类型  为EventBean  存的是Object

Subscriber 是多线程
listeners 是单线程

同时设置Subscriber 监听器和多个listeners监听器的事件发送优先级:
Subscriber 监听器比listeners监听器高,也就是事件先发送到Subscriber 监听器,再发送到listeners监听器,
如果设置了多个listeners监听器,这些listeners监听器的优先级别为EPStatement实例addListener方法的先后顺序,
越早添加的优先级别越高


2、esper里的时间模式
current time 是当前时间(系统时间)
simulated time 是模拟时间(自己设置)

默认使用current time
设置方法:
Configuration 实例 .getEngineDefaults().getThreading()
      .setInternalTimerEnabled(isDefaultTime)
isDefaultTime boolean
false 使用外部时间(如果要修改时间必须这样写)
true  使用内部时间(不能修改时间)

EPServiceProvider 实例   .getEPRuntime().sendEvent(
    new TimerControlEvent(
      TimerControlEvent.ClockType.CLOCK_EXTERNAL));
 
ClockType.CLOCK_EXTERNAL 使用外部时间(如果要修改时间必须这样写)
ClockType.CLOCK_INTERNAL 使用内部时间(不能修改时间)
在esper中可设置修改模拟时间

EPServiceProvider 实例  .getEPRuntime().sendEvent(new CurrentTimeEvent(time));
time 为要设置的时间的毫秒数,类型 Long


3、esper中的时间窗口
根据指定的epl语言中的win:Time() 获取指定范围的事件

如:

win:time
win:time(5 sec) 滑动窗口,存最近5秒内的数据
单位:年/year  月/month  周/week 天/day 小时/hour 分/min 秒/sec 毫秒/msec
可多项   如:
1 day 2 hour 20 min 15 sec 110 msec

win:ext_timed
win:ext_timed(timestamp,10 sec) 滑动窗口,存最近10秒内的数据,不再基于EPL引擎,而是系统时间戳

win:time_batch
win:time_batch(10 sec) 10秒间隔的窗口
win:time_batch(10 sec,"FORCE_UPDATE, START_EAGER") 加上这两个参数后,会在窗口初始化时就执行查询,并且在没有数据进入和移出时,强制查询出结果

win:time_length_batch
win:time_length_batch(10 sec,5),当时间或数量任意一个满足条件时,触发查询

win:time_accum
win:time_accum(15 sec) 阻塞输出,直到15秒内没有新进入的数据时,才输出并触发查询. 这些数据视为已移出窗口

win:firsttime
win:firsttime(10 sec) 保留窗口初始化后10秒内的所有数据

4、window 的概念和使用方式
相当于集合,esper通过一条或多条epl语句获取事件流,把事件存入到window窗口中,通过esper中定义的取值名称(as  xx)可在窗口中对这些事件进行增删改查


5、
std:firstunique
std:firstunique(id) 相同id的事件只取第一条


6、责任链模式
系统中将会存在多个有类似处理能力的对象。当一个请求触发后,请求将在这些对象组成的链条中传递,直到找到最合适的“责任”对象,并进行处理。
纯的责任链模式,规定一个具体处理者角色只能对请求作出两种动作:自己处理;传给下家。不能出现处理了一部分,把剩下的传给了下家的情况。
而且请求在责任链中必须被处理,而不能出现无果而终的结局。
反之,则就是不纯的责任链模式。

个人理解:
纯的责任链模式太过死板,不够灵活。

例子:
验证码: 
客户端请求验证码——服务器生成记录发送——客户端输入验证码——服务器验证——正确登录,错误返回重新输入

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值