声明式服务调用Feign

声明式服务调用Feign

一、Feign介绍

1.什么是feign?

  • feign是spring cloud提供的声明式的http客户端,工作在consumer端
  • feign支持springmvc注解
  • feign集成ribbon也支持负载均衡(restTemplate+ribbon=feign)

2.feign启动器

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

二、Feign入门案例

2.1创建服务提供者feign_provider模块

请添加图片描述

  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud_parent</artifactId>
        <groupId>com.bjpowernode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>feign_provider-1</artifactId>

  <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.bjpowernode</groupId>
            <artifactId>springcloud_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--nacos客户端-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
</project>
  • application.yml
server:
  port: 8090
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.19.132 #注册中心的地址
  application:
    name: feign-provider #注册到nacos的服务名
  • UserController类
package com.bjpowernode.controller;

import com.bjpowernode.pojo.User;
import com.bjpowernode.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/provider")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/getUserById/{id}")
    public User getUserById(@PathVariable Integer id){
        return userService.getUserById(id);
    }
}

  • UserService接口
package com.bjpowernode.service;

import com.bjpowernode.pojo.User;

public interface UserService {
    User getUserById(Integer id);
}

  • UserServiceImpl类
package com.bjpowernode.service;

import com.bjpowernode.pojo.User;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService{

    @Override
    public User getUserById(Integer id){
        return new User(id, "王粪堆-1", 18);
    }
}

  • FeignProviderApp启动类
package com.bjpowernode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient  //注册自己并发现其他服务
public class FeignProviderApp {
    public static void main(String[] args) {
        SpringApplication.run(FeignProviderApp.class, args);
    }
}

2.2创建feign_interface接口模块

请添加图片描述

  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud_parent</artifactId>
        <groupId>com.bjpowernode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>feign_interface</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.bjpowernode</groupId>
            <artifactId>springcloud_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
  • UserFeign接口
package com.bjpowernode.feign;

import com.bjpowernode.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
//问题1:feign的运行原理?
@FeignClient("feign-provider")
@RequestMapping("/provider")
public interface UserFeign {

    @RequestMapping("/getUserById/{id}")
    public User getUserById(@PathVariable("id") Integer id);//问题2:?必须("id")
}

2.3创建服务消费者feign_consumer模块

请添加图片描述

  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud_parent</artifactId>
        <groupId>com.bjpowernode</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ribbon_consumer</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.bjpowernode</groupId>
            <artifactId>springcloud_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--feign接口-->
        <dependency>
            <groupId>com.bjpowernode</groupId>
            <artifactId>feign_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
  • application.yml
server:
  port: 80
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.19.132:8848
  application:
    name: feign-consumer
  • UserController类
package com.bjpowernode.controller;

import com.bjpowernode.feign.UserFeign;
import com.bjpowernode.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.Random;

@RestController
@RequestMapping("/consumer")
public class UserController {

    @Autowired
    private UserFeign userFeign;//代理类

    @RequestMapping("/getUserById/{id}")
    public User getUserById(@PathVariable Integer id){
        System.out.println(userFeign.getClass());
        return userFeign.getUserById(id);
    }
}
  • FeignConsumerApp启动类
package com.bjpowernode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient  //注册自己并发现其他服务
@EnableFeignClients  //开启feign注解扫描
public class FeignConsumerApp {

    public static void main(String[] args) {
        SpringApplication.run(FeignConsumerApp.class, args);
    }
}
  • 测试

请添加图片描述

三、Feign原理

3.1 将Feign接口注入到Spring容器中

扫描feign接口生成代理类并交给spring容器管理

@EnableFeignClients开启feign接口扫描:FeignClientsRegistrar.registerFeignClients()扫描被@FeignClient标识的接口生成代理类并交给spring的容器管理

public void registerFeignClients(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		... ... ...
         //扫描feign接口   
		for (String basePackage : basePackages) {
			Set<BeanDefinition> candidateComponents = scanner
					.findCandidateComponents(basePackage);
			for (BeanDefinition candidateComponent : candidateComponents) {
				if (candidateComponent instanceof AnnotatedBeanDefinition) {
					AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) 
                                                              candidateComponent;
                      //获得UserFeign的详细信息
					AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
					Assert.isTrue(annotationMetadata.isInterface(),
							"@FeignClient can only be specified on an interface");

					Map<String, Object> attributes = annotationMetadata
							.getAnnotationAttributes(FeignClient.class.getCanonicalName());
					String name = getClientName(attributes);
					registerClientConfiguration(registry, name,
							attributes.get("configuration"));
                      //注入Feign接口到Spring容器中
					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	}

3.2 RequestTemplate封装请求信息

根据接口上的注解创建RequestTemplate

当controller调用feign代理类时,代理类会调用SynchronousMethodHandler.invoke()创建RequestTemplate(url、requestMethod、body)

    public Object invoke(Object[] argv) throws Throwable {
        //创建一个RequestTemplate
        RequestTemplate template = this.buildTemplateFromArgs.create(argv);
        Retryer retryer = this.retryer.clone();

        while(true) {
            try {
                //发出请求
                return this.executeAndDecode(template);
            } catch (RetryableException var8) {
                ... ... ...
            }
        }
    }
package feign;

public final class RequestTemplate implements Serializable {
    ... ... ... ... ... ...
    private UriTemplate uriTemplate;
    private HttpMethod method;
    private Body body;
    ... ... ... ... ... ...
}    

3.3 发起请求

接着通过RequestTemplate创建Request,然后client(HttpClient、OkHttp、URLConnection)使用Request发送请求

 Object executeAndDecode(RequestTemplate template) throws Throwable {
        //生成请求对象
        Request request = this.targetRequest(template);
        if (this.logLevel != Level.NONE) {
            this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
        }

        long start = System.nanoTime();

        Response response;
        try {
            //发起请求
            response = this.client.execute(request, this.options);
        } catch (IOException var15) {
            ... ... ...

            throw FeignException.errorExecuting(request, var15);
        }
 }       

四、feign接口三种传参方式

1.?传参

​ @RequestParam(“”)【拼接?形式的url】

2.restful传参

@PathVariable("")【拼接restful形式的url】

3.pojo传参

​ @RequestBody【获取请求体中的json串】

五、Feign请求超时

  • 修改feign_privider:
@Service
public class UserServiceImpl implements UserService {

	@Override
	public User getUser() {
        //模拟网络延迟
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return new User(1,"一直下雨天",18);
	}
}
  • 测试:

请添加图片描述

六、feign优化

6.1开启feign日志

feign虽然提供了日志增强功能,但是默然是不显示任何日志,不过开发者可以自己配置日志级别:

feign的日志级别如下:

NONE:不输出日志
BASIC:输出请求方法及url,响应的状态码及响应时间
HEADERS:输出请求和响应的头信息
FULL:输出请求和响应的请求头,消息体及元数据

在application.yml文件中配置日志级别

feign:
  client:
    config:
      default:
         loggerLevel: full #feign显示日志
logging:
   level:
     com.bjpowernode.feign: debug #log4j的日志级别

6.2 GZIP压缩

  • gzip 是一种数据格式,采用用 deflate 算法压缩 data;gzip 是一种流行的文件 压缩算法,应用十分广泛,尤其是在 Linux 平台。

  • 当 Gzip 压缩到一个纯文本文件时,效果是非常明显的,大约可以减少 70% 以上的文件大小。

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

server:
   compression:
       enabled: true #开启浏览器<----->consumer的gzip压缩
feign:
  compression:
     request:
        enabled: true #开启feign<---->provider的gzip压缩
     response:
        enabled: true

请添加图片描述

6.3 http连接池

两台服务器建立HTTP连接的过程涉及到多个数据包的交换,很消耗时间。采用HTTP连接池可以节约大量的时间提示吞吐量。

添加依赖,不需要在application.yml配置开启,默认为开启

<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-httpclient</artifactId>
</dependency>

6.4 feign超时

feign超时优化方式一:
  ribbon:
    ConnectionTimeout: 5000 #请求连接的超时时间
    ReadTimeout: 5000 #请求处理的超时时间
feign超时优化方式二:
  feign:
    client:
      config:
        feign-provider:
           ConnectionTimeout: 5000 #请求连接的超时时间
           ReadTimeout: 5000 #请求处理的超时时间
  • 服务器建立HTTP连接的过程涉及到多个数据包的交换,很消耗时间。采用HTTP连接池可以节约大量的时间提示吞吐量。
  • 添加依赖,不需要在application.yml配置开启,默认为开启
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-httpclient</artifactId>
</dependency>

6.4 feign超时

feign超时优化方式一:
  ribbon:
    ConnectionTimeout: 5000 #请求连接的超时时间
    ReadTimeout: 5000 #请求处理的超时时间
feign超时优化方式二:
  feign:
    client:
      config:
        feign-provider:
           ConnectionTimeout: 5000 #请求连接的超时时间
           ReadTimeout: 5000 #请求处理的超时时间
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值