差異處

這裏顯示兩個版本的差異處。

連向這個比對檢視

java:apache_camel:throttler:helloworld [2019/03/17 20:38]
tony 建立
java:apache_camel:throttler:helloworld [2023/06/25 09:48]
行 1: 行 1:
-{{tag>​camel}} 
-====== Camel - Throttler Hello World ====== 
-===== Introduction ===== 
-在設計web應用程式時,有時會需要一個節流器,去幫我控制單位時間內能處理的請求數量,以避免過載;又或者是要根據不同使用者所買的授權,去控制單位時間內能呼叫的API次數等。Camel提供了Throttler,讓我們能輕鬆透過設定,去達到這些效果。我將透過HTTP GET請求/​events/​{id}做為範例,說明如何使用Throttler。首先介紹這個範例中的兩個RouteBuilder。 
-===== RestRouteBuilder ===== 
-REST核心設定集中在這個builder中,它負責宣告用什麼port與component去建立REST服務:​ 
-<code java> 
-package org.tonylin.practice.camel.rest;​ 
  
-import org.apache.camel.builder.RouteBuilder;​ 
-import org.apache.camel.model.rest.RestBindingMode;​ 
- 
-public class RestRouteBuilder extends RouteBuilder {  
-  
- @Override 
- public void configure() throws Exception { 
- restConfiguration().component("​netty4-http"​).port(8080).bindingMode(RestBindingMode.auto).endpointProperty("​ssl", ​ "​false"​);​ 
- } 
-} 
-</​code>​ 
-(我以http當範例,如果對https用法有興趣,可以參考[[java:​apache_camel:​rest_with_netty_http_and_ssl|這篇]]) 
-===== ThrottlerRouteBuilder ===== 
-接下來是今天的主角,我先列出程式碼內容,後面再針對重點configure做說明:​ 
-<code java> 
-package org.tonylin.practice.camel.throttler;​ 
- 
-import static com.google.common.base.Preconditions.checkState;​ 
- 
-import org.apache.camel.Exchange;​ 
-import org.apache.camel.Processor;​ 
-import org.apache.camel.builder.RouteBuilder;​ 
-import org.apache.camel.processor.ThrottlerRejectedExecutionException;​ 
-import org.slf4j.Logger;​ 
-import org.slf4j.LoggerFactory;​ 
- 
-public class ThrottlerRouteBuilder extends RouteBuilder { 
- private static Logger logger = LoggerFactory.getLogger(ThrottlerRouteBuilder.class);​ 
- private final static String GET_EVENTS = "​GET_EVENTS";​ 
-  
- private Object eventHandler;​ 
-  
- private int limit = 2; 
- private int period = 200; 
-  
- public ThrottlerRouteBuilder(Object eventHandler) { 
- this.eventHandler = eventHandler;​ 
- 
- } 
-  
- public void setLimit(int limit) { 
- this.limit = limit; 
- } 
-  
- public void setPeriod(int period) { 
- this.period = period; 
- } 
-  
- @Override 
- public void configure() throws Exception { 
- checkState(eventHandler!=null,​ "​Can'​t find eventHandler"​);​ 
-  
- onException(ThrottlerRejectedExecutionException.class) 
- .process(new Processor() { 
- @Override 
- public void process(Exchange exchange) throws Exception { 
- logger.debug("​handle ThrottlerRejectedExecutionException"​);​ 
- exchange.getOut().setHeader(Exchange.HTTP_RESPONSE_CODE,​ "​503"​);​ 
- } 
- }) 
- .handled(true);​ 
-  
- rest("/​events/​{id}"​).get().route().id(GET_EVENTS) 
- .throttle(limit) 
- .timePeriodMillis(period) 
- .rejectExecution(true) 
- .bean(eventHandler).endRest();​ 
- } 
-} 
-</​code>​ 
-我首要說明的是throttler的configure:​ 
-<code java> 
-rest("/​events/​{id}"​).get().route().id(GET_EVENTS) 
- .throttle(limit) 
- .timePeriodMillis(period) 
- .rejectExecution(true) 
- .bean(eventHandler).endRest();​ 
-</​code>​ 
-除了HTTP GET的宣告外,這些程式碼代表著以下意義:​ 
-  * throttle(limit):​ 限制的存取次數。 
-  * timePeriodMillis(period):​ 限制存取次數的單位時間,預設是1000ms。 
-  * rejectExecution(true):​ 當超過此限制時,是否要reject請求,預設為false。假如沒reject,後續超過限制的請求會block至單位時間後執行。 
-  * bean(eventHandler):​ 請求的處理者。 
-在我設定rejectExecution為true後,我發現camel會拋出ThrottlerRejectedExecutionException,且client會block住;因此這個設定必須與camel的errorHandler一同使用,這是我的使用範例:​ 
-<code java> 
-onException(ThrottlerRejectedExecutionException.class) 
- .process(new Processor() { 
- @Override 
- public void process(Exchange exchange) throws Exception { 
- logger.debug("​handle ThrottlerRejectedExecutionException"​);​ 
- exchange.getOut().setHeader(Exchange.HTTP_RESPONSE_CODE,​ "​503"​);​ 
- } 
- }) 
-.handled(true);​ 
-</​code>​ 
-我宣告當發生ThrottlerRejectedExecutionException例外時,會將回應給client的response code設為503以代表server過載。除此之外,別忘記把handled設為true,代表例外已被處理。 
-===== Unit Test ===== 
-最後我以單元測試來展示效果,包含testOverload與testThrottlePeriod兩個測試;而throttler的limit為2,period為200ms,testcase會在後面做說明:​ 
-<code java> 
-package org.tonylin.practice.camel.throttler;​ 
- 
-import java.util.ArrayList;​ 
-import java.util.List;​ 
- 
-import org.apache.camel.RoutesBuilder;​ 
-import org.apache.camel.test.junit4.CamelTestSupport;​ 
-import org.apache.http.HttpResponse;​ 
-import org.apache.http.client.HttpClient;​ 
-import org.apache.http.client.methods.HttpGet;​ 
-import org.apache.http.impl.client.HttpClientBuilder;​ 
-import org.junit.Test;​ 
-import org.tonylin.practice.camel.rest.RestHandler;​ 
-import org.tonylin.practice.camel.rest.RestRouteBuilder;​ 
- 
-public class ThrottlerRouteBuilderTest extends CamelTestSupport { 
- 
- private RestHandler hander = new RestHandler();​ 
- private static final int limit = 2; 
- private static final int period = 200; 
-  
- private HttpClient client = HttpClientBuilder.create().build();​ 
- private HttpGet httpGet = new HttpGet("​http://​localhost:​8080/​events/​123"​);​ 
-  
- @Override 
- protected RoutesBuilder[] createRouteBuilders() throws Exception { 
- ThrottlerRouteBuilder throttlerRouteBuilder = new ThrottlerRouteBuilder(hander);​ 
- throttlerRouteBuilder.setLimit(limit);​ 
- throttlerRouteBuilder.setPeriod(period);​ 
-  
- return new RoutesBuilder[] { 
- new RestRouteBuilder(),​ 
- throttlerRouteBuilder 
- }; 
- } 
-  
- private List<​HttpResponse>​ batchRequest(int times) throws Exception { 
- List<​HttpResponse>​ responses = new ArrayList<​HttpResponse>​();​ 
- for( int i = 0 ; i < times ; i++ ) { 
- responses.add(client.execute(httpGet));​ 
- } 
- return responses; 
- } 
-  
- @Test 
- public void testOverload() throws Exception { 
- // skip 
- } 
- 
- @Test 
- public void testThrottlePeriod() throws Exception ​ { 
- // skip 
- } 
-} 
-</​code>​ 
-測試中使用的RestHandler,負責收集請求的event id,用以確認請求內容是否正確:​ 
-<code java> 
-public class RestHandler { 
- private static Logger logger = LoggerFactory.getLogger(RestHandler.class);​ 
- private List<​String>​ requestIds = new ArrayList<​String>​();​ 
-  
- @Handler 
- public void handle(Exchange exchange) { 
- String requestId = exchange.getIn().getHeader("​id",​ String.class);​ 
- logger.debug("​Request id: {}", requestId); 
- requestIds.add(requestId);​ 
- } 
-  
- public List<​String>​ getRequestIds(){ 
- return requestIds; 
- } 
-} 
-</​code>​ 
-針對testOverload測試,是用來確認throttler單位時間內的請求是否有效;因此測試中,連續做了3次請求,最後會去確認這三次的請求結果是否正確:​ 
-<code java> 
-@Test 
-public void testOverload() throws Exception { 
- // when request 3 times 
- List<​HttpResponse>​ responses = batchRequest(3);​ 
- 
- // then 
- assertEquals(2,​ hander.getRequestIds().size());​ 
- assertEquals(200,​ responses.get(0).getStatusLine().getStatusCode());​ 
- assertEquals(200,​ responses.get(1).getStatusLine().getStatusCode());​ 
- assertEquals(503,​ responses.get(2).getStatusLine().getStatusCode());​ 
-} 
-</​code>​ 
-而testThrottlePeriod測試,用以確認throttler單位時間是否有作用;因此測試中,會先發3次請求,再等待此單位時間後,再發3次請求。最後確認請求結果:​ 
-<code java> 
-@Test 
-public void testThrottlePeriod() throws Exception ​ { 
- // when 
- List<​HttpResponse>​ responses = batchRequest(3);​ 
- Thread.sleep(period+1);​ 
-  
- responses.addAll(batchRequest(3));​ 
-  
- // then 
- assertEquals(2,​ hander.getRequestIds().size());​ 
- assertEquals(200,​ responses.get(0).getStatusLine().getStatusCode());​ 
- assertEquals(200,​ responses.get(1).getStatusLine().getStatusCode());​ 
- assertEquals(503,​ responses.get(2).getStatusLine().getStatusCode());​ 
- assertEquals(200,​ responses.get(3).getStatusLine().getStatusCode());​ 
- assertEquals(200,​ responses.get(4).getStatusLine().getStatusCode());​ 
- assertEquals(503,​ responses.get(5).getStatusLine().getStatusCode());​ 
-} 
-</​code>​ 
-透過這兩個測試範例,我們可以簡單地了解throttler的用法。 
-===== Library Info ===== 
-以下是我在寫這篇文章時,所使用的libraries版本:​ 
-<​code>​ 
-ext { 
- camelVersion='​2.23.1'​ 
- nettyAllVersion='​4.1.34.Final'​ 
- guavaVersion='​27.1-jre'​ 
- log4jVersion='​1.2.17'​ 
- slf4jVersion='​1.7.26'​ 
- httpClientVersion='​4.5.7'​ 
-} 
- 
-dependencies { 
-    compile group: '​org.apache.camel',​ name: '​camel-core',​ version: "​$camelVersion"​ 
-    compile group: '​org.apache.camel',​ name: '​camel-netty4-http',​ version: "​$camelVersion"​ 
-    compile group: '​org.apache.camel',​ name: '​camel-http-common',​ version: "​$camelVersion"​ 
-    compile group: '​org.apache.camel',​ name: '​camel-netty4',​ version: "​$camelVersion"​ 
-    compile group: '​io.netty',​ name: '​netty-all',​ version: "​$nettyAllVersion"​ 
-    compile group: '​com.google.guava',​ name: '​guava',​ version: "​$guavaVersion"​ 
-    compile group: '​log4j',​ name: '​log4j',​ version: "​$log4jVersion"​ 
-    compile group: '​org.slf4j',​ name: '​slf4j-api',​ version: "​$slf4jVersion"​ 
-    runtime group: '​org.slf4j',​ name: '​slf4j-log4j12',​ version: "​$slf4jVersion"​ 
-    testCompile group: '​org.apache.camel',​ name: '​camel-test',​ version: "​$camelVersion"​ 
-    testCompile group: '​org.apache.httpcomponents',​ name: '​httpclient',​ version: "​$httpClientVersion"​ 
-    testCompile '​junit:​junit:​4.12'​ 
-} 
-</​code>​ 
-===== Reference ===== 
-  * [[http://​camel.apache.org/​throttler.html|Camel - Throttler]] 
- 
-=====    ===== 
----- 
-\\ 
-~~DISQUS~~