GitHub源碼地址:https://github.com/AngelSXD/springcloudhtml
版本介紹:java
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-boot.version>2.0.4.RELEASE</spring-boot.version> <spring-cloud.version>Finchley.SR1</spring-cloud.version> <lcn.last.version>4.2.1</lcn.last.version> </properties>
====================================================mysql
參考地址:http://www.javashuo.com/article/p-tnehirch-dh.htmlgit
====================================================github
一.簡單介紹spring
Zuul做爲微服務系統的網關組件,用於構建邊界服務,致力於動態路由、過濾、監控、彈性伸縮和安全。sql
爲何須要Zuul數據庫
Zuul、Ribbon以及Eureka結合能夠實現智能路由和負載均衡的功能;網關將全部服務的API接口統一聚合,統一對外暴露。外界調用API接口時,不須要知道微服務系統中各服務相互調用的複雜性,保護了內部微服務單元的API接口;網關能夠作用戶身份認證和權限認證,防止非法請求操做API接口;網關能夠實現監控功能,實時日誌輸出,對請求進行記錄;網關能夠實現流量監控,在高流量的狀況下,對服務降級;API接口從內部服務分離出來,方便作測試。apache
Zuul經過Servlet來實現,經過自定義的ZuulServlet來對請求進行控制。核心是一系列過濾器,能夠在Http請求的發起和響應返回期間執行一系列過濾器。Zuul採起了動態讀取、編譯和運行這些過濾器。過濾器之間不能直接通訊,而是經過RequestContext對象來共享數據,每一個請求都會建立一個RequestContext對象。json
Zuul生命週期以下圖。 當一個客戶端Request請求進入Zuul網關服務時,網關先進入」pre filter「,進行一系列的驗證、操做或者判斷。而後交給」routing filter「進行路由轉發,轉發到具體的服務實例進行邏輯處理、返回數據。當具體的服務處理完成後,最後由」post filter「進行處理,該類型的處理器處理完成以後,將Request信息返回客戶端。
二.配置過程
1.pom依賴
<!--熔斷器 健康檢查--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <!--zuul核心依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>
2.配置application.properties
spring.application.name=springcloud-ms-gateway server.port=8001 eureka.client.service-url.defaultZone=http://127.0.0.1:8000/eureka/ #由於parent的pom.xml中 添加了鏈接數據庫的依賴,因此 須要配置數據庫鏈接相關配置 spring.datasource.continue-on-error=false spring.datasource.url=jdbc:mysql://localhost:3306/springcloudtest?useSSL=false&useUnicode=true&characterEncoding=UTF-8 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.username=root spring.datasource.password=root zuul.host.socket-timeout-millis=10000 #網關最大超時時間10s zuul.host.connect-timeout-millis=10000 #網關最大鏈接數 默認200 zuul.host.max-total-connections=200 #給要路由的API請求加前綴 可加可不加 zuul.prefix=/v1 #須要忽略的服務 ,號分隔 配置後將不會被路由 zuul.ignored-services=spring-cloud-ms-eureka #配置了zuul以後,那麼整個分佈式系統,對外則只暴露http://zuul-IP:zuul-port/v1/服務自定義地址/具體API請求地址 #這一組配置後,訪問http://localhost:8001/v1/ms-member/member/showMember 即至關於直接訪問會員服務http://localhost:9000/member/showMember #zuul.routes.xx xx隨便寫,zuul中惟一便可 #zuul.routes.xx.serviceId=eureka中註冊的服務名 即各個服務配置文件中的spring.application.name zuul.routes.member.serviceId=springcloud-ms-member #zuul.routes.xx.path=/自定義的地址 /**表示下級也能夠訪問 zuul.routes.member.path=/ms-member/** #這一組配置後,全部訪問積分服務的請求 便可直接訪問網關地址http://localhost:8001/v1/ms-integral/具體接口地址 ,由zuul進行路由轉發和負載均衡 zuul.routes.integral.serviceId=springcloud-ms-integral zuul.routes.integral.path=/ms-integral/** #這一組配置後,全部訪問商品服務的請求 便可直接訪問網關地址http://localhost:8001/v1/ms-goods/具體接口地址 ,由zuul進行路由轉發和負載均衡 zuul.routes.goods.serviceId=springcloud-ms-goods zuul.routes.goods.path=/ms-goods/** #txmanager地址 必填 tm.manager.url=http://127.0.0.1:7000/tx/manager/
3.啓動類註解
核心註解@EnableZuulProxy
同時須要添加註解,服務發現,註冊eureka
package com.swapping.springcloud.ms.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; import org.springframework.cloud.openfeign.EnableFeignClients; @EnableCircuitBreaker //Hystrix Dashboard必須加 @EnableHystrixDashboard//展現熔斷器儀表盤 @EnableZuulProxy //網關映射 註解 @EnableFeignClients //feign調用註解 @EnableDiscoveryClient @SpringBootApplication(scanBasePackages = "com.swapping") public class SpringcloudMsGatewayApplication { public static void main(String[] args) { SpringApplication.run(SpringcloudMsGatewayApplication.class, args); } }
>>>>>>>>>>>>>路由轉發
4.啓動網關服務
springcloud-ms-gateway 端口8001
啓動ms-member服務,端口9000
那麼如今訪問
http://localhost:9000/member/showMember 即直接訪問ms-member服務,的/member/showMember這個API
如今能夠訪問網關,經過zuul進行動態路由
http://localhost:8001/v1/ms-member/member/showMember 統一訪問網關地址,由網關進行統一路由轉發
至此,zuul實現路由轉發!
>>>>>>>>>>>>>路由攔截
5.使用zuul進行路由攔截
如今既然全部的訪問接口都是由zuul網關暴露出去的,那全部的請求都來找網關,這樣的話在網關的過濾器中就能夠作不少的事情。
Filter是Zuul的核心,用來實現對外服務的控制。Filter的生命週期有4個,分別是「PRE」、「ROUTING」、「POST」、「ERROR」,整個生命週期能夠用下圖來表示
5.1 Zuul大部分功能都是經過過濾器來實現的,這些過濾器類型對應於請求的典型生命週期。
5.2 在zuul中默認已經實現的filter有:
類型 | 順序 | 過濾器 | 功能 |
---|---|---|---|
pre | -3 | ServletDetectionFilter | 標記處理Servlet的類型 |
pre | -2 | Servlet30WrapperFilter | 包裝HttpServletRequest請求 |
pre | -1 | FormBodyWrapperFilter | 包裝請求體 |
route | 1 | DebugFilter | 標記調試標誌 |
route | 5 | PreDecorationFilter | 處理請求上下文供後續使用 |
route | 10 | RibbonRoutingFilter | serviceId請求轉發 |
route | 100 | SimpleHostRoutingFilter | url請求轉發 |
route | 500 | SendForwardFilter | forward請求轉發 |
post | 0 | SendErrorFilter | 處理有錯誤的請求響應 |
post | 1000 | SendResponseFilter | 處理正常的請求響應 |
5.3 zuul還提供了一類特殊的過濾器,分別爲:StaticResponseFilter和SurgicalDebugFilter
StaticResponseFilter:StaticResponseFilter容許從Zuul自己生成響應,而不是將請求轉發到源。
SurgicalDebugFilter:SurgicalDebugFilter容許將特定請求路由到分隔的調試集羣或主機
5.4 在zuul中定義自定義的過濾器
例如:自定義一個請求被路由以前的過濾器,用於驗證請求中是否攜帶auth,若是攜帶安全驗證,則成功路由;不然統一終止路由,返回響應
自定義filter須要繼承ZuulFilter【注意,自定義Filter須要注入spring 使用註解@Component】
package com.swapping.springcloud.ms.gateway.filter; import com.alibaba.fastjson.JSON; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import com.swapping.springcloud.ms.core.response.UniVerResponse; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; /** * 路由攔截 * * * >>>>>zuul的filter過濾器的生命週期有一下四個: * * PRE: 這種過濾器在請求被路由以前調用。咱們可利用這種過濾器實現身份驗證、在集羣中選擇請求的微服務、記錄調試信息等。 * ROUTING:這種過濾器將請求路由到微服務。這種過濾器用於構建發送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。 * POST:這種過濾器在路由到微服務之後執行。這種過濾器可用來爲響應添加標準的HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。 * ERROR:在其餘階段發生錯誤時執行該過濾器。 除了默認的過濾器類型,Zuul還容許咱們建立自定義的過濾器類型。例如,咱們能夠定製一種STATIC類型的過濾器,直接在Zuul中生成響應,而不將請求轉發到後端的微服務。 * * * Zuul中默認實現了不少Filter,也能夠本身自定義過濾器 * * 下面是本身自定義過濾器 * 實際使用中咱們能夠結合shiro、oauth2.0等技術去作鑑權、驗證 * */ @Component public class AuthFilter extends ZuulFilter{ @Override public String filterType() { return "pre";//能夠在請求被路由以前調用 } @Override public int filterOrder() { return 0;//filter執行順序,經過數字指定 ,優先級爲0,數字越大,優先級越低 } @Override public boolean shouldFilter() { return true;// 是否執行該過濾器,此處爲true,說明須要過濾 } /** * filter須要執行的具體操做 * * 例如:本filter實際執行的邏輯 是驗證全部的訪問請求中,是否包含安全信息auth * @return * @throws ZuulException */ @Override public Object run() throws ZuulException { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); String auth = request.getParameter("auth"); //TODO 此處能夠作日誌記錄 System.out.println("zuul攔截--請求前驗證---auth:"+auth); //成功的狀況 if (StringUtils.isNotBlank(auth)){ ctx.setSendZuulResponse(true); //對請求進行路由 ctx.setResponseStatusCode(200); ctx.set("isSuccess", true); }else { //失敗的狀況 UniVerResponse res = new UniVerResponse(); res.beFalse3("zuul攔截--請求前驗證---沒有auth登陸驗證",UniVerResponse.ERROR_BUSINESS); ctx.setSendZuulResponse(false); //不對請求進行路由 ctx.setResponseStatusCode(res.getCode());//設置返回狀態碼 ctx.setResponseBody(JSON.toJSONString(res));//設置返回響應體 ctx.set("isSuccess", false); ctx.getResponse().setContentType("application/json;charset=UTF-8");//設置返回響應體格式,可能會亂碼 } return null; } }
5.5 測試自定義攔截
啓動網關服務 ms-gateway 端口:8001
啓動服務註冊中心eureka
啓動要被路由的服務ms-member 端口:9000
@RestController @RequestMapping("/member") public class MemberController { @Autowired MemberService memberService; @RequestMapping(value = "/showMember") public String showMember(){ return "會員服務正常,會員服務是服務消費者,也就是服務調用者,會調用商品服務進行商品購買,減小庫存,增長銷量;\n\r同時調用積分服務,增長積分"; }
訪問地址:攜帶auth驗證
http://localhost:8001/v1/ms-member/member/showMember?auth=111
訪問地址:不攜帶auth
5.6 若是你想禁用 zuul中的filter,能夠這麼作:
若是你想禁用一個,只需設置zuul.<SimpleClassName>.<filterType> .disable = true。按照慣例,過濾器後面的包是Zuul過濾器類型。例如,要禁用org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter 需設置 zuul.SendResponseFilter.post.disable = true
>>>>>>>>>>>>>路由熔斷
6.使用zuul進行路由熔斷
相似與服務之間的feign調用熔斷器設定,網關路由各個服務的請求也能夠作路由熔斷,在不能成功路由到具體服務上的請求時,進行降級處理,定製返回內容【也就是設置統一響應體】
目前路由熔斷僅能 支持服務級別的熔斷,不支持具體到某個URL進行熔斷
自定義熔斷類須要實現FallbackProvider接口【注意,自定義熔斷類須要注入spring 使用註解@Component】
package com.swapping.springcloud.ms.gateway.fallback; import com.alibaba.fastjson.JSON; import com.swapping.springcloud.ms.core.response.UniVerResponse; import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; /** * 路由熔斷 * * 相似與服務之間的feign調用熔斷器設定,網關路由各個服務的請求也能夠作路由熔斷, * 在不能成功路由到具體服務上的請求時,進行降級處理,定製返回內容【也就是設置統一響應體】 * * 目前路由熔斷僅能 支持服務級別的熔斷,不支持具體到某個URL進行熔斷 * */ @Component public class UniVerFallback implements FallbackProvider { /** * 指定 能夠熔斷攔截 哪些服務 * @return */ @Override public String getRoute() { // return "springcloud-ms-member";//指定 可熔斷某個服務 服務名是配置文件中配置的各服務的serviceId return "*"; //指定 可熔斷全部服務 } /** * 指定 熔斷後返回的定製化內容 * @param route * @param cause * @return */ @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) { return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.OK; } @Override public int getRawStatusCode() throws IOException { return 200; } @Override public String getStatusText() throws IOException { return "OK"; } @Override public void close() { } /** * 設置響應體 * @return * @throws IOException */ @Override public InputStream getBody() throws IOException { //TODO 此處能夠作日誌記錄 UniVerResponse uniVerResponse = new UniVerResponse(); uniVerResponse.beFalse3(route+"服務涼涼了",UniVerResponse.ERROR_BUSINESS); return new ByteArrayInputStream(JSON.toJSONString(uniVerResponse).getBytes()); } /** * 設置響應頭信息 * @return */ @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON_UTF8);//指定響應體 格式+編碼方式 return headers; } }; } }
保證eureka啓動,
而後重啓網關服務ms-gateway,端口:8001
關閉ms-member服務,端口:9000
而後訪問地址:來訪問ms-member的接口
http://localhost:8001/v1/ms-member/member/showMember?auth=111
就能夠對指定的服務進行熔斷!!!
>>>>>>>>>>>>>路由重試
7.使用zuul進行路由重試【慎用】
在服務暫時不可用的狀況下,多是服務重啓正被註冊中心掃描或者其餘緣由,咱們想要對發起的請求進行重試,zuul結合Spring Retry實現路由重試
7.1 網關服務添加依賴
<!--路由重試 依賴--> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
7.2 application.properties添加配置
#路由重試 #是否開啓路由重試,針對於 查詢接口可使用,可是對於非冪等的新增或更新接口,使用路由重試會出現很大的問題,應該注意 #是否開啓重試功能 zuul.retryable=true #對當前服務的重試次數[不包含首次訪問],也就是說實際訪問接口的次數是3+1次 ribbon.MaxAutoRetries=3 #切換相同Server的次數 ribbon.MaxAutoRetriesNextServer=0
7.3 重啓網關服務,更改一下ms-member的測試接口
@RequestMapping(value = "/showMember") public String showMember(){ System.out.println("請求到達!!!!"); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } return "會員服務正常,會員服務是服務消費者,也就是服務調用者,會調用商品服務進行商品購買,減小庫存,增長銷量;\n\r同時調用積分服務,增長積分"; }
能夠看到,要訪問的接口 休眠10s,而網關配置的超時時間設置爲2s
結合上面的重試次數的配置,請求到達的次數應該是3+1次。
啓動ms-member服務,訪問地址:
http://localhost:8001/v1/ms-member/member/showMember?auth=111
能夠看到,請求總共到達了4次,首次+重試3次
。
請求結果能夠看到,最終服務被熔斷。
7.4 路由重試注意
是否開啓路由重試,針對於 查詢接口可使用,可是對於非冪等的新增或更新接口,使用路由重試會出現很大的問題,應該注意
開啓重試在某些狀況下是有問題的,好比當壓力過大,一個實例中止響應時,路由將流量轉到另外一個實例,頗有可能致使最終全部的實例全被壓垮。說到底,斷路器的其中一個做用就是防止故障或者壓力擴散。用了retry,斷路器就只有在該服務的全部實例都沒法運做的狀況下才能起做用。這種時候,斷路器的形式更像是提供一種友好的錯誤信息,或者僞裝服務正常運行的假象給使用者。 不用retry,僅使用負載均衡和熔斷,就必須考慮到是否可以接受單個服務實例關閉和eureka刷新服務列表之間帶來的短期的熔斷。若是能夠接受,就無需使用retry。
=============結束================
參考:http://www.ityouknow.com/springcloud/2018/01/20/spring-cloud-zuul.html
參考:https://www.cnblogs.com/cralor/p/9234697.html