Zuul實現(xiàn)服務網(wǎng)關路由全過程
前言
為什么會有路由層?
因為在微服務架構設計中,往往并不會直接將服務暴漏給調(diào)用端,而是通過調(diào)用路由層進行業(yè)務隔離,以達到不同的業(yè)務調(diào)用對應的服務模塊。
Spring Cloud Zuul
Spring Cloud Zuul 路由是微服務架構的不可或缺的一部分,提供動態(tài)路由、監(jiān)控、彈性、安全等的邊緣服務。
Zuul 是 Netflix 出品的一個基于 JVM 路由和服務端的負載均衡器。

環(huán)境準備
1.jdk 1.8、idea2018、Maven3
2.Spring Boot 2.0.6.RELEASE
3.Spring Cloud Finchley.SR2
代碼示例
itstack-demo-springcloud-08
├── itstack-demo-springcloud-eureka-client
│ └── src
│ └── main
│ ├── java
│ │ └── org.itstack.demo
│ │ ├── web
│ │ │ └── EurekaClientController.java
│ │ └── EurekaClientApplication.java
│ └── resources
│ └── application.yml
├── itstack-demo-springcloud-eureka-server
│ └── src
│ └── main
│ ├── java
│ │ └── org.itstack.demo
│ │ └── EurekaServerApplication.java
│ └── resources
│ └── application.yml
├── itstack-demo-springcloud-hystrix-feign
│ └── src
│ └── main
│ ├── java
│ │ └── org.itstack.demo
│ │ ├── service
│ │ │ ├── hystrix
│ │ │ │ └── FeignServiceHystrix.java
│ │ │ └── FeignService.java
│ │ ├── web
│ │ │ └── FeignController.java
│ │ └── FeignApplication.java
│ └── resources
│ └── application.yml
├── itstack-demo-springcloud-hystrix-ribbon
│ └── src
│ └── main
│ ├── java
│ │ └── org.itstack.demo
│ │ ├── service
│ │ │ └── RibbonService.java
│ │ ├── web
│ │ │ └── RibbonController.java
│ │ └── RibbonApplication.java
│ └── resources
│ └── application.yml
└── itstack-demo-springcloud-zuul
└── src
└── main
├── java
│ └── org.itstack.demo
│ └── ZuulApplication.java
└── resources
└── application.yml
itstack-demo-springcloud-eureka-client | 服務提供方
提供一個查詢用戶信息的簡單方法,在配置文件中通過修改端口啟動2次,模擬雙實例應用,為調(diào)用方負載做準備。
- web/EurekaClientController.java & 注意@EnableEurekaClient用于向注冊中心提供服務
@EnableEurekaClient
@RestController
public class EurekaClientController {
@Value("${server.port}")
private int port;
@RequestMapping(path = "/api/queryUserInfo", method = RequestMethod.GET)
public String queryUserInfo(@RequestParam String userId) {
return "Hi 微信公眾號:bugstack蟲洞棧 | " + userId + " >: from eureka client port: " + port;
}
}
- EurekaClientApplication.java & 服務啟動類
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
- pom.xml & 配置文件指向注冊中心
server:
port: 8001
spring:
application:
name: itstack-demo-springcloud-eureka-client
eureka:
client:
serviceUrl:
defaultZone: http://localhost:7397/eureka/
itstack-demo-springcloud-eureka-server | 單個服務注冊中心
服務注冊中心用于承載接口提供方向上注冊,同時正在調(diào)用方鏈接后可以獲取指定應用的服務實例。
- EurekaServerApplication.java & 通過注解@EnableEurekaServer啟動服務注冊與發(fā)現(xiàn)中心
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run( EurekaServerApplication.class, args );
}
}
- pom.xml & 服務注冊中心
server:
port: 7397
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
spring:
application:
name: itstack-demo-springcloud-eureka-server
itstack-demo-springcloud-feign | Feign服務調(diào)用方
Feign 是一個聲明式的 Web Service 客戶端,它的目的就是讓 Web Service 調(diào)用更加簡單。它整合了 Ribbon 和 Hystrix,從而讓我們不再需要顯式地使用這兩個組件。Feign 還提供了 HTTP 請求的模板,通過編寫簡單的接口和插入注解,我們就可以定義好 HTTP 請求的參數(shù)、格式、地址等信息。接下來,F(xiàn)eign 會完全代理 HTTP 的請求,我們只需要像調(diào)用方法一樣調(diào)用它就可以完成服務請求。
Feign 具有如下特性:
可插拔的注解支持,包括 Feign 注解和 JAX-RS 注解 支持可插拔的 HTTP 編碼器和解碼器 支持 Hystrix 和它的 Fallback 支持 Ribbon 的負載均衡 支持 HTTP 請求和響應的壓縮
service/FeignService.java |
注解方式調(diào)用,方便易用。@FeignClient會在調(diào)用時進行解析服務到具體的http://ip:port/
@FeignClient(value = "itstack-demo-springcloud-eureka-client", fallback = FeignServiceHystrix.class)
public interface FeignService {
@RequestMapping(value = "/api/queryUserInfo", method = RequestMethod.GET)
String queryUserInfo(@RequestParam String userId);
}
- service/hystrix/FeignServiceHystrix.java | 提供熔斷服務,當發(fā)生異常時主動返回預定結果
@Component
public class FeignServiceHystrix implements FeignService {
@Override
public String queryUserInfo(String userId) {
return "queryUserInfo by userId:" + userId + " err!from feign hystrix";
}
}
- web/FeignController.java | 使用接口提供服務 From Feign
@RestController
public class FeignController {
@Resource
private FeignService ribbonService;
@RequestMapping(path = "/api/queryUserInfo", method = RequestMethod.GET)
public String queryUserInfo(@RequestParam String userId) {
return ribbonService.queryUserInfo(userId) + " From Feign";
}
}
FeignApplication.java |
- 注解@EnableEurekaClient、@EnableFeignClients、@EnableDiscoveryClient獲取調(diào)用注冊中心服務
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
@EnableHystrix
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}
- application.yml | eureka服務配置,從注冊中心獲取可用服務。開啟hystrix=true
server:
port: 9001
spring:
application:
name: itstack-demo-springcloud-feign
eureka:
client:
serviceUrl:
defaultZone: http://localhost:7397/eureka/
feign.hystrix.enabled: true
itstack-demo-springcloud-ribbon | Ribbon服務調(diào)用方
Ribbon是一個基于 HTTP 和 TCP 的客戶端負載均衡器。它可以通過在客戶端中配置 ribbonServerList 來設置服務端列表去輪詢訪問以達到均衡負載的作用。
當 Ribbon 與 Eureka 聯(lián)合使用時,ribbonServerList 會被 DiscoveryEnabledNIWSServerList 重寫,擴展成從 Eureka 注冊中心中獲取服務實例列表。同時它也會用 NIWSDiscoveryPing 來取代 IPing,它將職責委托給 Eureka 來確定服務端是否已經(jīng)啟動。
service/RibbonService.java |
- 接口式硬編碼調(diào)用不太易于維護,因此也是比較少用的方式。hystrix實際通過getFallback()返回熔斷結果
@Service
public class RibbonService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "queryUserInfoFallback")
public String queryUserInfo(String userId) {
return restTemplate.getForObject("http://ITSTACK-DEMO-SPRINGCLOUD-EUREKA-CLIENT/api/queryUserInfo?userId=" + userId, String.class);
}
/**
* Specifies a method to process fallback logic.
* A fallback method should be defined in the same class where is HystrixCommand.
* Also a fallback method should have same signature to a method which was invoked as hystrix command.
* for example:
* <code>
* @HystrixCommand(fallbackMethod = "getByIdFallback")
* public String getById(String id) {...}
*
* private String getByIdFallback(String id) {...}
* </code>
* Also a fallback method can be annotated with {@link HystrixCommand}
* <p/>
* default => see {@link com.netflix.hystrix.contrib.javanica.command.GenericCommand#getFallback()}
*
* @return method name
*
* getFallback()
*
* @Override
* protected Object getFallback() {
* final CommandAction commandAction = getFallbackAction();
* if (commandAction != null) {
* try {
* return process(new Action() {
* @Override
* Object execute() {
* MetaHolder metaHolder = commandAction.getMetaHolder();
* Object[] args = createArgsForFallback(metaHolder, getExecutionException());
* return commandAction.executeWithArgs(metaHolder.getFallbackExecutionType(), args);
* }
* });
* } catch (Throwable e) {
* LOGGER.error(FallbackErrorMessageBuilder.create()
* .append(commandAction, e).build());
* throw new FallbackInvocationException(unwrapCause(e));
* }
* } else {
* return super.getFallback();
* }
* }
*/
public String queryUserInfoFallback(String userId) {
return "queryUserInfo by userId:" + userId + " err!from ribbon hystrix";
}
}
- web/RibbonController.java | 使用接口提供服務 From Ribbon
@RestController
public class RibbonController {
@Resource
private RibbonService ribbonService;
@RequestMapping(path = "/api/queryUserInfo", method = RequestMethod.GET)
public String queryUserInfo(@RequestParam String userId) {
return ribbonService.queryUserInfo(userId) + " From Ribbon";
}
}
RibbonApplication.java |
- 通過注解@LoadBalanced注冊rest模版,用于Ribbon接口調(diào)用。并啟動@EnableHystrix
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableHystrix
public class RibbonApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
- application.yml | eureka服務配置,從注冊中心獲取可用服務
server:
port: 9002
spring:
application:
name: itstack-demo-springcloud-ribbon
eureka:
client:
serviceUrl:
defaultZone: http://localhost:7397/eureka/
itstack-demo-springcloud-zuul | Zull路由層
Spring Cloud Zuul 路由是微服務架構的不可或缺的一部分,提供動態(tài)路由、監(jiān)控、彈性、安全等的邊緣服務。Zuul 是 Netflix 出品的一個基于 JVM 路由和服務端的負載均衡器。
- ZuulApplication.java & 路由服務啟動
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
@EnableDiscoveryClient
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
- pom.mxl & 路由配置
server:
port: 10001
spring:
application:
name: itstack-demo-ddd-zuul
eureka:
client:
serviceUrl:
defaultZone: http://localhost:7397/eureka/
# http://localhost:10001/route-a/api/queryUserInfo?userId=111
# http://localhost:10001/route-b/api/queryUserInfo?userId=111
zuul:
routes:
api-a:
path: /route-a/**
serviceId: itstack-demo-springcloud-feign
api-b:
path: /route-b/**
serviceId: itstack-demo-springcloud-ribbon
測試驗證
1.分別啟動如下系統(tǒng)模擬
- itstack-demo-springcloud-eureka-server 服務注冊發(fā)現(xiàn)中心
- itstack-demo-springcloud-eureka-client 測試接口提供方
- itstack-demo-springcloud-hystrix-feign 接口調(diào)用方Feign
- itstack-demo-springcloud-hystrix-ribbon 接口調(diào)用方Ribbon
- itstack-demo-springcloud-zuul 路由服務
2.測試接口
- 訪問Feign、Ribbon接口,驗證服務是否可用;http://localhost:9001/api/queryUserInfo?userId=111、http://localhost:9002/api/queryUserInfo?userId=111
- 訪問路由接口A;http://localhost:10001/route-a/api/queryUserInfo?userId=111
- 訪問路由接口B;http://localhost:10001/route-b/api/queryUserInfo?userId=111
Hello| 111 >: from eureka client port: 8001 From Ribbon
綜上總結
- zuul目前SpringCloud結合的是zuul 1, Netflix 已經(jīng)發(fā)布了 Zuul 2但目前還未整合
- SpringCloud還有自己的網(wǎng)關服務;Spring Cloud Gateway
- 通過最上層的路由功能可以很方便的隔離業(yè)務,但是路由層一定是高可用的,否則路由癱瘓整個服務將不可用
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Spring?@bean和@component注解區(qū)別
本文主要介紹了Spring?@bean和@component注解區(qū)別,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01
淺析Spring?Cloud?Gateway中的令牌桶限流算法
這篇文章主要為大家淺析了Spring?Cloud?Gateway中的令牌桶限流算法原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-02-02
帶你了解如何使用Spring基于ProxyFactoryBean創(chuàng)建AOP代理
這篇文章主要介紹了Spring基于ProxyFactoryBean創(chuàng)建AOP代理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2021-08-08
Spring中過濾器和攔截器的區(qū)別及具體實現(xiàn)方式
在Spring框架中,和都是用于處理 HTTP 請求的中間件,但它們在作用范圍、實現(xiàn)方式和生命周期上有顯著區(qū)別,本文給大家介紹Spring中過濾器和攔截器的區(qū)別及具體實現(xiàn),感興趣的朋友一起看看吧2025-07-07
SpringBoot運行時修改定時任務Cron表達式的實現(xiàn)方案
在項目開發(fā)中,定時任務是一個常見的需求,SpringBoot通過@Scheduled注解提供了簡便的定時任務實現(xiàn)方式,但默認情況下,一旦應用啟動,定時任務的Cron表達式就無法動態(tài)調(diào)整,本文將介紹如何在SpringBoot應用運行期間動態(tài)修改定時任務的Cron表達式,需要的朋友可以參考下2025-06-06
使用ServletInputStream在攔截器或過濾器中應用后重寫
這篇文章主要介紹了使用ServletInputStream在攔截器或過濾器中應用后重寫,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10

