SpringCloud OpenFeign

导读:本篇文章讲解 SpringCloud OpenFeign,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

SpringCloud OpenFeign

1.OpenFeign 介绍

1.OpenFeign 是什么

  1. OpenFeign 是个声明式 WebService 客户端,使用 OpenFeign 让编写 Web Service 客户端 更简单

  2. 它的使用方法是定义一个服务接口然后在上面添加注解

  3. OpenFeign 也支持可拔插式的编码器和解码器。

  4. Spring Cloud 对 OpenFeign 进 行 了 封 装 使 其 支 持 了 Spring MVC 标 准 注 解 和 HttpMessageConverters

  5. OpenFeign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡

2.官网

地址:openFeign官网

3.Feign 和 OpenFeign 区别

Feign

1.Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端 2.Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。

3.Feign的使用方式是:使用Feign的注解定义接口,调用服务注册中心的服务

4.Feign支持的注解和用法请参考官方文档:https://github.com/OpenFeign/feign

5.Feign本身不支持Spring MVC的注解,它有一套自己的注解

6.引入依赖

   <dependencies>
      <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-bom</artifactId>
        <version>??feign.version??</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>

OpenFeign

1.OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解,如 @RequesMapping等等。

2.OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口

3.OpenFeign通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务

4.引入依赖

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

精简一句话:OpenFeign 就是在 Feign 基础上做了加强 , 有些程序员为了方便,说 Feign 就是指的 OpenFeign

2.OpenFeign-应用实例

1.需求分析/图解

– 示意图

image-20220907225334073

2.创建服务消费模块 -通过 OpenFeigen 实现远程调用的三种方式

1.通过注册中心服务发现实现远程调用

1.参考 member-service-consumer-80 创建 member-service-consumer-openfeign-80模块

image-20220908195656799

2.修改 pom.xml,引入openfeign依赖

<!--引入openfeign场景启动器-->
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

3.创建 application.yml 内容如下:

server:
  port: 80

spring:
  application:
    name: member-service-consumer-openfeign-80

#配置eureka-client
eureka:
  client:
    #将自己注册到eureka-server
    register-with-eureka: true
    #表示从eureka-server获取注册信息
    #如果是单节点是可以不配置的,但如果是集群则必须配置为true,才能配合Ribbon实现负载均衡功能
    fetch-registry: true
    service-url:
      #表示将自己注册到哪个eureka-server
      defaultZone: http://eureka9001.com:9001/eureka/,http://eureka9002.com:9002/eureka/

4.创建主启动类

//启用OpenFeignClient
@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
public class MemberConsumerOpenfeignApplication {
    public static void main(String[] args) {
        SpringApplication.run(MemberConsumerOpenfeignApplication.class,args);
    }
}

5.创建MemberFeignService.java,

@Component
@FeignClient(value = "MEMBER-SERVICE-PROVIDER")
public interface MemberFeignService {

    //定义方法-远程调用接口

    /**
     * 1. 远程调用的方式是get
     * 2. 远程调用的url http://MEMBER-SERVICE-PROVIDER/member/get/{id}
     * 3. MEMBER-SERVICE-PROVIDER 就是服务提供方在Eureka Server 注册的服务
     * 4. openfeign 会根据负载均衡来决定调用10000/10002-默认是轮询
     * 5. 因为openfeign 好处是支持了springmvc注解 + 接口解耦
     *
     * openfeign是怎么样去远程调用的呢?
     * 1.注册中心通过MEMBER-SERVICE-PROVIDER服务注册名从注册中心去获取请求地址
     * 192.168.79.1:member-service-provider:10002 , 192.168.79.1:member-service-provider:10001
     * 2.通过springmvc注解 + 接口的方式实现远程调用
     * 3.openfeign接口地址需要和远程调用服务的地址一致
     */
    @GetMapping("/member/get/{id}")
    Result getMemberById(@PathVariable("id") Long id);

}

6.创建MemberConsumerFeignController.java

@RestController
@RequiredArgsConstructor
public class MemberConsumerFeignController {

    //装配MemberFeignService
    private final MemberFeignService memberFeignService;

    @GetMapping(value = "/member/consumer/openfeign/get/{id}")
    public Result getMemberById(@PathVariable("id") Long id) {
        return memberFeignService.getMemberById(id);
    }

}

7.启动测试

openfeign默认轮询的方式实现负载均衡

image-20220908202621540

image-20220908202631602

2.指定url地址,不经过Ribbon的服务选择,直接请求服务

@Component
/**
 * 指定远程调用url,用于固定访问某个服务不经过注册中心做服务的发现
 * value 和 name 的作用一样,如果没有配置url那么配置的值将作为服务名称,用于服务发现。反之只是一个名称。
 */
@FeignClient(name = "member",url = "192.168.79.1:10001")
public interface MemberFeignService {
    //定义方法-远程调用接口
    @GetMapping("/member/get/{id}")
    Result getMemberById(@PathVariable("id") Long id);

}

3.动态url地址,不经过Ribbon的服务选择,直接请求服务

/**
 * 1.指定远程调用url,用于固定访问某个服务不经过注册中心做服务的发现
 * 2.value 和 name 的作用一样,如果没有配置url那么配置的值将作为服务名称,用于服务发现。反之只是一个名称。
 * 3.contextId 当name/value相同时,指定不同的id来做区别
 * 也可以通过配置spring.main.allow-bean-definition-overriding=true来解决名称冲突的问题
 * 4.${member-provider.requesturl:} 从yml配置文件中读取
 */
@FeignClient(name = "member",url = "${member-provider.requesturl:}",contextId = "dynamicUrl")
@Component
public interface MemberFeignDynamicUrlService {

    @GetMapping("/member/get/{id}")
    Result getMemberById(@PathVariable("id") Long id);
}

3.OpenFeign-日志配置

1.基本介绍

  1. 说明: Feign 提供了日志打印功能,可以通过配置来调整日志级别,从而对 Feign 接口的 调用情况进行监控和输出

  2. 日志级别

    NONE∶默认的,不显示任何日志
    BASIC∶仅记录请求方法、URL、响应状态码及执行时间;

    HEADERS∶除了 BASIC中定义的信息之外,还有请求和响应的头信息;

    FULL∶除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。

image-20220908210215823

2.配置日志-应用实例

1.创建配置类

//@Configuration, OpenFeign全局日志配置
@Configuration
public class OpenFeignConfig {
    /**
     * 指定日志级别
     * @return
     */
    @Bean
    public Logger.Level logLevel(){
        return Logger.Level.FULL;
    }
}

2.修改yml文件,指定openfeign日志级别

logging:
  # openfeign接口远程调用过程打印信息 级别-Debug
  level:
    com.llp.springcloud.service.MemberFeignService: debug

3.启动测试

[MemberFeignService#getMemberById] 
2022-09-08 21:28:18.980 DEBUG 25628 --- [p-nio-80-exec-1] c.l.s.service.MemberFeignService         : [MemberFeignService#getMemberById] {"code":"200","msg":"查询会员成功member-service-provider-10000","data":{"id":1,"name":"smith","pwd":"202cb962ac59075b964b07152d234b70","mobile":"123456789000","email":"smith@sohu.com","gender":1}}
2022-09-08 21:28:18.980 DEBUG 25628 --- [p-nio-80-exec-1] c.l.s.service.MemberFeignService         : [MemberFeignService#getMemberById] <--- END HTTP (202-byte body)

4.OpenFeign-超时时间配置

1.先看一个问题

1.src\main\java\com\llp\springcloud\controller\MemberController.java新增一个测试超时的方法

/**
 * 测试openfeign超时,默认1秒
 * @return
 */
@GetMapping(value = "/member/timeout")
public Result timeout() {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return Result.success();
}

2.修改src\main\java\com\llp\springcloud\service\MemberFeignService.java

/**
 * 测试openfeign超时
 * @return
 */
@GetMapping(value = "/member/timeout")
Result timeout();

3.member-service-provider-10000、member-service-provider-10002两个服务端添加对应的模拟超时的方法

/**
 * 测试openfeign超时,默认1秒
 * @return
 */
@GetMapping(value = "/member/timeout")
public Result timeout() {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return Result.success();
}

4.访问测试

image-20220908214101611

openfeign默认超时时间为1秒,即调用得到响应的时间超出1秒则会超时抛出异常

2.设置超时时间

1.设置Ribbon的超时时间(不推荐)

设置很简单,在配置文件中添加如下设置:

ribbon:
  #1. 设置feign客户端超时时间(openfeign默认支持ribbon)
  #2. ReadTimeout: 5000: 建立连接从服务提供方获取可用资源的所用的全部时间
  #3. 时间单位是毫秒
  ReadTimeout: 5000
  #两端连接所用时间
  ConnectionTimeout: 5000

2.设置openFeign的超时时间(推荐)

openFeign设置超时时间非常简单,只需要在配置文件中配置,如下:

feign:
  client:
    config:
      ## default 设置的全局超时时间,指定服务名称可以设置单个服务的超时时间
      default:
        connectTimeout: 5000
        readTimeout: 5000

再次测试

image-20220908215021856

default设置的是全局超时时间,对所有的openFeign接口服务都生效

但是正常的业务逻辑中可能涉及到多个openFeign接口的调用,如下图:

image-20220908214818205

那么上面配置的全局超时时间能不能通过呢?很显然是serviceA、serviceB能够成功调用,但是serviceC并不能成功执行,肯定报超时。

此时我们可以给serviceC这个服务单独配置一个超时时间,配置如下:

feign:
  client:
    config:
      ## default 设置的全局超时时间,指定服务名称可以设置单个服务的超时时间
      default:
        connectTimeout: 5000
        readTimeout: 5000
      ## 为serviceC这个服务单独配置超时时间
      serviceC:
        connectTimeout: 30000
        readTimeout: 30000

这样A、B两个服务的超时时间就是50s,而C服务的超时时间就是30s了

5.OpenFeign-指定公共请求参数

1.创建FeignRequestHeaderInterceptor.java

@Slf4j
@RequiredArgsConstructor
public class FeignRequestHeaderInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        try {
            Date currentTime = new Date();
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.ENGLISH);
            String date = simpleDateFormat.format(currentTime);
            requestTemplate.header("Date", date);
            requestTemplate.header("Address", "00000");
            //requestTemplate.body();
        } catch (Exception e) {
            log.error("会话Authorization异常", e);
        }
    }
}

2.在openfeign接口中指定配置类

@Component
//configuration是配置Feign配置类,在配置类中可以自定义Feign的Encoder、Decoder、LogLevel、Contract等。
@FeignClient(value = "MEMBER-SERVICE-PROVIDER",configuration = {FeignRequestHeaderInterceptor.class})
public interface MemberFeignService {

    //定义方法-远程调用接口

    /**
     * 1. 远程调用的方式是get
     * 2. 远程调用的url http://MEMBER-SERVICE-PROVIDER/member/get/{id}
     * 3. MEMBER-SERVICE-PROVIDER 就是服务提供方在Eureka Server 注册的服务
     * 4. openfeign 会根据负载均衡来决定调用10000/10002-默认是轮询
     * 5. 因为openfeign 好处是支持了springmvc注解 + 接口解耦
     * <p>
     * openfeign是怎么样去远程调用的呢?
     * 1.注册中心通过MEMBER-SERVICE-PROVIDER服务注册名从注册中心去获取请求地址
     * 192.168.79.1:member-service-provider:10002 , 192.168.79.1:member-service-provider:10001
     * 2.通过springmvc注解 + 接口的方式实现远程调用
     * 3.openfeign接口地址需要和远程调用服务的地址一致
     */
    @GetMapping("/member/get/{id}")
    Result getMemberById(@PathVariable("id") Long id);

    /**
     * 测试openfeign超时
     * @return
     */
    @GetMapping(value = "/member/timeout")
    Result timeout();
}

6.OpenFeign发送Https请求如何忽略SSL证书认证

​ 我们在使用opefeign框架去发送Https请求调用服务器接口时,如果服务器没有证书或者证书过期,但还是要https去调用,我们可以直接绕过SSL证书认证,否则就可能出现SSLHandshakeException异常情况,那么我们该如何使OpenFeign绕过SSL验证呢?

FeginClient作为一个http请求工具,用来调用第三方接口,但是由于第三方接口常常是https开头,导致在调用的时候,触发SSL安全认证,这时候也就可以使用绕过认证的方法。

这里我们就以Springcloud项目整合OpenFeign框架为例,我们只需将Feign的配置类修改如下即可:

@Configuration
public class FeignConfiguration {
 
    @Bean
    public CachingSpringLoadBalancerFactory cachingFactory(SpringClientFactory clientFactory) {
        return new CachingSpringLoadBalancerFactory(clientFactory);
    }
 
    @Bean
    @ConditionalOnMissingBean
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
                              SpringClientFactory clientFactory) throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext ctx = SSLContext.getInstance("SSL");
        X509TrustManager tm = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) {
            }
            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }
            @Override
            public X509Certificate[] getAcceptedIssuers() {
               //如果这里后续报空指针,就return new X509Certificate[0]
                return null;
            }
        };
        ctx.init(null, new TrustManager[]{tm}, null);
        return new LoadBalancerFeignClient(new Client.Default(ctx.getSocketFactory(),
                (hostname, session) -> true),
                cachingFactory, clientFactory);
    }
}

更多细节可以参考:OpenFeign介绍和使用注意

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/81211.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!