摘要:不過在出來之后支持異步了,可以把業務操作放到獨立的線程池里面去,這樣可以盡快釋放線程,本身也支持異步了,本篇文章將帶你如何使用的異步特性來改造優化其性能。
? 我們知道spring-cloud-zuul是依賴springMVC來注冊路由的,而springMVC又是在建立在servlet之上的(這里微服務專家楊波老師寫過一篇文章講述其網絡模型,可以參考看看),在servlet3.0之前使用的是thread per connection方式處理請求,就是每一個請求需要servlet容器為其分配一個線程來處理,直到響應完用戶請求,才被釋放回容器線程池,如果后端業務處理比較耗時,那么這個線程將會被一直阻塞,不能干其他事情,如果耗時請求比較多時,servlet容器線程將被耗盡,也就無法處理新的請求了,所以Netflix還專門開發了一個熔斷的組件Hystrix 來保護這樣的服務,防止其因后端的一些慢服務耗盡資源,造成服務不可用。不過在servlet3.0出來之后支持異步servlet了,可以把業務操作放到獨立的線程池里面去,這樣可以盡快釋放servlet線程,springMVC本身也支持異步servlet了,本篇文章將帶你如何使用servlet3.0的異步特性來改造spring-cloud-zuul優化其性能。
? 我們先來創建一個zuul的maven項目,就叫async-zuul吧,具體代碼我放在github上了。項目依賴于consul做注冊中心,啟動時先要在本地啟動consul,為了能看到效果我們先來新建一個zuul的filter類:
@Component public class TestFilter extends ZuulFilter { //忽略無關代碼,具體看github上的源碼 @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); System.out.println("==============線程名稱:" + Thread.currentThread().getName() + ",訪問url:" + request.getRequestURI() + "================"); return null; } }
主要就是打印下線程的名稱,這個filter是zuul的前置過濾器,我們主要就是看下在zuul在執行路由時是由什么線程執行的。好了我們來啟動下main方法,不過我們還需要一個后端服務,很簡單,創建一個springcloud項目名叫book即可,并提供一個url:/book/borrow,啟動后把服務注冊到consul上,成功后我們通過zuul的代理來訪問下book服務:
http://localhost:8080/book/book/borrow
輸出:
==========線程名稱:http-nio-8080-exec-10,訪問url:/book/book/borrow=======
很清楚的看到執行filter的線程是servlet容器線程,等下我們改造成異步后再做一下對比。
? 還記得在文章spring-cloud-zuul原理解析(一)中我們分析到,spring-cloud-zuul的路由映射使用到springMVC的兩大組件ZuulHandlerMapping和ZuulController ,目前肯定是無法支持異步servlet的。那么這兩個類在哪里被加載的呢?答案就是ZuulServerAutoConfiguration,此類是spring-cloud-zuul自動配置類,源碼如下:
@Configuration @ConditionalOnBean(annotation=EnableZuulProxy.class) @EnableConfigurationProperties({ ZuulProperties.class }) @ConditionalOnClass(ZuulServlet.class) @Import(ServerPropertiesAutoConfiguration.class) public class ZuulServerAutoConfiguration { //無關代碼省略.......... @Bean public ZuulController zuulController() { return new ZuulController(); } @Bean public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) { ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController()); mapping.setErrorController(this.errorController); return mapping; } //無關代碼省略.......... }
可以看到這兩個類在spring-cloud-zuul中并沒有為我們提供擴展,沒法替換它們來實現servlet的異步邏輯,那該怎么辦呢?spring-cloud-zuul還有一個自動配置配ZuulProxyAutoConfiguration繼承自ZuulServerAutoConfiguration,我們把這兩個配置類全部替換掉,換成我們自己的不就可以了么?是的,不過首先我們得先排除加載這兩個自動配置類,springboot為我們提供這樣的設置:
@EnableZuulProxy //排除ZuulProxyAutoConfiguration配置類 @SpringBootApplication(exclude=ZuulProxyAutoConfiguration.class) public class Startup { public static void main(String[] args) { SpringApplication.run(Startup.class, args); } }
之后,我們創建兩個自己的配置配,完全拷貝ZuulServerAutoConfiguration和ZuulProxyAutoConfiguration這兩個類,不過光這兩個類還是不行,這兩個類使用到了類RibbonCommandFactoryConfiguration,里面的內部類是protected的,我們沒法使用,也得自己創建,也是拷貝自RibbonCommandFactoryConfiguration,然后我們還需修改ZuulController的邏輯改成異步方式,所以再新建一個類繼承ZuulController,這樣我們就新建了自己的三個配置類和一個自己的ZuulController`類,如下:
public class MyZuulController extends ZuulController{ private final AsyncTaskExecutor asyncTaskExecutor; public MyZuulController(AsyncTaskExecutor asyncTaskExecutor) { super(); this.asyncTaskExecutor = asyncTaskExecutor; } @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { //真正的異步化邏輯 final AsyncContext asyncCtx = request.startAsync(); this.asyncTaskExecutor.execute(new Runnable() { @Override public void run() { try { MyZuulController.this.handleRequestInternal((HttpServletRequest)asyncCtx.getRequest(), (HttpServletResponse)asyncCtx.getResponse()); }catch (Exception e) { e.printStackTrace(); }finally { asyncCtx.complete(); RequestContext.getCurrentContext().unset(); } } }); return null; } } @Configuration @ConditionalOnBean(annotation=EnableZuulProxy.class) @EnableConfigurationProperties({ ZuulProperties.class }) @ConditionalOnClass(ZuulServlet.class) @Import(ServerPropertiesAutoConfiguration.class) public class MyZuulServerAutoConfiguration { //省略代碼,完全拷貝自ZuulServerAutoConfiguration /** * 自定義線程池 * @return */ @Bean public AsyncTaskExecutor zuulAsyncPool() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setThreadNamePrefix("zuul-async-"); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(50); return executor; } //這里換成我們自己的MyZuulController類,并且傳入一個我們自定義的線程池 @Bean public ZuulController zuulController(AsyncTaskExecutor asyncTaskExecutor) { return new MyZuulController(asyncTaskExecutor); } @Bean public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes,AsyncTaskExecutor asyncTaskExecutor) { ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController(asyncTaskExecutor)); mapping.setErrorController(this.errorController); return mapping; } } @Configuration @Import({ MyRibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class, MyRibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class, MyRibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class }) @ConditionalOnBean(annotation=EnableZuulProxy.class) public class MyZuulProxyAutoConfiguration extends MyZuulServerAutoConfiguration { //省略代碼,完全拷貝自ZuulProxyAutoConfiguration } public class MyRibbonCommandFactoryConfiguration { //省略代碼,完全拷貝自RibbonCommandFactoryConfiguration }
這里我們稍作了一點修改
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class) //替換成: @ConditionalOnBean(annotation=EnableZuulProxy.class)
這樣做的目的主要是配合注解@EnableZuulProxy使用,只有開啟了此注解才加載配置類。我們還替換ZuulController成我們自定義的MyZuulController了,這里是異步化的主要邏輯,其實也非常簡單,就是使用了serv3.0為我們提供的api來開啟異步化。萬事已經具備啦,我們再次啟動zuul,訪問上面的url,輸出:
==========線程名稱:zuul-async-1,訪問url:/book/book/borrow==========
哈哈,執行filter的線程變成我們自定義的線程名稱了,達到了我們的需求,servlet已經變成異步的了。
這是我對spring-cloud-zuul實現異步servlet的想法,記錄下來,可能不是最好的實現方式,如果您有更好的方法歡迎留言給我一起探討下!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77109.html
摘要:大揭秘目標了解的新特性,以及版本升級的引導。四元數據改造我們知道以前的版本只有注冊中心,注冊中心的有數十個的鍵值對,包含了一個服務所有的元數據。 DUBBO——2.7大揭秘 目標:了解2.7的新特性,以及版本升級的引導。 前言 我們知道Dubbo在2011年開源,停止更新了一段時間。在2017 年 9 月 7 日,Dubbo 悄悄的在 GitHub 發布了 2.5.4 版本。隨后,版本...
摘要:之旅簡化開發的使命簡化開發為了降低開發的復雜性,采取如下關鍵策略基于的輕量級和最小侵入性編程通過依賴注入和面向接口實現松耦合基于切面和慣例進行聲明式編程通過切面和模版減少樣式代碼依賴注入耦合性具有兩面性一方面,緊密耦合的代碼難以測試難以復 Spring之旅 簡化Java開發 Spring的使命:簡化Java開發 為了降低Java開發的復雜性,采取如下關鍵策略:基于POJO的輕量級和最...
摘要:是開源的微服務網關,它可以和,等組件配合使用,網上也有很多如何使用的文章,我們也在生產環境使用了,所以讀了下的源碼,下面把它分享出來,與大家探討下核心原理。 Zuul是Netflix開源的微服務網關,它可以和Eureka,consul,Ribbon,Hystrix等組件配合使用,網上也有很多如何使用zuul的文章,我們也在生產環境使用了,所以讀了下zuul的源碼,下面把它分享出來,與大...
摘要:前言在體系中扮演著統一網關的角色,負責與外部交互。與結合使用,可以根據服務名來訪問后端的服務,對于而言,也是一個。這段代碼表示,如果請求中沒有信息,就會報錯。 前言 Zuul在Spring Cloud 體系中扮演著統一網關的角色,負責與外部交互。用戶可以通過不同的URL特征來訪問不同的后端服務,類似于Nginx代理的效果。Zuul與Eureka結合使用,可以根據服務名來訪問后端的服務,...
摘要:最近對服務器推送技術比較感興趣,在網上也看了好些文章,由于每個人理解的不同,實現細節或者語言表達方式不同,本人被各種名詞或者技術實現搞的頭大,于是自己準備整理下。定時器就可以實現,每次請求如果服務器端有更新數據則響應到客戶端。 最近對服務器推送技術比較感興趣,在網上也看了好些文章,由于每個人理解的不同,實現細節或者語言表達方式不同,本人被各種名詞或者技術實現搞的頭大,于是自己準備整理下...
閱讀 671·2023-04-25 18:59
閱讀 1211·2021-09-22 16:00
閱讀 1889·2021-09-22 15:42
閱讀 3594·2021-09-22 15:27
閱讀 1246·2019-08-30 15:54
閱讀 1104·2019-08-30 11:16
閱讀 2445·2019-08-29 16:24
閱讀 820·2019-08-29 12:14