如何使用Jersey实现Java微服务对非Java服务的接口请求代理转发
Jersey实现微服务接口代理转发方案
Jersey本身没有内置反向代理组件,但可以通过两种低成本方案实现你要求的路由规则,不需要引入额外网关组件,其余路径的请求也不会受影响。
路由规则适配说明
你要求的转发逻辑可以直接匹配:
- 路径前缀为
/api/microservice-b/alias-to-microservice-a/*的所有请求,拦截后转发到微服务A - 其余路径请求会正常走微服务B自身的资源类处理逻辑,无冲突
方案1:Jersey过滤器 + HTTP客户端实现(通用无侵入,推荐)
这个方案是最灵活的,不需要适配微服务A的接口定义,所有符合前缀的请求直接自动转发,适合接口数量多、迭代频繁的场景。
实现步骤
- 选择任意HTTP客户端,比如JDK11+自带的HttpClient、OkHttp、Apache HttpClient都可以,下面以JDK自带的HttpClient为例。
- 编写自定义请求过滤器,拦截指定前缀的请求,转发到微服务A后直接返回响应,不走后续Jersey资源匹配逻辑。
代码示例
import org.glassfish.jersey.server.ContainerRequest; import javax.annotation.Priority; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.core.Response; import javax.ws.rs.ext.Provider; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.List; @Provider @Priority(1) // 优先级设为最高,保证在资源匹配前执行 public class ServiceAProxyFilter implements ContainerRequestFilter { // 微服务A的根地址,建议放到配置文件中读取 private static final String SERVICE_A_BASE = "http://微服务A的IP:端口/"; // 需要代理的路径前缀 private static final String PROXY_PATH_PREFIX = "/api/microservice-b/alias-to-microservice-a/"; private static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient(); @Override public void filter(ContainerRequestContext requestCtx) { String fullPath = ((ContainerRequest) requestCtx).getPath(true); // 非代理前缀的请求直接放过,走微服务B自身逻辑 if (!fullPath.startsWith(PROXY_PATH_PREFIX)) { return; } // 拼接目标地址:去掉前缀部分,加上微服务A的根地址,同时拼接原请求的查询参数 String targetPath = fullPath.substring(PROXY_PATH_PREFIX.length()); String queryStr = requestCtx.getUriInfo().getRequestUri().getRawQuery(); String targetUrl = SERVICE_A_BASE + targetPath + (queryStr != null ? "?" + queryStr : ""); try { // 复制原请求的方法、请求体、请求头 HttpRequest.Builder reqBuilder = HttpRequest.newBuilder() .uri(URI.create(targetUrl)) .method(requestCtx.getMethod(), HttpRequest.BodyPublishers.ofInputStream(requestCtx::getEntityStream)); // 复制请求头,按需过滤不需要的头(比如host、content-length等) requestCtx.getHeaders().forEach((key, values) -> { if (!List.of("host", "content-length").contains(key.toLowerCase())) { values.forEach(v -> reqBuilder.header(key, v)); } }); // 发送请求到微服务A HttpResponse<byte[]> resp = HTTP_CLIENT.send(reqBuilder.build(), HttpResponse.BodyHandlers.ofByteArray()); // 构建返回响应,复制微服务A返回的状态码、响应头、响应体 Response.ResponseBuilder respBuilder = Response.status(resp.statusCode()); resp.headers().map().forEach((key, values) -> values.forEach(v -> respBuilder.header(key, v))); respBuilder.entity(resp.body()); // 直接中止后续请求处理,返回代理结果 requestCtx.abortWith(respBuilder.build()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); requestCtx.abortWith(Response.status(Response.Status.SERVICE_UNAVAILABLE) .entity("转发请求被中断").build()); } catch (Exception e) { requestCtx.abortWith(Response.status(Response.Status.BAD_GATEWAY) .entity("微服务A请求失败").build()); } } }
注册配置
把写好的过滤器注册到你的Jersey配置类即可,比如使用ResourceConfig的场景:
import org.glassfish.jersey.server.ResourceConfig; public class JerseyConfig extends ResourceConfig { public JerseyConfig() { // 注册你自己的业务资源包 packages("com.xxx.microserviceb.resources"); // 注册代理过滤器 register(ServiceAProxyFilter.class); } }
方案2:使用jersey-proxy-client(Jersey生态原生方案)
如果微服务A的接口数量少且迭代频率低,可以用Jersey官方的proxy-client模块,省去自己写请求、响应复制的逻辑。
实现步骤
- 引入jersey-proxy-client依赖,版本要和你项目使用的Jersey版本一致:
<dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-proxy-client</artifactId> <version>${jersey.version}</version> </dependency>
- 手写和微服务A接口路径、入参、出参完全匹配的Java接口,接口的
@Path注解前缀设置为/alias-to-microservice-a。 - 用
WebResourceFactory生成指向微服务A地址的代理实例,注册为Jersey的资源Bean即可,Jersey会自动把匹配路径的请求转发到微服务A。
这个方案的缺点是如果微服务A是非Java项目,手写接口的维护成本比较高,适合接口规模小的场景。
内容的提问来源于stack exchange,提问作者Gregory01
相关产品推荐
相关产品推荐

