15. Bloqueio e condição (abaixo): Como o Dubbo usa o processo de pipe para obter sincronização de transferência assíncrona? Ferramentas de concorrência


Condição implementa as variáveis ​​de condição no modelo de gerenciamento.

Há apenas uma variável de condição no programa Java interno, e o programa de controle implementado pelo Lock & Condition suporta várias variáveis ​​de condição, o que é uma diferença importante entre as duas.

1. Como usar duas variáveis ​​de condição para implementar rapidamente uma fila de bloqueio?

Uma fila de bloqueio requer duas variáveis ​​de condição, uma é que a fila não esteja vazia (a fila vazia não pode sair da fila) e a outra é que a fila não está cheia (a fila está cheia e a fila não é permitida).

public class BlockedQueue<T> {
	final Lock lock = new ReentrantLock();
	// 条件变量:队列不满
	final Condition notFull = lock.newCondition();
	// 条件变量:队列不空
	final Condition notEmpty = lock.newCondition();

	// 入队
	void enq(T x) {
		lock.lock();
		try {
			while (队列已满) {
				// 等待队列不满
				notFull.await();
			}
			// 省略入队操作...
			// 入队后, 通知可出队
			notEmpty.signal();
		} finally {
			lock.unlock();
		}
	}

	// 出队
	void deq() {
		lock.lock();
		try {
			while (队列已空) {
				// 等待队列不空
				notEmpty.await();
			}
			// 省略出队操作...
			// 出队后,通知可入队
			notFull.signal();
		} finally {
			lock.unlock();
		}
	}
}

Os procedimentos implementados de bloqueio e condição, espera e notificação de encadeamento, podem apenas chamar a espera (), sinal (), sinalAll (), suas semânticas e espera (), notify (), notifyAll () usadas pelo sincronizado para alcançar o processo de gerenciamento. Da mesma maneira, os métodos usados ​​para implementar os dois procedimentos não podem ser confundidos.

2. Síncrono e Assíncrono

Em termos leigos, é se o chamador precisa esperar pelo resultado, se necessário, é síncrono; se não, é assíncrono.

Dois métodos para obter assíncrono:

  • O chamador cria um sub-thread e executa a chamada de método no sub-thread, chamado de chamada assíncrona;
  • Quando o método é implementado, um novo encadeamento é criado para executar a lógica principal, e o encadeamento principal retorna diretamente, chamado método assíncrono.

3. Análise de código fonte do Dubbo

O protocolo TCP em si é assíncrono.No nível do protocolo TCP, após o envio de uma solicitação RPC, o thread não aguardará o resultado da resposta do RPC.

Dubbo fez algo assíncrono para síncrono.
Para a seguinte chamada RPC simples, o método sayHello () é um método síncrono por padrão, ou seja, quando service.sayHello ("dubbo") é executado, o encadeamento pára e aguarda o resultado.

DemoService service = 初始化部分省略
String message = service.sayHello("dubbo");
System.out.println(message);

O encadeamento de despejo saiu e descobriu que o encadeamento estava bloqueado e o status era TIMED_WAITING. Originalmente, a solicitação era enviada de forma assíncrona, mas o encadeamento de chamada estava bloqueado, indicando que o Dubbo fazia a coisa assíncrona para síncrona. O encadeamento está bloqueado no método DefaultFuture.get ().
Insira a descrição da imagem aqui
A linha 108 do DubboInvoker chama DefaultFuture.get (). Essa linha primeiro chama o método request (inv, timeout) .Este método está realmente enviando uma solicitação RPC e aguardando o resultado do retorno RPC chamando o método get ().

public class DubboInvoker{
  Result doInvoke(Invocation inv){
    // 下面这行就是源码中 108 行
    // 为了便于展示,做了修改
    return currentClient 
      .request(inv, timeout)
      .get();
  }
}

O código relevante é simplificado, repetindo nossos requisitos: quando o RPC retornar o resultado, bloqueie o thread de chamada e deixe o thread de chamada aguardar; quando o RPC retornar o resultado, ative o thread de chamada e deixe o thread de chamada executar novamente. Mecanismo clássico de notificação de espera. A seguir está a implementação do Dubbo

// 创建锁与条件变量
	private final Lock lock = new ReentrantLock();
	private final Condition done = lock.newCondition();

	// 调用方通过该方法等待结果
	Object get(int timeout) {
		long start = System.nanoTime();
		lock.lock();
		try {
			while (!isDone()) {
				done.await(timeout);
				long cur = System.nanoTime();
				if (isDone() || cur - start > timeout) {
					break;
				}
			}
		} finally {
			lock.unlock();
		}
		if (!isDone()) {
			throw new TimeoutException();
		}
		return returnFromResponse();
	}

	// RPC 结果是否已经返回
	boolean isDone() {
		return response != null;
	}

	// RPC 结果返回时调用该方法
	private void doReceived(Response res) {
		lock.lock();
		try {
			response = res;
			if (done != null) {
				done.signal();
			}
		} finally {
			lock.unlock();
		}
	}

O encadeamento de chamada aguarda o RPC retornar o resultado chamando o método get (). Nesse método, tudo o que você vê são "rostos" familiares: bloqueio de chamada () para adquirir o bloqueio e, finalmente, desbloquear () para liberar o bloqueio; após adquirir o bloqueio, A espera é obtida chamando o método waitit () em um loop clássico.

Quando o resultado da RPC retornar, ele chamará o método doReceived (). Nesse método, chame lock () para adquirir o bloqueio e, finalmente, chame unlock () para liberar o bloqueio. Após adquirir o bloqueio, chame o sinal () para notificar o thread de chamada que o resultado retornou. , Não precisa mais esperar.

Publicado 97 artigos originais · elogiados 3 · 10.000+ visualizações

Acho que você gosta

Origin blog.csdn.net/qq_39530821/article/details/102536337
Recomendado
Clasificación