In a microservice project, if we wanted to implement inter-service invocation, we would typically choose Feign. Retrofit is an HTTP client tool that works well with SpringBoot. Retrofit supports not only normal HTTP calls, but also calls between microservices, load balancing and fusing limiting. Today we will introduce the use of Retrofit under Spring Cloud Alibaba, hoping to help you!

SpringBoot e-commerce project mall (50K + STAR) address: github.com/macrozheng/…

Front knowledge

This article mainly introduces the use of Retrofit under Spring Cloud Alibaba, which requires Nacos and Sentinel. If you are not familiar with these technologies, you can refer to the previous article.

  • Spring Cloud Alibaba: Nacos is used as a registry and configuration center
  • Spring Cloud Alibaba: Sentinel realizes fusing and current limiting
  • Still using HttpUtil? Try this elegant HTTP client tool that works perfectly with SpringBoot!

Set up

Before using it, we need to set up Nacos and Sentinel first, and then prepare a service to be called, using the previous nacos-user-service.

  • Start by downloading Nacos from the official websiteNacos - server - 1.3.0. ZipFile download address:Github.com/alibaba/nac…

  • Decompress the installation package to a specified directory and run it directlybinIn the directorystartup.cmd, and access Nacos after successful operation. The account and password arenacos, visit address:http://localhost:8848/nacos

  • And then you can download Sentinel from the website, which is hereSentinel dashboard - 1.6.3. JarFile download address:Github.com/alibaba/Sen…

  • After downloading, enter the following command to run Sentinel console.
Java jar sentinel - dashboard - 1.6.3. JarCopy the code
  • The Sentinel console runs by default on8080On the port, the login account and password aresentinel, can be accessed through the following address:http://localhost:8080

  • So let’s start upnacos-user-serviceThe service, which contains the CRUD operation interface to the User object, will be registered with Nacos upon successful startup.
/** * 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("Operation successful".200);
    }

    @GetMapping("/{id}")
    public CommonResult<User> getUser(@PathVariable Long id) {
        User user = userService.getUser(id);
        LOGGER.info("Get user information by 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("Get user information according to IDS, user list is: {}",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("Operation successful".200);
    }

    @PostMapping("/delete/{id}")
    public CommonResult delete(@PathVariable Long id) {
        userService.delete(id);
        return new CommonResult("Operation successful".200); }}Copy the code

use

Let’s take a look at the basic uses of Retrofit, including inter-service invocation, service limiting, and fuse downgrading.

Integration and Configuration

  • First of all inpom.xmlNacos, Sentinel and Retrofit dependencies were added to
<dependencies>
    <! --Nacos registry dependencies -->
     <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
     </dependency>
    <! - Sentinel dependence - >
     <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
     </dependency>
     <! - Retrofit dependence - >
     <dependency>
         <groupId>com.github.lianjiatech</groupId>
         <artifactId>retrofit-spring-boot-starter</artifactId>
         <version>2.2.18</version>
     </dependency>
 </dependencies>
Copy the code
  • Then, inapplication.ymlTo configure Nacos, Sentinel and Retrofit, and log and fuse downgrade can be enabled under Retrofit configuration.
server:
  port: 8402
spring:
  application:
    name: nacos-retrofit-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 Configure the Nacos address
    sentinel:
      transport:
        dashboard: localhost:8080 # Configure the sentinel Dashboard address
        port: 8719
retrofit:
  log:
    # Enable log printing
    enable: true
    # log print interceptor
    logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor
    # global log print level
    global-log-level: info
    # Global log printing policy
    global-log-strategy: body
  Fuse downgrading configuration
  degrade:
    Whether to enable fuse downgrading
    enable: true
    # Implementation of fuse downgrading
    degrade-type: sentinel
    # fuses the resource name resolver
    resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParser
Copy the code
  • Add a Java configuration for Retrofit and configure the beans that select the service instance.
/** * Created by macro on 2022/1/26. */
@Configuration
public class RetrofitConfig {

    @Bean
    @Autowired
    public ServiceInstanceChooser serviceInstanceChooser(LoadBalancerClient loadBalancerClient) {
        return newSpringCloudServiceInstanceChooser(loadBalancerClient); }}Copy the code

Interservice invocation

  • Implementing microservice to microservice invocation with Retrofit is straightforward to use@RetrofitClientAnnotation, through SettingsserviceIdIs the ID of the service to be invoked;
/** 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);
}
Copy the code
  • We can start two of themnacos-user-serviceService and 1nacos-retrofit-serviceService, at which point the Nacos registry appears as follows;

  • And tested by Swagger, call details get the user interface, found that can successfully return to remote data, access the address: http://localhost:8402/swagger-ui/

  • To viewnacos-retrofit-serviceThe service prints the log, the request calls of two instances are printed alternately, and we can find Retrofit through configurationserviceIdIt can realize the call and load balancing between micro services.

Service current limiting

  • Retrofit basically relies on Sentinel for its current-limiting functionality, which is no different from using Sentinel directly. We created a test classRateLimitControllerLet’s try its current limiting function;
/** * Created by macro on 2019/11/7. */
@API (tags = "RateLimitController",description = "Stream limiting function ")
@RestController
@RequestMapping("/rateLimit")
public class RateLimitController {

    @apiOperation (" Stream limiting by resource name, need to specify flow limiting logic ")
    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource(a) {
        return new CommonResult("Limiting traffic by Resource Name".200);
    }

    @apiOperation (" stream limiting by URL, with default stream limiting logic ")
    @GetMapping("/byUrl")
    @SentinelResource(value = "byUrl",blockHandler = "handleException")
    public CommonResult byUrl(a) {
        return new CommonResult("Limiting traffic by URL".200);
    }

    @apiOperation (" Custom generic flow limiting logic ")
    @GetMapping("/customBlockHandler")
    @SentinelResource(value = "customBlockHandler", blockHandler = "handleException",blockHandlerClass = CustomBlockHandler.class)
    public CommonResult blockHandler(a) {
        return new CommonResult("Flow limiting succeeded".200);
    }

    public CommonResult handleException(BlockException exception){
        return new CommonResult(exception.getClass().getCanonicalName(),200); }}Copy the code
  • Next create a basis in the Sentinel consoleThe name of the resourceRules for limiting current;

  • When we then access the interface at a higher speed, traffic limiting is triggered and the following information is returned.

Fusing the drop

  • Retrofit’s fuse degrade functionality also relies heavily on Sentinel, where we created a test classCircleBreakerControllerLet’s try its fusible downscaling function;
/** * Created by macro on 2019/11/7. */
@API (tags = "CircleBreakerController",description = "Fuse downgrade ")
@RestController
@RequestMapping("/breaker")
public class CircleBreakerController {

    private Logger LOGGER = LoggerFactory.getLogger(CircleBreakerController.class);
    @Autowired
    private UserService userService;

    ApiOperation(" Fuse downgrade ")
    @RequestMapping(value = "/fallback/{id}",method = RequestMethod.GET)
    @SentinelResource(value = "fallback",fallback = "handleFallback")
    public CommonResult fallback(@PathVariable Long id) {
        return userService.getUser(id);
    }

    ApiOperation(" Ignore exceptions to fuse downgrade ")
    @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,"Service degraded return".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,"Service degraded return".200); }}Copy the code
  • Because we’re not therenacos-user-serviceDefined in theThe id of 4The following interface will return the result of service degradation and return our default user information.

conclusion

Retrofit gives us a third option to call between microservices in addition to Feign and Dubbo, which is still very easy to use. Remember that in the previous process of using Feign, the Controller of the implementation side often had to extract an interface for the convenience of the caller to implement the call. The coupling degree between the interface implementation side and the caller was very high. This would have been much improved if Retrofit had been used. In general, Retrofit gives us a much more elegant way to make HTTP calls, not only in individual applications, but also in microservice applications!

The resources

Official documentation: github.com/LianjiaTech…

Project source code address

Github.com/macrozheng/…