客户端库实例是可重复使用的,并且设计为长期存在。通常,应用会维护客户端库的单个实例,而不是为每个请求创建一个库。
以下代码段以 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。请确保请求在关闭客户端之前完成。