Parameter passing when calling between OpenFeign component services

Posted by arunmj82 on Wed, 05 Jan 2022 05:30:53 +0100

Communication mode between microservices??

There are two main modes of communication between services:

1. Based on Http application layer protocol

characteristic

1. Use http rest and json for data exchange

2. Low efficiency, but low coupling (low coupling means that if different services are developed using different technical frameworks, the cost of communication is low)

Typical implementation
1.RestTemplate
Disadvantages:
1. The service call makes the path write dead, and the coupling degree is high
2. To achieve load balancing, you must combine Ribbon components to create redundant code

2. Openfeign (recommended)
characteristic:
1.OpenFeign component is a project formed by the spring team based on the Feign component of netflix, which is a pseudo HttpClient;

OpenFeign is a pseudo HTTP client. The underlying layer is RestTemplate, which encapsulates RestTemplate

2.OpenFeign supports pluggable encoders and decoders.

For example, help us implement the installation and replacement of json and other data types by default

3.OpenFeign integrates Ribbon by default

By default, the effect of load balancing is realized. We don't need to use ribbon to write relevant code to realize load balancing like RestTemlate;
Spring cloud adds spring MVC annotation support to feign.

4. Easy to use, less code

When using, you only need to add a few annotations and write an interface class, with low coupling

2. Based on RPC transport layer protocol

characteristic

1. Transport layer protocol is used to transfer data in binary mode

2. High efficiency, but high coupling. If different services are developed using different technical frameworks, the cost of communication is high

Typical RPC framework: Dubbo

Summary: ` in spring cloud, inter service invocation mainly uses http restful method for inter service invocation

Parameter passing during communication between OpenFeign services

1. Basic data type

GoodsClient interface

@FeignClient(value = "GOODS")
public interface GoodsClient {

    /*
    * Note: the method here needs to ensure that the return type, formal parameter and request path are consistent
    * The method name can be different from the service name being called
    *
    * feign Load balancing is realized by default!!
    * */
    @GetMapping("/test")
    String test(String name,Integer age);
}

Goods and services controller

 	@GetMapping("/test")
    public String test(String name,Integer age){
        log.info("name:{},age:{}",name,age);
        return name + age;
    }

CategoryController calls the test method of commodity service

@GetMapping
    public String testService(){
        String test = goodsClient.test("jw", 20);
        return test;
    }

Too many parameters are displayed for startup error

Caused by: java.lang.IllegalStateException: Method has too many Body parameters: public abstract java.lang.String com.jw.feignclient.GoodsClient.test(java.lang.String,java.lang.Integer)

as a result of:

We know that there are two ways to pass through QueryString. One is
/test? Form of name = JW & age = 20
The other is the rest method, which directly writes the parameters in the path
/test/jw/20

Because OpenFeign is a pseudo client, the underlying layer is still based on RestTemplate when calling the service. When we call the test service through GoodsClient, GoodsClient will pass the received parameters to RestTemplate for encapsulation; At this time, RestTemplate does not know which method to encapsulate, so an error will be reported;

If we only write one parameter, the default is? Form of name=xx
If there are multiple parameters, we need to display the declaration with annotations

@RequestParams
If you want to use / test? If parameters are passed in the form of name = XXX & age = XXX, @ RequestParams;

@FeignClient(value = "GOODS")
public interface GoodsClient {

    /*
    * Note: the method here needs to ensure that the return type, formal parameter and request path are consistent
    * The method name can be different from the service name being called
    *
    * feign Load balancing is realized by default!!
    * */
   
    @GetMapping("/test")
    String test(@RequestParam String name,@RequestParam Integer age);
}

Note: if we assign @ RequestParam, the callee needs to be consistent

@GetMapping("/test")
String test(@RequestParam("aa") String name,@RequestParam("bb") Integer age);

//Goods and services
@GetMapping("/test")
public String test(String aa,Integer bb){
     	log.info("name:{},age:{}",aa,bb);
       return aa + bb;
}

@GetMapping("/test")
public String test(@RequestParam("aa") String name,@RequestParam("bb") Integer age){
     	log.info("name:{},age:{}",name,age);
       return name + age;
}

@PathVariable
If you want to pass parameters as / test/jw/20, @ PathVariable is used

@FeignClient(value = "GOODS")
public interface GoodsClient {

    /*
    * Note: the method here needs to ensure that the return type, formal parameter and request path are consistent
    * The method name can be different from the service name being called
    *
    * feign Load balancing is realized by default!!
    * */

    @GetMapping("/test1/{name}/{age}")
    String test1(@PathVariable("name") String name, @PathVariable("age") Integer age);
}

	//Goods and services controller
	@GetMapping("/test1/{name}/{age}")
    public String test1(@PathVariable("name")String name,@PathVariable("age") Integer age){
        log.info("name:{},age:{}",name,age);
        return name+age;
    }
    
  	//**CategoryController calls test1 method of commodity service**
	 @GetMapping
    public String testService(){
        String test = goodsClient.test1("jw", 20);
        return test;
    }

2. Transfer object type

Using the @ RequestBody annotation will transfer the object to the parameter in the form of application / json. The invoked service also needs to add the @ RequestBody annotation to convert the received json into the corresponding object

	//categoryController
	@GetMapping
    public String testService(){
        //String test = goodsClient.test1("jw", 20);
        goodsClient.save(new Goods(1,"aa",22.12,new Date()));
        return null;
    }

    //Interface
	@FeignClient(value = "GOODS")
	public interface GoodsClient {
    /*
    * Note: the method here needs to ensure that the return type, formal parameter and request path are consistent
    * The method name can be different from the service name being called
    *
    * feign Load balancing is realized by default!!
    * */
    @PostMapping("/savegoods")
    void save(@RequestBody Goods goods);
	}
	
 	//GoodsController
	@PostMapping("/savegoods")
    public void save(@RequestBody Goods goods){
        log.info("goods:{}",goods);
    }

3. Pass array and collection types

**Array type using @ RequestParam will assemble ids array into
/test3/? Form of IDS = 1 & IDs = 2 & IDs = 3

	@FeignClient(value = "GOODS")
	public interface GoodsClient {
    @GetMapping("/test3")
    void test3(@RequestParam("ids") String[] ids);
	}
	==================================
	  //goodsController
	@GetMapping("/test3")
    public void test3( String[] ids){
        log.info("ids:{}",ids);
    }	

Collection type
The get method in spring MVC cannot use set cooperation parameters
For example, / test4 /? IDS = 1 & IDs = 2 & IDs = 3, not received

	//no way
   @GetMapping("/test4")
   public void test4(ArrayList<String> ids){
       
   }
 

If you insist on receiving a collection, you need to treat the collection as an attribute of the class

	@GetMapping("/test3")
    public void test3( GoodsVO vo){
        log.info("ids:{}",ids);
    }	
	
	class ProductVO{
    private ArrayList<String> ids;

    public ArrayList<String> getIds() {
        return ids;
    }

    public void setIds(ArrayList<String> ids) {
        this.ids = ids;
    }
}

At this time, it is the same as the array in FeignClient, because @ RequestParam will splice the received ids into / test4 /? ids = 1 & ids = 2 & ids = 3. When GoodController receives, it will find ids in Goodvo for assignment

   @FeignClient(value = "GOODS")
   public interface GoodsClient {
   @GetMapping("/test4")
   void test3(@RequestParam("ids") String[] ids);
   }

Note: This is the GET mode. You need to receive a List collection
If it is POST mode, you can directly add @ RequestBody annotation and pass it in json mode

Topics: Java Spring Cloud Spring MVC OpenFeign