我們都知道,zuul 網(wǎng)關(guān)可以攔截微服務(wù)系統(tǒng)中請求做統(tǒng)一的處理礁遵,比如說可以結(jié)合 JWT 實(shí)現(xiàn)無狀態(tài)的身份驗(yàn)證单起,拒絕 Cookie 中沒有攜帶 Token 或者 Token 解析失敗的的請求,這樣做的好處是減輕了其他微服務(wù)的壓力蛀柴,但也無疑加大 zuul 網(wǎng)關(guān)的工作量螃概,那要怎么解決這個(gè)問題呢?答案是可以使用負(fù)載均衡服務(wù)器(比如說 Nginx)鸽疾,將請求均勻地轉(zhuǎn)發(fā)到 zuul 網(wǎng)關(guān)集群吊洼,下面我來嘗試實(shí)現(xiàn)這一方案,大致的流程如下:
- 搭建 Eureka 注冊中心
- 創(chuàng)建業(yè)務(wù)相關(guān)的微服務(wù)模塊(僅作簡單的模擬)
- 搭建 zuul 集群
- 配置 Nginx制肮,實(shí)現(xiàn)負(fù)載均衡
- 測試
1. Eureka 注冊中心
創(chuàng)建 Maven 模塊冒窍,取名為 eureka-registry,在 pom.xml 中設(shè)置相關(guān)依賴
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
</dependencies>
在 resources 目錄下新建 application.yml 配置文件
server:
port: 10086
spring:
application:
name: eureka-registry
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:${server.port}/eureka
register-with-eureka: false # 把自己注冊到eureka服務(wù)列表
fetch-registry: false # 拉取eureka服務(wù)信息
server:
enable-self-preservation: false # 關(guān)閉自我保護(hù)
eviction-interval-timer-in-ms: 5000 # 每隔5秒鐘豺鼻,進(jìn)行一次服務(wù)列表的清理
編寫啟動(dòng)類
@SpringBootApplication
@EnableEurekaServer
public class RegistryApplication {
public static void main(String[] args) {
SpringApplication.run(RegistryApplication.class, args);
}
}
項(xiàng)目架構(gòu)如下所示
2. 創(chuàng)建業(yè)務(wù)相關(guān)的微服務(wù)模塊
創(chuàng)建 micro-service 模塊综液,在 pom.xml 文件中設(shè)置相關(guān)依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
在 resources 目錄下新建 application.yml 配置文件
server:
port: 8084
spring:
application:
name: micro-service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
lease-renewal-interval-in-seconds: 5 # 每隔5秒發(fā)送一次心跳
lease-expiration-duration-in-seconds: 10 # 10秒不發(fā)送就過期
編寫啟動(dòng)類
@SpringBootApplication
@EnableDiscoveryClient
public class MicroApplication {
public static void main(String[] args) {
SpringApplication.run(MicroApplication.class, args);
}
}
在 cc.geekeye.micro.pojo 包下新建
package cc.geekeye.micro.pojo;
public class CommonObject {
private Integer id;
private String col1;
private String col2;
//constructors...
//getters and setters
}
添加 controller
@Controller
@RequestMapping("common")
public class CommonController {
@ResponseBody
@RequestMapping("get")
public CommonObject getCommonObject() {
System.out.println("bfjkabfjk");
return new CommonObject(1, "col1", "col2");
}
}
3. 搭建 zuul 集群
創(chuàng)建 zuul-gateway 模塊,在 pom.xml 文件中設(shè)置相關(guān)依賴
<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-client</artifactId>
</dependency>
</dependencies>
在 resources 目錄下新建 application.yml 配置文件
server:
port: 10010
spring:
application:
name: zuul-gateway
eureka:
client:
registry-fetch-interval-seconds: 5
service-url:
defaultZone: http://127.0.0.1:10086/eureka
zuul:
prefix: /api
routes:
micro-service: /micro/**
編寫啟動(dòng)類
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
編寫網(wǎng)關(guān)過濾器
@Component
public class AccessFilter extends ZuulFilter {
private static final Logger logger = LogManager.getLogger(AccessFilter.class);
@Value("${server.port}")
private String serverPort;
@Override
public String filterType() {
return "pre"; //
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
logger.info("網(wǎng)關(guān)執(zhí)行端口號:" + serverPort);
return null;
}
}
這里使用端口區(qū)分集群節(jié)點(diǎn)儒飒,為了方便測試谬莹,所以將當(dāng)前處理請求的 zuul 網(wǎng)關(guān)的響應(yīng)端口輸出到日志文件,而此處使用的日志框架是 log4j2桩了。
現(xiàn)在有一個(gè)網(wǎng)關(guān)了届良,那要怎么搭建集群呢?部署3份 jar 包嗎圣猎?但如果我們還要修改一些代碼又要重新打包和部署士葫,豈不是很麻煩?其實(shí)我們可以使用 IDEA 提供的 Run Dashboard 選項(xiàng)運(yùn)行 zuul 網(wǎng)關(guān)微服務(wù)送悔,等到運(yùn)行它成功后慢显, application.yml 配置文件已被加載進(jìn) Tomcat 容器,這時(shí)我們再修改 application.yml 配置文件中的運(yùn)行端口欠啤,然后復(fù)制運(yùn)行配置就行了荚藻。
保留默認(rèn)配置,這里只是改了個(gè)名字
運(yùn)行配置洁段,結(jié)果如下所示
4. Nginx 負(fù)載均衡
修改 nginx.conf 配置文件应狱,在 http 節(jié)點(diǎn)中配置三個(gè)上游服務(wù)器
http {
upstream backServer {
server 127.0.0.1:10010;
server 127.0.0.1:10011;
server 127.0.0.1:10012;
}
server {
listen 80;
server_name api.blabla.com;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location / {
proxy_pass http://backServer/;
proxy_connect_timeout 600;
proxy_read_timeout 600;
}
}
}
其中 api.blabla.com 是在 host 文件中配置解析到 localhost 的。
5. 測試
現(xiàn)在環(huán)境已經(jīng)搭建好祠丝,下面先來通過一張整體的結(jié)構(gòu)圖來了解我在上面做了什么事情疾呻。
- 客戶端訪問 http://api.blabla.com/api/micro/common/get
- 由于 Nginx 代理了80端口除嘹,所以該請求交給它處理,它將解析到 http 節(jié)點(diǎn)中配置的 api.blabla.com 需要轉(zhuǎn)發(fā)的上游服務(wù)器并根據(jù)其負(fù)載均衡策略進(jìn)行轉(zhuǎn)發(fā)岸蜗。
- 比如說轉(zhuǎn)發(fā)到 http://127.0.0.1:10010/api/micro/common/get 尉咕,則交由監(jiān)聽該端口的網(wǎng)關(guān)處理,它將先消掉前綴 /api璃岳,進(jìn)行相關(guān)的過濾和處理后年缎,因?yàn)?/micro 匹配 micro-service,它查找其在 Eureka 注冊中心中拉取到的服務(wù)列表铃慷,若找到則進(jìn)行轉(zhuǎn)發(fā)单芜。
- 請求轉(zhuǎn)發(fā)到 http://127.0.0.1:8084/micro/common/get ,這時(shí) micro-service 就可以進(jìn)行處理并響應(yīng)了犁柜。
了解整個(gè)流程后就可以進(jìn)行測試缓溅,為了方便,我使用 HttpClient 訪問 http://api.blabla.com/api/micro/common/get 赁温,通過比較網(wǎng)關(guān)中輸出的日志信息檢驗(yàn)負(fù)載均衡是否成功。
public class HttpTests {
private CloseableHttpClient httpClient;
@Before
public void init() {
httpClient = HttpClients.createDefault();
}
@Test
public void testGetPojo() throws IOException {
for (int i = 0; i < 1000; i++) {
HttpGet request = new HttpGet("http://api.blabla.com/api/micro/common/get");
this.httpClient.execute(request, new BasicResponseHandler());
Thread.sleep(200);
}
}
}
為了看得更直觀淤齐,我在日志配置文件 log4j2.xml 中過濾掉了其他類庫的日志信息股囊,測試運(yùn)行成功后查看輸出日志: