为什么要研究Sofa-Bolt
- 1.用于做远程桌面控制的基础框架,实现远程桌面鼠标键盘事件的透传
- 2.用于IM,sofa-bolt本身支持连接管理,可根据接收方id找到接收方的channel,进而实现发送消息。
- 3.实现分布式集群中间件状态同步工具,比如Raft协议实现
- 4.用于云盒指令透传执行,云盒为指令执行方实体域sofa client,服务器为下发指令方实体域为sofa server,通过sofa-bolt的rpc能力,可以轻易的实现指令的同步、异步执行;完美适配我们的云盒指令“透传->执行->响应”的场景,不过缺陷是集群的实现需要自己基于zookeeper或其他注册中心实现。
- 5:想基于sofa-bolt实现设备协议对接还是挺困难的,毕竟设备的协议格式不可能按照sofa-bolt的协议定义,而sofa-bolt想调整协议实现需要修改太多。
SOFA-BOLT性能优化技巧
1.修改Netty源码中的ByteToMessageDecoder.channelRead方法将管道消息传递改为支持批量
//Netty源码,修改之前
for (int i = 0; i < numElements; i ++) {
ctx.fireChannelRead(msgs.getUnsafe(i));
}
//Sofa-Bolt源码,修改之后
if (size == 1) {
ctx.fireChannelRead(out.get(0));
} else {
ArrayList<Object> ret = new ArrayList<Object>(size);
for (int i = 0; i < size; i++) {
ret.add(out.get(i));
}
ctx.fireChannelRead(ret);
}
在RpcCommandHandler.handleCommand方法中,要对传递过来的消息进行判断是否需要批量还是单个处理
private void handle(final RemotingContext ctx, final Object msg) {
try {
if (msg instanceof List) {
final Runnable handleTask = new Runnable() {
@Override
public void run() {
if (logger.isDebugEnabled()) {
logger.debug("Batch message! size={}", ((List<?>) msg).size());
}
for (final Object m : (List<?>) msg) {
RpcCommandHandler.this.process(ctx, m);
}
}
};
if (RpcConfigManager.dispatch_msg_list_in_default_executor()) {
// If msg is list ,then the batch submission to biz threadpool can save io thread.
// See com.alipay.remoting.decoder.ProtocolDecoder
processorManager.getDefaultExecutor().execute(handleTask);
} else {
handleTask.run();
}
} else {
process(ctx, msg);
}
} catch (final Throwable t) {
processException(ctx, msg, t);
}
}
2.反序列机制的优化
sofabolt默认在IO线程里只序列化className。其余的数据都由业务线程进行反序列化,以最大化的利用 IO 线程处理连接的能力。同时,SOFABolt 也提供了更多场景的下的反序列化时机,例如 IO 密集型的业务,为了防止大量上下文切换,就可以直接在 IO 线程处理所有任务,包括业务逻辑。同时也提供业务线程池隔离的场景,此时 IO 线程在反序列化 ClassName 的基础上,再反序列化 header,剩下的交有业务线程池。不可谓不灵活。
3.创建连接池的无锁设计
public class RunStateRecordedFutureTask<V> extends FutureTask<V> {
private AtomicBoolean hasRun = new AtomicBoolean();
public RunStateRecordedFutureTask(Callable<V> callable) {
super(callable);
}
@Override
public void run() {
this.hasRun.set(true);
super.run();
}
public V getAfterRun() throws InterruptedException, ExecutionException,
FutureTaskNotRunYetException, FutureTaskNotCompleted {
if (!hasRun.get()) {
throw new FutureTaskNotRunYetException();
}
if (!isDone()) {
throw new FutureTaskNotCompleted();
}
return super.get();
}
}
private ConnectionPool getConnectionPoolAndCreateIfAbsent(String poolKey,
Callable<ConnectionPool> callable)
throws RemotingException,
InterruptedException {
RunStateRecordedFutureTask<ConnectionPool> initialTask;
ConnectionPool pool = null;
int retry = Constants.DEFAULT_RETRY_TIMES;
int timesOfResultNull = 0;
int timesOfInterrupt = 0;
for (int i = 0; (i < retry) && (pool == null); ++i) {
initialTask = this.connTasks.get(poolKey);
if (null == initialTask) {
RunStateRecordedFutureTask<ConnectionPool> newTask = new RunStateRecordedFutureTask<ConnectionPool>(
callable);
initialTask = this.connTasks.putIfAbsent(poolKey, newTask);
if (null == initialTask) {
initialTask = newTask;
initialTask.run();
}
}
try {
pool = initialTask.get();
if (null == pool) {
if (i + 1 < retry) {
timesOfResultNull++;
continue;
}
this.connTasks.remove(poolKey);
String errMsg = "Get future task result null for poolKey [" + poolKey
+ "] after [" + (timesOfResultNull + 1) + "] times try.";
throw new RemotingException(errMsg);
}
} catch (InterruptedException e) {
if (i + 1 < retry) {
timesOfInterrupt++;
continue;// retry if interrupted
}
this.connTasks.remove(poolKey);
logger
.warn(
"Future task of poolKey {} interrupted {} times. InterruptedException thrown and stop retry.",
poolKey, (timesOfInterrupt + 1), e);
throw e;
} catch (ExecutionException e) {
// DO NOT retry if ExecutionException occurred
this.connTasks.remove(poolKey);
Throwable cause = e.getCause();
if (cause instanceof RemotingException) {
throw (RemotingException) cause;
} else {
FutureTaskUtil.launderThrowable(cause);
}
}
}
return pool;
}