You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

两个Jersey进程监听同一端口但请求响应范围不同的排查求助

端口绑定疑问与解决思路

我有两个基于Jersey 2.X的简单应用,都监听TCP HTTP端口4466:一个是通过java UncompiledMain.java运行的JVM进程,另一个是Java原生镜像(native-image)生成的进程(正在从JIT编译迁移到原生镜像)。通过端口查看工具得到如下结果:

COMMAND   PID     USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
1) java    71457 sarnobat   73u  IPv6 0x5e313f1de8147dd9      0t0  TCP *:4466 (LISTEN)
2) app     71552 sarnobat    5u  IPv6 0x5e313f1db096eb79      0t0  TCP localhost:4466 (LISTEN)

两者的运行时绑定方式不同(详情见下文)。我知道新版Linux内核允许多个监听器绑定同一端口,但不确定这和我在Mac OS X 10.15.7上遇到的情况是否相关。想搞懂这一现象,最终实现让单个程序处理所有请求,但目前对相关网络基础知识理解不足,求指点。


详情

抱歉代码比较杂乱——本来打算先跑通再整理!

服务器1(JVM运行)- org.glassfish.jersey.jdkhttp.JdkHttpServerFactory

可响应网络请求(包括SSH隧道过来的请求)。

JdkHttpServerFactory.createHttpServer(new URI("http://localhost:" + args[0] + "/"),
        new ResourceConfig(MyResource.class));
...
@javax.ws.rs.Path("")
public static class MyResource { // 必须为public

    @GET
    @javax.ws.rs.Path("")
    @Produces("application/json")
    public Response list(@QueryParam("value") String iValue)
                throws JSONException, IOException {
        System.err.println("list()");
        System.out.println(iValue);

        return Response.ok().header("Access-Control-Allow-Origin", "*").type("application/json").build();
    }

服务器2(原生镜像)- org.glassfish.grizzly.http.server.HttpServer

仅能响应本地请求,无法处理网络请求。

private static final URI BASE_URI = URI.create("http://localhost:4466/");
public static final String ROOT_PATH = "";
    
public static void main(String[] args) {
    try {
        System.out.println("\"Hello World\" Jersey Example App");

        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, create(), true);
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                server.shutdownNow();
            }
        }));
        server.start();

...
public static ResourceConfig create() {
    final Resource.Builder resourceBuilder = Resource.builder(ROOT_PATH);

    resourceBuilder.addMethod("GET").handledBy(new Inflector<ContainerRequestContext, Response>() {

         @Override
         public Response apply(ContainerRequestContext data) {
             String iValue = data.getUriInfo().getQueryParameters().getFirst("value");
             System.err.println("list()");
             System.out.println(iValue);
             getMethodCalled = true;
             return Response.ok().header("Access-Control-Allow-Origin", "*").type("application/json").build();
          }
      });

问题解析与解决方法

1. 为何两个进程能同时绑定4466端口?

从端口监听信息能看到关键差异:

  • JVM进程绑定的是*:4466:*代表通配所有网络接口(包括本地回环、物理网卡等所有可用IP)
  • 原生镜像进程绑定的是localhost:4466:仅绑定本地回环接口(127.0.0.1或::1)

在Mac OS X(BSD内核衍生)中,这种绑定是允许的:当一个进程绑定通配接口,另一个进程绑定特定接口的同一端口,系统会根据请求的目标IP来分发流量。

2. 为何原生镜像进程无法响应网络请求?

因为它只绑定了本地回环接口,只有来自本机的请求(比如curl http://localhost:4466)会被它处理,而外部网络请求(包括SSH隧道的请求,目标是机器的公网/局域网IP)会被绑定通配接口的JVM进程接收。

3. 实现单程序处理所有请求的方案

方案一:修改原生镜像的绑定地址

把绑定URI从http://localhost:4466/改成http://0.0.0.0:4466/(IPv4通配)或http://[::]:4466/(IPv6通配),这样原生镜像进程会绑定所有网络接口。之后关闭JVM进程,所有请求就会由原生镜像进程处理。

修改后的代码片段:

private static final URI BASE_URI = URI.create("http://0.0.0.0:4466/");

方案二:统一绑定策略

如果要保留其中一个进程,确保它绑定通配接口*(对应代码里用0.0.0.0或省略主机名的方式),同时关闭另一个进程即可。

补充说明

  • 若两个进程都绑定通配接口*:4466,Mac OS X会抛出端口占用错误,无法同时启动。
  • localhost绑定仅适用于本地调试场景,对外提供服务时必须绑定通配接口或特定的公网/局域网IP。

内容的提问来源于stack exchange,提问作者Sridhar Sarnobat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 23:20:34