SpringCloud(三)——OpenFeign组件
前言
OpenFeign组件也是一个专门做负载均衡的组件工具,Ribbon现在已经开始维护,所以我们在低版本中使用Ribbon是完全可行的,但是在近两年的新版本中,还是比较推荐OpenFeign组件。
OpenFeign组件相较于Ribbon来说,使用起来更为便捷,不需要像Ribbon那样需要工厂管理对象,然后在加上Ribbon的注解,在需要调用的时候明文书写路径;OpenFeign将这些操作简化后封装成接口,我们通过接口 + 注解的方式来完成负载均衡的调用。
OpenFeign组件同样是通过http
协议来进行远程通信,但是该组件是一个伪HTTP协议,因为真正用来远程通信的不是它,依旧是RestTemplate。该组件依旧只是完成负载均衡,所以在使用的时候有一些规则需要注意。
令人欣慰的是,该组件支持SpringMVC
的注解,所以在使用上会更好理解。
文章目录
项目搭建
这次的项目搭建需要一些小技巧,具体为何这么创建项目我们在下面会有讲解。
我们在SpringCloud 架构搭建的时候一直都是在一个父项目中搭建,这样可以控制整个项目的版本依赖和加载核心工具类。在搭建测试OpenFeign工具的服务时需要在父项目中提供一个调用的服务之间都需要的实体类。
// springcloud-parent 项目中搭建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class ServerEntity implements Serializable {
private String sid;
private String sname;
}
实体类完成后我们需要将父项目中的实体类在它的子项目中可以直接引用,这个时候我们就需要在子项目中引入府项目的依赖。
**【注】:**我们在父项目下创建子项目的时候,从父项目中继承的是父项目的依赖,而父项目中所定义的一些类并不会因为<parent>
标签而被子项目引入。
<dependency>
<groupId>com.test</groupId>
<artifactId>springcloud_parent</artifactId>
<version>1.0</version>
</dependency>
现在项目架构搭建完毕,接下来将子项目中的依赖引入。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--health check-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--consul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
接下来需要将consul
的相关配置以及控制器定义好,在此不做赘述。
OpenFeign简单使用
前文说过,该组件的使用是将负载均衡等调用其他服务的代码和操作写一个接口,该接口定义在服务调用方,定义的接口如下:
@FeignClient(value="SERVER") // 该接口的value属性表示未来需要调用哪个服务的ID
public interface ServerClient {
// OpenFeign组件支持SpringMVC注释,该接口方法表示我们将要在Client项目中调用Server端的方法
@GetMapping("/server")
public String server();
}
服务被调用方需要有一个和上述接口中调用的参数、请求方式以及路径相同的方法:
@RestController
public String ServerController(){
@Value("${server.port}")
private String port;
@GetMapping("/server")
public String server(){
return port;
}
}
接下来,就可以在服务调用方调用该方法了。
@RestController
public String ClientController{
@Autowired
private ServerClient serverClient;
@Value("${server.port}")
private String port;
@GetMapping("/client")
public String client(){
String result = serverClient.server();
return "client is success: " + port;
}
}
以上,最简单的OpenFeign组件的负载均衡使用已经搭建完毕,至于多服务的负载均衡,大家可以自行的搭建多服务测试。
OpenFeign的参数传递
参数传递方面相比之下会有一些小小的复杂,前文说过该组件是一个伪HTTP协议,即真正进行HTTP连接不是该组件。所以HTTP可以直接理解并支持的传参方法和格式并不会被该组件识别。
所以我们需要更加严格的语法支持(其实这些语法和注解都是SpringMVC框架在开发的时候建议我们使用的,只是在SpringMVC中的大部分情况属于默认或者框架和HTTP协议可以直接理解的方式,主键被我们忽略掉而已)。
零散参数传递
零散参数在传递过程中,需要我们将各个参数的key值告诉对应的方法,这样才可以按照key来获取对应的value。
Restful风格
改风格下,参数按照路径的方式传递和获取,我们需要使用@PathVariable
注解来表示:
@GetMapping("/test1/{name}/{age}")
public String test1(@PathVariable("name")Stirng name, @PathVariable("age")String age){
}
需要注意的是,不单单在客户端控制器将该注解使用,同时在OpenFeign接口以及服务被调用方的控制器中也必须按照这种格式来完成。(有些同学可能会发现不需要完全按照这个格式来实现也可以执行相关操作,个人建议还是需要按照官方推荐的规则来书写代码,以便提升代码健壮性)。
form表单
form表单传参需要使用@RequestParam
来识别对应的字段键值:
@GetMapping("/test2")
public String test2(@RequestParam("name")Stirng name, @RequestParam("age")String age){
}
对象类型传参
对象类型传参需要的是将对象以json格式方式传递给控制器,所以需要在控制器的对象参数前加上@RequestBody
来表示我们将参数以json格式来解析。
@PostMapping("/test3")
public String test3(@RequestBody Entity entity){
}
当然,如果从服务调用方以表单的方式传递给服务被调用方,并且是以对象方式来传参的话,现在只能说可以实现但没必要(较为复杂)。
OpenFeign组件时用来做服务与服务之间调用的负载均衡组件,它在form表单方面的执行并不是很出色,因为需要服务之间传递对象的时候可以直接传递,为什么还要以form表单方式来实现。如果是调用方的前端需要form表单提交对象最终交给被调用方处理,我们依旧可以通过调用方的控制器来处理。
集合类型传参
如果是想传递数组的话我们依旧是使用@RequestParam("")
注解来传递参数:
@GetMapping("/test4")
public String test4(@RequestParam("strs") String[] strs){
String[] strs = new String[]{"12","23","34"};
for (String str : strs) {
System.out.println(str);
}
}
如果需要传递的参数类型为集合类型则需要费一些功夫,因为我们的OpenFeign组件无法直接识别集合类型的数据,所以我们需要将集合类型放在一个自定义的对象中,按照对象的规则来传递参数。
这种情况是否很麻烦?其实,这种方式是标准的项目开发方式。我们在日常开发的时候不会将零散的参数或者多个集合对象相应给前端或者其他服务,我们都会将所有的多个参数或多个集合封装为一个对象相应给其他人。(具体思想请参照POJO、POVO、DTO
的相关概念)。
集合类型
我们需要定义一个POVO来讲集合类型封装在一个对象中,参考如下:
@Data
public class ConnectionVO{
private List<String> strList;
}
首先,OpenFeign组件无法直接处理集合类型的参数,这个无法处理表示服务调用方的接口处不能接受并处理集合类型。那么就需要将集合类型的参数以另一种方式来呈现。在这里使用List
集合举例:
List集合的底层实现是数组,所以在实现过程中使用数组的方式来传递参数。
@GetMapping("/clientForConnection")
public String clientForConnection(){
String[] strList = new String[]{"123","456"};
String result = serverClient.test3(strList);
return result;
}
然后在OpenFeign的组件接口中定义一个接收数组数据的接口方法:
@GetMapping("/test3")
public String test3(@RequestParam("strList") String[] strList);
最后,在服务被调用方中,需要定义一个控制器,该控制器的参数为之前做好的POVO
:
@GetMapping("/test3")
public String test3(ConnectionVO vo){
List<String> list = vo.getStrList();
list.forEach(str -> System.out.println(str));
return "list is success: " + port;
}
在服务被调用方的控制器中,如果想直接将调用方传递的数据封装为集合,则需要使用定义好的POVO
对象来接收(不知道为什么会自动封装为对象的同学回去好好恶补SSM
阶段的知识)。
集合小结
集合类型参数的传递思想概括来说分以下几个过程:
- SpringMVC接口可以正常接收前端传递的集合类型;OpenFeign组件处理的接口无法处理集合类型;
- 调用方接口可以接收集合,将集合以OpenFeign可以处理的格式发送;
- 被调用方将OpenFeign处理的数据接收后,自动封装入
POVO
中、
总结
本篇笔记中记录了OpenFeign组件的使用以及该组件在参数传递方面的规则。尤其要记住的是参数传递的时候依据不同的情况需要使用不同的注解或处理规则。
上文说过,从编程逻辑的角度来理解,为什么OpenFeign
组件在表单和集合方面的处理十分不友好。其实在标准的编程思想中,所有更新以及改进应该都遵循一个原则,那就是向下兼容;但是,如果之前的功能或组件在使用过程或功能实现的过程中过于鸡肋的话,新开发的组件在是否兼容的选项中要慎重考虑,如果可以给出一个更加便捷、安全的方案,其实是可以遗弃其他组件的功能或操作的(就好比之前的JSP被弃用一样)。
OpenFeign
组件在使用的时候相比Ribbon
来说并不是很简单,但是在实际使用的时候可以兼容很多业务。尤其在负载均衡方面有很大的优势。