关闭异步 resteasy 客户端调用的最佳方法

Posted

技术标签:

【中文标题】关闭异步 resteasy 客户端调用的最佳方法【英文标题】:Best way to close async resteasy client call 【发布时间】:2020-05-14 09:22:43 【问题描述】:

我有以下代码:

var threadsWaiter = new CountDownLatch(customers.size());
for(var c: List<Customer> customers) 
   sendSms(c.phoneNr, threadsWaiter)

threadsWaiter.await();

public void sendSms(String phoneNr, CountDownLatch threadsWaiter) 
  ResteasyClientBuilder.newClient()
            .target(smsUrl)                
            .queryParam("to", phoneNr)
            .queryParam("message", message)
            .request()
            .async()
            .get(new InvocationCallback<String>() 
                @Override
                public void completed(String res) 
                    threadsWaiter.countDown();
                    if (res != null && !res.contains("code=ok") 
                        logger.error("Received sms response for ''\n", phoneNr, res);
                     else                             
                        logger.debug("Sms sent to ''", phoneNr);
                    
                

                @Override
                public void failed(Throwable throwable) 
                    threadsWaiter.countDown();
                    logger.error("Error sending sms for : \n", phoneNr, throwable.getMessage());
                
            );

我从控制台收到以下警告:

RESTEASY004687: Closing a class org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient43Engine instance for you. Please close clients yourself.

关闭此客户呼叫的正确方法是什么?因为这可能是应用程序中潜在内存泄漏的来源。甚至我从 RestEasy 收到警告,它会自动为我关闭客户端,但我有一种强烈的感觉,它不会关闭所有客户端,因为我看到指标中的内存大幅增加,而且这不会“去一段时间后“下来”。

我已经在 try-finally 之间放置了其余的客户端调用,但这样做的问题是您可以在调用完成之前关闭客户端。 InvocationCallback 中的completed(..)failed(..) 方法中关闭客户端是否可以,还是有更好的方法?

【问题讨论】:

【参考方案1】:

如果通过 JaxRS 接口的客户端定义对您来说没问题,那么上面rowing-goul 的答案就是要走的路。 Quarkus Arc 在 bean 销毁过程中自动关闭注入的客户端。如果您手动创建客户端,则必须明确关闭它们 - 最好的方法是 try - finally

【讨论】:

【参考方案2】:

使用 Quarkus 执行此操作的最佳方法是使用 REST client with async support。 示例:

    /**
     * This is the client stub.
     */
    @Path("/sms/response") // base URL is set in application.yml
    @RegisterRestClient
    public interface SmsServiceClient 
        
        @GET
        @Produces(MediaType.TEXT_PLAIN)
        CompletionStage<String> sendSms(
            @QueryParam("to") String phoneNr,
            @QueryParam("message") String message);
    

在以下示例中,我使用SmallRye Mutiny 将CompletionStage 转换为具有更精简API 的Uni。但是您可以使用CompletionStage 实现相同的目的。通常,我不会使用 CountDownLatch.await() 方法阻止执行。我把它卡在那里以保持代码与您的示例相似。

    /**
     * This class will actually use the client.
     */
    @Slf4J
    @ApplicationScoped
    public class MySomething 
        
        @Inject
        @RestClient
        SmsServiceClient smsClient;

        public void sendSmsInLoop() 
            var waiter = new CountDownLatch(customers.size());
            customers.forEach(customer -> 
                Uni.createFrom().completionStage(
                    smsClient.sendSms(customer.getPhoneNr(), "Lorem Ipsum...")
                ).onItem().invoke(responseString -> 
                    if (responseString == null || !responseString.contains("code=ok")) 
                        log.error("Unexpected sms response for ''\n", customer.getPhoneNr(), responseString); 
                     else 
                        log.debug("Sms sent to ''", customer.getPhoneNr());
                    
                ).onFailure().invoke(throwable -> 
                    log.error("Error sending sms to ''\n", customer.getPhoneNr(), throwable.getMessage());
                ).eventually(() -> waiter.countDown());
            );
            waiter.await();
        
    

【讨论】:

以上是关于关闭异步 resteasy 客户端调用的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

创建 web 应用程序的最佳方法,该应用程序在外部客户端调用他自己的休息服务时动态更新 UI [关闭]

将 Django 模型中的保存方法覆盖为使用 celery 异步的最佳实践

创建 API 的最佳方法 [关闭]

RestEasy 异步请求中的空指针异常

RestEasy 异步请求中的空指针异常

在地图中调用异步函数的最佳方法?