OpenFeign组件服务间调用时参数传递

OpenFeign组件

微服务间通信的方式??

服务间通信的方式主要有以下两种:


1.基于Http应用层协议

特点

1.使用http rest方式,使用json作为数据交换的

2.效率较低,但是耦合度低(耦合度低意味着如果不同服务使用不同的技术框架进行开发后,进行通信的成本低)

典型的实现
1.RestTemplate
缺点:
1.服务调用使路径写死,耦合度高

2.要想实现负载均衡,必须得结合Ribbon组件,造成冗余代码


2.OpenFeign(推荐)
特点:
1.OpenFeign组件是spring团队基于netflix的Feign组件进行封装和扩展形成的自己的一个项目,它是一个伪HttpClient;

OpenFeign是一个伪HTTP客户端,底层还是RestTemplate,是对RestTemplate的封装

2.OpenFeign支持可插拔的编码器和解码器

比如帮我们默认实现json和其他数据类型的装换

3.OpenFeign默认集成了Ribbon

默认实现了负载均衡的效果,不需要我们再像RestTemlate那样,利用ribbon编写实现负载均衡的相关代码;
并且springcloud为feign添加了springmvc注解的支持。

4.使用方便,代码量少

使用时只需要添加几个注解,编写一个接口类即可,耦合度低


2.基于RPC传输层协议

特点

1.使用的是传输层的协议,以二进制方式传递数据

2.效率高,但是耦合度高,如果不同服务使用不同的技术框架进行开发后,进行通信的成本高

典型的RPC框架:Dubbo

总结:`在springcloud中服务间调用方式主要是使用 http restful方式进行服务间调用

OpenFeign服务间通信时参数传递

1.基本数据类型

GoodsClient接口

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

    /*
    * 注意:这里的方法需要保证返回类型,形参,请求路径一致
    * 方法名可以与被调用的服务名不同
    *
    * feign默认给我们实现了负载均衡!!
    * */
    @GetMapping("/test")
    String test(String name,Integer age);
}

商品服务controller

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

CategoryController调用商品服务的test方法

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

启动报错显示太多参数

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)

原因是:

我们知道通过QueryString传递有两种方式,一种是
/test?name=jw&age=20的形式
另一种是rest的方式,直接把参数写在路径中
/test/jw/20

由于OpenFeign是伪客户端,底层在调用服务时还是基于RestTemplate,当我们通过GoodsClient调用test服务是,GoodsClient会把接收的参数传递给RestTemplate进行封装;此时RestTemplate不知道该基于哪种方式进行封装,所以会报错;

如果我们只写一个参数,那么默认就是?name=xx的形式
如果有多个参数就需要我们用注解显示声明

@RequestParams
如果是想以/test?name=xxx&age=xxx 的形式传递参数,那么使@RequestParams;

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

    /*
    * 注意:这里的方法需要保证返回类型,形参,请求路径一致
    * 方法名可以与被调用的服务名不同
    *
    * feign默认给我们实现了负载均衡!!
    * */
   
    @GetMapping("/test")
    String test(@RequestParam String name,@RequestParam Integer age);
}

注意:如果我们@RequestParam赋值,被调用方需要一致

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

//商品服务
@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
如果是想以/test/jw/20形式传递参数,那么使用@PathVariable

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

    /*
    * 注意:这里的方法需要保证返回类型,形参,请求路径一致
    * 方法名可以与被调用的服务名不同
    *
    * feign默认给我们实现了负载均衡!!
    * */

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

	//商品服务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调用商品服务的test1方法**
	 @GetMapping
    public String testService(){
        String test = goodsClient.test1("jw", 20);
        return test;
    }

2.传递对象类型

使用@RequestBody注解会把对象以aplication/json的形式传参,被调用的服务也需要加上@RequestBody该注解,把收到的json转成对应的对象

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

    //接口
	@FeignClient(value = "GOODS")
	public interface GoodsClient {
    /*
    * 注意:这里的方法需要保证返回类型,形参,请求路径一致
    * 方法名可以与被调用的服务名不同
    *
    * feign默认给我们实现了负载均衡!!
    * */
    @PostMapping("/savegoods")
    void save(@RequestBody Goods goods);
	}
	
 	//GoodsController
	@PostMapping("/savegoods")
    public void save(@RequestBody Goods goods){
        log.info("goods:{}",goods);
    }

3.传递数组和集合类型

**数组类型使用@RequestParam会把ids数组拼装成
/test3/?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);
    }	

集合类型
springMVC中get方式是不能用集合作参数的
比如以/test4/?ids=1&ids=2&ids=3,接收不到

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

如果硬要接收集合,那么需要把集合作为类的属性

	@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;
    }
}

此时在FeignClient中还是同数组一样,因为@RequestParam会把接收到的ids拼接成/test4/?ids=1&ids=2&ids=3,在GoodController接收时,就会去Goodvo中找到ids进行赋值

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

注意:这里是GET方式需要接收一个List集合
如果是POST方式直接加上@RequestBody注解以json方式传递即可

上一篇:OpenFeign源码分析


下一篇:Spring Cloud OpenFeign 动态Url