官方文档请参考:https://www.sofastack.tech/projects/sofa-bolt/sofa-bolt-handbook/
目录
ConnectionEventProcessor连接事件接口类
DISCONNECTEventProcessor断连事件处理器
两类处理器
sofa-bolt提供两种类型的处理器:用户请求处理器和连接事件处理器。
用户请求处理器主要用来对请求数据进行响应处理,比如服务端接受到请求后给与客户端一个特定的响应消息。
连接事件处理器主要用来对连接,关闭,连接异常等事件进行监听,从而进行一些相关操作。
用户请求处理器
提供了两种用户请求处理器,SyncUserProcessor 与 AsyncUserProcessor。 二者的区别在于,前者需要在当前处理线程以return返回值的形式返回处理结果;而后者,有一个 AsyncContext 存根,可以在当前线程,也可以在异步线程,调用 sendResponse
方法返回处理结果。
继承关系类图如下:
示例可参考如下两个类:
同步请求处理器抽象类SyncUserProcessor
/**
* 继承这个类去处理用户用同步的方式定义的请求
* Extends this to process user defined request in SYNC way.<br>
* 如果你想用异步的方式处理请求,请拓展AsyncUserProcessor处理器
* If you want process request in ASYNC way, please extends {@link AsyncUserProcessor}.
*
* @author xiaomin.cxm
* @version $Id: SyncUserProcessor.java, v 0.1 May 19, 2016 2:47:21 PM xiaomin.cxm Exp $
*/
public abstract class SyncUserProcessor<T> extends AbstractUserProcessor<T> {
/**
* @see com.alipay.remoting.rpc.protocol.UserProcessor#handleRequest(com.alipay.remoting.BizContext, java.lang.Object)
*/
@Override
public abstract Object handleRequest(BizContext bizCtx, T request) throws Exception;
/**
* unsupported here!
*
* @see com.alipay.remoting.rpc.protocol.UserProcessor#handleRequest(com.alipay.remoting.BizContext, com.alipay.remoting.AsyncContext, java.lang.Object)
*/
@Override
public void handleRequest(BizContext bizCtx, AsyncContext asyncCtx, T request) {
throw new UnsupportedOperationException(
"ASYNC handle request is unsupported in SyncUserProcessor!");
}
/**
* @see com.alipay.remoting.rpc.protocol.UserProcessor#interest()
*/
@Override
public abstract String interest();
}
可以看到这个抽象类中定义了两个handleRequest()方法,一个有返回值的和一个没有返回值的,而没有返回值的那个方法直接给了一个异常处理,提示不支持异步请求。所以对于用户使用同步方式的请求,就需要实现有返回值的handleRequest(),具体事例请参考官方示例:自定义用户同步处理器
以下是截取的复写handleRequest()的部分代码,我们可以看到return了一个默认的服务端响应。
// ~~~ override methods
@Override
public Object handleRequest(BizContext bizCtx, RequestBody request) throws Exception {
logger.warn("Request received:" + request + ", timeout:" + bizCtx.getClientTimeout()
+ ", arriveTimestamp:" + bizCtx.getArriveTimestamp());
if (bizCtx.isRequestTimeout()) {
String errMsg = "Stop process in server biz thread, already timeout!";
processTimes(request);
logger.warn(errMsg);
throw new Exception(errMsg);
}
this.remoteAddr = bizCtx.getRemoteAddress();
//test biz context get connection
Assert.assertNotNull(bizCtx.getConnection());
Assert.assertTrue(bizCtx.getConnection().isFine());
Long waittime = (Long) bizCtx.getInvokeContext().get(InvokeContext.BOLT_PROCESS_WAIT_TIME);
Assert.assertNotNull(waittime);
if (logger.isInfoEnabled()) {
logger.info("Server User processor process wait time {}", waittime);
}
latch.countDown();
logger.warn("Server User processor say, remote address is [" + this.remoteAddr + "].");
Assert.assertEquals(RequestBody.class, request.getClass());
processTimes(request);
if (!delaySwitch) {
return RequestBody.DEFAULT_SERVER_RETURN_STR;
}
try {
Thread.sleep(delayMs);
} catch (InterruptedException e) {
e.printStackTrace();
}
return RequestBody.DEFAULT_SERVER_RETURN_STR;
}
查看源码我们可以看到这里使用了适配器模式
其中 AbstractUserProcessor 可以看做是接口的适配器(为接口的每一个方法做了默认实现,使得最底层的四个抽象仅仅实现自己需要实现的方法就可以),所以个人更倾向于取名为 UserProcessorAdapter
异步请求处理器抽象类AsyncUserProcessor
/**
* Extends this to process user defined request in ASYNC way.<br>
* If you want process request in SYNC way, please extends {@link SyncUserProcessor}.
*
* @author xiaomin.cxm
* @version $Id: AsyncUserProcessor.java, v 0.1 May 16, 2016 8:18:03 PM xiaomin.cxm Exp $
*/
public abstract class AsyncUserProcessor<T> extends AbstractUserProcessor<T> {
/**
* unsupported here!
*
* @see com.alipay.remoting.rpc.protocol.UserProcessor#handleRequest(com.alipay.remoting.BizContext, java.lang.Object)
*/
@Override
public Object handleRequest(BizContext bizCtx, T request) throws Exception {
throw new UnsupportedOperationException(
"SYNC handle request is unsupported in AsyncUserProcessor!");
}
/**
* @see com.alipay.remoting.rpc.protocol.UserProcessor#handleRequest(com.alipay.remoting.BizContext, com.alipay.remoting.AsyncContext, java.lang.Object)
*/
@Override
public abstract void handleRequest(BizContext bizCtx, AsyncContext asyncCtx, T request);
/**
* @see com.alipay.remoting.rpc.protocol.UserProcessor#interest()
*/
@Override
public abstract String interest();
}
可以看到这个抽象类中定义了两个handleRequest()方法,一个有返回值的和一个没有返回值的,而有返回值的那个方法直接给了一个异常处理,提示不支持同步请求。所以对于用户使用异步方式的请求,就需要实现没有返回值的handleRequest(),具体事例请参考官方示例:自定义用户异步处理器
以下是截取的复写handleRequest()的部分代码,我们可以看到有一个 AsyncContext 存根,可以在当前线程,也可以在异步线程,在异步线程,调用 sendResponse
方法返回处理结果。
@Override
public void handleRequest(BizContext bizCtx, AsyncContext asyncCtx, RequestBody request) {
this.asyncExecutor.execute(new InnerTask(bizCtx, asyncCtx, request));
}
class InnerTask implements Runnable {
private BizContext bizCtx;
private AsyncContext asyncCtx;
private RequestBody request;
public InnerTask(BizContext bizCtx, AsyncContext asyncCtx, RequestBody request) {
this.bizCtx = bizCtx;
this.asyncCtx = asyncCtx;
this.request = request;
}
public void run() {
logger.warn("Request received:" + request);
remoteAddr = bizCtx.getRemoteAddress();
latch.countDown();
logger.warn("Server User processor say, remote address is [" + remoteAddr + "].");
Assert.assertEquals(RequestBody.class, request.getClass());
processTimes(request);
if (isException) {
this.asyncCtx.sendResponse(new IllegalArgumentException("Exception test"));
} else if (isNull) {
this.asyncCtx.sendResponse(null);
} else {
if (!delaySwitch) {
this.asyncCtx.sendResponse(RequestBody.DEFAULT_SERVER_RETURN_STR);
return;
}
try {
Thread.sleep(delayMs);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.asyncCtx.sendResponse(RequestBody.DEFAULT_SERVER_RETURN_STR);
}
}
}
连接事件处理器
提供了两种事件监听,建连事件(ConnectionEventType.CONNECT)与断连事件(ConnectionEventType.CLOSE),用户可以创建自己的事件处理器,并注册到客户端或者服务端。客户端与服务端,都可以监听到各自的建连与断连事件。
看一下如下枚举类,里面包含四种事件,连接,连接失败,关闭,异常。
/**
* Event triggered by connection state.
*
* @author jiangping
* @version $Id: ConnectionEventType.java, v 0.1 Mar 4, 2016 8:03:27 PM tao Exp $
*/
public enum ConnectionEventType {
CONNECT, CONNECT_FAILED, CLOSE, EXCEPTION;
}
ConnectionEventProcessor连接事件接口类
/**
* Process connection events.
* @author jiangping
* @version $Id: ConnectionEventProcessor.java, v 0.1 Mar 5, 2016 11:01:07 AM tao Exp $
*/
public interface ConnectionEventProcessor {
/**
* Process event.<br>
*
* @param remoteAddress remoting connection
* @param connection Connection
*/
void onEvent(String remoteAddress, Connection connection);
}
CONNECTEventProcessor连接事件处理器
如下是连接事件处理器源码
/**
* ConnectionEventProcessor for ConnectionEventType.CONNECT
*
* @author xiaomin.cxm
* @version $Id: CONNECTEventProcessor.java, v 0.1 Apr 8, 2016 10:58:48 AM xiaomin.cxm Exp $
*/
public class CONNECTEventProcessor implements ConnectionEventProcessor {
private AtomicBoolean connected = new AtomicBoolean();
private AtomicInteger connectTimes = new AtomicInteger();
private Connection connection;
private String remoteAddr;
private CountDownLatch latch = new CountDownLatch(1);
@Override
public void onEvent(String remoteAddr, Connection conn) {
Assert.assertNotNull(remoteAddr);
doCheckConnection(conn);
this.remoteAddr = remoteAddr;
this.connection = conn;
connected.set(true);
connectTimes.incrementAndGet();
latch.countDown();
}
/**
* do check connection
* @param conn
*/
private void doCheckConnection(Connection conn) {
Assert.assertNotNull(conn);
Assert.assertNotNull(conn.getPoolKeys());
Assert.assertTrue(conn.getPoolKeys().size() > 0);
Assert.assertNotNull(conn.getChannel());
Assert.assertNotNull(conn.getUrl());
Assert.assertNotNull(conn.getChannel().attr(Connection.CONNECTION).get());
}
public boolean isConnected() throws InterruptedException {
latch.await();
return this.connected.get();
}
public int getConnectTimes() throws InterruptedException {
latch.await();
return this.connectTimes.get();
}
public Connection getConnection() throws InterruptedException {
latch.await();
return this.connection;
}
public String getRemoteAddr() throws InterruptedException {
latch.await();
return this.remoteAddr;
}
public void reset() {
this.connectTimes.set(0);
this.connected.set(false);
this.connection = null;
}
}
可以看到实现了onEvent()方法,主要是拿到了当前的连接和远程访问的地址,然后给连接状态设为true。另外还提供了 是否连接、获取连接次数、获取连接、获取远程访问的地址等方法。
那么有一个疑问,onEvent()是在什么时候调用的呢?
我们来看下客户端启动的代码,如下
@Override
public void startup() throws LifeCycleException {
super.startup();
for (UserProcessor<?> userProcessor : userProcessors.values()) {
if (!userProcessor.isStarted()) {
userProcessor.startup();
}
}
if (this.addressParser == null) {
this.addressParser = new RpcAddressParser();
}
ConnectionSelectStrategy connectionSelectStrategy = option(BoltGenericOption.CONNECTION_SELECT_STRATEGY);
if (connectionSelectStrategy == null) {
connectionSelectStrategy = new RandomSelectStrategy(switches());
}
this.connectionManager = new DefaultClientConnectionManager(connectionSelectStrategy,
new RpcConnectionFactory(userProcessors, this), connectionEventHandler,
connectionEventListener, switches()); //创建默认的连接管理器
this.connectionManager.setAddressParser(this.addressParser);
this.connectionManager.startup();
this.rpcRemoting = new RpcClientRemoting(new RpcCommandFactory(), this.addressParser,
this.connectionManager);
this.taskScanner.add(this.connectionManager);
this.taskScanner.startup();
if (switches().isOn(GlobalSwitch.CONN_MONITOR_SWITCH)) {
if (monitorStrategy == null) {
connectionMonitor = new DefaultConnectionMonitor(new ScheduledDisconnectStrategy(),
this.connectionManager);
} else {
connectionMonitor = new DefaultConnectionMonitor(monitorStrategy,
this.connectionManager);
}
connectionMonitor.startup();
logger.warn("Switch on connection monitor");
}
if (switches().isOn(GlobalSwitch.CONN_RECONNECT_SWITCH)) {
reconnectManager = new ReconnectManager(connectionManager);
reconnectManager.startup();
connectionEventHandler.setReconnector(reconnectManager);
logger.warn("Switch on reconnect manager");
}
}
我们看到在初始化connectionManager时传了一个connectionEventListener监听器
DISCONNECTEventProcessor断连事件处理器
这个处理器原理与上大同小异,就不做过多介绍了。