Feign声明式服务调用

Feign声明式服务调用

1、什么是Feign

Feign是Spring Cloud Netflix组件中的一个轻量级RESTful的HTTP服务客户端,实现了负载均衡和rest调用的开源框架,封装了Ribbon和RestTemplate,实现了WebService的面向接口编程,进一步降低了项目的耦合度。

Feign本身并不支持Spring MVC的注解,它有一套自己的注解,为了更方便的使用,Spring Cloud孵化了OpenFeign。

2、Feign解决什么问题

Feign旨在使编写Java HTTP客户端变得更加容易,Feign简化了RestTemplate代码,实现了Bibbon负载均衡,使代码变得更加简洁,也减少了客户端调用的代码,使用Feign实现负载均衡是官方首选,只需要你创建一个接口,然后在上面添加注解即可。

Feign是声明式服务调用组件,其核心是:像调用本地方法一样调用远程方法,无感知远程HTTP请求。

  • 它解决了让开发者调用远程接口就跟调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求,无需关注与远程交互的细节。
  • 它像Dubbo一样,Consumer直接调用Provider接口方法,而不需要通过常规的HTTP Client构造请求再解析返回数据。

3、Feign VS OpenFeign

OpenFeign是Spring Cloud在Feign的基础上支持了Spring MVC的注解,如@RequestMapping、@Pathvariable等等。

OpenFeign的@FeignClient可以解析Spring MVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,在实现类中做负载均衡并调用服务。

4、Hello World

Feign的使用主要分为以下几个步骤

  • 服务消费者添加Feign依赖
  • 创建业务接口,添加@FeignClient注解声明需要的调用的服务
  • 业务层抽象方法使用SpringMVC注解配置服务地址及参数
  • 启动类添加@EnableFeignClients注解

示例代码地址:https://gitee.com/junweihu/eureka-demo

添加依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

创建业务接口

// 第二步,声明要远程调用的微服务
@FeignClient("service-provider")
public interface ProductService {

    // 第三步,使用springMVC注解实现远程调用
    @GetMapping("/product/list")
    List<Product> selectProductList();
}

启动类添加注释@EnableFeignClients

@EnableFeignClients
@SpringBootApplication
public class ServiceConsumerApplication
{
    public static void main( String[] args )
    {
        SpringApplication.run(ServiceConsumerApplication.class);
    }
}

5、Feign负载均衡

Feign封装了Ribbon自然的也就集成了负载均衡的功能,默认采用轮询策略。修改负载均衡策略与Ribbon配置一致。

image-20220419150648852

5.1、全局

在启动类或者配置类中注入负载均衡策略对象,所有请求使用该策略。

@Configuration
public class AppConfig {
    /**
     * 随机负载均衡
     * @return
     */
    @Bean
    public RandomRule randomRule() {
        return new RandomRule();
    }
}

5.2、局部

修改配置文件中指定服务的负载均衡策略。格式:服务应用名.ribbon.NFLoadBalancerRuleClassName

# 局部负载均衡策略
# service-provider为调用的服务名称
service-provider:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

6、Feign请求传参

6.1、GET

使用@PathVariable注解或@RequestParam注解接口请求参数。

6.1.1、服务提供者

ProductService.java

Product selectProductById(Integer id);

ProductServiceImpl.java

    @Override
    public Product selectProductById(Integer id) {
        return new Product(id, "饮水机", 1, 500d);
    }

ProductController.java

@RestController
@RequestMapping("/product")
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping("/{id}")
    public Product selectProductById(@PathVariable Integer id) {
        return productService.selectProductById(id);
    }
}
6.1.2、服务消费者

ProductService.java

@FeignClient("service-provider")
public interface ProductService {

    @GetMapping("/product/{id}")
    Product selectProductById(@PathVariable Integer id);
}

OrderServiceImpl.java

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private ProductService productService;

    @Override
    public Order selectOrderById(Integer id) {
        return new Order(id, "order-001", "中国", 311990d, Arrays.asList(productService.selectProductById(id)));
    }

6.2、POST

使用@RequestBody注解接受请求参数。

6.2.1、服务提供者

ProductService.java

Map addProduct(Product product);

ProductServiceImpl.java

@Override
public Map addProduct(Product product) {
    return new HashMap() {
        {
            put("code",200);
            put("message", "success");
            put("data", product);
        }
    };
}

ProductController.java

  @PostMapping("/add")
    public Map addProduct(@RequestBody Product product) {
        return productService.addProduct(product);
    }
6.2.2、服务消费者

ProductService.java

@PostMapping("/product/add")
Map addProduct(@RequestBody Product product);

OrderController.java

    @PostMapping("/addProduct")
    public Map addProduct(@RequestBody Product product) {
        return productService.addProduct(product);
    }

7、Feign性能优化

7.1、Gzip

gzip:是一种数据格式,采用deflate算法压缩数据,应用广泛,尤其是在Linux平台。

gzip能力:当用gzip压缩一个纯文本文件时,大约可以减少70%以上的文件大小。

gzip作用:网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页的加载速度。网络加载速度加快的好处不言而喻,除了节省流量,改善用户体验外,另一个潜在的好处是gzip与搜索引擎的抓取工具有着更好的关系,例如Google就可以通过直接读取gzip文件来比普通手工抓取更快地检索网页。

image-20220419174044302

7.1.1、HTTP协议关于压缩传输协议的规定
  1. 客户端向服务器请求中带有:Accept-Encoding: gzip, deflate字段,向服务器标识客户端支持的压缩格式,如果不发送该消息头,服务器默认是不会压缩的。
  2. 服务端在收到请求之后,如果发现请求头中含有Accept-Encoding字段,并且支持该类型压缩,就会对响应报文压缩之后返回给客户端,并且携带Content-Encoding: gzip消息头,表示响应报文是根据该格式进行压缩的。
  3. 客户端收到响应后,先判断是否有Content-Encoding消息头,如果有,按该格式解压报文。否则按正常报文处理。
7.1.2、gzip压缩案例
7.1.2.1、局部

只配置Consumer通过Feign到Provider的请求与相应的gzip压缩。

服务消费者配置

# 局部压缩
feign:
  compression:
    request:
      enabled: true                                      # 是否开启gzip压缩
      mime-types: text/xml,application/json,application/xml # 配置压缩支持的mime type
      min-request-size: 512                              # 配置压缩数据大小的最小值,默认2048
    response:
      enabled: true                                      # 响应是否开启gzip
7.1.2.2、全局

对客户端浏览器的请求以及Consumer对Provider的请求响应都实现gzip。

服务消费者配置

server:
  port: 9090
  compression:
    enabled: true                                                                 # 是否开启压缩
    mime-types: application/json,application/xml,text/html,text/xml,text/plain    # 配置压缩支持的mime type

image-20220419203341422

7.2、HTTP连接池

7.2.1、为什么HTTP连接池能提升性能
  • 两台服务器建立HTTP连接的过程是一个很复杂的过程,涉及到多个数据包的交换,很耗时。
  • HTTP连接需要3次握手4次挥手开销很大。
7.2.2、解决方案

采用HTTP连接池,可以节约大量的3次握手4次挥手,这样能大大提升吞吐量。

Feign的HTTP客户端支持3种框架,HttpURLConnection、HttpClient、OkHttp,默认是HttpURLConnection。相关源码:org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration

image-20220420120948030

  • 传统的HttpURLConnection是JDK自带的,并不支持连接池,如果要实现连接池机制,还需要自己来管理连接对象。对于网络请求这种底层相对复杂的操作,如果有可用的其他方案,没有必要自己去管理连接对象。
  • HttpClient相比传统JDK自带的HttpURLConnection,它封装了访问HTTP的请求头,参数,内容体,响应等等;它不仅使客户端发送HTTP请求变得容易,而且也方便了开发人员测试接口,既提高了开发效率,又提升了代码健壮性;另外在高并发的时候,可以使用“连接池”提升吞吐量。
7.2.3、使用HttpClient

将Feign的Http客户端修改为HttpClient

添加配置

feign:
  httpclient:
    enabled: true  # 开启httpClient

添加依赖

	  <!-- feign和apache httpclient整合 -->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
            <version>10.7.4</version>
        </dependency>

如果使用HttpClient作为Feign的客户端工具,那么在定义接口的注解时需要注意,如果传递的参数是一个自定义的对象(对象会使用JSON格式来传递),需要配置参数类型,例如:@GetMapping(value=“xxx”, consumes=MediaType.APPLICATION_JSON_VALUE)。本文中使用的Spring Cloud版本,已无需手动配置。并且使用httpClient,我们还可以通过GET请求传递对象参数。

7.3、请求超时

Feign的负载均衡底层就是Ribbon,所以这里的请求超时其实就是配置Ribbon。

分布式项目中,在服务压力比较大的情况下,可能处理服务的过程需要花费一定的时间,而默认情况下请求超时的配置是1s,所以我们需要调整该配置延长请求超时时间。

修改服务提供者

    @GetMapping("/{id}")
    public Product selectProductById(@PathVariable Integer id) {
        try {
            // 睡眠1.5秒
            TimeUnit.MILLISECONDS.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return productService.selectProductById(id);
    }

消费者调用

image-20220420153330470
7.3.1、全局

Consumer项目中配置请求超时

ribbon:
  ConnectionTimeout: 3000  # 请求连接的超时时间,默认1s
  ReadTimeout: 3000        # 处理请求的超时时间
7.3.2、局部

一般根据服务的压力大小配置不同的服务超时处理,使用局部配置。

# service-provider为调用的服务名称
service-provider:
  ribbon:
    OkToRetryOnAllOperations: true    # 对所有请求都进行重试
    MaxAutoRetries: 3                 # 重试次数
    MaxAutoRetriesNextServer: 0       # 切换实例重试次数
    ConnectionTimeout: 3000           # 请求连接的超时时间
    ReadTimeout: 3000                 # 处理请求的超时时间

7.4、日志打印

Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求的细节,对Feign的调用情况进行监控和输出。

日志级别:

  • NONE:默认的,不显示任何日志
  • BASIC:仅记录请求方法、url、响应状态码以及执行时间
  • HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息
  • FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据

添加配置信息

logging:
  level:
    org.example.service.ProductService: debug   #feign日志以什么级别监控哪个接口

添加配置类

@Bean
public Logger.Level feignLoggerLevel() {
    return Logger.Level.FULL;
}

日志
image-20220420181957793

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值