再見 Feign!推薦一款微服務(wù)間調(diào)用神器,跟 SpringCloud 絕配!
在微服務(wù)項目中,如果我們想實現(xiàn)服務(wù)間調(diào)用,一般會選擇Feign。之前介紹過一款HTTP客戶端工具
Retrofit,配合SpringBoot非常好用!其實Retrofit不僅支持普通的HTTP調(diào)用,還能支持微服務(wù)間的調(diào)用,負載均衡和熔斷限流都能實現(xiàn)。今天我們來介紹下Retrofit在Spring Cloud Alibaba下的使用,希望對大家有所幫助!
搭建
在使用之前我們需要先搭建Nacos和Sentinel,再準備一個被調(diào)用的服務(wù),使用之前的
nacos-user-service即可。
首先從官網(wǎng)下載Nacos,這里下載的是 nacos-server-1.3.0.zip文件,下載地址:https://github.com/alibaba/nacos/releases

解壓安裝包到指定目錄,直接運行 bin目錄下的startup.cmd,運行成功后訪問Nacos,賬號密碼均為nacos,訪問地址:http://localhost:8848/nacos

接下來從官網(wǎng)下載Sentinel,這里下載的是 sentinel-dashboard-1.6.3.jar文件,下載地址:https://github.com/alibaba/Sentinel/releases

下載完成后輸入如下命令運行Sentinel控制臺;
java?-jar?sentinel-dashboard-1.6.3.jar
Sentinel控制臺默認運行在 8080端口上,登錄賬號密碼均為sentinel,通過如下地址可以進行訪問:http://localhost:8080

接下來啟動 nacos-user-service服務(wù),該服務(wù)中包含了對User對象的CRUD操作接口,啟動成功后它將會在Nacos中注冊。
/**
?*?Created?by?macro?on?2019/8/29.
?*/
@RestController
@RequestMapping("/user")
public?class?UserController?{
????private?Logger?LOGGER?=?LoggerFactory.getLogger(this.getClass());
????@Autowired
????private?UserService?userService;
????@PostMapping("/create")
????public?CommonResult?create(@RequestBody?User?user)?{
????????userService.create(user);
????????return?new?CommonResult("操作成功",?200);
????}
????@GetMapping("/{id}")
????public?CommonResult?getUser(@PathVariable?Long?id)? {
????????User?user?=?userService.getUser(id);
????????LOGGER.info("根據(jù)id獲取用戶信息,用戶名稱為:{}",user.getUsername());
????????return?new?CommonResult<>(user);
????}
????@GetMapping("/getUserByIds")
????public?CommonResult>?getUserByIds(@RequestParam?List?ids)?{
????????List?userList=?userService.getUserByIds(ids);
????????LOGGER.info("根據(jù)ids獲取用戶信息,用戶列表為:{}",userList);
????????return?new?CommonResult<>(userList);
????}
????@GetMapping("/getByUsername")
????public?CommonResult?getByUsername(@RequestParam?String?username)? {
????????User?user?=?userService.getByUsername(username);
????????return?new?CommonResult<>(user);
????}
????@PostMapping("/update")
????public?CommonResult?update(@RequestBody?User?user)?{
????????userService.update(user);
????????return?new?CommonResult("操作成功",?200);
????}
????@PostMapping("/delete/{id}")
????public?CommonResult?delete(@PathVariable?Long?id)?{
????????userService.delete(id);
????????return?new?CommonResult("操作成功",?200);
????}
}
使用
接下來我們來介紹下Retrofit的基本使用,包括服務(wù)間調(diào)用、服務(wù)限流和熔斷降級。
集成與配置
首先在 pom.xml中添加Nacos、Sentinel和Retrofit相關(guān)依賴;
<dependencies>
????
?????<dependency>
?????????<groupId>com.alibaba.cloudgroupId>
?????????<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
?????dependency>
????
?????<dependency>
?????????<groupId>com.alibaba.cloudgroupId>
?????????<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
?????dependency>
?????
?????<dependency>
?????????<groupId>com.github.lianjiatechgroupId>
?????????<artifactId>retrofit-spring-boot-starterartifactId>
?????????<version>2.2.18version>
?????dependency>
?dependencies>
然后在 application.yml中對Nacos、Sentinel和Retrofit進行配置,Retrofit配置下日志和開啟熔斷降級即可;
server:
??port:?8402
spring:
??application:
????name:?nacos-retrofit-service
??cloud:
????nacos:
??????discovery:
????????server-addr:?localhost:8848?#配置Nacos地址
????sentinel:
??????transport:
????????dashboard:?localhost:8080?#配置sentinel?dashboard地址
????????port:?8719
retrofit:
??log:
????#?啟用日志打印
????enable:?true
????#?日志打印攔截器
????logging-interceptor:?com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor
????#?全局日志打印級別
????global-log-level:?info
????#?全局日志打印策略
????global-log-strategy:?body
??#?熔斷降級配置
??degrade:
????#?是否啟用熔斷降級
????enable:?true
????#?熔斷降級實現(xiàn)方式
????degrade-type:?sentinel
????#?熔斷資源名稱解析器
????resource-name-parser:?com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParser
再添加一個Retrofit的Java配置,配置好選擇服務(wù)實例的Bean即可。
/**
?*?Retrofit相關(guān)配置
?*?Created?by?macro?on?2022/1/26.
?*/
@Configuration
public?class?RetrofitConfig?{
????@Bean
????@Autowired
????public?ServiceInstanceChooser?serviceInstanceChooser(LoadBalancerClient?loadBalancerClient)?{
????????return?new?SpringCloudServiceInstanceChooser(loadBalancerClient);
????}
}
服務(wù)間調(diào)用
使用Retrofit實現(xiàn)微服務(wù)間調(diào)用非常簡單,直接使用 @RetrofitClient注解,通過設(shè)置serviceId為需要調(diào)用服務(wù)的ID即可;
/**
?*?定義Http接口,用于調(diào)用遠程的User服務(wù)
?*?Created?by?macro?on?2019/9/5.
?*/
@RetrofitClient(serviceId?=?"nacos-user-service",?fallback?=?UserFallbackService.class)
public?interface?UserService?{
????@POST("/user/create")
????CommonResult?create(@Body?User?user);
????@GET("/user/{id}")
????CommonResult?getUser(@Path("id")?Long?id) ;
????@GET("/user/getByUsername")
????CommonResult?getByUsername(@Query("username")?String?username) ;
????@POST("/user/update")
????CommonResult?update(@Body?User?user);
????@POST("/user/delete/{id}")
????CommonResult?delete(@Path("id")?Long?id);
}
我們可以啟動2個 nacos-user-service服務(wù)和1個nacos-retrofit-service服務(wù),此時Nacos注冊中心顯示如下;

然后通過Swagger進行測試,調(diào)用下獲取用戶詳情的接口,發(fā)現(xiàn)可以成功返回遠程數(shù)據(jù),訪問地址:http://localhost:8402/swagger-ui/

查看 nacos-retrofit-service服務(wù)打印的日志,兩個實例的請求調(diào)用交替打印,我們可以發(fā)現(xiàn)Retrofit通過配置serviceId即可實現(xiàn)微服務(wù)間調(diào)用和負載均衡。

服務(wù)限流
Retrofit的限流功能基本依賴Sentinel,和直接使用Sentinel并無區(qū)別,我們創(chuàng)建一個測試類 RateLimitController來試下它的限流功能;
/**
?*?限流功能
?*?Created?by?macro?on?2019/11/7.
?*/
@Api(tags?=?"RateLimitController",description?=?"限流功能")
@RestController
@RequestMapping("/rateLimit")
public?class?RateLimitController?{
????@ApiOperation("按資源名稱限流,需要指定限流處理邏輯")
????@GetMapping("/byResource")
????@SentinelResource(value?=?"byResource",blockHandler?=?"handleException")
????public?CommonResult?byResource()?{
????????return?new?CommonResult("按資源名稱限流",?200);
????}
????@ApiOperation("按URL限流,有默認的限流處理邏輯")
????@GetMapping("/byUrl")
????@SentinelResource(value?=?"byUrl",blockHandler?=?"handleException")
????public?CommonResult?byUrl()?{
????????return?new?CommonResult("按url限流",?200);
????}
????@ApiOperation("自定義通用的限流處理邏輯")
????@GetMapping("/customBlockHandler")
????@SentinelResource(value?=?"customBlockHandler",?blockHandler?=?"handleException",blockHandlerClass?=?CustomBlockHandler.class)
????public?CommonResult?blockHandler()?{
????????return?new?CommonResult("限流成功",?200);
????}
????public?CommonResult?handleException(BlockException?exception){
????????return?new?CommonResult(exception.getClass().getCanonicalName(),200);
????}
}
接下來在Sentinel控制臺創(chuàng)建一個根據(jù) 資源名稱進行限流的規(guī)則;

之后我們以較快速度訪問該接口時,就會觸發(fā)限流,返回如下信息。

熔斷降級
Retrofit的熔斷降級功能也基本依賴于Sentinel,我們創(chuàng)建一個測試類 CircleBreakerController來試下它的熔斷降級功能;
/**
?*?熔斷降級
?*?Created?by?macro?on?2019/11/7.
?*/
@Api(tags?=?"CircleBreakerController",description?=?"熔斷降級")
@RestController
@RequestMapping("/breaker")
public?class?CircleBreakerController?{
????private?Logger?LOGGER?=?LoggerFactory.getLogger(CircleBreakerController.class);
????@Autowired
????private?UserService?userService;
????@ApiOperation("熔斷降級")
????@RequestMapping(value?=?"/fallback/{id}",method?=?RequestMethod.GET)
????@SentinelResource(value?=?"fallback",fallback?=?"handleFallback")
????public?CommonResult?fallback(@PathVariable?Long?id)?{
????????return?userService.getUser(id);
????}
????@ApiOperation("忽略異常進行熔斷降級")
????@RequestMapping(value?=?"/fallbackException/{id}",method?=?RequestMethod.GET)
????@SentinelResource(value?=?"fallbackException",fallback?=?"handleFallback2",?exceptionsToIgnore?=?{NullPointerException.class})
????public?CommonResult?fallbackException(@PathVariable?Long?id)?{
????????if?(id?==?1)?{
????????????throw?new?IndexOutOfBoundsException();
????????}?else?if?(id?==?2)?{
????????????throw?new?NullPointerException();
????????}
????????return?userService.getUser(id);
????}
????public?CommonResult?handleFallback(Long?id)?{
????????User?defaultUser?=?new?User(-1L,?"defaultUser",?"123456");
????????return?new?CommonResult<>(defaultUser,"服務(wù)降級返回",200);
????}
????public?CommonResult?handleFallback2(@PathVariable?Long?id,?Throwable?e)?{
????????LOGGER.error("handleFallback2?id:{},throwable?class:{}",?id,?e.getClass());
????????User?defaultUser?=?new?User(-2L,?"defaultUser2",?"123456");
????????return?new?CommonResult<>(defaultUser,"服務(wù)降級返回",200);
????}
}
由于我們并沒有在 nacos-user-service中定義id為4的用戶,調(diào)用過程中會產(chǎn)生異常,所以訪問如下接口會返回服務(wù)降級結(jié)果,返回我們默認的用戶信息。

總結(jié)
Retrofit給了我們除Feign和Dubbo之外的第三種微服務(wù)間調(diào)用選擇,使用起來還是非常方便的。記得之前在使用Feign的過程中,實現(xiàn)方的Controller經(jīng)常要抽出一個接口來,方便調(diào)用方來實現(xiàn)調(diào)用,接口實現(xiàn)方和調(diào)用方的耦合度很高。如果當時使用的是Retrofit的話,這種情況會大大改善??偟膩碚f,Retrofit給我們提供了更加優(yōu)雅的HTTP調(diào)用方式,不僅是在單體應(yīng)用中,在微服務(wù)應(yīng)用中也一樣!
參考資料
官方文檔:https://github.com/LianjiaTech/retrofit-spring-boot-starter
項目源碼地址
https://github.com/macrozheng/springcloud-learning

往期推薦
2022-02-18
2022-02-21
2022-02-16

