六、API網關:Zuul
zuul也叫路由網關,具體啥作用咱目前也不用管,個人喜歡先上手,會用了再去了解它到底是什麼。咱們暫時可以參考controller來理解zuul,簡單來說就類似於路徑帶"/order"訪問訂單服務,帶"/goods"訪問商品服務。
開幹,新建模塊zuul,pom.xml如下:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mujio</groupId>
<artifactId>zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zuul</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<!-- 引入zuul時使用Hoxton.SR3 -->
<!-- <spring-cloud.version>Greenwich.RC2</spring-cloud.version>-->
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
</properties>
<dependencies>
<!-- 需要eureka依賴 -->
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
注意:引入zuul時,會發現依賴報錯,將spring cloud版本換成Hoxton.SR3可解決。
啓動類開啓eureka和zuul的註解:
@EnableDiscoveryClient
@EnableZuulProxy
修改配置文件爲application.yml:
server:
port: 7100
spring:
application:
#指定服務名稱
name: zuul-server
eureka:
client:
#是否註冊到Eureka服務中
register-with-eureka: true
#是否從Eureka服務中獲取註冊信息
fetch-registry: true
service-url:
#Eureka客戶端與服務端進行交互的地址
defaultZone: http://mujio:123456@localhost:7000/eureka/
instance:
#把ip地址註冊到Eureka服務中
prefer-ip-address: true
ip-address: 127.0.0.1
zuul:
routes:
goods-server:
# 將所有/goods/的路徑映射到goods-server上
path: /goods/**
serviceId: goods-server
strip-prefix: false
啓動,測試http://localhost:7100/goods/1和http://localhost:7100/goods-server/goods/1:
我們訪問的是zuul的端口,加上的是商品服務的映射路徑,成功獲取到商品服務的返回值,但是爲什麼這兩個路徑都是正常的呢?大家可以去掉配置文件中的 strip-prefix: false 試試,把path改爲其他值再試試。
在zuul實現路由時,還可以通過繼承ZuulFilter來實現路由前置後置等方法,且看代碼:
package com.mujio.zuul.filter;
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.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* 可以通過不同的filter繼承ZuulFilter來實現前置後置等方法
* FilterConstants.PRE_TYPE請求被路由前調用
* FilterConstants.POST_TYPE在ROUTE和ERROR後調用
* FilterConstants.ROUTE_TYPE請求時調用
* FilterConstants.ERROR_TYPE請求出現錯誤時調用
*/
@Component
public class UserFilter extends ZuulFilter{
// 請求被路由前調用
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
//數值越大優先級越靠後
@Override
public int filterOrder() {
return 0;
}
//是否進行過濾
@Override
public boolean shouldFilter() {
return true;
}
//具體的過濾規則實現
@Override
public Object run() throws ZuulException {
HttpServletRequest req = RequestContext.getCurrentContext().getRequest();
String token = req.getParameter("token");
if (StringUtils.isEmpty(token)){
RequestContext.getCurrentContext().setSendZuulResponse(false);//不進行路由
RequestContext.getCurrentContext().setResponseStatusCode(200);
RequestContext.getCurrentContext().setResponseBody("{\"error\":\"invalid token\"}");
}
return null;
}
}
重啓,測試http://localhost:7100/goods/1:
http://localhost:7100/goods/1?token=1:
如上效果,通過zuul實現了token驗證的功能。
解決上一節提到的兩個問題:
- eureka服務掛了咋辦?
- 啓動多個訂單服務有什麼用?
首先看第一個問題,我們先複製兩份eureka,端口設爲7001、7002。修改三個eureka服務配置文件爲:
eureka:
server:
port: 7000
spring:
application:
name: eureka-server
security:
user:
#認證信息
name: mujio
password: 123456
eureka:
client:
#是否註冊到Eureka服務中
register-with-eureka: true
#是否從Eureka服務中獲取註冊信息---修改爲true
fetch-registry: true
service-url:
#Eureka客戶端與服務端進行交互的地址,加入認證信息---修改爲用","隔開的兩個eureka地址
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@localhost:7001/eureka/,http://${spring.security.user.name}:${spring.security.user.password}@localhost:7002/eureka/
eureka01:
server:
port: 7001
spring:
application:
name: eureka-server
security:
user:
#認證信息
name: mujio
password: 123456
eureka:
client:
#是否註冊到Eureka服務中
register-with-eureka: true
#是否從Eureka服務中獲取註冊信息---修改爲true
fetch-registry: true
service-url:
#Eureka客戶端與服務端進行交互的地址,加入認證信息---修改爲用","隔開的兩個eureka地址
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@localhost:7000/eureka/,http://${spring.security.user.name}:${spring.security.user.password}@localhost:7002/eureka/
eureka02:
server:
port: 7002
spring:
application:
name: eureka-server
security:
user:
#認證信息
name: mujio
password: 123456
eureka:
client:
#是否註冊到Eureka服務中
register-with-eureka: true
#是否從Eureka服務中獲取註冊信息---修改爲true
fetch-registry: true
service-url:
#Eureka客戶端與服務端進行交互的地址,加入認證信息---修改爲用","隔開的兩個eureka地址
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@localhost:7000/eureka/,http://${spring.security.user.name}:${spring.security.user.password}@localhost:7001/eureka/
重啓,測試http://localhost:9000/order/1:
停止eureka,再次訪問;停止eureka01再次訪問;停止eureka02再次訪問。再逐次停止goods-server再次訪問。
可以發現,eureka服務並沒有影響到order服務的運行,但是商品服務的宕機,影響到了order獲取商品的信息,直到所有的eureka和商品服務都宕機了,訂單服務任然能運行,只是獲取不到正確的商品信息。真正實現了訂單服務的高可用。
至此,我們的分佈式系統已經相對完整了,這個時候我們來考慮第二個問題:
2.訂單服務掛了怎麼辦?
其實這個問題我並沒有找到滿意的解答。準確的問題中心並不單單指訂單服務,可以是登陸中心,可以是Zuul網關等等。
假設用戶走進"門店",門店可以通過各種方法保證提供穩定的服務,但是保證門店屹立不倒呢?這個問題還是留着繼續思考吧。
下一節,引入config配置中心。