使用 esper 简易实现限流功能

 <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 中,这样就简单实现了一个还算像模像样的限流功能

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值