使用Sentinel對訪問端流量控制
阿新 • • 發佈:2018-11-05
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;
}