基於Netflix
的開源框架zuul
實現的 各個微服務之間都不存在單點,而且都註冊於 Eureka ,基於此進行服務的註冊於發現,再經過 Ribbon 進行服務調用,並具備客戶端負載功能。 問題點?java
針對上述問題:SpringCloud 全家桶天然也有對應的解決方案: Zuul
。 Spring Cloud Zuul 將自身註冊爲 Eureka 服務治理下的應用,從 Eureka 中獲取服務實例信息,從而維護路由規則和服務實例。nginx
咱們在全部的請求進來以前抽出一層網關應用,將服務提供的全部細節都進行了包裝,這樣全部的客戶端都是和網關進行交互(統一入口)
,簡化了客戶端開發git
客戶端負載
:Zuul 註冊於 Eureka 並集成了 Ribbon 因此天然也是能夠從註冊中心獲取到服務列表進行客戶端負載。動態路由
:解放運維。監控與審查
身份認證與安全
壓力測試
: 逐漸增長某一個服務集羣的流量,以瞭解服務性能;金絲雀測試
服務遷移
負載剪裁
: 爲每個負載類型分配對應的容量,對超過限定值的請求棄用;靜態應答處理
添加依賴github
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
複製代碼
spring-cloud-starter-zuul自己已經集成了hystrix和ribbon
注意點:正則表達式
(傳統路由)
當使用path與url的映射關係來配置路由規則時,對於路由轉發的請求則不會採用HystrixCommand來包裝,因此這類路由請求就沒有線程隔離和斷路器保護功能,而且也不會有負載均衡的能力(服務路由)
使用Zuul的時候儘可能使用**path和serviceId
**的組合進行配置,這樣不只能夠保證API網關的健壯和穩定,也能用到Ribbon的客戶端負載均衡功能。開啓服務註冊spring
@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
複製代碼
路由配置順序注意:api
YAML(文件格式,相似propeties)
propeties
文件,則會丟失順序。# 項目配置
spring.application.name=sbc-gateway-zuul
server.port=8383
# 簡化路由配置[zuul.routes.微服務Id = 指定路徑]
zuul.routes.user = /api/**(指定正則表達式來匹配路徑)
# 忽略指定微服務
zuul.ignored-services=微服務Id1,微服務Id2...
# 指定多個微服務負載
zuul.routes.user.path: /user/**
zuul.routes.user.serviceId: user
ribbon.eureka.enabled=false
user.ribbon.listOfServers: 微服務1, 微服務2...
# forward跳轉本地
zuul.routes.user.path=/user/**
zuul.routes.user.url=forward:/user
# 對指定路由開啓自定義敏感頭
zuul.routes.[route].customSensitiveHeaders=true
zuul.routes.[route].sensitiveHeaders=[這裏設置要過濾的敏感頭]
# 全局
zuul.sensitiveHeaders=[這裏設置要過濾的敏感頭]
# --Zuul的Http客戶端支持Apache Http、Ribbon的RestClient和OkHttpClient,默認使用Apache HTTP客戶端。--
# 啓用Ribbon的RestClient
ribbon.restclient.enabled=true
# 啓用OkHttpClient
ribbon.okhttp.enabled=true
# eureka地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
複製代碼
粒度
是微服務
,而不是某個API
ZuulFallbackProvider
接口(最新版本建議直接繼承類FallbackProvider
,新版增長異常處理
),實現該接口就能夠爲Zuul實現回退功能/** * @program: spring-cloud * @description: zuul容錯與回退 * @author: Mr.Tang * @create: 2018-06-20 14:25 **/
@Component
public class ProductServiceFallbackProvider implements FallbackProvider {
protected final Logger logger = LoggerFactory.getLogger(ProductServiceFallbackProvider.class);
@Override
public String getRoute() {
// 注意: 這裏是route的名稱,不是服務的名稱,不然沒法起到回退做用
return "ribbon-consumer";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return headers;
}
@Override
public InputStream getBody() throws IOException {
logger.info("ribbon-consumer: 服務不能夠");
return new ByteArrayInputStream("該服務暫不可用,請稍後重試!".getBytes());
}
@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() {
}
};
}
@Override
public ClientHttpResponse fallbackResponse(Throwable cause) {
if (cause != null && cause.getCause() != null) {
String reason = cause.getCause().getMessage();
logger.info("Excption {}",reason);
}
return fallbackResponse();
}
}
複製代碼
路由重試(或者啓動備用服務來分散壓力) 網絡等緣由致使服務不可用,須要進行重試,zuul結合Spring Retry
添加Spring Retry依賴安全
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
複製代碼
開啓重試 不使用重試,就必須考慮到是否可以接受單個服務實例關閉和eureka刷新服務列表之間帶來的短期的熔斷(在未刷新以前仍是會訪問到熔斷中的服務降級方法)服務器
#是否開啓重試功能
zuul.retryable=true
#對當前服務的重試次數
ribbon.MaxAutoRetries=2
#切換相同Server的次數
ribbon.MaxAutoRetriesNextServer=0
複製代碼
注意 斷路器的其中一個做用就是防止故障或者壓力擴散。當壓力過大是,一個服務的宕機,路由將壓力轉向其它服務時有可能也會壓垮其它服務。斷路器的形式更像是提供一種友好的錯誤信息,提升服務之間的容錯性。網絡
Zuul大部分功能都是經過過濾器來實現的。Zuul中定義了四種標準過濾器類型
PRE
: 路由以前。咱們可利用這種過濾器實現身份驗證、在集羣中選擇請求的微服務、記錄調試信息等。ROUTING
: 路由之時。這種過濾器用於構建發送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。POST
: 路由以後。這種過濾器可用來爲響應添加標準的HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。ERROR
: 在其餘階段發生錯誤時執行該過濾器。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 | 處理正常的請求響應 |
除了默認的過濾器類型,Zuul還容許咱們建立自定義的過濾器類型。自定義(PRE)
以下: ZuulFilter
是Zuul中核心組件,經過繼承該抽象類,覆寫幾個關鍵方法達到自定義調度請求的做用
/** * @program: spring-cloud * @description: token驗證 * @author: Mr.Tang * @create: 2018-06-21 10:08 **/
public class TokenFilter extends ZuulFilter{
protected final Logger logger = LoggerFactory.getLogger(TokenFilter.class);
@Override
public String filterType() {
//pre:路由以前
//routing:路由中
//post:路由後
//error:發生錯誤時
return "pre";
}
@Override
public int filterOrder() {
// filter執行順序,經過數字指定 ,優先級爲0,數字越大,優先級越低
return 0;
}
@Override
public boolean shouldFilter() {
// 是否執行該過濾器,此處爲true,說明須要過濾
return true;
}
@Override
public Object run() {
this.logger.info("This is pre-type zuul filter.");
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String token = request.getParameter("token");
if(StringUtils.isNotBlank(token)){
requestContext.setSendZuulResponse(true); //對請求進行路由
requestContext.setResponseStatusCode(200);
requestContext.set("isSuccess", true);
return null;
}else{
requestContext.setSendZuulResponse(false); //不對其進行路由
requestContext.setResponseStatusCode(400);
requestContext.setResponseBody("token is empty");
requestContext.set("isSuccess", false);
return null;
}
}
}
複製代碼
filterType()
方法是該過濾器的類型;filterOrder()
方法返回的是執行順序;shouldFilter()
方法則是判斷是否須要執行該過濾器;run()
則是所要執行的具體過濾動做。過濾器之間並不會直接進行通訊,而是經過RequestContext
來共享信息,RequestContext
是線程安全的。 開啓過濾器 在程序的啓動類 添加 Bean
@Bean
public TokenFilter tokenFilter() {
return new TokenFilter();
}
複製代碼
禁用過濾器 格式爲:zuul.[filter-name].[filter-type].disable=true
如:
zuul.FormBodyWrapperFilter.pre.disable=true
複製代碼
@EnableZuulServer和@EnableZuulProxy
@EnableZuulProxy
包含@EnableZuulServer
的功能,不會自動加載任何代理過濾器。@EnableZuulServer
替代 @EnableZuulProxy
。 這時候咱們能夠添加任何 ZuulFilter
類型實體類都會被自動加載,Zuul 如今既然做爲了對外的第一入口,那確定不能是單節點,對於 Zuul 的高可用有如下兩種方式實現。
Eureka 高可用
:部署多個 Zuul 節點,而且都註冊於 Eureka(有一個嚴重的缺點:那就是客戶端也得註冊到 Eureka 上才能對 Zuul 的調用作到負載,這顯然是不現實的。)基於 Nginx 高可用
:在調用 Zuul 以前使用 Nginx 之類的負載均衡工具進行負載,這樣 Zuul 既能註冊到 Eureka ,客戶端也能實現對 Zuul 的負載
在github
上有關於Spring Cloud
完整的部署。
其它相關文章
Spring cloud(1)-簡介以及選擇
Spring cloud(2)-服務發現(Eureka,Consul)
Spring cloud(3)-負載均衡(Feign,Ribbon)
Spring cloud(4)-熔斷(Hystrix)
Spring cloud(5)-路由網關(Zuul)
Spring cloud(6)-配置管理及刷新(Config,Bus)
最後,給個 star 吧~
我的博客~
簡書~