有一點上篇文章忘了 講述,nacos的加載優先級別最高。服務啓動優先拉去配置信息。因此上一篇服務搭建我沒有講述在nacos 中心建立的配置文件
能夠看到服務端口和註冊中心都在配置文件中配置化java
上一篇咱們講了如何搭建網關zuul 服務。實現了基本的轉發功能。這篇文章咱們要講述zuul過濾器的使用。和三個參數的使用git
ribbon-isolation-strategygithub
隔離機制 Spring Cloud 默認的隔離機制hystrix 去作限流、熔斷、隔離。一個是基於信號量。一個是基於線程池去完成web
ignored-servicesspring
ignored—patternsapache
ignored-headersjson
sensitiveHeaderssegmentfault
在上面ignored-headers 和 sensitiveHeaders 職責是有重疊的部分。其實他們是有着關係存在的。sensitiveHeaders的內容會最終合併到ignored-headers內。安全
驗證一下 sensitiveHeaders 字段設置全區忽略 X-ABC 服務器
消費者服務 sensitiveHeaders 指定忽略 X-ABC,Authorization
服務提供者轉發不作任何調整
在服務提供者 和 服務消費者 服務上。都建立一個 UrlFilter 攔截器
先設置一下 zuul的yml文件
zuul: host: # 目標主機的最大鏈接數,默認值爲200 max-total-connections: 500 # 每一個主機的初始鏈接數,默認值爲20 max-per-route-connections: 100 routes: discovery-server: path: /server/** serviceId: cloud-discovery-server client-common: path: /client/** serviceId: cloud-discovery-client sensitiveHeaders: X-ABC,X-Foo # 全部路由的默認Hystrix隔離模式(ExecutionIsolationStrategy)爲SEMAPHORE。若是此隔離模式是首選,則zuul.ribbonIsolationStrategy能夠更改成THREAD ribbon-isolation-strategy: thread # 這個屬性意思,* 忽略未指定的服務列表 有具體服務名稱 忽略指定的服務 ignored-services: "*" # 忽略指定的url 路徑 ignored—patterns: # 忽略指定的header 頭部信息 ignored-headers: Authorization # 字段比較敏感,不但願傳遞給下游微服務。 設置空沒有要忽略的敏感字段。所有傳給下游服務 sensitive-headers: X-ABC ribbon: eager-load: # 強制加載,不設置會進行懶加載。spring 第一次請求會很是慢 enabled: true
package com.xian.cloud.filter; import lombok.extern.slf4j.Slf4j; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Enumeration; /** * <Description> * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/29 13:57 */ @WebFilter(filterName = "test",urlPatterns = "/*") @Slf4j public class UrlFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { log.warn("UrlFilter init......."); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { Enumeration<String> attributeNames = servletRequest.getAttributeNames(); HttpServletRequest req = (HttpServletRequest) servletRequest; String requestURI = req.getRequestURI(); String header = req.getHeader("X-Foo"); String abc = req.getHeader("X-ABC"); String authorization = req.getHeader("Authorization"); String tom = req.getParameter("tom"); String mike = req.getParameter("mike"); log.warn("過濾器:請求地址"+requestURI); log.warn("uuid:{}",header); log.warn("abc uuid:{}",abc); log.warn("authorization :{}",authorization); log.warn("tom :{}",tom); log.warn("mike :{}",mike); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { log.warn(" 過濾器被銷燬"); } }
啓動類上添加 @ServletComponentScan 用於掃描 過濾器 @WebFilter註解
package com.xian.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * @Author: xlr * @Date: Created in 2:44 PM 2019/10/27 */ @EnableDiscoveryClient @SpringBootApplication //新增註解 @ServletComponentScan public class DiscoveryServerApplication { public static void main(String[] args) { SpringApplication.run(DiscoveryServerApplication.class, args); } }
使用postman 請求服務
服務端打印日誌
2019-10-30 11:10:35.822 WARN 48882 --- [nio-9012-exec-8] com.xian.cloud.filter.UrlFilter : 過濾器:請求地址/server/hello 2019-10-30 11:10:35.826 WARN 48882 --- [nio-9012-exec-8] com.xian.cloud.filter.UrlFilter : uuid:x-foo 2019-10-30 11:10:35.826 WARN 48882 --- [nio-9012-exec-8] com.xian.cloud.filter.UrlFilter : abc uuid:null 2019-10-30 11:10:35.827 WARN 48882 --- [nio-9012-exec-8] com.xian.cloud.filter.UrlFilter : authorization :null 2019-10-30 11:10:35.827 WARN 48882 --- [nio-9012-exec-8] com.xian.cloud.filter.UrlFilter : tom :null 2019-10-30 11:10:35.828 WARN 48882 --- [nio-9012-exec-8] com.xian.cloud.filter.UrlFilter : mike :null 2019-10-30 11:10:35.852 INFO 48882 --- [nio-9012-exec-8] c.x.cloud.controller.DiscoverCotroller : invoked name = xian age = 20
sensitive-headers、ignored-headers (abc、authorization都在zuul被過濾掉)全局設置生效。
咱們再次請求client 服務接口
在看一下 客戶端打印日誌
2019-10-30 11:17:05.813 WARN 50223 --- [nio-9011-exec-3] com.xian.cloud.filter.UrlFilter : 過濾器:請求地址/client/test 2019-10-30 11:17:05.813 WARN 50223 --- [nio-9011-exec-3] com.xian.cloud.filter.UrlFilter : uuid:null 2019-10-30 11:17:05.813 WARN 50223 --- [nio-9011-exec-3] com.xian.cloud.filter.UrlFilter : abc uuid:null 2019-10-30 11:17:05.813 WARN 50223 --- [nio-9011-exec-3] com.xian.cloud.filter.UrlFilter : authorization :null 2019-10-30 11:17:05.813 WARN 50223 --- [nio-9011-exec-3] com.xian.cloud.filter.UrlFilter : tom :null 2019-10-30 11:17:05.813 WARN 50223 --- [nio-9011-exec-3] com.xian.cloud.filter.UrlFilter : mike :null
全局設置,和單個服務定製設置 所有生效。
spring cloud Zuul包含了對請求的路由和過濾2個功能。路由功能負責將請求轉發到具體的微服務上,而過濾器負責對請求的處理過程進行干預,是實現權限校驗、服務聚合等功能的基礎。
在實際運行時,路由映射和請求轉發是由幾個不一樣的過濾器完成的。每個進入zuul的http請求都會通過一系列的過濾器處理鏈獲得請求響應並返回給客戶端。
zuul1.0 對流量的路由分發和過濾倆個功能。路由讓流量分發到內部的微服務集羣上。過濾對請求進行修改或者作個性化處理。實現鑑權、安全、監控、日誌等。
運行過程當中,流量請求會通過一系列的過濾器最終轉發到對應的服務上去。
zuul 路由器主要根絕FilterType類型分爲四種類型。一個靜態響應StaticResponseFilter
FilterTypeEnum 枚舉類
package com.xian.cloud.enums; import lombok.Getter; /** * <Description> * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/29 13:01 */ @Getter public enum FilterTypeEnum { PRE("pre","前置過濾"), ROUTE("route","路由請求時被調用"), POST("post","後置過濾器"), ERROR("error","後置錯誤處理"); private String type; private String desc; FilterTypeEnum(String type,String desc){ this.type = type; this.desc = desc; } }
過濾器代碼編寫
package com.xian.cloud.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import com.xian.cloud.enums.FilterTypeEnum; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** * <Description> * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/29 12:57 */ @Component @Slf4j public class BannedAccessFilter extends ZuulFilter { /** * 路由器的類型指定 * @return */ @Override public String filterType() { return FilterTypeEnum.PRE.getType(); } /** * 執行順序 數值越小。執行順序越靠前 * @return */ @Override public int filterOrder() { return 0; } /** * 返回一個boolean值來判斷該過濾器是否要執行。咱們能夠經過此方法來指定過濾器的有效範圍。 * @return */ @Override public boolean shouldFilter() { RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); String requestURI = request.getRequestURI(); if(StringUtils.isNotBlank(requestURI) && (requestURI.contains("client") || requestURI.contains("server"))){ return true; } return false; } /** * 核心業務處理 * @return * @throws ZuulException */ @Override public Object run() throws ZuulException { RequestContext context = RequestContext.getCurrentContext(); HttpServletResponse servletResponse = context.getResponse(); String uuid = UUID.randomUUID().toString(); //重寫 X-Foo X-ABC值 context.addZuulRequestHeader("X-Foo", uuid); context.addZuulRequestHeader("X-ABC",uuid); log.info("X-Foo:{}",uuid); return null; } }
run方法是核心業務處理的方法
postman 請求 http://127.0.0.1:9083/server/server/hello?name=1111 攜帶以前設置的header參數
服務器打印日誌
2019-10-30 14:44:44.841 WARN 48882 --- [nio-9012-exec-9] com.xian.cloud.filter.UrlFilter : 過濾器:請求地址/server/hello 2019-10-30 14:44:44.842 WARN 48882 --- [nio-9012-exec-9] com.xian.cloud.filter.UrlFilter : uuid:a65b2244-e9a5-456d-968f-eaaba45b6f49 2019-10-30 14:44:44.842 WARN 48882 --- [nio-9012-exec-9] com.xian.cloud.filter.UrlFilter : abc uuid:null 2019-10-30 14:44:44.842 WARN 48882 --- [nio-9012-exec-9] com.xian.cloud.filter.UrlFilter : authorization :null 2019-10-30 14:44:44.842 WARN 48882 --- [nio-9012-exec-9] com.xian.cloud.filter.UrlFilter : tom :null 2019-10-30 14:44:44.842 WARN 48882 --- [nio-9012-exec-9] com.xian.cloud.filter.UrlFilter : mike :null 2019-10-30 14:44:44.844 INFO 48882 --- [nio-9012-exec-9] c.x.cloud.controller.DiscoverCotroller : invoked name = 1111 age = 20
能夠看到 postman設置的X-Foo 值被 BannedAccessFilter run方法業務覆蓋掉了
攔截的請求對參數的修改操做都是在這裏。這裏單獨介紹一下 RequestContext 這個類
官方文檔描述
To pass information between filters, Zuul uses a RequestContext. Its data is held in a ThreadLocal specific to each request. Information about where to route requests, errors and the actual HttpServletRequest and HttpServletResponse are stored there. The RequestContext extends ConcurrentHashMap, so anything can be stored in the context. FilterConstants contains the keys that are used by the filters installed by Spring Cloud Netflix (more on these later).
請求要在過濾器之間傳遞信息,Zuul使用 RequestContext。其數據按照每一個請求的ThreadLocal進行。關於路由請求,錯誤以及實際HttpServletRequest和HttpServletResponse的路由信息。RequestContext擴展ConcurrentHashMap,因此任何東西均可以存儲在上下文中。
ThreadLocal保證了請求與請求都是獨立的,相互不影響。擴展ConcurrentHashMap 保證內部過濾器併發操做的安全性。
須要注意點 都在代碼註釋上描述出來了。請根據本身的業務場景定製本身的過濾器。
package com.xian.cloud.filter; import com.alibaba.fastjson.JSONObject; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import com.netflix.zuul.http.HttpServletRequestWrapper; import com.netflix.zuul.http.ServletInputStreamWrapper; import com.xian.cloud.enums.FilterTypeEnum; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import org.springframework.util.StreamUtils; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * <Description> * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/10/29 16:07 */ @Component @Slf4j public class UpdateParamsFilter extends ZuulFilter { @Override public String filterType() { return FilterTypeEnum.PRE.getType(); } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { // 獲取到request RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); // 獲取請求參數name String name = ""; try { // 請求方法 String method = request.getMethod(); log.info(String.format("%s >>> %s", method, request.getRequestURL().toString())); // 獲取請求的輸入流 InputStream in = request.getInputStream(); String body = StreamUtils.copyToString(in, Charset.forName("UTF-8")); // 若是body爲空初始化爲空json if (StringUtils.isBlank(body)) { body = "{}"; } log.info("body" + body); // 轉化成json JSONObject json = JSONObject.parseObject(body); // 關鍵步驟,必定要get一下,下面才能取到值requestQueryParams if ("GET".equals(method)) { request.getParameterMap(); Map<String, List<String>> requestQueryParams = ctx.getRequestQueryParams(); if (requestQueryParams == null) { requestQueryParams = new HashMap<>(); } //TODO 寫業務代碼 List<String> arrayList = new ArrayList<>(); arrayList.add("hello mike"); //requestQueryParams.put("mike",arrayList); ctx.setRequestQueryParams(requestQueryParams); } //post和put需重寫HttpServletRequestWrapper if ("POST".equals(method) || "PUT".equals(method)) { //TODO 在這裏修改或者添加內容 json.put("tom","hello tom"); String newBody = json.toString(); final byte[] reqBodyBytes = newBody.getBytes(); // 重寫上下文的HttpServletRequestWrapper ctx.setRequest(new HttpServletRequestWrapper(request) { @Override public ServletInputStream getInputStream() throws IOException { return new ServletInputStreamWrapper(reqBodyBytes); } @Override public int getContentLength() { return reqBodyBytes.length; } @Override public long getContentLengthLong() { return reqBodyBytes.length; } }); } } catch (IOException e) { e.printStackTrace(); } return null; } }
本章過濾器內容講述完畢。下個章節 咱們來介紹 ,基於網關zuul服務的 動態路由 功能。
摘自參考 spring cloud 官方文檔
服務器nacos 地址 http://47.99.209.72:8848/nacos
往期地址 spring cloud alibaba 地址
Spring Cloud Alibaba (nacos 註冊中心搭建)
Spring Cloud Alibaba 使用nacos 註冊中心
Spring Cloud Alibaba nacos 配置中心使用
如何喜歡能夠關注分享本公衆號。