客户端库实例可重复使用,并且旨在长期存在。通常,应用会维护客户端库的单个实例,而不是为每个请求创建一个库。
以 Java-KMS 为例,以下代码段展示了如何使用同一客户端实例调用多个请求:
// Create one instance of KMS's KeyManagementServiceClient
KeyManagementServiceClient keyManagementServiceClient =
KeyManagementServiceClient.create();
keyManagementServiceClient.listKeyRings();
keyManagementServiceClient.asymmetricSign();
keyManagementServiceClient.createCryptoKey();
// ... other code ...
// Create one instance of KMS's AutokeyClient
AutokeyClient autokeyClient = AutokeyClient.create();
autokeyClient.listKeyHandles();
autokeyClient.getKeyHandle();
关闭客户端
客户端生命周期的管理方式可能因使用情形和具体客户端库而异。例如,如果您使用的是框架,请遵循该框架的客户端管理指南。虽然在某些情况下,可能会使用 try-with-resources 来创建生命周期较短的客户端,但为了提高效率,一般建议重用生命周期较长的客户端实例
对客户端调用 close() 会尝试有序关闭,并确保现有任务继续执行直至完成。客户端不会接受新任务。如果不关闭客户端,其资源会继续存在,导致应用出现内存泄漏。
以下示例使用了 Java-KMS,并展示了如何关闭客户端:
KeyManagementServiceClient keyManagementServiceClient =
KeyManagementServiceClient.create(keyManagementServiceSettings);
// ... other code ...
keyManagementServiceClient.close();
// For gRPC clients, it's recommended to call awaitTermination to ensure a
// graceful shutdown and avoid the following error message in the logs:
// ERROR i.g.i.ManagedChannelOrphanWrapper - *~*~*~ Channel ManagedChannelImpl
// was not shutdown properly!!! ~*~*~*
// java.lang.RuntimeException: ManagedChannel allocation site
// at io.grpc.internal.ManagedChannelOrphanWrapper$ManagedChannelReference.<init>
keyManagementServiceClient.awaitTermination(DURATION, TimeUnit.SECONDS);
// Optionally include a shutdownNow() call after awaitTermination() to force
// close any lingering resources
keyManagementServiceClient.shutdownNow();
除了 close() 之外,一些 Java 客户端库还公开了一些相关方法来管理客户端生命周期:
shutdown():等同于close()。shutdownNow():立即调用关闭进程。停止所有正在执行的任务,但不等待任务完成。isShutdown():如果后台任务已关闭,则返回true。isTerminated():如果所有任务在关闭后都已完成,则返回true。awaitTermination():在关闭后阻塞一段时间,直到所有工作完成。
多个客户的案例
在某些特定的客户使用场景中,可能需要同时存在多个客户端库实例。拥有多个客户端的主要使用场景是必须将请求发送到多个不同端点的情况。客户端实例连接到单个端点。如需连接到多个端点,请为每个端点创建一个客户端。
潜在的多客户风险
使用多个客户端库实例的应用存在以下一些常见风险:
- 增加了内存泄漏的可能性。如果未正确关闭所有客户端实例,资源会长时间存在。
- 性能影响。初始化多个 gRPC 通道会产生性能开销,这在对性能要求较高的应用中可能会累积起来。
- 正在处理的请求存在问题。在请求仍在处理中时关闭客户端可能会导致
RejectedExecutionException。确保在关闭客户端之前完成请求。