服務網關之Zuul

•Zuul能夠經過加載動態過濾機制,從而實現如下各項功能:spring

•驗證與安全保障: 識別面向各種資源的驗證要求並拒絕那些與要求不符的請求。後端

•審查與監控: 在邊緣位置追蹤有意義數據及統計結果,從而爲咱們帶來準確的生產狀態結論。api

•動態路由: 以動態方式根據須要將請求路由至不一樣後端集羣處。緩存

•壓力測試: 逐漸增長指向集羣的負載流量,從而計算性能水平。安全

•負載分配: 爲每一種負載類型分配對應容量,並棄用超出限定值的請求。併發

•靜態響應處理: 在邊緣位置直接創建部分響應,從而避免其流入內部集羣。app

•多區域彈性: 跨越AWS區域進行請求路由,旨在實現ELB使用多樣化並保證邊緣位置與使用者儘量接近。socket

配置以下ide

zuul.ignored-services=*

zuul.routes.api-service.path=/api/**
zuul.routes.api-service.serviceId=api

zuul.sensitiveHeaders=Authorization

zuul.semaphore.max-semaphores=1000
zuul.host.maxTotalConnections=1000
zuul.host.maxPerRouteConnections=500
zuul.SendResponseFilter.post.disable=false
zuul.SendErrorFilter.post.disable=true
#默認1000
zuul.host.socket-timeout-millis=3000
#默認2000
zuul.host.connect-timeout-millis=10000
zuul.SendResponseFilter.post.disable=false
zuul.SendErrorFilter.post.disable=true

 

Zuul 是ZuulFilter鏈來實現的,分別有pre,routing,post,error ZuulFilter,具體類在微服務

這裏主要看下RibbonRoutingFilter實現

@Override
	public Object run() {
		RequestContext context = RequestContext.getCurrentContext();
		this.helper.addIgnoredHeaders();
		try {
			RibbonCommandContext commandContext = buildCommandContext(context);
			ClientHttpResponse response = forward(commandContext);
			setResponse(response);
			return response;
		}
		catch (ZuulException ex) {
			context.set(ERROR_STATUS_CODE, ex.nStatusCode);
			context.set("error.message", ex.errorCause);
			context.set("error.exception", ex);
		}
		catch (Exception ex) {
			context.set("error.status_code",
					HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			context.set("error.exception", ex);
		}
		return null;
	}

這裏會看到有個RequestContext context = RequestContext.getCurrentContext(); 該context來源於pre階段的初始,例如Servlet30WrapperFilter

@Override
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		if (request instanceof HttpServletRequestWrapper) {
			request = (HttpServletRequest) ReflectionUtils.getField(this.requestField,
					request);
			ctx.setRequest(new Servlet30RequestWrapper(request));
		}
		else if (RequestUtils.isDispatcherServletRequest()) {
			// If it's going through the dispatcher we need to buffer the body
			ctx.setRequest(new Servlet30RequestWrapper(request));
		}
		return null;
	}

,下面看下RibbonCommandContext 這裏封裝了個ribbo的context 說明zuul默認是有集成ribbo,,下面繼續看forward(。。。)方法,

RibbonCommand command = this.ribbonCommandFactory.create(context);
		try {
			ClientHttpResponse response = command.execute();
			this.helper.appendDebug(info, response.getStatusCode().value(),
					response.getHeaders());
			return response;
		}
		catch (HystrixRuntimeException ex) {
			return handleException(info, ex);
		}

第一步建立RibboCommand  他繼承了HystrixExecutable 說明Zuul也集成了Hystrix,能夠看到Zuul最後也是經過HttpCient 或者OkHttp RestClient 發起轉發請求,

默認Zuul是使用HttpClient ,那咱們來看下HttpClientRibboCommandFactory怎麼建立HystrixCommand的

@Override
	public HttpClientRibbonCommand create(final RibbonCommandContext context) {
		ZuulFallbackProvider zuulFallbackProvider = getFallbackProvider(context.getServiceId());
		final String serviceId = context.getServiceId();
		final RibbonLoadBalancingHttpClient client = this.clientFactory.getClient(
				serviceId, RibbonLoadBalancingHttpClient.class);
		client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));

		return new HttpClientRibbonCommand(serviceId, client, context, zuulProperties, zuulFallbackProvider);
	}

Hystrix 是否是支持fallback,既然Zuul集成了Hystrix 是否也能夠實現FallBack,那咱們看下下面這段代碼

ZuulFallbackProvider zuulFallbackProvider = getFallbackProvider(context.getServiceId());

 這裏的ZuulFallbackProvider 看似應該跟fallback有關係,那咱們證明下在RibboCommand裏有斷代碼

@Override
	protected ClientHttpResponse getFallback() {
		if(zuulFallbackProvider != null) {
			return zuulFallbackProvider.fallbackResponse();
		}
		return super.getFallback();
	}

這是實現HystrixCommand的getFallback由此能夠判定zuulFallbackProvider.fallbackResponse();提供了fallback方法,那就簡單了 咱們只須要建立個ZuulFallbackProvider就能實現Zuul端fallback;

咱們再看下ZuulFallbackProvider 具體代碼

並無找到它的實現類那咱們須要建立個fallbackprovider 裏面有兩個方法,一個是獲取路由名稱,一個是獲取ClientHttpRreponse;那這兩個方法有什麼用,從AbstractRibbonCommandFactory能夠看到
fallbackProviderCache.put(provider.getRoute(), provider);經過service爲Key緩存在
fallbackProviderCache中,官方說經過*能夠通用一個provider目前並未發現有這樣的代碼,具體例子

@Bean
	public ZuulFallbackProvider buildFallbackProvider(){
		return new ZuulFallbackProvider() {
			
			@Override
			public String getRoute() {
				// TODO Auto-generated method stub
				return null;
			}
			
			@Override
			public ClientHttpResponse fallbackResponse() {
				// TODO Auto-generated method stub
				return null;
			}
		};
	}

Ok 進入下一步return new HttpClientRibbonCommand(serviceId, client, context, zuulProperties, zuulFallbackProvider); 幹嗎的不用說了吧 ,看下參數Client RibbonLoadBalancingHttpClient 實現LB,Ribbo 與Hystrix 再也不細說,後面章節會重點敘述;

ClientHttpResponse response = command.execute(); 這就是經過熔斷器,而後LB實現請求service響應結果;並將結果保存RequestContext中, 到目前爲止RibbonRoutingFilter已經處理完

接下來看下一個的ZuulFilter SendResponseFilter 字面就能夠看出來就是發送結果,題外話爲何說說這個類重要 由於在這個類 咱們能夠作不少事。好比 在此處作統一響應處理,異常處理等,

咱們來梳理下流程

首先pre階段初始化請求數據,如頭部,參數,url 映射,route 轉發service ,post  響應結果至用戶;

流程很簡單也很清晰

轉述下優化策略 ttp://www.tuicool.com/articles/aMRnIfr

 

zuul 內置參數

zuul.host.maxTotalConnections

適用於ApacheHttpClient,若是是okhttp無效。每一個服務的http客戶端鏈接池最大鏈接,默認是200.

zuul.host.maxPerRouteConnections

適用於ApacheHttpClient,若是是okhttp無效。每一個route可用的最大鏈接數,默認值是20。

zuul.semaphore.max-semaphores

Hystrix最大的併發請求 execution.isolation.semaphore.maxConcurrentRequests ,這個值並不是TPS 、 QPS 、 RPS 等都是相對值,指的是1秒時間窗口內的事務/查詢/請求, semaphore.maxConcurrentRequests 是一個絕對值,無時間窗口,至關於亞毫秒級的。當請求達到或超過該設置值後,其其他就會被拒絕。默認值是100。參考: Hystrix semaphore和thread隔離策略的區別及配置參考

這個參數原本直接能夠經過Hystrix的命名規則來設置,但被zuul從新設計了,使得在zuul中semaphores的最大併發請求有4個方法的參數能夠設置,若是4個參數都存在優先級(1~4)由高到低:

  • [優先級1]zuul.eureka.api.semaphore.maxSemaphores
  • [優先級2]zuul.semaphore.max-semaphores
  • [優先級3]hystrix.command.api.execution.isolation.semaphore.maxConcurrentRequests
  • [優先級4]hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests

須要注意的是:在Camden.SR3版本的zuul中 hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests 設置不會起做用,這是由於在 org.springframework.cloud.netflix.zuul.filters.ZuulProperties.HystrixSemaphore.maxSemaphores=100 設置了默認值100,所以 zuul.semaphore.max-semaphores 的優先級高於 hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests 。

zuul.eureka.[commandKey].semaphore.maxSemaphores:

其中commandKey爲

參考設置參數:

#
zuul.host.maxTotalConnections: 200
zuul.host.maxPerRouteConnections: 10
#zuul.semaphore.max-semaphores: 128
# 建議使用這種方式來設置,能夠給每一個不一樣的後端微服務設置不一樣的信號量
zuul.eureka.[service id].semaphore.maxSemaphores: 128

其餘Hystrix參數:

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 用來設置thread和semaphore兩種隔離策略的超時時間,默認值是1000。

  • 建議設置這個參數,在Hystrix 1.4.0以前,semaphore-isolated隔離策略是不能超時的,從1.4.0開始semaphore-isolated也支持超時時間了。
  • 建議經過CommandKey設置不一樣微服務的超時時間,對於zuul而言,CommandKey就是service id: hystrix.command.[CommandKey].execution.isolation.thread.timeoutInMilliseconds

ribbon參數

ribbon:
#  # Max number of next servers to retry (excluding the first server)
#  MaxAutoRetries: 1
#  # Whether all operations can be retried for this client
#  MaxAutoRetriesNextServer: 1
#  # Interval to refresh the server list from the source
#  OkToRetryOnAllOperations: true
#  # Interval to refresh the server list from the source
#  ServerListRefreshInterval: 2000
#  # Connect timeout used by Apache HttpClient
  ConnectTimeout: 3000
#  # Read timeout used by Apache HttpClient
  ReadTimeout: 3000

主要是 ConnectTimeout 和 ReadTimeout 2個參數,最終會設置到http Client中

相關文章
相關標籤/搜索