Как иметь в потоке, но многоразовые объекты (PubNub) в приложении Spring?
я подключаюсь к PubNub в приложении Spring Boot. из документации можно повторно использовать объекты PubNub но лучше иметь по одному на поток. Каков подходящий метод для хранения и извлечения одного объекта на поток в Spring Boot?
4 ответов
это, как вы будете хранить и извлекать объект на поток весной, используя ThreadLocal
, этот пример основан на собственном ThreadLocalSecurityContextHolderstrategy для хранения SecurityContext
в каждом потоке.
кроме того, взгляните на InheritableThreadLocal особенно, если ваш код запускает новый поток, например Spring's @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();
}
}
вы можете использовать поддержку Java ThreadLocal, как упоминалось выше @SergeyB. Другой способ сделать это-использовать область потока для ваших бобов:
@Configuration
public class AppConfig {
//Register thread scope for your application
@Bean
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
return beanFactory -> beanFactory.registerScope("thread", new SimpleThreadScope());
}
}
затем вы можете создать боб с областью потока (режим прокси будет объяснен ниже):
@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.
надеюсь, что это помогает!
прежде всего,
поскольку безопасно использовать один объект PubNub в нескольких потоках,
вам нужно несколько объектов PubNub, только если вам нужно увеличить производительность
Если это ваш случай - мое предложение будет организовать бассейн объектов PubNub (случай использования довольно близок к случаю использования соединения с БД).
Я предполагаю, что мы имеем дело с подключением к PubNub для входного запроса к серверу tomcat, когда вы говорите о новом потоке.
Я считаю, что хороший способ справиться с такой ситуацией-создать новый объект PubNub для каждого запроса и позволить spring обрабатывать жизненный цикл объекта.
наличие приведенного ниже определения компонента с областью прототипа позволяет создавать новый объект 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
}
}
Я надеюсь, что мое предположение о сценарии / usecase, который вы хотите решить, правильно. Напишите нам больше информации, если предположение неверно / нуждается в коррекции. Держи нас в курсе.