一、簡單微服務網關搭建
maven依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lxt</groupId>
<artifactId>springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.lxt.gateaway</groupId>
<artifactId>zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zuul</name>
<description>Demo project for Spring Boot</description>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
</project>
配置文件
spring:
application:
name: gateway-zuul
server:
port: 8006
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8000/eureka/ #註冊中心eurka地址
啓動類添加@EnableZuulProxy
package com.lxt.gateaway.zuul;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
Spring Cloud Zuul默認配置說明
- 默認情況下,Zuul會代理所有註冊到Eureka Server的微服務,並且Zuul的路由規則如下:http://ZUUL_HOST:ZUUL_PORT/微服務在Eureka上的serviceId/**會被轉發到serviceId對應的微服務
- eg:http://localhost:8006/spring-cloud-consumer/hello/lxt =>http://spring-cloud-consumer/hello/lxt=>http://localhost:9001/hello/lxt
- 也可手動配置指定
- 自定義微服務訪問路徑
zuul: routes: spring-cloud-consumer-hystrix: /test/**
- 忽略指定微服務
zuul: ignored-services: spring-cloud-consumer-hystrix,spring-cloud-consumer
- 同時指定微服務ServiceId和對應路徑
zuul: routes: config-client: path: /lxt/** serviceId: spring-cloud-consumer-hystrix
- 等等…
- 自定義微服務訪問路徑
啓動測試
- 分別啓動exureka-server、service-consumer、service-provider和zuul
- 瀏覽器輸入
http://localhost:8006/spring-cloud-consumer/hello/lxt
- 返回
hello lxt,this is first messge
,測試成功
二、Zuul路由端點
- 由於 endpoints 中會包含很多敏感信息,除了 health 和 info 兩個支持 web 訪問外,其他的默認不支持 web 訪問,需手動添加配置暴露
routes
路由端點
management:
endpoints:
web:
exposure:
# 2.x手動開啓 這個是用來暴露 endpoints 的。由於 endpoints 中會包含很多敏感信息,除了 health 和 info 兩個支持 web 訪問外,其他的默認不支持 web 訪問
include: routes
- 瀏覽器
http://localhost:8006/actuator/routes
,返回如下
// 20191121213009
// http://localhost:8006/actuator/routes
{
"/spring-cloud-consumer/**": "spring-cloud-consumer",
"/spring-cloud-provider/**": "spring-cloud-provider"
}
三、Zuul中的Filter使用
Zuul中默認實現的Filter
- PRE: 這種過濾器在請求被路由之前調用。我們可利用這種過濾器實現身份驗證、在集羣中選擇請求的微服務、記錄調試信息等。
- ROUTING:這種過濾器將請求路由到微服務。這種過濾器用於構建發送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。
- POST:這種過濾器在路由到微服務以後執行。這種過濾器可用來爲響應添加標準的HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。
- ERROR:在其他階段發生錯誤時執行該過濾器。 除了默認的過濾器類型,Zuul還允許我們創建自定義的過濾器類型。例如,我們可以定製一種STATIC類型的過濾器,直接在Zuul中生成響應,而不將請求轉發到後端的微服務。
自定義Filter示例
必須包含token
的參數纔可訪問,否則直接返回,如下:
package com.lxt.gateaway.zuul;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
public class TokenFilter extends ZuulFilter {
private final Logger logger = LoggerFactory.getLogger(TokenFilter.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
logger.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString());
String token = request.getParameter("token");// 獲取請求的參數
if (StringUtils.isNotBlank(token)) {
ctx.setSendZuulResponse(true); //對請求進行路由
ctx.setResponseStatusCode(200);
ctx.set("isSuccess", true);
return null;
} else {
ctx.setSendZuulResponse(false); //不對其進行路由
ctx.setResponseStatusCode(400);
ctx.setResponseBody("token is empty");
ctx.set("isSuccess", false);
return null;
}
}
}
註冊Bean
@Bean
public TokenFilter tokenFilter() {
return new TokenFilter();
}
運行測試
- 瀏覽器輸入
http://localhost:8006/spring-cloud-consumer/hello/lxt
,返回token is empty
- 瀏覽器輸入
http://localhost:8006/spring-cloud-consumer/hello/lxt?token=2
,返回hello lxt,this is first messge
四、Zuul中的路由熔斷和重試
路由熔斷
實現FallbackProvider接口,重寫fallbackResponse方法
package com.lxt.gateaway.zuul;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ProducerFallback implements FallbackProvider{
private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class);
//指定要處理的 service。
@Override
public String getRoute() {
return "spring-cloud-provider";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
if (cause != null && cause.getCause() != null) {
String reason = cause.getCause().getMessage();
logger.info("Excption {}",reason);
}
return fallbackResponse();
}
public ClientHttpResponse fallbackResponse() {
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() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("The service is unavailable.".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
註冊bean
@Bean
public ProducerFallback producerFallback() {
return new ProducerFallback();
}
運行測試
- 再重啓網關
zuul
,啓動務提供者service-provider1
- 瀏覽多次器輸入
http://localhost:8006/spring-cloud-provider/foo?foo=lxt&token=2
- 交替返回
hello lxt,this is first messge
和hello lxt,this is two messge
- 關閉第二個服務提供者,繼續刷新瀏覽器
- 交替返回
hello lxt,this is first messge
和The service is unavailable.
路由重試
添加依賴
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
修改配置文件
zuul:
retryable: true #是否開啓重試功能
ribbon:
MaxAutoRetries: 2 #對當前服務的重試次數
MaxAutoRetriesNextServer: 0 #切換相同Server的次數
修改service-provider1的hello方法如下
@RequestMapping(value ="/hello", method = RequestMethod.GET)
public String index(String name) {
logger.info("request two name is "+name);
try{
Thread.sleep(1000000);
}catch ( Exception e){
logger.error(" hello two error",e);
}
return "hello "+name+",this is two messge";
}
運行測試
- 重啓網關
zuul
和service-provider1
- 瀏覽器輸入
http://localhost:8006/spring-cloud-provider/foo?foo=lxt&token=2
- 返回
The service is unavailable.
時,查看控制如下:
2019-11-21 22:38:31.049 INFO 11240 --- [nio-9002-exec-3] c.l.s.controller.HelloController : request two name is lxt
2019-11-21 22:38:32.054 INFO 11240 --- [nio-9002-exec-4] c.l.s.controller.HelloController : request two name is lxt
2019-11-21 22:38:33.060 INFO 11240 --- [nio-9002-exec-5] c.l.s.controller.HelloController : request two name is lxt
- 打印了三次,重試了兩次。