如何在Spring应用程序中使用每个线程但可重用的对象(PubNub)?

时间:2017-10-19 17:06:21

标签: spring spring-boot pubnub

我在Spring Boot应用程序中连接到PubNub。 From the documentation, it's ok to re-use PubNub objects但每个帖子最好有一个。在Spring Boot中每个线程存储和检索一个对象的适当方法是什么?

4 个答案:

答案 0 :(得分:7)

这是你使用ThreadLocal在Spring中为每个线程存储和检索一个对象的方法,这个例子基于Spring自己的ThreadLocalSecurityContextHolderStrategy,它用于存储每个线程SecurityContext

此外,请查看InheritableThreadLocal,特别是如果您的代码旋转新线程,例如Spring的@Async注释,它具有在创建子线程时传播现有或创建新线程局部值的机制。

import org.springframework.util.Assert;

final class ThreadLocalPubNubHolder {

    private static final ThreadLocal<PubNub> contextHolder = new ThreadLocal<PubNub>();

    public void clearContext() {
        contextHolder.remove();
    }

    public PubNub getContext() {
        PubNub ctx = contextHolder.get();

        if (ctx == null) {
            ctx = createEmptyContext();
            contextHolder.set(ctx);
        }

        return ctx;
    }

    public void setContext(PubNub context) {
        Assert.notNull(context, "Only non-null PubNub instances are permitted");
        contextHolder.set(context);
    }

    public PubNub createEmptyContext() {
        // TODO - insert code for creating a new PubNub object here
        return new PubNubImpl();
    }
}

答案 1 :(得分:3)

您可以使用@SergeyB上面提到的Java ThreadLocal支持。另一种方法是为您的bean使用Thread Scope:

@Configuration
public class AppConfig {
    //Register thread scope for your application
    @Bean
    public BeanFactoryPostProcessor beanFactoryPostProcessor() {
        return beanFactory -> beanFactory.registerScope("thread", new SimpleThreadScope());
    }
}

然后你可以创建一个带有线程范围的bean(代理模式将在下面解释):

@Scope(value = "thread", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class PubSubContext {

    private PubSub pubSub;

    public PubSub getPubSub() {
        return pubSub;
    }

    public void setPubSub(PubSub pubSub) {
        this.pubSub = pubSub;
    }

    @PostConstruct
    private void init() {
        // TODO: your code for initializing PubSub object
        log.info("RequiredMessageHeaders started in thread " + Thread.currentThread().getId());
    }

    @PreDestroy
    private void destroy() {
        // TODO: your code for cleaning resources if needed
        log.info("RequiredMessageHeaders destroyed in thread " + Thread.currentThread().getId());
    }
}

最后一步是在您需要的地方注入PubSubContext

@Controller
public class YourController {

    // Spring will inject here different objects specific for each thread. 
    // Note that because we marked PubSubContext with proxyMode = ScopedProxyMode.TARGET_CLASS we do not need to use applicationContext.get(PubSubContext.class) to obtain a new bean for each thread - it will be handled by Spring automatically.
    @Autowired
    private PubSubContext pubSubContext;

    @GetMapping
    public String yourMethod(){
        ...
        PubSub pubSub = pubSubContext.getPubSub();
        ...
    }

}

使用这种方法,你可以更进一步,将你的PubSubContext标记为@Lazy,这样就不会在yourMethod内请求它之前创建它:

@Controller
public class YourController {

    @Lazy
    @Autowired
    private PubSubContext pubSubContext;

    ...
}

正如您所看到的,PubSubContext基本上完成了ThreadLocal所做的事情,但是Spring功能正在利用它。

希望它有所帮助!

答案 2 :(得分:0)

首先,

因为在多个线程中使用单个PubNub对象是安全的,

如果您需要提高性能,则只需要多个PubNub对象

如果是这种情况 - 我的建议是组织pool PubNub对象(用例非常接近数据库连接用例)。

答案 3 :(得分:-1)

我假设当你谈到正在生成的新线程时,我们正在处理每个输入请求到tomcat服务器的连接到PubNub。

我认为处理这种情况的一个好方法是为每个请求创建一个新的PubNub对象,让spring处理对象的生命周期。

使用原型范围的下面的bean定义允许为应用程序上下文中的每个查找创建新的PubNub对象。

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
PubNub getPubNub() {
    PNConfiguration pnConfiguration = new PNConfiguration();
    pnConfiguration.setSubscribeKey("SubscribeKey");
    pnConfiguration.setPublishKey("PublishKey");
    pnConfiguration.setSecure(false);

    PubNub pubnub = new PubNub(pnConfiguration);
    return pubnub;
}

您希望使用上面创建的PubNub对象的服务类如下所示:

class PubNubService{
@Autowired
ApplicationContext context; 
...
public void process()
{

PubNub pubNub = context.getBean(PubNub.class);

pubNub.publish()....
//your code for publish goes here
}
} 

我希望我对你想要解决的场景/用例的假设是正确的。如果假设错误/需要纠正,请向我们提供更多信息。请告诉我们它是如何发展的。