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
相关产品推荐
相关产品推荐

