4

【微服务】- 服务调用 - OpenFeign - 怒放吧德德

 1 year ago
source link: https://www.cnblogs.com/lyd-code/p/16726936.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

服务调用 - OpenFeign

😄生命不息,写作不止
🔥 继续踏上学习之路,学之分享笔记
👊 总有一天我也能像各位大佬一样
🏆 一个有梦有戏的人 @怒放吧德德
🌝分享学习心得,欢迎指正,大家一起学习成长!

image

介绍

OpenFeign 全称 Spring Cloud OpenFeign,它是 Spring 官方推出的一种声明式服务调用与负载均衡组件,它的出现就是为了替代进入停更维护状态的 Feign。
Spring Cloud openfeign对Feign进行了增强,使其支持Spring MVC注解,另外还整合了
Ribbon和Nacos,从而使得Feign的使用更加方便。
Feign使用http远程调用方法就好像调用本地的方法,感觉不到是远程方法。他的使用就和直接写控制类那样,暴露接口提供调用,我们只需要编写调用接口+@FeignClient注解,在使用这个api的时候,只需要定义好方法,到时候调用这个方法就可以了。这种服务之间的调用使用起来是非常的方便,体验也比较好。

如何实现接口调用?

在平时开发的springboot项目中,像这种rest服务是如何被调用的呢?通常下是使用Httpclient、Okhttp、HttpURLConnection、RestTemplate,其中RestTemplate是最常见的。之前在 nacos配置中心 使用的是RestTemplate。

SpringCloud整合OpenFeign

就用一个例子来简单使用OpenFeign进行服务间的调用,通过实例来学习关于Feign组件的功能。

1|0引入依赖

使用OpenFeign组件需要引入客户端依赖



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

1|0编写调用接口

通过OpenFeign远程调用服务的时候,比RestTemplate更加方便,就跟编写controller接口是差不多的。
需要写上@FeignClient注解,里面配置微服务名字和rest的@RequestMapping("/api/store"),或者可以在声明调用pai的时候写上完整的路径。
简单的对应如下图所示

在这里插入图片描述


代码如下:



package com.lyd.demo.feign; import com.lyd.demo.feign.config.FeignOkhttpConfig; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import java.util.Map; /** * @author: lyd * @description: 远程调用 service-store 服务 * @Date: 2022/9/24 * 介绍: * name / value : 要调用的微服务名 * path:控制类上面的路径 --- @RequestMapping("/api/store") */ @FeignClient(name = "service-store", path = "/api/store") public interface StoreFeignService { // 声明要调用的rest @GetMapping("/{id}") Map<String, Object> getStoreNum(@PathVariable String id); } /** * @RestController * @RequestMapping("/api/store") * public class StoreController { * @Value("${server.port}") * private String currentPort; * @GetMapping("/{id}") * public Map<String, Object> getStoreNum(@PathVariable String id) throws InterruptedException { * Map<String, Object> map = new HashMap<>(); * map.put("port", currentPort); * map.put("num", 10); * return map; * } * } */

需要在启动类中写上注解 @EnableFeignClients



@Autowired private StoreFeignService storeFeignService; // 在业务中直接调用 storeFeignService.getStoreNum(uid);

OpenFeign自定义配置

Feign 提供了很多的扩展机制,让用户可以更加灵活的使用。
feign.Logger.Level:修改日志级别,包含四种不同的级别:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder:响应结果的解析器,http远程调用的结果做解析,例如解析json字符串为java对象
feign.codec.Encoder:请求参数编码,将请求参数编码,便于通过http请求发送
feign. Contract:支持的注解格式,默认是SpringMVC的注解
feign. Retryer:失败重试机制,请求失败的重试机制,默认是没有,不过会使用Ribbon的重试

1|0日志配置

可以通过配置Feign的日志级别来显示需要的日志。

1|01)、定义配置类

定义一个feign的配置文件,并交给spring管理。
feign的日志级别一开始默认是NONE,不显示任何的日志,可以通过定义一个bean,返回日志的级别



package com.lyd.demo.feign.config; import feign.Logger; import feign.Retryer; import org.springframework.context.annotation.Bean; import java.util.concurrent.TimeUnit; /** * @author: lyd * @description: feign配置文件 - 日志 * @Date: 2022/9/24 */ @Configuration public class FeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.BASIC; } }

日志级别有四种:

  • NONE【性能最佳,适用于生产】:不记录任何日志(默认值)。
  • BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态代码以及执行时间。
  • HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
  • FULL【比较适用于开发及测试环境定位问题】:记录请求和响应的header、body和元数据。

1|02)、配置文件设置级别

springboot默认的级别是info,级别比较高,需要在配置文件中配置,如果只在loggin.level下配置级别,就是全局配置,所以我们可以指定包,指定哪个包下面的日志级别。



logging: level: com.lyd.demo.feign: debug

1|03)、配置域

全局配置:
在feign配置类加上@Configuration注解,直接丢给spring来管理,达成全局配置。
局部配置:
①、局部配置可以通过在feign客户端中指定配置文件,只需要在注解后面加上指定配置类



@FeignClient(name = "service-store", path = "/api/store", configuration = FeignConfig.class)

②、局部配置还可以直接通过yml配置文件来指定。



feign: client: config: service-goods: FULL # 指定哪个服务,并且赋上类型。

1|0配置超时时间

通过yml直接配置超时时间



feign: client: config: default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置 connectTimeout: 2000 readTimeout: 2000

在store服务中加个Thread.sleep(5000),就能看到报超时异常SocketTimeoutException。

image

1|0重试机制配置

通过加入bean来实现
创建重试器 (重试周期(50毫秒),最大重试周期(2000毫秒),最多尝试次数 3次 ),feign没有采用线性的重试机制而是采用的是一种指数级(乘法)的重试机制 每次重试时间 当前重试时间*= 1.5



@Bean public Retryer retryer() { return new Retryer.Default(50, TimeUnit.SECONDS.toMillis(2), 3); }

在来看看default的构造器,就能更清楚参数含义。



public Default(long period, long maxPeriod, int maxAttempts) { this.period = period; this.maxPeriod = maxPeriod; this.maxAttempts = maxAttempts; this.attempt = 1; }

image


如图,会进行重试,直到最后报出异常。
不仅如此,还可以配置契约设置,添加拦截器等等。。。

Feign使用优化

Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:

  • URLConnection:默认实现,不支持连接池
  • Apache HttpClient :支持连接池
  • OKHttp:支持连接池

这次就采用OkHttp

1|0导入依赖



<!--okHttp--> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>

1|0设置配置类



package com.lyd.demo.feign.config; import okhttp3.ConnectionPool; import okhttp3.OkHttpClient; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.TimeUnit; /** * @author: lyd * @description: OkHttpFeign 的配置 * @Date: 2022/9/24 */ @Configuration @ConditionalOnClass({OkHttpClient.class}) @ConditionalOnProperty({"feign.okhttp.enabled"}) public class FeignOkhttpConfig { @Bean public okhttp3.OkHttpClient okHttpClient(OkhttpProperties okhttpProperties) { return new okhttp3.OkHttpClient.Builder() //设置连接超时 .connectTimeout(okhttpProperties.getConnectTimeout(), TimeUnit.MILLISECONDS) //设置读超时 .readTimeout(okhttpProperties.getReadTimeout(), TimeUnit.MILLISECONDS) //是否自动重连 .retryOnConnectionFailure(true) .connectionPool(new ConnectionPool()) .addInterceptor(new OkHttpLogInterceptor()) //构建OkHttpClient对象 .build(); } }

1|0yml配置



feign: client: config: default: connectTimeout: 2000 readTimeout: 2000 httpclient: enabled: false okhttp: enabled: true connectTimeout: 4000 readTimeout: 3000

通过类获取超时时间



package com.lyd.demo.feign.config;

import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;

/** * @author: lyd * @description: 配置参数 * @Date: 2022/9/24 */ @Data @Component @ConfigurationProperties(prefix = "feign.okhttp") public class OkhttpProperties { private Long connectTimeout; private Long readTimeout; }

1|0拦截器

可以在拦截器中配置业务需求的代码。



package com.lyd.demo.feign.config; import lombok.extern.slf4j.Slf4j; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; import java.io.IOException; /** * @author: lyd * @description: 拦截器 * @Date: 2022/9/24 */ @Slf4j public class OkHttpLogInterceptor implements Interceptor { @Override public Response intercept(Interceptor.Chain chain) throws IOException { //这个chain里面包含了request和response,所以你要什么都可以从这里拿 Request request = chain.request(); long t1 = System.nanoTime();//请求发起的时间 log.info(String.format("发送请求 %s on %s%n%s", request.url(), chain.connection(), request.headers())); Response response = chain.proceed(request); long t2 = System.nanoTime();//收到响应的时间 //注意这里不能直接使用response.body().string()的方式输出日志 //因为response.body().string()之后,response中的流会被关闭,程序会报错,我们需要创建出一个新的response给应用层处理 ResponseBody responseBody = response.peekBody(1024 * 1024); log.info(String.format("接收响应: [%s] %n返回json:【%s】 %.1fms%n%s", response.request().url(), responseBody.string(), (t2 - t1) / 1e6d, response.headers())); return response; } }

1|0引入配置



@FeignClient(name = "service-store", path = "/api/store", configuration = FeignOkhttpConfig.class)

运行结果:

image

👍创作不易,可能有些语言不是很通畅,如有错误请指正,感谢观看!记得点赞哦!👍

__EOF__


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK