目录
1.RestTemplate存在问题
2.OpenFeign介绍
一、主要特点
二、应用场景
3.OpenFeign快速上手
3.1引入依赖
3.2添加注解
3.3编写OpenFeign的客户端
3.4远程调用
编辑3.5测试
4.OpenFeign参数传递
4.1传递单个参数
4.2传递多个参数
4.3传递对象
4.4传递JSON
1.RestTemplate存在问题
观察原本远程调用的代码
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
String url = "http://product-service/product/"+ orderInfo.getProductId();
ProductInfo productInfo = restTemplate.getForObject(url,
ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
虽说RestTemplate对HTTP进行封装后,比直接使用HTTPClient方便很多,但还是存在一些问题:
- 需要拼接URL,灵活性高,但是封装臃肿,URL复杂时,容易出错
- 代码可读性差,风格不统一
微服务之间的通信方式,通常有两种:RPC和HTTP
在SpringCloud中,默认是使用HTTP来进行微服务的通信,最常用的实现形式有两种:RestTemplate和OpenFeign
RPC(Remote Procedure Call)远程过程调用,是一种通过网络从远程计算机上请求服务,而不需要了解底层网络通信细节。RPC可以使用多种网络协议进行通信,如HTTP、TCP、UDP等,并且在TCP/IP网络四层模型中跨越了传输层和应用层。简言之RPC就是像调用本地方法一样调用远程方法
常见的RPC框架有:
Dubbo: https://cn.dubbo.apache.org/zh-cn/Thrift :Apache Thrift - Home
gRPC:gRPC
2.OpenFeign介绍
OpenFeign是一种声明式的REST客户端,它是Netflix开源的一款基于Java的HTTP客户端库,后成为Spring Cloud的二级子项目。OpenFeign通过声明式的方式定义REST API接口,并自动生成实现该接口的客户端代码,从而极大地简化了HTTP请求和响应的处理过程,特别是在微服务架构中,它极大地简化了服务之间的调用。以下是对OpenFeign的详细介绍:
一、主要特点
- 声明式API:通过注解方式声明API接口,如使用@GetMapping、@PostMapping等Spring MVC注解,让代码更加简洁和易于维护。
- 自动封装HTTP请求:OpenFeign自动封装HTTP请求,包括HTTP方法、请求URL、请求参数、请求头等,开发者无需手动编写HTTP请求代码。
- 支持多种编解码器:支持JSON、XML、Form等多种数据格式的编解码,开发者可以根据需要选择合适的编解码器。
- 请求拦截器和响应拦截器:开发者可以通过实现请求拦截器和响应拦截器来对HTTP请求和响应进行处理,如添加认证信息、重试机制等。
- 负载均衡:支持通过Ribbon进行负载均衡,可以实现多个服务提供方的负载均衡。
- 熔断器支持:集成了Hystrix熔断器,可以在服务调用失败或超时时进行降级处理,保证服务的可靠性和可用性。
- 灵活的配置方式:支持多种配置方式,包括通过注解、属性文件、Java配置等方式进行配置,可以灵活地适应不同的需求。
二、应用场景
OpenFeign在微服务架构中有着广泛的应用场景,主要包括:
- 服务间调用:在微服务架构中,服务间的调用是常见的需求,OpenFeign提供了一种简单、高效的方式来实现服务间的远程调用。
- 客户端负载均衡:通过整合Ribbon,OpenFeign可以实现客户端的负载均衡,提高系统的可用性和响应速度。
- 服务降级:集成Hystrix熔断器,可以在服务调用失败或超时时进行降级处理,避免级联故障的发生。
3.OpenFeign快速上手
3.1引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3.2添加注解
在order-service的启动类添加注解 @EnableFeignclients,开启OpenFeign的功能
@EnableFeignClients
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
3.3编写OpenFeign的客户端
import com.bite.order.model.ProductInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
}
@FeignClient注解作用在接口上,参数说明:
- name/value:指定FeignClient的名称,也就是微服务的名称,用于服务发现,Feign底层会使用 SpringCloud LoadBalance进行负载均衡.也可以使用url属性指定一个具体的url
- path:定义当前FeignClient的统一前缀.
3.4远程调用
修改远程调用的方法
@Autowired
private ProductApi productApi;
/**
* Feign实现远程调⽤
* @param orderId
*/
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
ProductInfo productInfo = productApi.getProductById(orderInfo.getProductId());
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
代码对比:
3.5测试
4.OpenFeign参数传递
通过观察,我们可以发现,Feign的客户端和服务提供者的接口声明非常相似
上面例子中,演示了Feign从URL中获取参数,接下来演示下Feign参数传递的其他方式
4.1传递单个参数
服务提供方product-service
@RequestMapping("/product")
@RestController
public class ProductController {
@RequestMapping("/p1")
public String p1(Integer id){
return "p1接收到参数:"+id;
}
}
Feign客户端:
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/p1")
String p1(@RequestParam("id") Integer id);
}
注意: @RequestParam 做参数绑定, 不能省略
@RequestMapping("/feign")
@RestController
public class TestFeignController {
@Autowired
private ProductApi productApi;
@RequestMapping("/o1")
public String o1(Integer id){
return productApi.p1(id);
}
}
测试远程调用:http://127.0.0.1:8080/feign/o1?id=5
4.2传递多个参数
使用多个@RequestParam进行参数绑定即可
服务提供方product-service:
@RequestMapping("/p2")
public String p2(Integer id,String name){
return "p2接收到参数,id:"+id +",name:"+name;
}
Feign客户端:
@RequestMapping("/p2")
String p2(@RequestParam("id")Integer id,@RequestParam("name")String name);
服务消费方order-service:
@RequestMapping("/o2")
public String o2(@RequestParam("id")Integer id,@RequestParam("name")String name){
return productApi.p2(id,name);
}
4.3传递对象
服务提供方product-service:
@RequestMapping("/p3")
public String p3(ProductInfo productInfo){
return "接收到对象, productInfo:"+productInfo;
}
Feign客户端:
@RequestMapping("/p3")
String p3(@SpringQueryMap ProductInfo productInfo);
服务消费方order-service:
@RequestMapping("/o3")
public String o3(ProductInfo productInfo){
return productApi.p3(productInfo);
}
测试远程调用:http://127.0.0.1:8080/feign/o3?id=5&productName=zhangsan
4.4传递JSON
服务提供方product-service:
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo){
return "接收到对象, productInfo:"+productInfo;
}
Feign客户端:
@RequestMapping("/p4")
String p4(@RequestBody ProductInfo productInfo);
服务消费方order-service:
@RequestMapping("/o4")
public String o4(@RequestBody ProductInfo productInfo){
System.out.println(productInfo.toString());
return productApi.p4(productInfo);
}
测试远程调用:http://127.0.0.1:8080/feign/o4