1. 程式人生 > >使用Sentinel對訪問端流量控制

使用Sentinel對訪問端流量控制

from:http://blog.abreaking.com

訪問端流量控制

博主面臨的一個問題是,如何對該esb的呼叫者呼叫某個服務的次數進行控制。

也就是說:對於服務A,每個呼叫者呼叫該服務單位時間內呼叫次數不能超過閾值;反過來也一樣,對於呼叫者,呼叫任意服務單位時間內的呼叫次數不能超過閾值。

那麼此時,根據訪問端的ip地址來標識某個呼叫源 origin,用服務的介面名srvName來標識呼叫的那個服務。

對於sentinel最基本的用法,參考:

https://mp.csdn.net/postedit/82116993

加入依賴

同樣的,需要加入sentinel依賴

<dependency>

    <groupId>com.alibaba.csp</groupId>

    <artifactId>sentinel-core</artifactId>

</dependency>

定義規則

那麼此時,規則就不止一個了。我們改變一下定義規則的方法:

public  void addRule(String resource,String origin){
    List<FlowRule> rules = FlowRuleManager.getRules();
    if(rules==null){
        rules = new ArrayList<FlowRule>();
    }
    FlowRule rule = new FlowRule();
    rule.setResource(resource); //資源名
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS); //限流閾值型別,此處為qps型別
    rule.setCount(8);   //限流閾值,表示每秒鐘通過n次請求
    rule.setLimitApp(origin);   //對呼叫端進行控制
    rules.add(rule);
    FlowRuleManager.loadRules(rules);
}

如上,如果有新的規則, 直接放在FlowRuleManager中的規則List中去。限流閾值等先是寫死的。

定義資源resource,呼叫端origin

為了模擬呼叫者呼叫那個服務這種情況,博主使用一種簡單的方式來模擬:

通過url請求帶引數來模擬,比如:客戶端訪問:http://127.0.0.1:8080/cc?srvName=hello。就表示:ip為127.0.0.1的呼叫端呼叫hello這個方法。

首先,需要定義好resource,origin。

String host = request.getRemoteHost();
String srvName = request.getParameter("srvName");
String resource = host+srvName;
String origin = host;

而後,就是初始化規則的問題。此時注意,並不是每次請求過來都要進行初始化,導致程式變慢不說,這一次的請求如果與上一次的請求不同源,會將上一次定義的規則給覆蓋掉。

所以,定義一個List<String> resources存放resource,只有不同的呼叫端第一次請求時,才會新增新的規則到規則列表中。

if(!resources.contains(resource)){

    synchronized(resources){

        if(!resources.contains(resource)){

            addNewRule4NewOrigin(resource,origin);

            resources.add(resource);

        }

    }

}

 

整體Demo

最後,使用包圍住需要進行流量控制程式碼塊的。

@RequestMapping("/cc")
    public String controlClient(HttpServletRequest request){
        String host = request.getRemoteHost();
        String srvName = request.getParameter("srvName");
        String resource = host+srvName;
        String origin = host;

        if(!resources.contains(resource)){
            synchronized(resources){
                /**by liwei
                 * springmvc是單例模式,這裡就沒必要進行雙重校驗,
                 * 如果是非單例的servlet,要加上雙重校驗,防止重複添加了規則
                 */

                if(!resources.contains(resource)){
                    addNewRule4NewOrigin(resource,origin);
                    resources.add(resource);
                }
            }
        }
        String response = "";
        Entry entry = null;
        try {
            ContextUtil.enter(resource,origin);
            entry = SphU.entry(resource);
            //我的程式碼塊
            response = execSrv(resource);
        } catch (BlockException e1) {
            e1.printStackTrace();   //直接將異常打出來
            response = resource+e1.toString();
        } finally {
            if (entry != null) {
                entry.exit();
            }
            ContextUtil.exit();
        }
        return response;
    }

測試

寫個測試方法,

@Test
    public void test02() throws InterruptedException {
        String host1 = "127.0.0.1";
        String host2 = "192.168.236.1";
        String srv = "hello";
        for (int i = 0; i < 5; i++) {
            String url = "http://"+host1+":8080/cc?srvName="+srv+i;
            new Thread(new MyRunnable(url)).start();
        }
        for (int i = 0; i < 5; i++) {
            String url = "http://"+host2+":8080/cc?srvName="+srv+i;
            new Thread(new MyRunnable(url)).start();
        }
        Thread.sleep(2000);
    }
    class MyRunnable implements Runnable{
        String url;
        public MyRunnable(String url){
            this.url = url;
        }
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                String s = doPost(url);
                System.out.println(s);
            }
        }
    }

    private String doPost(String url) {
        HttpClient client = new DefaultHttpClient();
        //傳送get請求
        HttpGet request = new HttpGet(url);
        try {
            HttpResponse response = client.execute(request);
            String strResult = EntityUtils.toString(response.getEntity());
            return strResult;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }