<dependency>
<groupId>com.espertech</groupId>
<artifactId>esper</artifactId>
<version>5.4.0</version>
</dependency>
@SpringBootApplication
@Slf4j
public class BootApplication {
@Bean
public EPServiceProvider buildEPServiceProvider(){
EPServiceProvider epServiceProvider = EPServiceProviderManager.getDefaultProvider();
return epServiceProvider;
}
public static void main(String[] args) {
// 省略...
}
}
@RestController
@RequestMapping( "/test" )
public class TestController {
@Autowired
private EPServiceProvider epServiceProvider;
private EPRuntime epRuntime;
@PostConstruct
private void init(){
this.epRuntime = this.epServiceProvider.getEPRuntime();
}
@GetMapping( "/request" )
public String request(HttpServletRequest request){
String fromIp = request.getRemoteAddr();
Long requestTime = OneRequestUpdateListener.ip_requestTime_map.get(fromIp);
if( requestTime != null && requestTime.longValue() >= 10L ){
return "请求太频繁,5秒钟内最多请求9次";
}
OneRequest oneRequest = new OneRequest();
oneRequest.setFromIp( fromIp );
oneRequest.setTime( System.currentTimeMillis() );
this.epRuntime.sendEvent( oneRequest );
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date = sdf.format(new Date());
// todo 业务代码
return "请求成功: " + date;
}
}
import com.espertech.esper.client.ConfigurationOperations;
import com.espertech.esper.client.EPAdministrator;
import com.espertech.esper.client.EPServiceProvider;
import com.espertech.esper.client.EPStatement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.lang.reflect.ParameterizedType;
import java.util.HashSet;
import java.util.Set;
@Component
public class EsperStarter {
@Autowired
private EPServiceProvider epServiceProvider;
@PostConstruct
private void init(){
String[] beanNames = SpringContextUtils.getBeanNames(BaseUpdateListener.class);
if( beanNames == null || beanNames.length == 0 ){
return;
}
EPAdministrator epAdministrator = this.epServiceProvider.getEPAdministrator();
ConfigurationOperations configuration = epAdministrator.getConfiguration();
Set<Class> eventTypes = new HashSet<>();
for(String beanName:beanNames ){
BaseUpdateListener updateListener = SpringContextUtils.getBean(beanName, BaseUpdateListener.class);
ParameterizedType type= (ParameterizedType) updateListener.getClass().getGenericSuperclass();
Class eventTypeClass = (Class) type.getActualTypeArguments()[0];
eventTypes.add( eventTypeClass );
}
for( Class eventType:eventTypes ){
configuration.addEventType( eventType );
}
for(String beanName:beanNames ){
BaseUpdateListener updateListener = SpringContextUtils.getBean(beanName, BaseUpdateListener.class);
EPStatement epStatement = epAdministrator.createEPL(updateListener.getEpl());
epStatement.addListener( updateListener );
}
}
}
import com.espertech.esper.client.UpdateListener;
public abstract class BaseUpdateListener<EventType> implements UpdateListener {
public abstract String getEpl();
}
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class OneRequest {
private String fromIp;
private long time;
}
import com.espertech.esper.client.EventBean;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component( "oneRequestUpdateListener" )
public class OneRequestUpdateListener extends BaseUpdateListener<OneRequest>{
public static Map<String,Long> ip_requestTime_map = new ConcurrentHashMap<>();
@Override
public String getEpl() {
// 用于限流统计滑动( 动态 )时间窗口内的请求次数的 epl
// select fromIp,count( time ) as requestTime from OneRequest.win:time( 5sec ) group by fromIp
// 用于限流统计固定时间窗口内的请求次数的 epl
return "select fromIp,count( time ) as requestTime from OneRequest.win:time_batch( 5sec ) group by fromIp";
}
@Override
public void update(EventBean[] newEvents, EventBean[] oldEvents) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String now = sdf.format(new Date());
int len = newEvents.length;
for( int i = 0;i < len;i++ ){
EventBean eventBean = newEvents[ i ];
String fromIp = (String) eventBean.get("fromIp");
Long requestTime = (Long) eventBean.get("requestTime");
ip_requestTime_map.put( fromIp,requestTime );
System.out.println( now + ": " + fromIp + " 请求 " + requestTime + " 次!" );
}
}
}
可以把请求次数校验逻辑和 sendEvent 代码放在 顾虑器、拦截器或者 AOP 中,这样就简单实现了一个还算像模像样的限流功能