El procesador de interpretación del código fuente de sofa-bolt

 

Para documentos oficiales, consulte: https://www.sofastack.tech/projects/sofa-bolt/sofa-bolt-handbook/

Publicación de blog de alta calidad de libro corto: https://www.jianshu.com/p/f2b8a2099323

Tabla de contenido

Dos tipos de procesadores

Controlador de solicitudes de usuario

Procesador de solicitud de sincronización clase abstracta SyncUserProcessor

Procesador de solicitudes asincrónico clase abstracta AsyncUserProcessor

Controlador de eventos de conexión

Clase de interfaz de evento de conexión ConnectionEventProcessor

Controlador de eventos de conexión CONNECTEventProcessor

DISCONNECTEventProcessor controlador de eventos de desconexión


Dos tipos de procesadores

Sofa-bolt proporciona dos tipos de procesadores: procesador de solicitudes de usuario y procesador de eventos de conexión .

El procesador de solicitudes del usuario se utiliza principalmente para responder a los datos de la solicitud, por ejemplo, el servidor envía un mensaje de respuesta específico al cliente después de recibir la solicitud.

El controlador de eventos de conexión se utiliza principalmente para monitorear eventos como conexiones, cierres y excepciones de conexión, a fin de realizar algunas operaciones relacionadas.

Controlador de solicitudes de usuario

Se proporcionan dos procesadores de solicitudes de usuario, SyncUserProcessor y AsyncUserProcessor . La diferencia entre los dos es que el primero necesita devolver el resultado del procesamiento en forma de un valor de retorno en el subproceso de procesamiento actual; mientras que el último tiene un código auxiliar AsyncContext, que se puede llamar en el subproceso actual o en un subproceso asincrónico  sendResponse para devuelve el resultado del procesamiento.

El diagrama de clases de relación de herencia es el siguiente:

Los ejemplos pueden hacer referencia a las dos categorías siguientes:

Procesador de solicitud de sincronización clase abstracta 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();
}

Puede ver que hay dos métodos handleRequest () definidos en esta clase abstracta, uno con valor de retorno y otro sin valor de retorno, y el método sin valor de retorno proporciona directamente un manejo de excepciones, lo que indica que las solicitudes asincrónicas no son compatibles. Por lo tanto, para la solicitud del usuario que usa el método síncrono, es necesario implementar handleRequest () con un valor de retorno. Para ejemplos específicos, consulte el ejemplo oficial: Controlador de sincronización de usuario personalizado

El siguiente es el código parcial interceptado de handleRequest () duplicado, podemos ver que se devuelve una respuesta predeterminada del servidor.

    // ~~~ 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;
    }

Mirando el código fuente, podemos ver que aquí se usa el modo adaptador

Entre ellos, AbstractUserProcessor puede considerarse como un adaptador de una interfaz (se realiza una implementación predeterminada para cada método de la interfaz, de modo que las cuatro abstracciones de nivel más bajo solo pueden implementar los métodos que necesitan implementar), por lo que prefiero nombrar es UserProcessorAdapter

Procesador de solicitudes asincrónico clase abstracta 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();
}

Puede ver que hay dos métodos handleRequest () definidos en esta clase abstracta, uno con un valor de retorno y otro sin un valor de retorno, y el método con un valor de retorno proporciona directamente un manejo de excepciones, lo que indica que la solicitud síncrona no es compatible. . Por lo tanto, para la solicitud del usuario en modo asíncrono, es necesario implementar handleRequest () sin un valor de retorno. Para ejemplos específicos, consulte el ejemplo oficial: Manejador asíncrono de usuario personalizado

La siguiente es la parte interceptada del código que reescribe handleRequest (). Podemos ver que hay un stub de AsyncContext, que puede estar en el hilo actual, o en el hilo asíncrono, en el hilo asíncrono, llamando al  sendResponse método para devolver el resultado del procesamiento.

    @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);
            }
        }
    }

Controlador de eventos de conexión

Se proporcionan dos tipos de monitoreo de eventos, evento de conexión (ConnectionEventType.CONNECT) y evento de desconexión (ConnectionEventType.CLOSE). Los usuarios pueden crear sus propios controladores de eventos y registrarlos en el cliente o servidor. Tanto el cliente como el servidor pueden monitorear sus respectivos eventos de establecimiento de conexión y desconexión.

Eche un vistazo a la siguiente clase de enumeración, que contiene cuatro eventos, conexión, falla de conexión, cierre y excepción.

/**
 * 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;
}

Clase de interfaz de evento de conexión 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);
}

Controlador de eventos de conexión CONNECTEventProcessor

El siguiente es el código fuente del controlador de eventos de conexión

/**
 * 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;
    }
}

Puede ver que el método onEvent () está implementado, principalmente para obtener la conexión actual y la dirección de acceso remoto, y luego establecer el estado de la conexión en verdadero. Además, también proporciona métodos como conectarse, obtener el número de conexiones, obtener la conexión y obtener la dirección de acceso remoto.

Entonces, hay una pregunta, ¿cuándo se llama a onEvent ()?

Echemos un vistazo al código que inicia el cliente, de la siguiente manera

    @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");
        }
    }

Vemos que se pasa un escucha de connectionEventListener cuando se inicializa el connectionManager

DISCONNECTEventProcessor controlador de eventos de desconexión

El principio de este procesador es similar al anterior, por lo que no lo presentaré demasiado.

 

Supongo que te gusta

Origin blog.csdn.net/qq_34050399/article/details/115232386
Recomendado
Clasificación