目录
一、openfeign简介
openfeign是一个声明式的web服务客户端,它使得编写http客户端变得更简单。使用feign,你只需要创建一个接口并用注解来配置它,feign会自动完成接口的实现。openfeign具有可插拔的注解特性,同时也支持jax-rs注解和feign注解。此外,它还支持负载均衡和容错,通过集成spring cloud alibaba和spring cloud,可以轻松地查找服务并进行调用。
二、openfeign的使用
1. 添加依赖
首先需要在项目中添加openfeign的依赖。maven可以在pom.xml文件中添加以下依赖:
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-openfeign</artifactid>
</dependency>
2. 启用openfeign
在spring boot应用主类上添加@enablefeignclients
注解,以启用openfeign。
@springbootapplication
@enablefeignclients
public class myapp {
public static void main(string[] args) {
springapplication.run(myapp.class, args);
}
}
3. 定义feign客户端
定义feign客户端接口,并使用@feignclient
注解进行配置。假设有一个名为user-service
的远程服务,可以这样定义一个feign客户端:
@feignclient("user-service")
public interface userclient {
@getmapping("/users/{id}")
user getuser(@pathvariable("id") long id);
}
@feignclient
注解指定了要调用的远程服务的名称(在本例中是user-service
),而接口中的方法则定义了具体的http请求。@getmapping
注解表示这是一个get请求,@pathvariable
注解则用于从url路径中提取参数。
4. 注入和使用feign客户端
一旦你定义了feign客户端接口,你就可以在你的服务中注入并使用它了。例如:
@service
public class userservice {
private final userclient userclient;
@autowired
public userservice(userclient userclient) {
this.userclient = userclient;
}
public user getuser(long userid) {
return userclient.getuser(userid);
}
}
在这个例子中,userservice
类注入了userclient
的实例,并通过它来调用远程服务。当你调用getuser
方法时,openfeign会自动将http请求发送到指定的远程服务,并将响应映射到java对象上。
三、@feignclient注解
@feignclient注解是用于标记feign客户端接口的关键注解。通过此注解,openfeign能够自动生成接口的实现,使得开发者能够以声明式的方式调用远程服务。
下面是@feignclient注解的详细参数及其用法:
-
name:
- 含义:指定feign客户端的名称,这个名称通常用于服务发现和服务间的负载均衡。在微服务架构中,这个名称通常与服务提供者在服务注册中心注册的服务名相对应。
- 用法:
@feignclient(name = "servicename")
,其中"servicename"是远程服务的名称。
-
url:
- 含义:直接指定feign客户端的请求地址。当该参数被设置时,feign将不再从服务注册中心获取服务地址,而是直接使用这个url作为请求的目标地址。
- 用法:
@feignclient(url = "http://localhost:8080")
,指定具体的服务地址。 - 注意:如果同时指定了name和url,url会覆盖name中定义的地址。
-
configuration:
- 含义:指定feign客户端的配置类。在这个配置类中,可以自定义feign的各种参数,如请求拦截器、编码器、解码器等。
- 用法:
@feignclient(configuration = customfeignconfiguration.class)
,其中customfeignconfiguration
是自定义的配置类。
-
fallback:
- 含义:指定feign客户端的降级方案。当请求出现异常时,如远程服务不可用或请求超时,feign会返回fallback指定的实例的数据。这通常用于实现熔断和降级逻辑,以提高系统的稳定性。
- 用法:
@feignclient(fallback = userclientfallback.class)
,其中userclientfallback
是实现了feign客户端接口的降级处理类。
-
fallbackfactory:
- 含义:与fallback类似,但是提供了一个工厂类来创建降级逻辑的实例。这个工厂类需要实现
fallbackfactory
接口,并覆盖create(throwable cause)
方法。这个方法返回一个实现了feign客户端接口的实例,用于处理降级逻辑。 - 用法:通常与hystrix等熔断框架结合使用,提供更灵活的降级策略。
- 含义:与fallback类似,但是提供了一个工厂类来创建降级逻辑的实例。这个工厂类需要实现
-
path:
- 含义:定义所有方法请求的基础路径。这样可以在feign客户端的接口方法中省略这个基础路径。
- 用法:
@feignclient(path = "/api/users")
,指定所有请求的基础路径。
-
qualifier:
- 含义:当使用spring的自动装配功能时,可以使用qualifier参数来指定一个bean的名称,以便在多个相同类型的feign客户端之间进行区分。
- 用法:通常与
@qualifier
注解结合使用,用于解决自动装配时的歧义。
-
decode404:
- 含义:指定当服务端返回404状态时是否抛出
feignexception
。默认情况下,feign会将404视为一个正常的响应(即不会抛出异常)。如果将此参数设置为true,则当服务端返回404状态时,feign会抛出一个feignexception
。 - 用法:在需要特殊处理404响应的场景中使用。
- 含义:指定当服务端返回404状态时是否抛出
-
primary:
- 含义:当存在多个相同类型的bean时,将此参数设置为true可以将该bean设置为首选bean。这样,在使用
@autowired
进行自动装配时,spring会优先选择标记为primary的bean。 - 用法:在需要指定首选bean的场景中使用。
- 含义:当存在多个相同类型的bean时,将此参数设置为true可以将该bean设置为首选bean。这样,在使用
此外,@feignclient注解还支持使用spring expression language (spel) 表达式来动态地指定某些参数值。例如,可以使用${property.name}
来从配置文件或环境变量中读取值。这种动态配置方式使得feign客户端更加灵活和可配置。
四、feign缓存
1. feign缓存的意义
在网络请求中,频繁地调用相同的接口会消耗大量的时间和资源。通过引入缓存机制,可以将一些不经常变化且查询频繁的数据存储在本地,从而减少对远程服务的调用,提高系统的响应速度和性能。
feign 缓存是通过结合 spring framework 的缓存抽象来实现的。当你使用 @enablecaching
注解时,spring boot 会启用对 @cacheable
、@cacheevict
、@cacheput
等缓存相关注解的支持。对于 feign 客户端来说,这意味着你可以在 feign 接口的方法上使用这些注解来控制缓存行为。
2. feign缓存的使用
启用缓存
首先在 spring boot 应用中使用 @enablecaching
注解来启用缓存支持。这通常是在配置类或者启动类上添加的。
@enablecaching
@springbootapplication
public class myapp {
public static void main(string[] args) {
springapplication.run(myapp.class, args);
}
}
使用 @cacheable 注解
在feign 客户端接口中,可以使用 @cacheable
注解来标记需要缓存的方法。这个注解指定了缓存的名称(cachenames
)和用于生成缓存键的表达式(key
)。
@feignclient("some-service")
public interface someserviceclient {
@cacheable(cachenames = "infos", key = "#id")
@getmapping("/info/{id}")
public object info(@pathvariable("id") integer id);
}
info
方法被标记为可缓存的。当这个方法被调用时,spring 会检查名为 infos
的缓存中是否已经有对应 id
的缓存项。如果有,就直接返回缓存的数据;如果没有,就执行远程调用,并将结果存储到缓存中。
配置缓存管理器
通过配置文件来指定缓存管理器的类型和其他相关设置。使用了redis 作为缓存存储,并配置了 redis 缓存的相关参数。
spring:
cache:
type: redis # 指定缓存类型为 redis
redis:
key-prefix: 'feign:' # 设置 redis 缓存键的前缀
time-to-live: 60s # 设置缓存的有效期为 60 秒
这样配置后,feign 客户端的缓存数据会被存储到 redis 中,并且每个缓存项的有效期为 60 秒。这意味着在 60 秒内对同一个 id
的多次请求都会直接从 redis 缓存中获取数据,而不会触发远程调用。当缓存过期后,下一次请求会触发远程调用,并更新缓存。
五、@querymap支持
在feign中使用@querymap
或spring cloud openfeign中的@springquerymap
注解可以非常方便地将一个pojo(plain old java object,即简单的java对象)或map的属性和值映射为http get请求的查询参数。这在构建具有多个查询参数的api调用时特别有用,因为它允许你以声明式的方式组织这些参数,而不是手动构建查询字符串。
简单点说:可以将一个对象中的所有属性值以get的方式拼接到地址栏进行传递。
@querymap 和 @springquerymap
- @querymap: openfeign原生提供的注解,用于将pojo映射为查询参数。但是,它与spring不兼容,因为它没有
value
属性。 - @springquerymap: spring cloud openfeign提供的与spring兼容的注解,功能和@querymap类似,但可以在spring环境中使用。
使用方法
- 定义参数对象:
创建一个简单的java类params
,并为其添加属性和getter方法。这些属性和方法将用于生成查询参数。
public class params {
private long id;
private string name;
// getters and setters
}
- 在feign客户端中使用@springquerymap:
在feign客户端接口中,使用@getmapping
注解来定义一个get请求,并使用@springquerymap
注解来标记你的pojo参数。
@feignclient("some-service")
public interface demofeign {
@getmapping("/format")
object format(@springquerymap params params);
}
- 调用feign客户端:
在服务中创建一个params
对象,设置其属性,并通过feign客户端调用该方法。
@getmapping("/format")
public object format(params params) {
return demofeign.format(params);
}
- 日志输出:
当调用这个方法时,feign会自动将params
对象的属性转换为查询参数,并附加到get请求的url上。可以通过查看feign的请求日志来确认这一点。
[demofeign#format] ---> get http://localhost:8088/demos/format?name=test&id=111 http/1.1
通过这种方式,可以简洁地将复杂的查询参数组织到一个对象中,并让feign自动处理参数到查询字符串的转换,从而简化了代码并提高了可读性。
六、@matrixvariable支持
在spring cloud openfeign中,@matrixvariable
注解被用来处理 uri 中的矩阵变量。矩阵变量是 uri 的一种特殊参数形式,允许你在 uri 路径段中直接包含键值对。这种形式的参数在某些 restful api 设计中很有用,尤其是当你需要在 uri 中传递多个参数,并且这些参数都是针对同一资源的时候。
使用 @matrixvariable
@matrixvariable
被用来接收一个映射(map),其中键是字符串,值是字符串列表。这在处理可能包含多个值的矩阵变量时非常有用。
@getmapping("/m3/{params}")
public object matrix3(@matrixvariable map<string, list<string>> params) {
// ...
}
{params}
是一个路径占位符,它表示这个部分将由传递的矩阵变量填充。@matrixvariable
注解告诉 spring,params
参数应该接收来自 uri 矩阵变量的值。
uri 结构
一个包含矩阵变量的 uri 看起来像这样:
/m3/somevalue;key1=value1a,value1b;key2=value2
在这个 uri 中:
/m3/somevalue
是基本的路径。;key1=value1a,value1b;key2=value2
是矩阵变量部分,其中key1
和key2
是矩阵变量的名称,value1a,value1b
和value2
是相应的值。
注意事项
- 路径占位符的名称(在这个例子中是
{params}
)必须与@matrixvariable
注解的变量名一致,或者你可以在注解中明确指定名称。 - 矩阵变量的值可以是单个值,也可以是用逗号分隔的多个值,这取决于你的 api 如何设计。
- 使用矩阵变量时,要确保你的 uri 编码和解码逻辑能够正确处理这些变量,特别是当值中包含特殊字符时。
七、@collectionformat支持
在feign中,@collectionformat
注解用于指定集合参数的格式化方式。当你在feign客户端的方法中使用集合类型的参数(如 list<string>
)时,这个注解能够帮助你定义如何将集合中的元素串联起来,以便通过http请求发送给服务端。
@collectionformat
注解接受一个 feign.collectionformat
枚举值,该枚举定义了不同的格式化选项。在你提供的例子中,展示了两种格式化方式:csv
和 pipes
。
-
csv (comma separated values):
当使用feign.collectionformat.csv
时,集合中的元素会使用逗号,
进行分隔。这是最常见和默认的格式。示例:
@getmapping("/cf") @collectionformat(feign.collectionformat.csv) public object cf(@requestparam("ids") list<string> ids);
调用该方法并传递
list.of("s1", "s2", "s3")
时,生成的url查询参数会是ids=s1,s2,s3
。 -
pipes (
|
separator):
当使用feign.collectionformat.pipes
时,集合中的元素会使用竖线|
进行分隔。示例:
@getmapping("/cf") @collectionformat(feign.collectionformat.pipes) public object cf(@requestparam("ids") list<string> ids);
同样地,调用该方法并传递
list.of("s1", "s2", "s3")
时,生成的url查询参数会是ids=s1|s2|s3
。
这个注解在处理需要特定格式的集合参数时非常有用,尤其是当服务端期望接收特定格式的集合数据时。通过使用 @collectionformat
,你可以确保feign客户端发送的请求符合服务端的期望,从而避免格式错误导致的问题。
@collectionformat
可以应用于整个feign客户端类或者单独的方法上。如果应用于类上,它将影响该类中所有使用集合参数的方法。如果只需要对某个特定方法应用不同的集合格式,可以将注解直接应用于该方法上。
八、其他高级特性
openfeign还提供了许多高级特性,如负载均衡、容错、请求拦截和重试等。这些特性可以帮助构建更健壮、更可靠的微服务架构。
- 负载均衡:openfeign集成了ribbon,支持多种负载均衡策略,如轮询、随机等。你可以根据需要选择合适的策略来分发请求。
- 容错:通过与hystrix的集成,openfeign提供了熔断和降级功能。当远程服务出现故障或响应过慢时,可以自动切换到降级逻辑,保证系统的稳定性。
- 请求拦截:可以使用
requestinterceptor
接口实现自定义的请求拦截器,用于在发送请求之前或之后执行特定的逻辑,如添加认证信息、记录日志等。 - 重试机制:openfeign支持配置请求的重试次数和间隔,以提高系统的可靠性。
总结
openfeign是一个强大的声明式web服务客户端,它简化了远程服务调用的复杂性,并提供了负载均衡、容错等高级特性。通过使用openfeign,你可以更加专注于业务逻辑的实现,而无需关心底层的http通信细节。希望本文能帮助你更好地理解和使用openfeign,构建高效、稳定的微服务架构。
发表评论