Jersey @ManagedAsync并在HTTP线程和Worker线程之间复制数据

Hai*_*man 10 java rest multithreading jersey servlet-3.0

我正在研究一个有两种口味的项目,有多种租赁方式.

该项目公开了一个我希望异步的REST服务.所以我的基本服务看起来像

@Component
@Path("/resouce")
@Consumes(MediaType.APPLICATION_JSON)
public class ResouceEndpoint {
    @POST
    @ManagedAsync
    public void add(final Event event, @Suspended final AsyncResponse asyncResponse) {
        resouce.insert (event);
        asyncResponse.resume( Response.status(Response.Status.NO_CONTENT).build());     
    }
}
Run Code Online (Sandbox Code Playgroud)

没有多租户就可以正常工作,我可以免费获得内部Jersey执行器服务的好处.请参阅@ManagedAsync

当我切换到多租户时,我在请求上添加了一个过滤器,用于解析租户ID并将其放在本地线程上(在我们的例子中是HTTP线程).

当处理链命中上面的"add()"方法时,当前线程是Jersey执行器服务提供的线程,因此它不包括我的租户ID.我只能考虑以下选项来解决这个问题.

将ResouceEndpoint扩展为MutliTenantResouceEndpoint并使用我自己的线程执行器删除@ManagedAsync

public class MutliTenantResouceEndpoint extends ResouceEndpoint {
    @POST
    public void add(final Event event, @Suspended final AsyncResponse asyncResponse) {
        final String tenantId = getTeantIdFromThreadLocal();
        taskExecutor.submit(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                setTeantIdToThreadLocal(tenantId);
                browserEventsAnalyzer.insertEvent(event);
                Response response = Response.status(Response.Status.NO_CONTENT).build();
                asyncResponse.resume(response);
                return null;
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

但是这样我需要管理自己的线程执行器,感觉就像我在这里遗漏了一些东西.有关不同方法的任何建议吗?

Ald*_*den 16

以下是一些建议.

对于背景,我已经使用泽西岛2年了,18个月前就遇到了这个问题.

1.停止使用 @ManagedAsync

如果您可以控制运行Jersey的http服务器,我建议您停止使用@ManagedAsync.

而不是设置Jersey立即返回它的http处理线程并将实际请求工作卸载到托管执行程序服务线程,为您的http服务器使用Grizzly之类的东西,并将其配置为具有更大的工作线程池.这实现了同样的目的,但是将同步责任推到泽西岛下面的一层.

如果您使用@ManagedAsync任何中型到大型项目,您将在一年内遇到许多痛点.以下是他们中的一些:

  • 如果任何ContainerRequestFilter命中一个外部服务(例如,一个auth过滤器命中你的安全模块,它会访问数据库),你将失去你认为正在获得的好处
    • 如果您的数据库扼流并且该身份验证过滤器调用需要5秒,那么Jersey还没有将工作卸载到异步线程,因此需要阻止接收新conn的主线程被阻止
  • 如果在过滤器中设置logback的MDC,并且在整个请求中需要该上下文,则需要在托管异步线程上再次设置MDC
  • 资源方法对于新来者来说是神秘的,而且难以阅读,因为:
    • 他们需要一个额外的参数
    • 他们返回虚空,隐藏他们真正的反应类型
    • 他们可以"返回"任何地方,没有任何实际的return陈述
  • Swagger或其他API文档工具无法自动记录异步资源端点
  • Guice或其他DI框架在处理异步资源端点中的某些范围绑定和/或提供程序时可能会遇到问题

2.用途@ContextContainerRequest属性

这将涉及调用requestContext.setProperty("tenant_id", tenantId)您的过滤器,然后requestContext.getProperty("tenant_id")使用@Context注入的请求调用您的资源中的调用.

3.使用HK2 AOP代替Jersey过滤器

这将涉及设置HK2绑定,InterceptionService该绑定具有MethodInterceptor检查托管异步资源方法并手动执行所有RequestScoped绑定ContainerRequestFilter的方法.而不是你的过滤器在Jersey注册,你用HK2注册它们,由方法拦截器运行.

如果您愿意,我可以向选项2/3添加更多详细信息和代码示例,或者提供其他建议,但首先查看更多过滤器代码会有所帮助,如果可能,我再次建议选项1.