SpringCloud如何實(shí)現(xiàn)Zuul集群(負(fù)載均衡)
前言:
在微服務(wù)架構(gòu)中,有一個(gè)組件可以說(shuō)是必不可少的,那就是微服務(wù)網(wǎng)關(guān),微服務(wù)網(wǎng)關(guān)處理了負(fù)載均衡,緩存,路由,訪問(wèn)控制,服務(wù)代理,監(jiān)控,日志等。API網(wǎng)關(guān)在微服務(wù)架構(gòu)中正是以微服務(wù)網(wǎng)關(guān)的身份存在。
一般在微服務(wù)架構(gòu)中,網(wǎng)關(guān)都是部署多個(gè)服務(wù)的,以實(shí)現(xiàn)負(fù)載均衡和保證高可用。
一、使用 Nginx+Zuul 實(shí)現(xiàn)網(wǎng)關(guān)集群
1.互聯(lián)網(wǎng)公司中網(wǎng)關(guān)都是集群 搭建集群: Nginx+Zuul 一主一備,或者輪詢多個(gè)。
2.在微服務(wù)中,所有服務(wù)請(qǐng)求都會(huì)統(tǒng)一請(qǐng)求到Zuul網(wǎng)關(guān)上。
圖示:

過(guò)程:客戶端發(fā)送請(qǐng)求統(tǒng)一到Nginx上,在使用Nginx實(shí)現(xiàn)反向代理和負(fù)載均衡,采用輪詢算法轉(zhuǎn)發(fā)到網(wǎng)關(guān)上。
1.創(chuàng)建Eurek注冊(cè)中心、會(huì)員服務(wù)、訂單服務(wù) (略)
搭建Zull集群前,應(yīng)該對(duì)Eureka注冊(cè)中心,以及創(chuàng)建SpringBoot項(xiàng)目應(yīng)該有了解,這里就不一 一贅述了。
實(shí)際上創(chuàng)建會(huì)員服務(wù)、訂單服務(wù)這一步可以省略,因?yàn)槲覀儍H僅是為了演示Nginx對(duì)Zuul網(wǎng)關(guān)的負(fù)載均衡效果。
會(huì)員服務(wù)配置:
過(guò)程:客戶端發(fā)送請(qǐng)求統(tǒng)一到Nginx上,在使用Nginx實(shí)現(xiàn)反向代理和負(fù)載均衡,采用輪詢算法轉(zhuǎn)發(fā)到網(wǎng)關(guān)上。1.創(chuàng)建Eurek注冊(cè)中心、會(huì)員服務(wù)、訂單服務(wù) (略)
搭建Zull集群前,應(yīng)該對(duì)Eureka注冊(cè)中心,以及創(chuàng)建SpringBoot項(xiàng)目應(yīng)該有了解,這里就不一 一贅述了。實(shí)際上創(chuàng)建會(huì)員服務(wù)、訂單服務(wù)這一步可以省略,因?yàn)槲覀儍H僅是為了演示Nginx對(duì)Zuul網(wǎng)關(guān)的負(fù)載均衡效果。
會(huì)員服務(wù)配置:
#會(huì)員服務(wù)
server:
port: 8082
spring:
application:
name: member-service #服務(wù)名
#Eureka配置
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka/ #注冊(cè)中心的地址
訂單服務(wù):
#訂單服務(wù)
server:
port: 8081
spring:
application:
name: order-service #服務(wù)名
#Eureka配置
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka/ #注冊(cè)中心地址
2. 創(chuàng)建Zuul服務(wù)
application.yml文件中配置 (Zull的配置生產(chǎn)時(shí)一般是放到配置中心中)
#配置Zuul端口
server:
port: 81
spring:
application:
name: zull-gateway-service #服務(wù)名
#Eureka配置
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka/ #注冊(cè)中心地址
# 配置網(wǎng)關(guān)反向代理,例如訪問(wèn)/api-member/** 直接重定向到member-service服務(wù),實(shí)現(xiàn)路由轉(zhuǎn)發(fā),隱藏服務(wù)的真實(shí)ip(服務(wù)都實(shí)在內(nèi)網(wǎng)中)
#zull根據(jù)服務(wù)名,去Eureka獲取服務(wù)真實(shí)地址,并通過(guò)本地轉(zhuǎn)發(fā),而且默認(rèn)開(kāi)啟Ribbon實(shí)現(xiàn)負(fù)載均衡
#默認(rèn)讀取Eureka注冊(cè)列表 默認(rèn)30秒間隔
zuul:
routes:
api-a: #會(huì)員服務(wù)網(wǎng)關(guān)配置
path: /api-member/** #訪問(wèn)只要是/api-member/ 開(kāi)頭的直接轉(zhuǎn)發(fā)到member-service服務(wù)
#服務(wù)名
serviceId: member-service
api-b: #訂單服務(wù)網(wǎng)關(guān)配置
path: /api-order/**
serviceId: order-service
創(chuàng)建TokenFilet類繼承ZuulFilter,在run方法中輸入網(wǎng)關(guān)的端口,調(diào)用服務(wù)時(shí)方便查看Nginx轉(zhuǎn)發(fā)到哪個(gè)網(wǎng)關(guān)
package com.example.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import io.micrometer.core.instrument.util.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class TokenFilter extends ZuulFilter {
//統(tǒng)計(jì)當(dāng)前Zuul調(diào)用次數(shù)
int count = 0;
//獲取Zuul服務(wù)端口號(hào)
@Value("${server.port}")
private String prot;
/**
* 指定該Filter的類型
* ERROR_TYPE = "error";
* POST_TYPE = "post";
* PRE_TYPE = "pre";
* ROUTE_TYPE = "route";
*/
@Override
public String filterType() {
System.out.println("filterType()...");
return "pre";
}
/**
* 指定該Filter執(zhí)行的順序(Filter從小到大執(zhí)行)
* DEBUG_FILTER_ORDER = 1;
* FORM_BODY_WRAPPER_FILTER_ORDER = -1;
* PRE_DECORATION_FILTER_ORDER = 5;
* RIBBON_ROUTING_FILTER_ORDER = 10;
* SEND_ERROR_FILTER_ORDER = 0;
* SEND_FORWARD_FILTER_ORDER = 500;
* SEND_RESPONSE_FILTER_ORDER = 1000;
* SIMPLE_HOST_ROUTING_FILTER_ORDER = 100;
* SERVLET_30_WRAPPER_FILTER_ORDER = -2;
* SERVLET_DETECTION_FILTER_ORDER = -3;
*/
@Override
public int filterOrder() {
System.out.println("filterOrder()...");
return 0;
}
/**
* 指定需要執(zhí)行該Filter的規(guī)則
* 返回true則執(zhí)行run()
* 返回false則不執(zhí)行run()
*/
@Override
public boolean shouldFilter() {
System.out.println("shouldFilter()...");
return true;
}
/**
* 該Filter具體的執(zhí)行活動(dòng)
*/
@Override
public Object run() throws ZuulException {
// 獲取上下文
//RequestContext currentContext = RequestContext.getCurrentContext();
//HttpServletRequest request = currentContext.getRequest();
//獲取userToken
// String userToken = request.getParameter("userToken");
//System.out.println("userToken: "+userToken);
//if (StringUtils.isEmpty(userToken)) {
//不會(huì)繼續(xù)執(zhí)行調(diào)用服務(wù)接口,網(wǎng)關(guān)直接響應(yīng)給客戶端
//currentContext.setSendZuulResponse(false);
//currentContext.setResponseStatusCode(401);
// currentContext.setResponseBody("userToken is Null");
// return null;
// }else if(!userToken.equals("10010")){
// currentContext.setSendZuulResponse(false);
//currentContext.setResponseStatusCode(401);
//currentContext.setResponseBody("userToken is Error");
//return null;
//}
// 否則正常執(zhí)行業(yè)務(wù)邏輯,調(diào)用服務(wù).....
System.out.println("訪問(wèn)Zuul網(wǎng)關(guān)端口為:"+prot +"(total:"+ ( count++) +")");
return null;
}
}
啟動(dòng)兩個(gè)Zuul服務(wù),端口號(hào)分別為81和82
3. 下載Nginx服務(wù)器
這里演示使用Windows版本,Linux安裝也很簡(jiǎn)單,后面操作都一樣。
windows版下載地址:http://nginx.org/en/download.html

下載好后解壓,進(jìn)入conf目錄找到nginx.conf文件打開(kāi),配置如下:
#配置上游服務(wù)器 集群,默認(rèn)輪詢機(jī)制
upstream backServer{
server 127.0.0.1:81;
server 127.0.0.1:82;
# 補(bǔ)充: backup表示從服務(wù)器或者叫備用服務(wù)器 只有當(dāng)主服務(wù)器(81、82端口)都不能訪問(wèn)時(shí)才會(huì)訪問(wèn)此(83端口)備用服務(wù)器 當(dāng)主服務(wù)器恢復(fù)正常后 則訪問(wèn)主服務(wù)器
#server 127.0.0.1:83 backup;
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
##root html;
#指定上游負(fù)載均衡服務(wù)器
proxy_pass http://backServer/;
index index.html index.htm;
}
}
雙擊nginx.exe啟動(dòng)Nginx服務(wù)器
二、 測(cè)試
瀏覽器訪問(wèn)http://localhost/api-member
我們可以看到兩個(gè)網(wǎng)關(guān)分別輸出了日志,實(shí)現(xiàn)了負(fù)載均衡


我們看到訪問(wèn)的次數(shù)不一樣,這其實(shí)和使用Google瀏覽器有關(guān),可以換其他瀏覽器試試。
三、補(bǔ)充
Nginx和網(wǎng)關(guān)的區(qū)別在什么地方?
1、都可以實(shí)現(xiàn)反向代理。
2、都可以實(shí)現(xiàn)負(fù)載均衡,Nginx是服務(wù)端負(fù)載均衡,Zuul是本地負(fù)載均衡。
Nginx也可以實(shí)現(xiàn)網(wǎng)關(guān),為什么不用Nginx實(shí)現(xiàn)網(wǎng)關(guān)呢?
因?yàn)槲⒎?wù)網(wǎng)關(guān)是針對(duì)整個(gè)微服務(wù)實(shí)現(xiàn)統(tǒng)一的請(qǐng)求攔截,網(wǎng)關(guān)基本上都是采用自己熟悉的語(yǔ)言開(kāi)發(fā)的,目的方便易學(xué)。
網(wǎng)關(guān)的作用:
1、網(wǎng)關(guān)對(duì)所有服務(wù)會(huì)話進(jìn)行攔截
2、網(wǎng)關(guān)安全控制、統(tǒng)一異常處理、xxs、sql注入
3、權(quán)限控制、黑名單和白名單、性能監(jiān)控、日志打印等
關(guān)于Nginx負(fù)載均衡故障轉(zhuǎn)移:
設(shè)置備用服務(wù)器(主從架構(gòu)),只有當(dāng)所有主服務(wù)器不可用時(shí)才會(huì)負(fù)載到備服務(wù)器,當(dāng)主服務(wù)器恢復(fù)正常時(shí)則負(fù)載到主服務(wù)器。
upstream backServer{
server 127.0.0.1:81;
server 127.0.0.1:82;
# 補(bǔ)充: backup表示從服務(wù)器或者叫備用服務(wù)器 只有當(dāng)主服務(wù)器(81、82端口)都不能訪問(wèn)時(shí)才會(huì)訪問(wèn)此(83端口)備用服務(wù)器 當(dāng)主服務(wù)器恢復(fù)正常后 則訪問(wèn)主服務(wù)器
server 127.0.0.1:83 backup;
}
設(shè)置Nginx轉(zhuǎn)發(fā)請(qǐng)求超時(shí)時(shí)間
upstream backServer{
server 127.0.0.1:81;
server 127.0.0.1:82;
server 127.0.0.1:83 backup;
}
location / {
proxy_pass http://backServer/;
proxy_redirect default;
proxy_connect_timeout 1; # 超時(shí)1s則轉(zhuǎn)發(fā)到其他服務(wù),宕機(jī)情況下也適用
proxy_read_timeout 1;
proxy_send_timeout 1;
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用jenkins部署springboot項(xiàng)目的方法步驟
這篇文章主要介紹了使用jenkins部署springboot項(xiàng)目的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
java -jar設(shè)置添加啟動(dòng)參數(shù)實(shí)現(xiàn)方法
這篇文章主要介紹了java -jar設(shè)置添加啟動(dòng)參數(shù)實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
fastjson全局日期序列化設(shè)置導(dǎo)致JSONField失效問(wèn)題解決方案
這篇文章主要介紹了fastjson通過(guò)代碼指定全局序列化返回時(shí)間格式,導(dǎo)致使用JSONField注解標(biāo)注屬性的特殊日期返回格式失效問(wèn)題的解決方案2023-01-01
關(guān)于java的九個(gè)預(yù)定義Class對(duì)象
這篇文章主要介紹了關(guān)于java的九個(gè)預(yù)定義Class對(duì)象,在Java中,沒(méi)有類就無(wú)法做任何事情。然而,并不是所有的類都具有面向?qū)ο筇卣鳌H鏜ath.random,并只需要知道方法名和參數(shù),需要的朋友可以參考下2023-05-05
IDEA創(chuàng)建yml文件不顯示小樹(shù)葉創(chuàng)建失敗問(wèn)題的解決方法
這篇文章主要介紹了IDEA創(chuàng)建yml文件不顯示小樹(shù)葉創(chuàng)建失敗問(wèn)題的解決方法,需要的朋友可以參考下2020-07-07

