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

環(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 | 服務(wù)提供方
提供一個查詢用戶信息的簡單方法,在配置文件中通過修改端口啟動2次,模擬雙實例應(yīng)用,為調(diào)用方負載做準備。
- web/EurekaClientController.java & 注意@EnableEurekaClient用于向注冊中心提供服務(wù)
@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 & 服務(wù)啟動類
@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 | 單個服務(wù)注冊中心
服務(wù)注冊中心用于承載接口提供方向上注冊,同時正在調(diào)用方鏈接后可以獲取指定應(yīng)用的服務(wù)實例。
- EurekaServerApplication.java & 通過注解@EnableEurekaServer啟動服務(wù)注冊與發(fā)現(xiàn)中心
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run( EurekaServerApplication.class, args );
}
}
- pom.xml & 服務(wù)注冊中心
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服務(wù)調(diào)用方
Feign 是一個聲明式的 Web Service 客戶端,它的目的就是讓 Web Service 調(diào)用更加簡單。它整合了 Ribbon 和 Hystrix,從而讓我們不再需要顯式地使用這兩個組件。Feign 還提供了 HTTP 請求的模板,通過編寫簡單的接口和插入注解,我們就可以定義好 HTTP 請求的參數(shù)、格式、地址等信息。接下來,F(xiàn)eign 會完全代理 HTTP 的請求,我們只需要像調(diào)用方法一樣調(diào)用它就可以完成服務(wù)請求。
Feign 具有如下特性:
可插拔的注解支持,包括 Feign 注解和 JAX-RS 注解 支持可插拔的 HTTP 編碼器和解碼器 支持 Hystrix 和它的 Fallback 支持 Ribbon 的負載均衡 支持 HTTP 請求和響應(yīng)的壓縮
service/FeignService.java |
注解方式調(diào)用,方便易用。@FeignClient會在調(diào)用時進行解析服務(wù)到具體的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 | 提供熔斷服務(wù),當發(fā)生異常時主動返回預定結(jié)果
@Component
public class FeignServiceHystrix implements FeignService {
@Override
public String queryUserInfo(String userId) {
return "queryUserInfo by userId:" + userId + " err!from feign hystrix";
}
}
- web/FeignController.java | 使用接口提供服務(wù) 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)用注冊中心服務(wù)
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
@EnableHystrix
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}
- application.yml | eureka服務(wù)配置,從注冊中心獲取可用服務(wù)。開啟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服務(wù)調(diào)用方
Ribbon是一個基于 HTTP 和 TCP 的客戶端負載均衡器。它可以通過在客戶端中配置 ribbonServerList 來設(shè)置服務(wù)端列表去輪詢訪問以達到均衡負載的作用。
當 Ribbon 與 Eureka 聯(lián)合使用時,ribbonServerList 會被 DiscoveryEnabledNIWSServerList 重寫,擴展成從 Eureka 注冊中心中獲取服務(wù)實例列表。同時它也會用 NIWSDiscoveryPing 來取代 IPing,它將職責委托給 Eureka 來確定服務(wù)端是否已經(jīng)啟動。
service/RibbonService.java |
- 接口式硬編碼調(diào)用不太易于維護,因此也是比較少用的方式。hystrix實際通過getFallback()返回熔斷結(jié)果
@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 | 使用接口提供服務(wù) 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服務(wù)配置,從注冊中心獲取可用服務(wù)
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 路由是微服務(wù)架構(gòu)的不可或缺的一部分,提供動態(tài)路由、監(jiān)控、彈性、安全等的邊緣服務(wù)。Zuul 是 Netflix 出品的一個基于 JVM 路由和服務(wù)端的負載均衡器。
- ZuulApplication.java & 路由服務(wù)啟動
@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 服務(wù)注冊發(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 路由服務(wù)
2.測試接口
- 訪問Feign、Ribbon接口,驗證服務(wù)是否可用;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
綜上總結(jié)
- zuul目前SpringCloud結(jié)合的是zuul 1, Netflix 已經(jīng)發(fā)布了 Zuul 2但目前還未整合
- SpringCloud還有自己的網(wǎng)關(guān)服務(wù);Spring Cloud Gateway
- 通過最上層的路由功能可以很方便的隔離業(yè)務(wù),但是路由層一定是高可用的,否則路由癱瘓整個服務(wù)將不可用
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot服務(wù)端數(shù)據(jù)校驗過程詳解
這篇文章主要介紹了SpringBoot服務(wù)端數(shù)據(jù)校驗過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-02-02
Java 代碼實例解析設(shè)計模式之監(jiān)聽者模式
所謂監(jiān)聽者模式,我理解的是構(gòu)建一個容器存放所有被監(jiān)聽的線程或?qū)ο?,監(jiān)聽每個線程或?qū)ο蟀l(fā)生的變化,若某個線程或?qū)ο笥|發(fā)指定規(guī)則,那么則對所有被監(jiān)聽的線程或?qū)ο蟾鶕?jù)業(yè)務(wù)需要做處理2021-10-10
java實現(xiàn)從網(wǎng)上下載圖片到本地的方法
這篇文章主要介紹了java實現(xiàn)從網(wǎng)上下載圖片到本地的方法,涉及java針對文件操作的相關(guān)技巧,非常簡單實用,需要的朋友可以參考下2015-07-07
解析SpringSecurity自定義登錄驗證成功與失敗的結(jié)果處理問題
這篇文章主要介紹了SpringSecurity系列之自定義登錄驗證成功與失敗的結(jié)果處理問題,本文通過實例給大家講解的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-11-11

