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方式传递即可