nats java client 的多线程处理细节
nats java client 的设计的时候默认是由一个单线程的Dispatcher处理的,这样就会有一个问题,除非我们的服务运行的比较快,否则就会有一个阻塞任务造成服务不可用的问题
默认参考处理
- 代码
我只摘取部分代码
public void run() {
try {
while (running.get() && !Thread.interrupted()) {
NatsMessage msg = this.incoming.pop(this.waitForMessage);
if (msg != null) {
NatsSubscription sub = msg.getNatsSubscription();
if (sub != null && sub.isActive()) {
MessageHandler handler = nonDefaultHandlerBySid.get(sub.getSID());
if (handler == null) {
handler = defaultHandler;
}
// A dispatcher can have a null defaultHandler. You can't subscribe without a handler,
// but messages might come in while the dispatcher is being closed or after unsubscribe
// and the [non-default] handler has already been removed from subscriptionHandlers
if (handler != null) {
sub.incrementDeliveredCount();
this.incrementDeliveredCount();
try {
handler.onMessage(msg);
} catch (Exception exp) {
connection.processException(exp);
} catch (Error err) {
connection.processException(new Exception(err));
}
if (sub.reachedUnsubLimit()) {
connection.invalidate(sub);
}
可以看到如果任务阻塞,其他也就只能等着,因为就是单线程的
造成的影响
一个是处理会排队阻塞,一个是如果在web 系统中使用,比如基于nats 的micro service 会造成整个服务不可用
解决方法
机制上官方是提供了支持配置的线程池,同时还与一个开启支持线程池的Dispatcher
- 参考配置
配置之后会有一个无界的线程池,一般场景够用,但是也可以自己调整
options = new Options.Builder().useDispatcherWithExecutor()
- 内部实际处理
可以看到每次消息的处理都是基于线程池的,这样机制上就可以可以规避因为特定处理慢,造成整个服务不稳定
public void run() {
try {
while (running.get() && !Thread.interrupted()) {
NatsMessage msg = this.incoming.pop(this.waitForMessage);
if (msg != null) {
NatsSubscription sub = msg.getNatsSubscription();
if (sub != null && sub.isActive()) {
MessageHandler handler = nonDefaultHandlerBySid.get(sub.getSID());
if (handler == null) {
handler = defaultHandler;
}
// A dispatcher can have a null defaultHandler. You can't subscribe without a handler,
// but messages might come in while the dispatcher is being closed or after unsubscribe
// and the [non-default] handler has already been removed from subscriptionHandlers
if (handler != null) {
sub.incrementDeliveredCount();
this.incrementDeliveredCount();
MessageHandler finalHandler = handler;
connection.getExecutor().execute(() -> {
try {
finalHandler.onMessage(msg);
} catch (Exception exp) {
connection.processException(exp);
} catch (Error err) {
connection.processException(new Exception(err));
}
if (sub.reachedUnsubLimit()) {
connection.invalidate(sub);
}
});
说明
以上是结合源码的一个简单说明,实际上边说的参数还是很重要的,否则可能会造成服务看着不稳定,实际就是一个简单的配置参数
参考资料
https://javadoc.io/static/io.nats/jnats/2.24.1/io/nats/client/Options.Builder.html
https://javadoc.io/static/io.nats/jnats/2.24.1/io/nats/client/Dispatcher.html
浙公网安备 33010602011771号