Source code address: gitee.com/fighter3/es…

Continuously updated…

After successfully registering the service in the Nacos registry and implementing service registration and service discovery in the previous section, we are going to do inter-service invocation.

What are the ways we call interfaces on a daily basis? There are Common JDK network connection class HttpURLConnection, Apache Common package HttpClient, Spring package RestTemplate. None of these call interface tools might seem difficult to you, but if you introduce Feign, using declarative calls, calling remote services is as slippery as calling local apis.

OpenFeign project address: github.com/OpenFeign/f…

1. Introduction to Feign

Feign is a declarative, templated HTTP client. With Feign, declarative invocation is possible.

Feign is one of the most widely used remote invocation frameworks today, even though it is no longer iterative and in a maintenance state.

Within SpringCloud Alibaba’s ecosystem, there is another widely used remote service invocation framework, Dubbo, which we’ll look at later.

Feign is a further encapsulation of RestTemplate and Ribbon, using RestTemplate for Http calls and Ribbon for load balancing.

Next, let’s learn how to use Feign, very simple!

2. Used by Feign

2.1. OpenFeign was introduced

In the previous section, we introduced SpringCloud, now we just need to add dependencies to the submodules we need to introduce:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
Copy the code

2.2 Feign Remote call

Let’s do one business now: add goods

This business involves two sub-services, adding inventory when adding items and querying inventory when querying items. Goods and services as consumers, inventory services as producers.

2.2.1 Service providers

The inventory service as a service provider is simple, providing two interfaces to add inventory and get inventory based on the item ID.

  • Control layer
@RestController
@RequestMapping("/shop-stock/api")
@Slf4j
@api (value = "tags ", tags =" tags ")
public class ShopStockApiController {
    @Autowired
    private IShopStockService shopStockService;

    @PostMapping(value = "/add")
    @apiOperation (" Add inventory ")
    public Integer addStock(@RequestBody StockAddDTO stockAddDTO) {
        log.info("client call add stock interface,param:{}", stockAddDTO);
        return this.shopStockService.addStockApi(stockAddDTO);
    }

    @GetMapping(value = "/account/get")
    @apiOperation (" Get inventory by commodity ID ")
    public Integer getAccountById(@RequestParam Integer goodsId) {
        return this.shopStockService.getAccountById(goodsId); }}Copy the code

Note that in order to demonstrate a similar effect of local calls, these two interfaces are different from normal front – and back-end interfaces.

We did not return CommonResult as we had set before, but returned the data directly.

  • The business layer

    Ordinary increase, check only

    /** * Add inventory - return primary key ** directly@param stockAddDTO
     * @return* /
    public Integer addStockApi(StockAddDTO stockAddDTO) {
        ShopStock stock = new ShopStock();
        stock.setGoodsId(stockAddDTO.getGoodsId());
        stock.setInventory(stockAddDTO.getAccount());
        log.info("Ready to add inventory, parameter :{}", stock.toString());
        this.baseMapper.insert(stock);
        Integer stockId =stock.getStockId();
        log.info("Stock added successfully,stockId:{}", stockId);
        return stockId;
    }

    /** * Get the inventory of goods according to the goods ID **@param goodsId
     * @return* /
    public Integer getAccountById(Integer goodsId) {
        ShopStock stock = this.getOne(Wrappers.<ShopStock>lambdaQuery().eq(ShopStock::getGoodsId, goodsId));
        Integer account = stock.getInventory();
        return account;
    }

Copy the code
  • Add an inventory entity class
@Data
@EqualsAndHashCode(callSuper = false)
@apiModel (value = "inventory add ", description = "")
public class StockAddDTO implements Serializable {
    private static final long serialVersionUID = 1L;

    @apiModelProperty (value = "primary key ")
    private Integer goodsId;

    @apiModelProperty (value = "quantity ")
    private Integer account;
}
Copy the code

At this point, our service provider related development to the complete, open the address http://localhost:8050/doc.html, you can see our development interface:

2.2.2 Service consumers

Okay, now we’re going to start our service consumer, which is the development of goods and services.

  • Remotely invoke the Feign client

Declarative calls — Take a look at the Feign client code to see what a declarative call is:

/ * * *@Author: Three points of evil *@Date: 2021/5/26
 * @Description: Inventory service Feign client **/
@FeignClient(value = "stock-service")
public interface StockClientFeign {

    /** * calls the Add inventory interface **@param stockAddDTO
     * @return* /
    @PostMapping(value = "/shop-stock/api/add")
    Integer addStock(@RequestBody StockAddDTO stockAddDTO);

    /** * invokes the get inventory by item ID interface **@param goodsId
     * @return* /
    @GetMapping(value = "/shop-stock/api/account/get")
    Integer getAccountById(@RequestParam(value = "goodsId") Integer goodsId);
}
Copy the code
  • Once the definition is complete, we also need to annotate the startup class@EnableFeignClientsTo scan the Feign client.
@SpringBootApplication
@MapperScan("cn.fighter3.mapper")
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "cn.fighter3.client")
public class EshopGoodsApplication {

    public static void main(String[] args) { SpringApplication.run(EshopGoodsApplication.class, args); }}Copy the code

Using the Feign client is also easy, just inject it where it needs to be used.

@Autowired
private StockClientFeign stockClientFeign;
Copy the code
  • Commodity service control layer
/** ** <p> * Front-end controller * </p> **@authorThree points *@sinceThe 2021-05-18 * /
@RestController
@RequestMapping("/shop-goods")
@api (value = "product management interface ", tags =" product interface ")
@Slf4j
public class ShopGoodsController {

    @Autowired
    private IShopGoodsService goodsService;

    @PostMapping(value = "/add")
    @apiOperation (value = "add product ")
    public CommonResult addGoods(@RequestBody GoodsAddDTO goodsAddDTO) {
        return this.goodsService.addGoods(goodsAddDTO);
    }

    @GetMapping(value = "/get/by-id")
    @apiOperation (value = "get goods by ID ")
    public CommonResult<GoodsVO> getGoodsById(@RequestParam Integer goodsId) {
        return this.goodsService.getGoodsById(goodsId); }}Copy the code
  • The service layer

In the service layer, in addition to the operation of the commodity library, the Feign client remotely calls the interface of the inventory service.

@Service
@Slf4j
public class ShopGoodsServiceImpl extends ServiceImpl<ShopGoodsMapper.ShopGoods> implements IShopGoodsService {

    @Autowired
    private StockClientFeign stockClientFeign;

    /** * add item **@param goodsAddDTO
     * @return* /
    public CommonResult addGoods(GoodsAddDTO goodsAddDTO) {
        ShopGoods shopGoods = new ShopGoods();
        BeanUtils.copyProperties(goodsAddDTO, shopGoods);
        this.baseMapper.insert(shopGoods);
        log.info("Add item, primary key of item: {}", shopGoods.getGoodsId());
        log.info(shopGoods.toString());
        StockAddDTO stockAddDTO = StockAddDTO.builder().goodsId(shopGoods.getGoodsId()).account(goodsAddDTO.getAccount()).build();
        log.info("Ready to add inventory, parameter: {}", stockAddDTO.toString());
        Integer stockId = this.stockClientFeign.addStock(stockAddDTO);
        log.info("Finished adding inventory, inventory primary key :{}", stockId);
        return CommonResult.ok();
    }

    /** * get the goods **@param goodsId
     * @return* /
    public CommonResult<GoodsVO> getGoodsById(Integer goodsId) {
        GoodsVO goodsVO = new GoodsVO();
        // Get basic commodity information
        ShopGoods shopGoods = this.baseMapper.selectById(goodsId);
        BeanUtils.copyProperties(shopGoods, goodsVO);
        // Get the quantity of goods in stock
        Integer account = this.stockClientFeign.getAccountById(goodsId);
        log.info("Quantity of Goods :{}", account);
        goodsVO.setAccount(account);
        returnCommonResult.ok(goodsVO); }}Copy the code
  • Entity class

    Add inventory entity class and inventory service same, skip, goods display entity class

@Data
@EqualsAndHashCode(callSuper = false)
@apiModel (value = "product ", description = "")
public class GoodsVO implements Serializable {
    private static final long serialVersionUID = 1L;

    @apiModelProperty (value = "primary key ")
    private Integer goodsId;

    @apiModelProperty (value = "product name ")
    private String goodsName;

    @apiModelProperty (value = "price ")
    private BigDecimal price;

    @apiModelProperty (value = "product description ")
    private String description;

    @apiModelProperty (value = "quantity ")
    private Integer account;
}
Copy the code

2.2.3 Effect demonstration

Next start nacOS-Server, goods service, inventory service.

Visit the address http://127.0.0.1:8848/nacos/index.html, after login, can see in the list of services we registered two services:

Access to goods and services Knife4j address: http://localhost:8020/doc.html, you can see add goods and according to the commodities ID search interface, debugging call:

  • Add the goods

  • Get goods by ID

You can see that the corresponding database also has data generation:

The overall remote call diagram looks something like this:

2.3. Ribbon Load Balancing

As for load balancing, I’m not going to demonstrate it here.

For those interested, package the inventory service, start it on a different port, then add the goods and see the load of the goods service call through the log.

Feign load balancing is implemented through the Ribbon, which is a client-side load balancing — that is, the Ribbon gets a list of services from a registry, and the client decides which remote service to call.

The Ribbon has the following load balancing strategies:

Rule name The characteristics of
AvailabilityFilteringRule Filter out back-end servers tagged with circuit tripped that repeatedly failed to connect and filter out those with high concurrency or use an AvailabilityPredicate that includes the logic to filter servers, Check the running status of each server recorded in status
BestAvailableRule Pick a server with minimal concurrent requests, inspect servers one by one, and skip them if they tripped
RandomRule Select a Server at random
ResponseTimeWeightedRule Deprecated, same function as WeightedResponseTimeRule
WeightedResponseTimeRule Based on response time weighting, the longer the response time, the smaller the weight and the lower the probability of being selected
RetryRule The retry mechanism is added to the load balancing policy. If the Server selection fails during the configuration period, the system tries to use subRule to select an available Server
RoundRobinRule Poll Select, poll index, and select the Server corresponding to index
ZoneAvoidanceRule The default load balancing strategy, which is to select the Server based on the performance and availability of the Server in the context of no region, is similar to polling (RandomRule).

I won’t expand on it here, but I’m interested in it.

3. Unexpected conditions

  • Read response time timed out from remote call:
java.net.SocketTimeoutException: Read timed out
Copy the code

Modify the Ribbon timeout Settings:

Ribbon ribbon: ReadTimeout:30000
  ConnectTimeout: 30000
Copy the code
  • Feign interface, used@RequestParamAn error

Error found:

Caused by: java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0
Copy the code

The Feign declaration needs to include a value:

Integer getAccountById(@RequestParam(value = "goodsId") Integer goodsId);
Copy the code


“Do simple things repeatedly, do repetitive things carefully, and do serious things creatively!” –

I am three points evil, can call me old three/three minutes/three elder brother/three son, a full stack of literary and military development, let’s see next period!





Reference:

[1] : Small column “SpringCloudAlibaba Micro-service Practice”

[2] : SpringCloud Alibaba micro-service combat iii – service invocation

[3] : Ribbon load balancing strategies, principles, and extensions