
告别feign:基于spring 6.1 restclient构建高可靠声明式http客户端
引言
在微服务架构持续演进的今天,高效的远程服务通信始终是系统设计的核心挑战之一。传统的spring生态中,resttemplate长期作为同步http客户端的首选,但其阻塞式模型、繁琐的配置方式以及与现代云原生架构的兼容性问题逐渐显露。随着spring framework 6.1的发布,全新的restclient以更简洁的api设计、更强大的扩展能力登上舞台,标志着spring在同步http客户端领域的一次革新。
本文聚焦于如何基于spring framework 6.1+ 的新特性,构建一套生产级的声明式http通信方案。我们将深入探讨以下核心命题:在非响应式架构下,如何通过restclient.builder实现媲美feign的声明式接口?如何无缝集成spring cloud loadbalancer实现智能负载均衡?如何在同步调用链路中实现安全令牌的自动透传?这些问题的答案不仅关乎代码的优雅性,更直接影响着微服务系统的性能、可维护性和扩展性。
通过剖析restclient的设计哲学,读者将掌握三大关键技术:
- 其一,声明式接口驱动开发,借助
@httpexchange注解实现类feign的简洁定义,告别冗余的模板代码; - 其二,深度集成spring cloud生态,通过定制
clienthttprequestinterceptor打通服务发现与负载均衡链路,彻底解决“503 service unavailable”和403等典型问题; - 其三,工业级连接池配置,基于
apache httpclient5的精细化连接管理策略,确保高并发场景下的稳定吞吐。
文章还将揭示spring 6.1+中鲜为人知的高级特性:例如如何通过restclientadapter实现与声明式接口的无缝对接,将帮助开发者在传统webmvc架构中,以零侵入改造的成本,获得接近响应式编程的性能表现。
对于正在面临以下痛点的团队,本文提供了一站式解决方案:
- 遗留系统需要从
resttemplate向现代客户端迁移。 - 同步服务调用存在性能瓶颈或连接泄漏问题
- 需要统一管理多服务的负载均衡与安全认证。
- 期望在不引入响应式编程的前提下优化资源利用率。
一、环境准备(pom.xml)
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactid>
</dependency>
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-loadbalancer</artifactid>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-web</artifactid>
<version>6.1.0+</version> <!-- 必须6.1+ -->
</dependency>二、声明式http接口定义
/**
* <pre>
* 调用远程同步角色权限服务
* 注:http://horus/horus这个配置,非常关键:
* 1. 第一个horus表示远程服务应用的名字(注册到nacos注册中心的应用名,运行期间会被负载均衡拦截并替换成实际的ip和端口)
* 2. 第二个horus配置表示远程服务的base上下文路径,即contextpath,如果远程应用不带上下文路径,这里配置成http://horus即可
* </pre>
* @author sinhy
* @since 2022-07-13 17:00
* @version v1.0
*/
@httpexchange(url = "http://horus/horus")
public interface authoritysyncsdkservice
{
/**
* 同步角色权限
* @author sinhy
* @since 2022-07-13 16:06 void
*/
@postexchange("/syncroleauthority")
void syncroleauthority();
}三、同步restclient配置
@configuration
public class httpexchangefluxconfiguration
{
/**
* @loadbalanced负载均衡注入,此注解用于标记resttemplate、restclient或webclient.builder这几类对象。将生成器bean配置为使用loadbalancerclient。
* @author lilinhai
* @since 2025-04-15 16:23
* @return webclient.builder
*/
@loadbalanced
@bean
public restclient.builder loadbalancedwebclientbuilder()
{
return restclient.builder().requestfactory(new httpcomponentsclienthttprequestfactory(httpclient()));
}
@bean
public tokenpropagationexchangefilterfunction tokenfilter()
{
return new tokenpropagationexchangefilterfunction();
}
@bean
public httpserviceproxyfactory httpserviceproxyfactory(restclient.builder restclientbuilder, tokenpropagationexchangefilterfunction tokenfilter)
{
restclientbuilder.requestinterceptor(tokenfilter);
restclient restclient = restclientbuilder.build();
return httpserviceproxyfactory.builderfor(restclientadapter.create(restclient)).build();
}
private poolinghttpclientconnectionmanager poolingconnmanager()
{
poolinghttpclientconnectionmanager pool = new poolinghttpclientconnectionmanager();
// 最大连接数
pool.setmaxtotal(200);
// 每个路由的最大连接数
pool.setdefaultmaxperroute(50);
return pool;
}
private httpclient httpclient()
{
return httpclients.custom()
.setconnectionmanager(poolingconnmanager())
.setconnectionreusestrategy(new defaultclientconnectionreusestrategy())
.evictexpiredconnections()
.evictidleconnections(timevalue.ofseconds(30))
.build();
}
}四、token透传实现(同步拦截器)
/**
* 令牌透传过滤器
* @author lilinhai
* @since 2025-04-15 14:35
* @version v1.0
*/
public class tokenpropagationexchangefilterfunction implements clienthttprequestinterceptor
{
@override
public clienthttpresponse intercept(httprequest request, byte[] body, clienthttprequestexecution execution) throws ioexception
{
// 设置授权信息
requestattributes ra = requestcontextholder.getrequestattributes();
if (ra != null)
{
servletrequestattributes servletrequestattributes = (servletrequestattributes) ra;
string clientinfojsonstr = servletrequestattributes.getrequest().getheader(webhttpconstant.client_info_header_name);
if (clientinfojsonstr != null)
{
request.getheaders().add(webhttpconstant.client_info_header_name, clientinfojsonstr);
}
string acceptlanguage = servletrequestattributes.getrequest().getheader(httpheaders.accept_language);
if (acceptlanguage != null)
{
request.getheaders().add(httpheaders.accept_language, acceptlanguage);
}
}
return execution.execute(request, body);
}
}五、控制器层使用示例
@restcontroller
@requestmapping("/authority")
public class authoritysynccontroller {
private final authoritysyncsdkservice authoritysyncsdkservice;
public authoritysynccontroller(authoritysyncsdkservice authoritysyncsdkservice)
{
this.authoritysyncsdkservice = authoritysyncsdkservice;
}
@getmapping("/sync")
public void sync() {
authoritysyncsdkservice.syncroleauthority();
}
}六、核心机制解析
- 负载均衡流程
- 通过自定义
clienthttprequestinterceptor实现服务发现 - 使用
loadbalancerclient.choose()获取服务实例 - 重写请求uri为实际ip:port
- 通过自定义
- 声明式接口原理
httpserviceproxyfactory基于jdk动态代理生成实现类- 底层使用
restclient执行实际http请求
- 与resttemplate方案的差异
- 更现代的流式api配置方式
- 原生支持uri重写拦截器链
- 更好的异常处理机制
七、方案优势
- 官方推荐:完全基于spring 6.1+官方组件栈
- 性能优化:底层使用
httpcomponents连接池 - 配置灵活:支持细粒度的超时和连接管理
- 未来兼容:为迁移到响应式架构保留接口兼容性
注意事项:
- 确保服务调用url格式为
http://service-name/path - spring security配置需要正确设置
securitycontextholder策略 - 生产环境建议启用
spring-cloud-starter-circuitbreaker-resilience4j实现熔断
到此这篇关于基于spring 6.1 restclient构建高可用声明式http客户端方案详解的文章就介绍到这了,更多相关spring restclient声明式http客户端内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论