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

HCL Domino代理中使用okhttp3出现线程清理错误的解决求助

Lotus Notes代理使用OkHttp3时的线程清理错误解决

问题场景

在Lotus Notes Agent中使用OkHttp3发起HTTP请求后,控制台始终提示Error cleaning up agent threads错误。无论是在Notes 14客户端还是Domino 12.02.FP2服务器上运行,问题都一致。已将OkHttp的jar包放入代理的设计对象中,手动调用client.dispatcher().executorService().shutdownNow()后,控制台显示线程池已终止并关闭,但仍存在多个OkHttp相关线程,错误无法解决。

初始实现代码:

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class JavaAgent extends AgentBase {

    OkHttpClient client;
    public void NotesMain() {

        try {
            Session session = getSession();
            AgentContext agentContext = session.getAgentContext();

            client = new OkHttpClient();
            Response response = null;
            Request request = new Request.Builder().url("https://www.cnn.com").build();

            try {
                response = client.newCall(request).execute();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (response != null) {
                    response.close();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

尝试修改后的代码及运行结果:

try {
    Session session = getSession();
    AgentContext agentContext = session.getAgentContext();
    Response response = null;
    try {

        client = new OkHttpClient();
        Request request = new Request.Builder().url("https://www.cnn.com").build();
        response = client.newCall(request).execute();

        System.out.println("isTerminated: " + client.dispatcher().executorService().isTerminated());
        System.out.println("isShutDown: " + client.dispatcher().executorService().isShutdown());
        System.out.println("Calling ShutDownNow");
        client.dispatcher().executorService().shutdownNow();
        System.out.println("isTerminated: " + client.dispatcher().executorService().isTerminated());
        System.out.println("isShutDown: " + client.dispatcher().executorService().isShutdown());
        
        dumptg(null);
        
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (response != null) {
            response.close();
        }
    }

} catch (Exception e) {
    e.printStackTrace();
}

运行结果:

isTerminated: false
isShutDown: false
Calling ShutDownNow
isTerminated: true
isShutDown: true
ThreadGroup UTG: JavaAgent, objid = -728930514
  subgroups  = 0
  total threads = 6

Thread Name, ThreadID, ThreadGroup
OkHttp TaskRunner, 1876182108, UTG: JavaAgent
OkHttp TaskRunner, -536891291, UTG: JavaAgent
Okio Watchdog, 511103762, UTG: JavaAgent
OkHttp TaskRunner, -485203566, UTG: JavaAgent
OkHttp www.cnn.com, -1274389829, UTG: JavaAgent
AgentThread: JavaAgent, 201675379, UTG: JavaAgent
Error cleaning up agent threads

解决方案

问题根源在于OkHttp默认的线程池、连接池以及内部守护线程(比如Okio Watchdog)没有被彻底关闭,导致Notes代理无法清理所有线程。以下是具体解决步骤:

1. 手动关闭OkHttp的所有资源

除了关闭线程池,还需要关闭连接池、Dispatcher的任务队列,并触发资源清理:

// 取消所有待处理任务
client.dispatcher().cancelAll();
// 关闭线程池并等待终止
client.dispatcher().executorService().shutdown();
if (!client.dispatcher().executorService().awaitTermination(5, TimeUnit.SECONDS)) {
    client.dispatcher().executorService().shutdownNow();
}
// 清空连接池
client.connectionPool().evictAll();
// 关闭调度器(如果存在)
if (client.dispatcher().scheduler() != null) {
    client.dispatcher().scheduler().shutdown();
}

2. 使用自定义OkHttpClient配置,限制线程资源

创建OkHttpClient时,显式配置线程池参数,避免默认的无限线程,同时设置连接超时和空闲连接回收:

OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(10, TimeUnit.SECONDS)
    .writeTimeout(10, TimeUnit.SECONDS)
    .dispatcher(new Dispatcher(new ThreadPoolExecutor(
        1, // 核心线程数
        1, // 最大线程数
        60, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(),
        r -> new Thread(r, "OkHttp-Custom-Thread")
    )))
    .connectionPool(new ConnectionPool(1, 5, TimeUnit.MINUTES))
    .build();

3. 在代理结束前确保所有资源被释放

将资源关闭逻辑放在finally块中,确保无论请求成功或失败都能执行:

public void NotesMain() {
    OkHttpClient client = null;
    Response response = null;
    try {
        Session session = getSession();
        AgentContext agentContext = session.getAgentContext();
        
        // 初始化自定义配置的client
        client = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .dispatcher(new Dispatcher(new ThreadPoolExecutor(1,1,60,TimeUnit.SECONDS,new LinkedBlockingQueue<>())))
            .connectionPool(new ConnectionPool(1,5,TimeUnit.MINUTES))
            .build();
            
        Request request = new Request.Builder().url("https://www.cnn.com").build();
        response = client.newCall(request).execute();
        
        // 处理响应逻辑
        if (response.isSuccessful()) {
            // 业务处理代码
        }
        
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 关闭响应
        if (response != null) {
            response.close();
        }
        // 彻底关闭OkHttp资源
        if (client != null) {
            client.dispatcher().cancelAll();
            client.dispatcher().executorService().shutdown();
            try {
                if (!client.dispatcher().executorService().awaitTermination(3, TimeUnit.SECONDS)) {
                    client.dispatcher().executorService().shutdownNow();
                }
            } catch (InterruptedException ie) {
                client.dispatcher().executorService().shutdownNow();
            }
            client.connectionPool().evictAll();
        }
    }
}

4. 验证线程清理结果

在资源关闭后再次打印线程组,确认所有OkHttp相关线程已被销毁:

// 在finally块末尾添加
dumptg(null);

原理说明

  • OkHttp默认会创建多个后台线程(TaskRunner、Watchdog等)用于连接管理和任务调度,这些线程默认是非守护线程,会阻止Notes代理的线程组被销毁。
  • 仅关闭线程池不足以清理所有资源,必须同时关闭连接池、取消所有任务,并等待线程终止。
  • 自定义线程池配置可以减少不必要的线程创建,降低代理结束时的清理难度。

内容的提问来源于stack exchange,提问作者Thomas Adrian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 23:17:09