一:Zuul介紹
Zuul是Netflix開源的微服務網關,他可以和Eureka,Ribbon,Hystrix等組件配合使用。Zuul組件的核心是一系列的過濾器,這些過濾器可以完成以下功能:
# 身份認證和安全: 識別每一個資源的驗證要求,並拒絕那些不符的請求
# 審查與監控:
# 動態路由:動態將請求路由到不同後端集羣
# 壓力測試:逐漸增加指向集羣的流量,以瞭解性能
# 負載分配:爲每一種負載類型分配對應容量,並棄用超出限定值的請求
# 靜態響應處理:邊緣位置進行響應,避免轉發到內部集羣
# 多區域彈性:跨域AWS Region進行請求路由,旨在實現ELB(ElasticLoad Balancing)使用多樣化
二:創建Zuul模塊
我們基於上一篇的工程,在RediaMallCloud中新增一個模塊Mall_ZuulCenter,新增的步驟請參考《Sping Boot多模塊項目的創建和配置》。其中要注意的是我們在建立新模塊其中一個勾選zuul網關依賴。
新建完成之後,我們得修改父工程的pom和子工程的pom文件
父工程裏面的module變成了四個
<!--在父pom文件當中添加模塊的名稱,子模塊pom中<name>標籤當中的值-->
<modules>
<module>Mall_EurekaCenter</module>
<module>Mall_ManagerService</module>
<module>Mall_WechatService</module>
<module>Mall_ZuulCenter</module>
</modules>
zuul新模塊的pom文件如下:
<?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>
<groupId>com.smartisan</groupId>
<artifactId>mallzuulcenter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Mall_ZuulCenter</name>
<description>Demo project for Spring Boot</description>
<!-- 把原有的parent的信息註釋掉,繼承我們的父工程-->
<parent>
<!--<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/>--> <!-- lookup parent from repository -->
<groupId>com.smartisan</groupId>
<artifactId>RediaMallCloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- JSON Configuration -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.6</version>
</dependency>
<!--標籤友好化-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>、
配置:application.yml文件
eureka:
client:
serviceUrl:
defaultZone: http://admin:password@localhost:8761/eureka/
spring:
application:
name: zuul_center
cloud:
loadbalancer:
retry:
enabled: true
thymeleaf:
mode: LEGACYHTML5
cache: false
http:
encoding:
charset: UTF-8
force: true
enabled: true
server:
port: 8050
tomcat:
uri-encoding: UTF-8
zuul:
# max:
# host:
# connections: 500
host:
socket-timeout-millis: 180000
connect-timeout-millis: 180000
routes:
mall-wechatservice:
path: /wechat/**
sensitiveHeaders:
mall-managerservice:
path: /manager/**
sensitiveHeaders:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 180000
ribbon:
ConnectTimeout: 60000
ReadTimeout: 60000
MaxAutoRetriesNextServer: 0
MaxAutoRetries: 1
在zuul主類上加上註解:@EnableZuulProxy,開啓zuul的功能。@EnableEurekaClient註解向註冊中心註冊。
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 MallZuulCenterApplication {
public static void main(String[] args) {
SpringApplication.run(MallZuulCenterApplication.class, args);
}
}
我們分別在mall-wechatservice和mall-managerservice編寫兩個接口對外提供服務。
mall-managerservice:
@Controller
public class ManagerController {
private final Logger logger= LoggerFactory.getLogger(this.getClass());
@Value("${server.port}")
String port;
//get請求
@ResponseBody
@RequestMapping("/helloA")
public String sayHelloA(){
Map<String,Object> map = new HashMap<String,Object>();
map.put("name","king james");
map.put("age",33);
map.put("team","Cleveland Cavaliers");
map.put("port",port);
logger.info(JSON.toJSONString(map));
return JSON.toJSONString(map);
}
}
@Controller
public class WechatController {
private final Logger logger= LoggerFactory.getLogger(this.getClass());
@Value("${server.port}")
String port;
//get請求
@ResponseBody
@RequestMapping("/helloA")
public String sayHelloA(){
Map<String,Object> map = new HashMap<String,Object>();
map.put("name","kobe");
map.put("age",36);
map.put("team","Los Lakers");
map.put("port",port);
logger.info(JSON.toJSONString(map));
return JSON.toJSONString(map);
}
}
現在整個工程目錄如下:
啓動順序:先啓動Mall_EurekaCenter,再啓動Mall_ZuulCenter,然後再啓動Mall_ManagerService/Mall_WechatService
分別請求http://localhost:8050/manager/helloA和http://localhost:8050/wechat/helloA,頁面顯示如下:
說明Zuul服務網關已經能夠正確的路由到我們的微服務上面去了。
三:zuul服務過濾
編寫服務過濾器
@Component
public class MyZuulFilter extends ZuulFilter {
private final Logger logger= LoggerFactory.getLogger(this.getClass());
/**
* 前置過濾器。
* 但是在 zuul 中定義了四種不同生命週期的過濾器類型:
* 1、pre:可以在請求被路由之前調用;
* 2、route:在路由請求時候被調用;
* 3、post:在route和error過濾器之後被調用;
* 4、error:處理請求時發生錯誤時被調用;
*/
@Override
public String filterType() {
return "pre";
}
/**
* 過濾的優先級,數字越大,優先級越低。
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 是否執行該過濾器。
* true:說明需要過濾;
* false:說明不要過濾;
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 過濾器的具體邏輯。
*/
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
logger.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
Object accessToken = request.getParameter("token");
if(accessToken == null) {
logger.warn("token is empty");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("token is empty");
}catch (Exception e){}
return null;
}
logger.info("ok");
return null;
}
}
再次訪問http://localhost:8050/manager/helloA,頁面顯示如下:已經能夠正確的過濾信息了。