Registro de aprendizaje de Dubbo (15) - Llamada de servicio [1] - Proceso de empaquetado del manipulador de Netty del lado del servidor y modelo de subprocesos del lado del servidor

Llamada de servicio de Dubbo

He escrito más de una docena de artículos antes y tengo cierta comprensión del funcionamiento de Dubbo. La llamada de servicio de Dubbo es lo más importante, y se necesitan al menos 5-6 artículos para escribir este proceso visualmente;

Empaquetado del hander de Netty en el lado del servidor

En el proceso de exportación del servicio, se harán dos cosas. Una cosa es convertir la información del proveedor del servicio en una URL y colocarla en el centro de registro; la otra es iniciar el servidor; y el proceso de datos de las solicitudes de procesamiento de red depende en cada controlador, por lo que es necesario comprender el controlador que maneja la solicitud;

    private ExchangeServer createServer(URL url) {
    
    
    //省略部分代码
        ExchangeServer server;
        try {
    
    
            // requestHandler是请求处理器,类型为ExchangeHandler
            // 表示从url的端口接收到请求后,requestHandler来进行处理
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {
    
    
            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
        }
        return server;
    }

En primer lugar, se pasará requestHandler, y este controlador es una clase ExchangeHandlerAdapter;

ExchangeHandlerAdapterExchangeHandlerAdapter

Cuando llega una solicitud, primero llama a recibido (canal de canal, mensaje de objeto) y luego llama a respuesta (canal de ExchangeChannel, mensaje de objeto) para procesar la solicitud. El mensaje son los datos solicitados y el canal representa la conexión larga con el cliente. el
proceso de trabajo: finalmente, llame a Method.invoke para ejecutar el servicio a través de la tecnología de reflexión;

  1. Tipo de conversión, convertir Objeto a Invocación;
  2. Llame a getInvoker para obtener el ejecutor del proveedor de servicios; (este Invoker estará envuelto en múltiples capas)
  3. Establezca la dirección remota remoteAddress en RpcContext
  4. Llame al método de invocación del ejecutor y devuelva el resultado;
  5. devolver una instancia de CompletionFuture;
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
    
    

        @Override
        public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
    
    
            Invocation inv = (Invocation) message;
            Invoker<?> invoker = getInvoker(channel, inv); 
			//省略部分代码
            RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
            Result result = invoker.invoke(inv);
            return result.completionFuture().thenApply(Function.identity());
        }

        @Override
        public void received(Channel channel, Object message) throws RemotingException {
    
    
            if (message instanceof Invocation) {
    
    
                // 这是服务端接收到Invocation时的处理逻辑
                reply((ExchangeChannel) channel, message);
            } else {
    
    
                super.received(channel, message);
            }
        }
}

Intercambiadores.bind(url, controlador de solicitud)

proceso:

  1. Llame a getExchanger(url) para obtener la clase de implementación extendida de Exchanger a través del mecanismo SPI. La clase de implementación predeterminada es HeaderExhanger
  2. Llame al método bind para iniciar netty;
    public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    
    
        // codec表示协议编码方式
        url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
        // 通过url得到HeaderExchanger, 利用HeaderExchanger进行bind,将得到一个HeaderExchangeServer
        return getExchanger(url).bind(url, handler);
    }

HeaderExchanger#bind(URL, manejador)

Trabajar:

  1. En primer lugar, el controlador de instancias de ExchangeHandlerAdapter entrante se empaquetará como HeaderExchangerHandler;
  2. Luego envuelva HeaderExchangerHandler como una instancia de DecodeHandler;
  3. Llame al método Transporters#bind para crear un NettyServer,
  4. Envuelva la instancia de NettyServer como HeaderExchangerServer; return;
    @Override
    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    
    

        // 下面会去启动Netty
        // 对handler包装了两层,表示当处理一个请求时,每层Handler负责不同的处理逻辑
        // 为什么在connect和bind时都是DecodeHandler,解码,解的是把InputStream解析成RpcInvocation对象
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }

Transportadores.bind(url, manejadores)

Proceso de trabajo:

  1. Si se vinculan varios controladores, cuando entra una conexión, cada controlador se ciclará para manejar la conexión.
  2. Llame al método getTransport() para obtener una instancia de Transporter a través del mecanismo SPI, que es una instancia de NettyTransporter de forma predeterminada;
  3. Llame al método NettyTransport#bind para crear un nettyServer;
    public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
    
    
		//....

        // 
        ChannelHandler handler;
        if (handlers.length == 1) {
    
    
            handler = handlers[0];
        } else {
    
    
            handler = new ChannelHandlerDispatcher(handlers);
        }
        // 调用NettyTransporter去绑定,Transporter表示网络传输层
        return getTransporter().bind(url, handler);
    }

NettyTransporter#bind(url, oyente)

Cree una instancia de Nettyserver;

public class NettyTransporter implements Transporter {
    
    

    public static final String NAME = "netty";

    @Override
    public Server bind(URL url, ChannelHandler listener) throws RemotingException {
    
    
        return new NettyServer(url, listener);
    }

    @Override
    public Client connect(URL url, ChannelHandler listener) throws RemotingException {
    
    
        return new NettyClient(url, listener);
    }

}

NettyServer (URL URL, controlador ChannelHandler)

  1. Llame al método ChannelHandlers.wrap para encapsular la instancia de DecoderHandler;
  2. llamar al constructor de la clase padre;
public class NettyServer extends AbstractServer implements Server {
    
    
    private Map<String, Channel> channels;

    private ServerBootstrap bootstrap;

	private io.netty.channel.Channel channel;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;

    public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
    
    
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
    }

ChannelHandlers.wrap (controlador ChannelHandler, URL URL)

  1. Llame al método ChannelHandlers#getInstance() para obtener los ChannelHandlers singleton, usando el estilo chino hambriento;
  2. Llame a wrapInternal(handler, url) para envolver el controlador de instancias de DecoderHandler:
    public static ChannelHandler wrap(ChannelHandler handler, URL url) {
    
    
        return ChannelHandlers.getInstance().wrapInternal(handler, url);
    }

ChannelHandlers#wrapInternal(manejador, url)

Proceso de trabajo:

  1. Llame a la instancia de DecoderHandler como una instancia de AllChannelHandler a través del mecanismo Spi;
  2. Luego envuelva AllChannelHandler como una instancia de MultiMessageHandler;
public class ChannelHandlers {
    
    
    // 单例模式
    private static ChannelHandlers INSTANCE = new ChannelHandlers();

    protected ChannelHandlers() {
    
    
    }

    public static ChannelHandler wrap(ChannelHandler handler, URL url) {
    
    
        return ChannelHandlers.getInstance().wrapInternal(handler, url);
    }

    protected static ChannelHandlers getInstance() {
    
    
        return INSTANCE;
    }

    static void setTestingChannelHandlers(ChannelHandlers instance) {
    
    
        INSTANCE = instance;
    }

    protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
    
    
        // 先通过ExtensionLoader.getExtensionLoader(Dispatcher.class).getAdaptiveExtension().dispatch(handler, url)
        // 得到一个AllChannelHandler(handler, url)
        // 然后把AllChannelHandler包装成HeartbeatHandler,HeartbeatHandler包装成MultiMessageHandler
        // 所以当Netty接收到一个数据时,会经历MultiMessageHandler--->HeartbeatHandler---->AllChannelHandler
        // 而AllChannelHandler会调用handler
        return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
                .getAdaptiveExtension().dispatch(handler, url)));
    }
}

Aquí Dispatter implica el modelo de subprocesamiento del servidor, marque

AbstractServer (URL URL, controlador ChannelHandler)

La creación de una instancia de NettyServer llamará al constructor de la clase principal, clase principal AbstractServer, clase abstracta;
flujo de trabajo:

  1. Llame al constructor de la clase principal y asigne el controlador al atributo del controlador de la clase principal AbstractPeer;
  2. Obtenga la dirección local localAddress;
  3. Obtener la IP vinculada al servicio;
  4. Obtener el número de puerto vinculado al servicio;
  5. Cree una instancia de InetSocketAddress, que se utiliza para crear los parámetros de construcción de la conexión Socket;
  6. Llame a doOpen() para iniciar netty;
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
    
    
        super(url, handler);
        localAddress = getUrl().toInetSocketAddress();

        String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
        int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
        if (url.getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
    
    
            bindIp = ANYHOST_VALUE;
        }
        bindAddress = new InetSocketAddress(bindIp, bindPort);
        this.accepts = url.getParameter(ACCEPTS_KEY, DEFAULT_ACCEPTS);
        this.idleTimeout = url.getParameter(IDLE_TIMEOUT_KEY, DEFAULT_IDLE_TIMEOUT);
        try {
    
    
            doOpen();
            if (logger.isInfoEnabled()) {
    
    
                logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
            }
        } catch (Throwable t) {
    
    
           //....
        }
//...
    }

AbstractEndpoint(url, controlador)

Llame al constructor de la clase principal, establezca el método de codificación, establezca el período de tiempo de espera del proveedor de servicios y establezca el período de tiempo de espera para crear una conexión.

    public AbstractEndpoint(URL url, ChannelHandler handler) {
    
    
        super(url, handler);
        this.codec = getChannelCodec(url);
        this.timeout = url.getPositiveParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
        this.connectTimeout = url.getPositiveParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT);
    }

AbstractPeer (URL URL, controlador ChannelHandler)

Guarde el controlador;

public abstract class AbstractPeer implements Endpoint, ChannelHandler {
    
    

    private final ChannelHandler handler;
    private volatile URL url;
    private volatile boolean closing;
    private volatile boolean closed;
    public AbstractPeer(URL url, ChannelHandler handler) {
    
    
        this.url = url;
        this.handler = handler;
    }

NettyServer#doOpen()

En el proceso anterior, llamar al nuevo NettyServer asignará MultiMessageHandler a la propiedad Handler de NettyServer;
por lo tanto, otra identidad de NettyServer es un MultiMessageHandler:
flujo de trabajo:

  1. Cree un servidor ServerBootstrap;
  2. Crear grupo de subprocesos de trabajo, grupo de subprocesos de eventos de IO;
  3. Cree un NettyServerHandler, transmítalo, es decir, transfiera un MultiMessageHandler y empáquelo como una instancia de NettyServerHandler;
  4. Configuración de los parámetros del servidor;
@Override
    protected void doOpen() throws Throwable {
    
    
        bootstrap = new ServerBootstrap();

        bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
        workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
                new DefaultThreadFactory("NettyServerWorker", true));

        final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
        channels = nettyServerHandler.getChannels();

        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
                .childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
    
    
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
    
    
                        // FIXME: should we use getTimeout()?
                        int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
                        NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                        ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
                                .addLast("decoder", adapter.getDecoder())
                                .addLast("encoder", adapter.getEncoder())
                                .addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
                                .addLast("handler", nettyServerHandler);
                    }
                });
        // bind
        ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
        channelFuture.syncUninterruptibly();
        channel = channelFuture.channel();

    }

Resumen del proceso de empaquetado de hander de Netty en el lado del servidor

  1. La instancia de requestHandler de ExchangeHandlerAdapter se ajusta como HeaderExchangerhandler
  2. Envuelva la instancia de HandlerExchangerHandler como Decodehandler;
  3. Envuelva la instancia de DecodeHandler como AllChannelHandler;
  4. Envuelva la instancia de AllChannelHandler como una instancia de MultiMessageHandler;
  5. Envuelva la instancia de MultiMessageHandler como NettyServerHandler;
  6. Finalmente, vincule NettyServerHandler al procesador del controlador de PipeLine;

Modelo de subprocesamiento del lado del servidor

El proceso involucrado es: el proceso de envolver DecodeHandler como AllChannelhandler;

    protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
    
    
        return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
                .getAdaptiveExtension().dispatch(handler, url)));
    }

Obtenga la clase de implementación extendida de Dispatcher a través del mecanismo SPI,

@SPI(AllDispatcher.NAME)
public interface Dispatcher {
    
    

    /**
     * dispatch the message to threadpool.
     *
     * @param handler
     * @param url
     * @return channel handler
     */
    @Adaptive({
    
    Constants.DISPATCHER_KEY, "dispather", "channel.handler"})
    // The last two parameters are reserved for compatibility with the old configuration
    ChannelHandler dispatch(ChannelHandler handler, URL url);

}

De forma predeterminada, se utiliza el método AllDispatcher#dispatch;

AllDispatcher #dispatch(manejador, url)

Cree una instancia de AllChannelHandler, envolviendo un controlador;

public class AllDispatcher implements Dispatcher {
    
    

    public static final String NAME = "all";

    @Override
    public ChannelHandler dispatch(ChannelHandler handler, URL url) {
    
    
        return new AllChannelHandler(handler, url);
    }

}

Controlador de todos los canales

Cuando netty recibe los datos, llama al método recibido.


public class AllChannelHandler extends WrappedChannelHandler {
    
    


    public AllChannelHandler(ChannelHandler handler, URL url) {
    
    
        // 会生成一个线程池
        super(handler, url);
    }
	//连接完成处理
    @Override
    public void connected(Channel channel) throws RemotingException {
    
    
        ExecutorService executor = getExecutorService();
        try {
    
    
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CONNECTED));
        } catch (Throwable t) {
    
    
            throw new ExecutionException("connect event", channel, getClass() + " error when process connected event .", t);
        }
    }
	//连接断开处理
    @Override
    public void disconnected(Channel channel) throws RemotingException {
    
    
        ExecutorService executor = getExecutorService();
        try {
    
    
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.DISCONNECTED));
        } catch (Throwable t) {
    
    
            throw new ExecutionException("disconnect event", channel, getClass() + " error when process disconnected event .", t);
        }
    }

    @Override
    public void received(Channel channel, Object message) throws RemotingException {
    
    
        ExecutorService executor = getExecutorService();
        try {
    
    
            // 交给线程池去处理message
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
        } catch (Throwable t) {
    
    
            //TODO A temporary solution to the problem that the exception information can not be sent to the opposite end after the thread pool is full. Need a refactoring
            //fix The thread pool is full, refuses to call, does not return, and causes the consumer to wait for time out
        	if(message instanceof Request && t instanceof RejectedExecutionException){
    
    
        		Request request = (Request)message;
        		if(request.isTwoWay()){
    
    
        			String msg = "Server side(" + url.getIp() + "," + url.getPort() + ") threadpool is exhausted ,detail msg:" + t.getMessage();
        			Response response = new Response(request.getId(), request.getVersion());
        			response.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR);
        			response.setErrorMessage(msg);
        			channel.send(response);
        			return;
        		}
        	}
            throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
        }
    }
	//异常处理;
    @Override
    public void caught(Channel channel, Throwable exception) throws RemotingException {
    
    
        ExecutorService executor = getExecutorService();
        try {
    
    
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CAUGHT, exception));
        } catch (Throwable t) {
    
    
            throw new ExecutionException("caught event", channel, getClass() + " error when process caught event .", t);
        }
    }
}

EnvueltoChannelHandler

Esta clase es la clase principal de AllChannelHandler;
el flujo de trabajo del método de construcción:

  1. Dar la instancia de DecodeHandler al controlador de atributos
  2. A través del mecanismo SPI, obtenga un grupo de subprocesos fijos; la clase de extensión predeterminada del grupo de subprocesos SPI utilizado es FixedThreadPool,
  3. Determine el valor de componentKey, si es un consumidor, componentKey es un consumidor, si es un proveedor de servicios, es java.util.concurrent.ExecutorService;
  4. Obtener un DataStore a través del mecanismo SPI, se utiliza el método SimpleDataStore#put, la estructura interna es un Map<String, Map>, que es una estructura Map de dos capas, la Key de la primera capa Map es componentKey, y la segunda mapa de capas La clave es el número de puerto del servicio y el valor es un grupo de subprocesos creado por 2;

public class WrappedChannelHandler implements ChannelHandlerDelegate {
    
    

    protected static final Logger logger = LoggerFactory.getLogger(WrappedChannelHandler.class);

    protected static final ExecutorService SHARED_EXECUTOR = Executors.newCachedThreadPool(new NamedThreadFactory("DubboSharedHandler", true));

    protected final ExecutorService executor;

    protected final ChannelHandler handler;

    protected final URL url;

    public WrappedChannelHandler(ChannelHandler handler, URL url) {
    
    
        this.handler = handler;
        this.url = url;
        executor = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);

        String componentKey = Constants.EXECUTOR_SERVICE_COMPONENT_KEY;
        if (CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) {
    
    
            componentKey = CONSUMER_SIDE;
        }

        // DataStore底层就是一个map,存储的格式是这样的:{"java.util.concurrent.ExecutorService":{"20880":executor}}
        // 这里记录了干嘛?应该是在请求处理的时候会用到
        DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
        dataStore.put(componentKey, Integer.toString(url.getPort()), executor);
    }

}

Conjunto de subprocesos fijos

Cree un grupo de subprocesos;

public class FixedThreadPool implements ThreadPool {
    
    

    @Override
    public Executor getExecutor(URL url) {
    
    
        String name = url.getParameter(THREAD_NAME_KEY, DEFAULT_THREAD_NAME);
        int threads = url.getParameter(THREADS_KEY, DEFAULT_THREADS);
        int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES);
        return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS,
                queues == 0 ? new SynchronousQueue<Runnable>() :
                        (queues < 0 ? new LinkedBlockingQueue<Runnable>()
                                : new LinkedBlockingQueue<Runnable>(queues)),
                new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url));
    }

}

SimpleDataStore

Hay un atributo de datos dentro de DataStore, que es una estructura Map<String,Map>;



public class SimpleDataStore implements DataStore {
    
    

    // <component name or id, <data-name, data-value>>
    private ConcurrentMap<String, ConcurrentMap<String, Object>> data =
            new ConcurrentHashMap<String, ConcurrentMap<String, Object>>();

    @Override
    public Map<String, Object> get(String componentName) {
    
    
        ConcurrentMap<String, Object> value = data.get(componentName);
        if (value == null) {
    
    
            return new HashMap<String, Object>();
        }

        return new HashMap<String, Object>(value);
    }

    @Override
    public Object get(String componentName, String key) {
    
    
        if (!data.containsKey(componentName)) {
    
    
            return null;
        }
        return data.get(componentName).get(key);
    }

    @Override
    public void put(String componentName, String key, Object value) {
    
    
        Map<String, Object> componentData = data.get(componentName);
        if (null == componentData) {
    
    
            data.putIfAbsent(componentName, new ConcurrentHashMap<String, Object>());
            componentData = data.get(componentName);
        }
        componentData.put(key, value);
    }

    @Override
    public void remove(String componentName, String key) {
    
    
        if (!data.containsKey(componentName)) {
    
    
            return;
        }
        data.get(componentName).remove(key);
    }

}

Grupo de subprocesos involucrado en Dubbo

Como servidor, hay varios grupos de subprocesos

  1. Netty es responsable de manejar el grupo de subprocesos del servicio de eventos de conexión BossGroup;
  2. Netty es responsable de procesar el grupo de subprocesos de trabajo workerGroup de eventos de lectura y escritura;
  3. El grupo de subprocesos de negocios responsable de procesar el negocio, es decir, el grupo de subprocesos correspondiente a AllChannelHandler;

Modelo de subprocesamiento de Dubbo

inserte la descripción de la imagen aquí

  1. Si es un evento de aceptación, será procesado por el grupo de subprocesos del servicio BossGroup de Netty. Después del procesamiento, la información del canal del canal se registrará en el grupo de subprocesos de trabajo.
  2. Si es un evento de lectura/escritura, será procesado por el grupo de subprocesos de trabajo WorkerGroup de Netty, que se convierte en el grupo de subprocesos de E/S en Dubbo, porque es responsable de procesar las solicitudes de E/S de la red.
  3. El grupo de subprocesos de trabajo llama al procesamiento de eventos de datos recibidos de AllChannelHandler y creará una instancia de channelEventRunnable con el canal actual, el tipo de tiempo y los parámetros de datos de solicitud, y lo entregará al grupo de subprocesos comerciales para su procesamiento; el subproceso de trabajo volverá al proceso otro evento de lectura y escritura;
  • ¿Por qué crear un grupo de subprocesos comerciales para manejar las solicitudes de esta manera?
  • Debido a que la cantidad de subprocesos en el grupo de subprocesos de trabajo es generalmente la cantidad de núcleos de CPU + 1, y si los subprocesos de IO se utilizan para procesar negocios, si cada subproceso de IO ejecuta negocios que consumen mucho tiempo, todo el servidor/consumidor estará bloqueado. state, short El servicio no está disponible durante el tiempo. Por lo tanto, el procesamiento empresarial del subproceso de E/S se transfiere al grupo de subprocesos de negocios de Dubbo para su procesamiento, y el subproceso de E/S vuelve directamente para procesar nuevos eventos de solicitud de lectura y escritura; esto puede mejorar el rendimiento del sistema; por lo tanto, en Dubbo, si el modelo de subproceso no se especifica, por lo general se utiliza el modelo de subproceso de todo tipo, es decir, se creará AllchennelHandler para crear un grupo de subprocesos comerciales;

Otros modelos de roscado de Dubbo

  • Todos
    Todos los mensajes se envían al grupo de subprocesos, incluidas las solicitudes, las respuestas, los eventos de conexión, los eventos de desconexión, los latidos, etc.
  • directo
    Todos los mensajes se procesan directamente en el subproceso de IO, es decir, el subproceso de IO se utiliza para procesar el negocio;
  • solo el mensaje
    de respuesta de solicitud se envía al grupo de subprocesos, y otros eventos de desconexión de conexión, latidos y otros mensajes se ejecutan directamente en el subproceso IO
  • La ejecución
    solo envía mensajes de solicitud al grupo de subprocesos, excluyendo respuestas, respuestas y otros eventos de desconexión de conexión, latidos y otros mensajes, y se ejecuta directamente en el subproceso de IO.
  • La conexión
    está en el subproceso IO, coloca el evento de desconexión de la conexión en la cola, los ejecuta uno por uno de manera ordenada y envía otros mensajes al grupo de subprocesos.

Grupo de subprocesos centralizado correspondiente: todos son clases de implementación de la interfaz ThreadPool;

  • grupo de subprocesos de tamaño fijo fijo
    , los subprocesos se crean al inicio, no se cierran y se mantienen todo el tiempo.
    En consecuencia, se utiliza el grupo de subprocesos creado por FixedThreadPool;
  • El
    grupo de subprocesos en caché se elimina automáticamente cuando está inactivo durante un minuto y se reconstruirá cuando sea necesario. En consecuencia,
    se utiliza el grupo de subprocesos creado por CachedThreadPool.
  • El
    grupo de subprocesos escalables limitado, pero la cantidad de subprocesos en el grupo solo crecerá y no se reducirá. El propósito de solo crecer y no reducirse es evitar problemas de rendimiento causados ​​por un gran tráfico repentino al reducirse.
    En consecuencia, se utiliza el grupo de subprocesos creado por LimitedThreadPool
  • Eager
    da prioridad a la creación de un grupo de subprocesos de trabajo. Cuando la cantidad de tareas es mayor que corePoolSize pero menor que maximumPoolSize, los trabajadores se crean primero para procesar las tareas. Cuando el número de tareas es mayor que el tamaño máximo de grupo, coloque las tareas en la cola de bloqueo. Lanza RejectedExecutionException cuando la cola de bloqueo está llena. (En comparación con el almacenamiento en caché: el almacenamiento en caché arroja directamente una excepción cuando la cantidad de tareas excede el tamaño máximo del grupo en lugar de poner las tareas en una cola de bloqueo)
    se usa el grupo de subprocesos correspondiente creado por EagertThreadPool

Supongo que te gusta

Origin blog.csdn.net/yaoyaochengxian/article/details/124230156
Recomendado
Clasificación