在微服務(wù)項(xiàng)目中,如果我們想實(shí)現(xiàn)服務(wù)間調(diào)用,一般會(huì)選擇Feign。之前介紹過(guò)一款HTTP客戶端工具Retrofit,配合SpringBoot非常好用!其實(shí)Retrofit不僅支持普通的HTTP調(diào)用,還能支持微服務(wù)間的調(diào)用,負(fù)載均衡和熔斷限流都能實(shí)現(xiàn)。今天我們來(lái)介紹下Retrofit在Spring Cloud Alibaba下的使用,希望對(duì)大家有所幫助!
前置知識(shí)
本文主要介紹Retrofit在Spring Cloud Alibaba下的使用,需要用到Nacos和Sentinel,對(duì)這些技術(shù)不太熟悉的朋友可以先參考下之前的文章。
- Spring Cloud Alibaba:Nacos 作為注冊(cè)中心和配置中心使用
- Spring Cloud Alibaba:Sentinel實(shí)現(xiàn)熔斷與限流
- 還在用HttpUtil?試試這款優(yōu)雅的HTTP客戶端工具吧,跟SpringBoot絕配!
搭建
在使用之前我們需要先搭建Nacos和Sentinel,再準(zhǔn)備一個(gè)被調(diào)用的服務(wù),使用之前的nacos-user-service即可。
- 首先從官網(wǎng)下載Nacos,這里下載的是nacos-server-1.3.0.zip文件,下載地址:https://github.com/alibaba/nacos/releases

- 解壓安裝包到指定目錄,直接運(yùn)行bin目錄下的startup.cmd,運(yùn)行成功后訪問(wèn)Nacos,賬號(hào)密碼均為nacos,訪問(wèn)地址:http://localhost:8848/nacos

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

- 下載完成后輸入如下命令運(yùn)行Sentinel控制臺(tái);
java -jar sentinel-dashboard-1.6.3.jar
- Sentinel控制臺(tái)默認(rèn)運(yùn)行在8080端口上,登錄賬號(hào)密碼均為sentinel,通過(guò)如下地址可以進(jìn)行訪問(wèn):http://localhost:8080

- 接下來(lái)啟動(dòng)nacos-user-service服務(wù),該服務(wù)中包含了對(duì)User對(duì)象的CRUD操作接口,啟動(dòng)成功后它將會(huì)在Nacos中注冊(cè)。
/**
* 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<User> getUser(@PathVariable Long id) {
User user = userService.getUser(id);
LOGGER.info("根據(jù)id獲取用戶信息,用戶名稱為:{}",user.getUsername());
return new CommonResult<>(user);
}
@GetMapping("/getUserByIds")
public CommonResult<List<User>> getUserByIds(@RequestParam List<Long> ids) {
List<User> userList= userService.getUserByIds(ids);
LOGGER.info("根據(jù)ids獲取用戶信息,用戶列表為:{}",userList);
return new CommonResult<>(userList);
}
@GetMapping("/getByUsername")
public CommonResult<User> 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);
}
}
使用
接下來(lái)我們來(lái)介紹下Retrofit的基本使用,包括服務(wù)間調(diào)用、服務(wù)限流和熔斷降級(jí)。
集成與配置
- 首先在pom.xml中添加Nacos、Sentinel和Retrofit相關(guān)依賴;
<dependencies>
<!--Nacos注冊(cè)中心依賴-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--Sentinel依賴-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--Retrofit依賴-->
<dependency>
<groupId>com.github.lianjiatech</groupId>
<artifactId>retrofit-spring-boot-starter</artifactId>
<version>2.2.18</version>
</dependency>
</dependencies>
- 然后在application.yml中對(duì)Nacos、Sentinel和Retrofit進(jìn)行配置,Retrofit配置下日志和開(kāi)啟熔斷降級(jí)即可;
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
# 全局日志打印級(jí)別
global-log-level: info
# 全局日志打印策略
global-log-strategy: body
# 熔斷降級(jí)配置
degrade:
# 是否啟用熔斷降級(jí)
enable: true
# 熔斷降級(jí)實(shí)現(xiàn)方式
degrade-type: sentinel
# 熔斷資源名稱解析器
resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParser
- 再添加一個(gè)Retrofit的Java配置,配置好選擇服務(wù)實(shí)例的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實(shí)現(xiàn)微服務(wù)間調(diào)用非常簡(jiǎn)單,直接使用@RetrofitClient注解,通過(guò)設(shè)置serviceId為需要調(diào)用服務(wù)的ID即可;
/**
* 定義Http接口,用于調(diào)用遠(yuǎn)程的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<User> getUser(@Path("id") Long id);
@GET("/user/getByUsername")
CommonResult<User> getByUsername(@Query("username") String username);
@POST("/user/update")
CommonResult update(@Body User user);
@POST("/user/delete/{id}")
CommonResult delete(@Path("id") Long id);
}
- 我們可以啟動(dòng)2個(gè)nacos-user-service服務(wù)和1個(gè)nacos-retrofit-service服務(wù),此時(shí)Nacos注冊(cè)中心顯示如下;

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

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

服務(wù)限流
- Retrofit的限流功能基本依賴Sentinel,和直接使用Sentinel并無(wú)區(qū)別,我們創(chuàng)建一個(gè)測(cè)試類RateLimitController來(lái)試下它的限流功能;
/**
* 限流功能
* 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限流,有默認(rèn)的限流處理邏輯")
@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);
}
}
- 接下來(lái)在Sentinel控制臺(tái)創(chuàng)建一個(gè)根據(jù)資源名稱進(jìn)行限流的規(guī)則;

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

熔斷降級(jí)
- Retrofit的熔斷降級(jí)功能也基本依賴于Sentinel,我們創(chuàng)建一個(gè)測(cè)試類CircleBreakerController來(lái)試下它的熔斷降級(jí)功能;
/**
* 熔斷降級(jí)
* Created by macro on 2019/11/7.
*/
@Api(tags = "CircleBreakerController",description = "熔斷降級(jí)")
@RestController
@RequestMapping("/breaker")
public class CircleBreakerController {
private Logger LOGGER = LoggerFactory.getLogger(CircleBreakerController.class);
@Autowired
private UserService userService;
@ApiOperation("熔斷降級(jí)")
@RequestMapping(value = "/fallback/{id}",method = RequestMethod.GET)
@SentinelResource(value = "fallback",fallback = "handleFallback")
public CommonResult fallback(@PathVariable Long id) {
return userService.getUser(id);
}
@ApiOperation("忽略異常進(jìn)行熔斷降級(jí)")
@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ù)降級(jí)返回",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ù)降級(jí)返回",200);
}
}
- 由于我們并沒(méi)有在nacos-user-service中定義id為4的用戶,調(diào)用過(guò)程中會(huì)產(chǎn)生異常,所以訪問(wèn)如下接口會(huì)返回服務(wù)降級(jí)結(jié)果,返回我們默認(rèn)的用戶信息。

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