当前位置:   article > 正文

微服务间的远程接口调用:OpenFeign的使用并理解原理_openfeign远程调用原理

openfeign远程调用原理

openFeign能做什么?

openFeign是一种声明式、模板化的HTTP客户端。在SpringCloud中使用openFeign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。

其用法就是编写一个接口,在接口上添加注解,如此就能轻而易举的调用远程服务

有如此强大的东西,我们肯定不能放过使用的机会,就像有时你有特殊的要求必须拉别的女孩的手,而此时有个中间人能帮你实现这个愿望,你拉别的女孩子的手就像拉自己女朋友的手一样方便

openFeign在微服务中的作用就像中间方一样,当你需要调用另一个微服务的接口时,使用openFeign就像调用本服务的接口一样丝滑

操作

既然是远程调用,那肯定至少得有2个微服务,consumer and provider。下面我们在consumer中记录以下使用步骤

导入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>
<!-- 对JAXRS支持 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-jaxrs</artifactId>
    <version>10.0.0</version>
</dependency>
<!--    调用soap webservice支持    -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-soap</artifactId>
    <version>10.2.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
启动类

在启动类上添加@EnableFeignClients注解

@SpringBootApplication
@EnableFeignClients
public class OpenFeignServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OpenFeignServiceApplication.class, args);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
@EnableFeignClients工作原理
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

	String[] value() default {};

	String[] basePackages() default {};

	Class<?>[] basePackageClasses() default {};

	Class<?>[] defaultConfiguration() default {};

	Class<?>[] clients() default {};

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

注解@EnableFeignClients告诉框架扫描所有使用注解@FeignClient定义的Feign客户端。它又通过注解@Import导入了类FeignClientRegistrar(feign客户端注册器)

创建openFeign-Client接口

分别写三个接口:GET请求、POST请求、SOAP请求

@FeignClient(name = "dynamicFeignClient", configuration = FeignClientsConfiguration.class)
public interface DynamicFeignClient {

    // 发送GET请求
    @RequestLine("GET")
    @GetMapping("/product/{id}")
    String getProductById(@PathVariable("id") Long id);

    // 发送POST请求
    @RequestLine("POST")
    @Headers(value = "Content-Type: application/json;charset=UTF-8")
    String requestPOST(URI baseUri, @RequestBody JSONObject args);

    // 发送webservice-SOAP请求
    @RequestLine("POST")
    @Headers(value = "Content-Type: text/xml;charset=UTF-8")
    String requestSOAP(URI baseUri, @RequestBody String params);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
@FeignClient源码解析
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
	@AliasFor("name")
	String value() default "";

	String serviceId() default "";

	String contextId() default "";

	String name() default "";

	String qualifier() default "";

	String url() default "";

	boolean decode404() default false;

	Class<?>[] configuration() default {};

	Class<?> fallback() default void.class;

	Class<?> fallbackFactory() default void.class;

	String path() default "";

	boolean primary() default true;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

@FeignClient注解解析

  • name: 是一个任意得客户端名称,用于创SpringCloud LoadBalancer 客户端
  • url: url一般用于调试,可以手动指定@FeignClient调用的地址
  • configuration: Feign配置类,可自定义Feign的Encode,Decode,LogLevel,Contract
  • fallback: 定义容错的类,当远程调用的接口失败或者超时的时候,会调用对应接口的容错逻辑,fallback执行的类必须实现@FeignClient标记的接口
  • fallbackFactory: 工厂类,用于生成fallback类实例,通过此属性可以实现每个接口通用的容错逻辑,以达到减少重复的代码
  • path: 定义当前FeignClient的统一前缀
创建请求类发送POST请求
public interface RequestClient {

    String sendPost(String url, JSONObject request);
    
}
  • 1
  • 2
  • 3
  • 4
  • 5
@Service
@Slf4j
@Import(FeignClientsConfiguration.class)
public class RequestClientImpl implements RequestClient {

    private DynamicFeignClient client;

    @Autowired
    public RequestClientImpl(Decoder decoder, Encoder encoder) {
        client = Feign.builder()
                .decoder(decoder)
                .encoder(encoder)
                .options(new Request.Options(1000, 1000))
                .target(Target.EmptyTarget.create(DynamicFeignClient.class));
    }

    @Override
    public String sendPost(String url, JSONObject request) {
        log.info("url:{},入参:{}", url, request);
        String response;
        try {
            response = client.requestPOST(new URI(url), request);
        } catch (FeignException feignException) {
            throw new BusinessException("调用接口异常!地址:" + url);
        } catch (URISyntaxException uriSyntaxException) {
            throw new BusinessException("调用地址异常!地址:" + url);
        }
        log.info("url:{},出参:{}", url, response);
        return response;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
编写Controller请求提供者
@SneakyThrows
    @PostMapping("/getOtherServiceData")
    public JSONObject getOtherServiceData(@RequestBody JSONObject jsonObject) {
        String responseString = client.sendPost("http://localhost:9907/remindMedication/getRemindMedication", jsonObject);
        return JSONObject.parseObject(responseString);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
使用postman进行测试

可以看到调用了远程服务的接口
在这里插入图片描述

优化

日志

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

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

使用方式

第一种. 设置 Feign Logger Level
@Configuration
public class FeignConfig {

    @Bean
    public Logger.Level feignLoggerLevel() {
        // 开启详细日志
        return Logger.Level.FULL;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

设置什么级别,根据情况而定

第二种. 在配置文件中给指定的FeignClient接口加指定的日志级别
@Autowired
    public RequestClientImpl(Decoder decoder, Encoder encoder) {
        client = Feign.builder()
                .decoder(decoder)
                .encoder(encoder)
                .options(new Request.Options(5000, 5000))
                .logLevel(Logger.Level.FULL)
                .target(Target.EmptyTarget.create(DynamicFeignClient.class));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
超时时间
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

在@FeignClient注解中,当设定了客户端名称后,便默认使用了Spring Cloud LoadBalancer进行负载均衡访问,在老版本中,集成的是Ribbon,它默认的响应时间是1s,可以通过ribbon.ReadTimeout和ribbon.ConnectTimeout来设置客户端超时时间

在新版本中,我们可以在options设置,例如,我们设置为1s

@Autowired
public RequestClientImpl(Decoder decoder, Encoder encoder) {
    client = Feign.builder()
            .decoder(decoder)
            .encoder(encoder)
            .options(new Request.Options(1000, 1000))
            .logLevel(Logger.Level.FULL)
            .target(Target.EmptyTarget.create(DynamicFeignClient.class));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

并且在服务提供方延迟3s
在这里插入图片描述
然后再进行测试
在这里插入图片描述
在这里插入图片描述

Feign的工作原理

  • 主程序入口添加了@EnableFeignClients注解开启对FeignClient扫描加载处理。根据FeignClient的开发规范,定义接口并加@FeignClients注解
  • 当程序启动时,会进行包扫描,扫描所有@FeignClients注解的类,并且讲这些信息注入Spring IOC容器中,当定义的Feign接口中的方法被调用时,通过JDK的代理方式,来生成具体的RequestTemplate。当生成代理时,Feign会为每个接口方法创建一个RequestTemplate。当生成代理时,Feign会为每个接口方法创建一个RequestTemplate对象,改对象封装可HTTP请求需要的全部信息,如请求参数名,请求方法等信息都是再这个过程中确定的
  • 然后RequestTemplate生成Request,然后把Request交给Client去处理,这里指的是Client可以把JDK原生的URLConnection,Apache的HttpClient,也可以是OKhttp,最后Client被封装到LoadBalanceClient类,这个类结合Ribbon负载均衡发起服务之间的调用
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/428879
推荐阅读
相关标签
  

闽ICP备14008679号