sofa-bolt源码解读之处理器

 

官方文档请参考:https://www.sofastack.tech/projects/sofa-bolt/sofa-bolt-handbook/

简书优质博文:https://www.jianshu.com/p/f2b8a2099323

目录

两类处理器

用户请求处理器

同步请求处理器抽象类SyncUserProcessor

异步请求处理器抽象类AsyncUserProcessor

连接事件处理器

ConnectionEventProcessor连接事件接口类

CONNECTEventProcessor连接事件处理器

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断连事件处理器

这个处理器原理与上大同小异,就不做过多介绍了。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SOFABolt 是蚂蚁金融服务集团开发的一套基于 Netty 实现的网络通信框架。 为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO 的实现以及处理难以调试的网络问题,Netty 应运而生。 为了让中间件开发者能将更多的精力放在产品功能特性实现上,而不是重复地一遍遍制造通信框架的轮子,SOFABolt 应运而生。 Bolt 名字取自迪士尼动画-闪电狗,是一个基于 Netty 最佳实践的轻量、易用、高性能、易扩展的通信框架。 这些年我们在微服务与消息中间件在网络通信上解决过很多问题,积累了很多经验,并持续的进行着优化和完善,我们希望能把总结出的解决方案沉淀到 SOFABolt 这个基础组件里,让更多的使用网络通信的场景能够统一受益。 目前该产品已经运用在了蚂蚁中间件的微服务 (SOFARPC)、消息中心、分布式事务、分布式开关、以及配置中心等众多产品上。 SOFABolt的基础功能包括: 1、基础通信功能 ( remoting-core ) 基于 Netty 高效的网络 IO 与线程模型运用 连接管理 (无锁建连,定时断链,自动重连) 基础通信模型 ( oneway,sync,future,callback ) 超时控制 批量解包与批量提交处理器 心跳与 IDLE 事件处理 2、协议框架 ( protocol-skeleton ) 命令与命令处理器 编解码处理器 心跳触发器 3、私有协议定制实现 - RPC 通信协议 ( protocol-implementation ) RPC 通信协议的设计 灵活的反序列化时机控制 请求处理超时 FailFast 机制 用户请求处理器 ( UserProcessor ) 双工通信

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值