使用 Grizzly HTTP 服务器线程加载和共享数据存储缓存

Load and share data-store cache with Grizzly HTTP Server threads

我正在 Java SE 应用程序中创建一个 Grizzly http 服务器,指向一个带有 class 的资源包,它处理来自浏览器的 @GET 调用。

默认情况下,Grizzly 最多启动 16 个线程 (0-15),然后再次循环回到线程 0。每次初始化这些线程时(而且,看起来,即使它再次返回到线程 0),它也会调用资源 class 上的构造函数 - 即在每个 GET 请求上调用构造函数,而不仅仅是第一个甚至只是前 16 个(每个线程一个)。

我的构造函数创建了一个 Kafka Streams 数据存储,它不能被多次初始化(好吧,我可以每次分配一个随机标识符,但最终我仍然会遇到冲突),所以我想创建一次并让所有线程都可以访问它。

在资源代码本身有没有一种简单的方法可以做到这一点,还是我最好在别处创建数据存储,然后进行某种跨线程调用来获取信息(在这种情况下,是否有有什么方法可以将该引用注入资源代码,例如将其传递给构造函数?)

或者,我能否让 Grizzly 以某种方式让对象保持活动状态,这样它就不需要继续调用构造函数?如果可能的话,这种方法有什么缺陷?

当前代码:

服务器创建并启动如下:

public static HttpServer startServer() 
{
    final ResourceConfig rc = new ResourceConfig().packages("com.project.resource.provider");    
    HttpServer server = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);          
    return server;
}

JAX-RS 资源:

@Path("/GetFromHere")
public class GetFromHereProvider // in package  com.project.resource.provider
{
    private final ReadOnlyKeyValueStore<String,DetailObject> kafkaDetailObjectStore;
    
    public GetFromHereProvider() throws Exception
    {
        // create kafka streams objects and populate kafkaDetailObjectStore. This is being called on every GET request too, for some reason.
    }

    @GET
    @Path("{id}")
    @Produces(MediaType.TEXT_PLAIN)
    public String getObject(@PathParam("id") String objectID)
    {
        DetailObject obj= kafkaDetailObjectStore(objectID);
        return (obj == null ? "" : obj.toString()); 
    }
}

(以下回答对告知此人非常有帮助:

这样做的关键是将构造函数逻辑移动到单独的服务 class(术语?),然后在运行时使用依赖注入将该对象的单例实例推送到构造函数中。

新代码如下所示:

  1. 创建新服务class:
@Singleton
public ServiceProviderClass
{
    private final ReadOnlyKeyValueStore<String, DetailObject> kafkaDetailObjectStore;

    public ServiceProviderClass()
    {
        // instantiate kafkaDetailObjectStore...
    }

    public ReadOnlyKeyValueStore<String, DetailObject> getKafkaDetailObjectStore()
    {
        return kafkaDetailObjectStore;
    }
}
  1. 创建一个新的绑定程序 class,这将允许注入上述服务 class。我们还告诉它把它当作一个单例,这样它只创建一个对象实例:
public class ServiceProviderClassBinder extends AbstractBinder{
    @Override
    protected void configure() {
        bind(ServiceProviderClass.class)
            .to(ServiceProviderClass.class)
            .in(Singleton.class);

        // alternatively, bind(new ServiceProviderClass()).to(ServiceProviderClass.class) should also work, and then you don't need the .in call. 
        //If you have Interfaces and Implementations of those Interfaces, the Interface goes in the "to" call, and the concrete implementation in the "bind" call.
        //If you don't specify an instance and you don't specify the .in() call, it defaults to instantiating per-call.
    }
}
  1. 更新 GetFromHereProvider class 以告诉它将服务注入构造函数
@Path("/GetFromHere")
public class GetFromHereProvider // in package  com.project.resource.provider
{
    private final ServiceProviderClass kafkaDetailObjectStoreServiceProvider;
    
    // CHANGED
    @Inject
    public GetFromHereProvider(ServiceProviderClass kafkaDetailObjectStoreServiceProvider)
    {
        this.kafkaDetailObjectStoreServiceProvider = kafkaDetailObjectStoreServiceProvider;
    }

    @GET
    @Path("{id}")
    @Produces(MediaType.TEXT_PLAIN)
    public String getObject(@PathParam("id") String objectID)
    {
        DetailObject obj= kafkaDetailObjectStoreServiceProvider.getKafkaDetailObjectStore()(objectID);
        return (obj == null ? "" : obj.toString()); 
    }
}
  1. 最后,在为 Grizzly HTTP 服务器创建 ResourceConfig 时注册绑定器:
public static HttpServer startServer() 
{
    final ResourceConfig rc = new ResourceConfig().packages("com.project.resource.provider");   
    // NEW
    rc.register(new ServiceProviderClassBinder()); 
    HttpServer server = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);          
    return server;
}