Jersey WebTarget线程安全

时间:2017-04-20 19:26:54

标签: java jersey thread-safety dropwizard

为了改进我们服务的故障排除,我们实施了一个机制,即“请求ID"”,其中为每个请求分配一个唯一的ID,然后当服务在为特定请求提供服务时调用另一个ID时,它会发送请求ID作为HTTP标头,因此如果在被叫服务中发生错误,我们可以很容易地找出原始请求是什么。为了实现此功能,我们创建了两个过滤器:

  • 一个ContainerRequestFilter,它从请求标头中读取ID并将其设置在ContainerRequestContext
  • ClientRequestFilter,将请求ID添加到Client
  • 发出的所有请求中

为此,我们在每个请求上注册ClientRequestFilter,如下所示:

public class MyServiceClient {

    private WebTarget target;

    @Inject
    public MyServiceClient(Client client, @Context ContainerRequestContext ctx) {
        // This is called on every request, with a new context
        this.target = client
                .target("/target/endpoint")
                .register(new RequestIdFilter(ctx)); // custom ClientRequestFilter
    }

    public void doSomething() {
        this.target.path("resource")
                .request()
                .get();
    }
}

这在大多数情况下都可以正常工作,但我们遇到了零星错误,这些错误似乎是由线程安全问题引起的。会发生的情况是,如果两个线程同时调用MyServiceClient::doSomethingWebTarget实例最终会调用ClientConfig::initRuntime,它会在某个时刻收集所有已配置的Binder并调用{{} 1}}在他们身上。现在,Binder::bind显然不是线程安全的,因此有时请求会失败:

AbstractBinder::bind

根据Is java Jersey 2.1 client thread safe?,此问题可能是由其他人(可能是JacksonBinder,来自Dropwizard)注册的非线程安全java.lang.IllegalArgumentException: Recursive configuration call detected. at org.glassfish.hk2.utilities.binding.AbstractBinder.bind(AbstractBinder.java:179) at org.glassfish.jersey.model.internal.CommonConfig.configureBinders(CommonConfig.java:676) at org.glassfish.jersey.model.internal.CommonConfig.configureMetaProviders(CommonConfig.java:641) at org.glassfish.jersey.client.ClientConfig$State.configureMetaProviders(ClientConfig.java:372) at org.glassfish.jersey.client.ClientConfig$State.initRuntime(ClientConfig.java:405) at org.glassfish.jersey.client.ClientConfig$State.access$000(ClientConfig.java:90) at org.glassfish.jersey.client.ClientConfig$State$3.get(ClientConfig.java:122) at org.glassfish.jersey.client.ClientConfig$State$3.get(ClientConfig.java:119) at org.glassfish.jersey.internal.util.collection.Values$LazyValueImpl.get(Values.java:340) at org.glassfish.jersey.client.ClientConfig.getRuntime(ClientConfig.java:733) at org.glassfish.jersey.client.ClientRequest.getConfiguration(ClientRequest.java:286) at org.glassfish.jersey.client.JerseyInvocation.validateHttpMethodAndEntity(JerseyInvocation.java:135) at org.glassfish.jersey.client.JerseyInvocation.<init>(JerseyInvocation.java:105) at org.glassfish.jersey.client.JerseyInvocation.<init>(JerseyInvocation.java:101) at org.glassfish.jersey.client.JerseyInvocation.<init>(JerseyInvocation.java:92) at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:420) at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:316) 引起的。你觉得怎么样?

如果方法有误,您是否知道实施此方法的更好方法&#34;请求ID&#34;功能?

谢谢!

编辑1:以下是Binder

的代码
RequestIdFilter

编辑2:在public class RequestIdFilter implements ClientRequestFilter { private ContainerRequestContext context; public RequestIdFilter(ContainerRequestContext context) { this.context = context; } @Override public void filter(ClientRequestContext clientCtx) throws IOException { String requestId = (String) context.getProperty(X_REQUEST_ID); // This is set by a ContainerRequestFilter clientCtx.getHeaders().putSingle(X_REQUEST_ID, requestId); } } 构造函数中创建WebTarget时,通过将请求ID作为查询参数传递(暂时)修复了该问题。这似乎是线程安全的,并且避免在每个请求上注册新的过滤器。

0 个答案:

没有答案