Java EE 目錄:https://blog.csdn.net/dkbnull/article/details/87932809
Spring Cloud 專欄:https://blog.csdn.net/dkbnull/column/info/36820
Spring Boot 專欄:https://blog.csdn.net/dkbnull/column/info/26341
0. 開發環境
-
IDE:IntelliJ IDEA 2017.1 x64
-
JDK:1.8.0_91
-
Spring Boot:2.0.9.RELEASE
-
Spring Cloud:Finchley.RELEASE
1. Zuul簡介
Zuul是Netflix開源的一個基於JVM路由和服務端的API Gateway服務器,是一個負載均衡器。
Zuul的主要功能是路由轉發和過濾器。路由轉發功能是微服務中很重要的一部分。比如 api/sale/* 接口轉發到sale服務, api/pay/* 接口轉發到pay服務。
Zuul默認整合了Ribbon,實現了負載均衡。
1.1 Zuul功能
- Authentication:認證
- Insights:洞察
- Stress Testing:壓力測試
- Canary Testing:金絲雀測試
- Dynamic Routing:動態路由
- Service Migration:服務遷移
- Load Shedding:負載均衡
- Security:安全
- Static Response handling:靜態響應處理
- Active/Active traffic management:主動/主動交通管理
2.路由轉發
2.1 新建路由網關服務
新建路由網關服務spring-cloud-zuul
2.2 引入依賴
<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>
2.3 新建啓動類
@EnableZuulProxy 註解表示開啓Zuul功能。
package cn.wbnull.springcloudzuul;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class SpringCloudZuulApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudZuulApplication.class, args);
}
}
2.4 新建application.yml
server:
port: 8091
servlet:
context-path: /springcloudzuul
spring:
application:
name: spring-cloud-zuul
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8090/springcloudeureka/eureka/
zuul:
routes:
zuul-a:
path: /zuul-a/**
serviceId: spring-boot-provider
zuul-b:
path: /zuul-b/**
serviceId: spring-boot-provider
2.5 測試
依次啓動spring-cloud-eureka、spring-boot-provider、spring-boot-provider-v2、spring-cloud-zuul
瀏覽器訪問 http://127.0.0.1:8091/springcloudzuul/zuul-a/springbootprovider/gateway 和 http://127.0.0.1:8091/springcloudzuul/zuul-b/springbootprovider/gateway , hello world,this is spring-boot-provider 和 hello world,this is spring-boot-provider-v2 交替返回,路由成功。
-
127.0.0.1:8091/springcloudzuul 是 spring-cloud-zuul 的訪問地址(ip:port/server.servlet.context-path)
-
zuul-a 和 zuul-b 是我們要路由的地址,根據application.yml配置,這倆地址被路由到 服務id名稱爲 spring-boot-provider 的服務。
-
springbootprovider/gateway 是 spring-boot-provider 服務的接口訪問地址(server.servlet.context-path/接口名),與之前相同,serviceId替換的只是ip:port部分。
2.6 路由配置方式2
也可以使用如下方式配置路由地址
zuul:
routes:
zuul-a:
path: /zuul-a/**
url: http://localhost:8081/springbootprovider
zuul-b:
path: /zuul-b/**
url: http://localhost:8083/springbootprovider
依次啓動spring-cloud-eureka、spring-boot-provider、spring-boot-provider-v2、spring-cloud-zuul
瀏覽器訪問 http://127.0.0.1:8091/springcloudzuul/zuul-a/gateway ,返回 hello world,this is spring-boot-provider ;瀏覽器訪問 http://127.0.0.1:8091/springcloudzuul/zuul-b/gateway ,返回 hello world,this is spring-boot-provider-v2 ,路由成功。
3. 服務過濾
剛纔我們已經說過,Zuul的主要功能是路由轉發和過濾器,現在我們來介紹下過濾器。
3.1 新建過濾器類
cn.wbnull.springcloudzuul 包下新建 filter 包,再新建 GlobalFilter 類,並 注入到IoC容器中。
package cn.wbnull.springcloudzuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
public class GlobalFilter extends ZuulFilter {
@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 context = RequestContext.getCurrentContext();
HttpServletRequest servletRequest = context.getRequest();
Object token = servletRequest.getHeader("token");
if (token == null) {
context.setSendZuulResponse(false);
context.setResponseStatusCode(401);
try {
context.getResponse().getWriter().write("error: token is null");
} catch (IOException e) {
}
}
return null;
}
}
- filterType():過濾器類型,執行過濾器的時機
- pre:路由前
- routing:路由時
- post:路由後
- error:發送錯誤時
- filterOrder():過濾順序
- shouldFilter():是否需要過濾,可以豐富代碼進行邏輯判斷,true:過濾;false:不過濾。就像上面示例代碼中不進行邏輯判斷直接 return true 表示所有路由都過濾。
- run():過濾器具體邏輯實現。
3.2 測試
依次啓動spring-cloud-eureka、spring-boot-provider、spring-boot-provider-v2、spring-cloud-zuul,瀏覽器訪問 http://127.0.0.1:8091/springcloudzuul/zuul-a/springbootprovider/gateway ,返回 error: token is null 。
我們換Postman,Header加上token,再次請求,返回正常,且 hello world,this is spring-boot-provider 和 hello world,this is spring-boot-provider-v2 交替返回,過濾成功。
4. Zuul整合Hystrix
上面我們測試,路由配置的是服務提供者的地址,這樣我們之前配置的負載均衡策略、斷路器就都無法生效了。
下面我們修改路由服務Id名稱,使路由到服務消費者。
4.1 修改spring-boot-consumer-feign-hystrix
修改 spring-boot-consumer-feign-hystrix 服務 application.yml 配置文件
eureka:
client:
register-with-eureka: true
其他配置不變。
4.1 修改spring-cloud-zuul
修改 spring-cloud-zuul 服務 application.yml 配置文件
這裏我們不僅要配置zuul路由,還要配置ribbon超時時間,否則可能啓動服務失敗或斷路器不起作用。
ribbon:
ReadTimeout: 5000
ConnectTimeout: 5000
zuul:
routes:
zuul-a:
path: /zuul-a/**
serviceId: spring-boot-consumer-feign-hystrix
4.2 測試
1、依次啓動 spring-cloud-eureka、spring-boot-provider、spring-boot-provider-v2、spring-boot-consumer-feign-hystrix、spring-cloud-zuul,使用Postman進行測試,測試地址:http://127.0.0.1:8091/springcloudzuul/zuul-a/springbootconsumer/users ,Header加上token,可以看到返回正常,且兩組返回信息交替返回。
{
"name": "Zuul 測試 name",
"hello world": "spring-boot-provider"
}
{
"name": "Zuul 測試 name",
"hello world": "spring-boot-provider-v2"
}
2、將 spring-boot-provider、spring-boot-provider-v2 服務停止,再次請求,可以看到返回熔斷方法內參數。
{
"name": "Zuul 測試 name",
"fallback": "spring-boot-consumer-feign-hystrix by GatewayFallbackFactory",
"throwable": "feign.RetryableException: Connection refused: connect executing POST http://spring-boot-provider/springbootprovider/users"
}
GitHub:https://github.com/dkbnull/SpringCloudDemo
微信:https://mp.weixin.qq.com/s/lWMudofECNpskWTioI2cIg