Recursos avançados do Dubbo: controle de serviço

Wang Youzhi , um pescador de ouro mútuo que compartilha a tecnologia Java hard-core,
junta-se ao grupo de pessoas de Java que correm com baldes: pessoas de Java que são ricas juntas

No artigo anterior , apresentamos as funcionalidades fornecidas pelo DUbbo em termos de governança de serviços.Hoje vamos dar uma olhada nas funcionalidades fornecidas pelo Dubbo em outros aspectos. Assim como o artigo de governança de serviços, o objetivo deste artigo é aprender a usar os recursos fornecidos pelo Dubbo no gerenciamento e controle de serviços, e ainda não envolve nenhum princípio de implementação.

estrutura de engenharia

hmm~~

Este é o caso, porque o computador é muito extenso e o IDEA está realmente consumindo memória, então juntei os projetos de teste de acordo com os subprojetos. A estrutura do projeto que estou usando atualmente é a seguinte:

https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/4EZlwNDQ7kPYOxAY/img/6f26e516-ce78-49d8-ba20-cb4e23826805.png

O nome do submódulo consiste em duas partes: método de configuração + função, como: XMLProvider, que significa um provedor de serviços baseado no método de configuração XML.

Dicas : IDEA está prestes a alcançar o CLion "leão da memória".

Esboço local (Esboço)

Ao usar o Dubbo, o consumidor do serviço integra apenas a interface e todas as implementações estão no provedor de serviços. No entanto, em alguns cenários, esperamos que o consumidor do serviço possa concluir algum processamento lógico para reduzir o consumo de desempenho causado pela interação RPC, por exemplo : Coloque a verificação de parâmetro no usuário do serviço para reduzir uma interação de rede com o chamador do serviço.

https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/4EZlwNDQ7kPYOxAY/img/8dd6c9c7-451c-4681-b225-8f823cc4092c.jpeg

Nesse cenário, podemos usar o recurso de stub local fornecido pelo Dubbo. Contamos com a seguinte estrutura de engenharia da prestadora de serviço:

https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/4EZlwNDQ7kPYOxAY/img/0a7169a4-cef6-40f8-afe5-94206631503a.png

A interface XMLProviderService que fornece serviços externos é definida no módulo xml-provider-api. O código é o seguinte:

public interface XMLProviderService {
  String say(String message);
}

E o stub da interface XMLProviderServiceStub, o código é o seguinte:

public class XMLProviderServiceStub implements XMLProviderService {
  
  private final XMLProviderService xmlProviderService;
  
  public XMLProviderServiceStub(XMLProviderService xmlProviderService) {
    this.xmlProviderService = xmlProviderService;
  }
  
  @Override
  public String say(String message) {
    if (StringUtils.isBlank(message)) {
      return "message不能为空!";
    }
    
    try {
      return this.xmlProviderService.say(message);
    } catch (Exception e) {
      return "远程调用失败:" + e.getMessage();
    }
  }
}

Em seguida, configuramos o stub da interface no projeto do consumidor do serviço:

<dubbo:reference id="xmlProviderService" interface="com.wyz.api.XMLProviderService" stub="com.wyz.api.stub.XMLProviderServiceStub"/>

Dicas : Para usar um stub local, a classe de implementação do stub deve ter um construtor que passe na instância do Proxy (a instância do Proxy gerada pelo consumidor do serviço).

Camuflagem local (Mock)

A camuflagem local é a degradação do serviço que mencionamos em " Recursos avançados do Dubbo: governança de serviço ", e faremos uma pequena adição hoje. O mascaramento local é um subconjunto de stubs locais. Os stubs locais podem lidar com vários erros e exceções no link de chamada RPC, enquanto o mascaramento local se concentra no tratamento de RpcException (como falha de rede, tempo limite de resposta, etc.) que requer processamento tolerante a falhas. anormal .

Adicionamos um serviço de máscara local XMLProviderServiceMock para XMLProviderService, a estrutura do projeto é a seguinte:

https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/4EZlwNDQ7kPYOxAY/img/37396cfe-7f16-4a7b-aac4-6e9a91e59edd.png

O código do XMLProviderServiceMock é o seguinte:

public class XMLProviderServiceMock implements XMLProviderService {
  
  @Override
  public String say(String message) {
    return "服务出错了!";
  }
}

O arquivo de configuração pode ser configurado da seguinte forma:

<dubbo:reference id="xmlProviderService" interface="com.wyz.api.XMLProviderService" mock="true"/>

Nesta configuração, a implementação de Mock deve ser nomeada de acordo com o método "nome da interface + sufixo Mock"; caso você não queira usar este método de nomenclatura, pode usar o nome totalmente qualificado:

<dubbo:reference id="xmlProviderService" interface="com.wyz.api.XMLProviderService" mock="com.wyz.api.mock.XMLProviderServiceMock"/>

Dicas : O motivo de "repetir" o Mock novamente é que houve um erro no artigo anterior. Escrevi <dubbo:reference>a configuração que deveria ter sido feita na etiqueta <dubbo:service>, mas o motivo do erro ainda não está funcionando. Ei, eu realmente cumpriu a frase "o que vem no papel é sempre raso, mas nunca sei que essa matéria tem que ser praticada".

retorno de chamada de parâmetro

O Dubbo oferece suporte à função de retorno de chamada do parâmetro, para que o provedor de serviços possa "reverter" a chamada do consumidor do serviço. Essa função é implementada com base no proxy reverso gerado pelo link longo e o efeito é semelhante a uma chamada assíncrona. Vamos dar um exemplo de pagamento:

Adicione a interface PaymentService ao módulo xml-provider-api do projeto XMLProvider e adicione o PaymentNotifyService para notificar o resultado do PaymentService:

public interface PaymentService {
  void payment(String cardNo, PaymentNotifyService paymentNotifyService);
}

public interface PaymentNotifyService {
  void paymentNotify(String message);
}

A interface PaymentService é implementada no módulo xml-provider-service do projeto XMLProvider:

public class PaymentServiceImpl implements PaymentService {
  @Override
  public void payment(String cardNo, PaymentNotifyService paymentNotifyService) {
    System.out.println("向卡号[" + cardNo + "]付钱!");
    // 业务逻辑
    paymentNotifyService.paymentNotify("付款成功");
  }
}

Execute PaymentService#paymento método e chame PaymentNotifyService#paymentNotifyo método para notificar o chamador do serviço sobre o resultado da execução.

Implemente a interface PaymentNotifyService no projeto XMLConsumer:

public class PaymentNotifyServiceImpl implements PaymentNotifyService {
  @Override
  public void paymentNotify(String message) {
    System.out.println("支付结果:" + message);
  }
}

Dê uma olhada na estrutura do projeto neste momento:

https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/4EZlwNDQ7kPYOxAY/img/a45fd029-2f1b-4f8b-83c1-c54997100c6d.png

A seguir vem a configuração do XML. No callback do parâmetro, precisamos atentar para a configuração do módulo xml-provider-service do projeto XMLProvider do provedor de serviço:

<bean id="paymentServiceImpl" class="com.wyz.service.impl.PaymentServiceImpl"/>
<dubbo:service interface="com.wyz.api.PaymentService" ref="paymentServiceImpl" callbacks="10">
  <dubbo:method name="payment">
    <dubbo:argument index="1" callback="true"/>
  </dubbo:method>
</dubbo:service>

<dubbo:argument index="1" callback="true"/>A configuração é determinada pela linha 4. PaymentService#paymentO segundo parâmetro no método (índice começa em 0) é um parâmetro de retorno de chamada, callbacks limita o número de retornos de chamada no mesmo link longo, não o número total de retornos de chamada.

Dicas : No cenário real de negócios de pagamento, é mais inclinado ao processamento assíncrono. Por exemplo, quando o provedor de serviços recebe o pagamento, ele inicia um novo encadeamento para processar o negócio de pagamento e chama a interface de notificação, e o encadeamento principal retorna isso a solicitação de pagamento foi recebida com sucesso.

chamada assíncrona

As chamadas assíncronas permitem que o provedor de serviços retorne uma resposta imediatamente, enquanto o segundo plano continua realizando o processamento da solicitação.Quando o consumidor do serviço solicita um resultado de resposta, o provedor de serviços retorna o resultado.

https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/4EZlwNDQ7kPYOxAY/img/8dd1ba48-cfc6-4e05-ba5b-2a7a5dcb5170.jpeg

DUBbo suporta dois métodos de chamada assíncrona:

  • Use a interface CompletableFuture
  • Usar RpcContext

Depois do DUbbo 2.7, o DUbbo usa a interface CompletableFuture como base para programação assíncrona.

Usando CompletableFuture para implementar chamadas assíncronas

Vamos primeiro ver como usar CompletableFuture para implementar chamadas assíncronas e declarar a interface CompletableFutureAsyncService:

public interface CompletableFutureAsyncService {
  CompletableFuture<String> async(String message);
}

Em seguida, vem a implementação da interface:

public class CompletableFutureAsyncServiceImpl implements CompletableFutureAsyncService {
  @Override
  public CompletableFuture<String> async(String message) {
    return CompletableFuture.supplyAsync(() -> {
      System.out.println(Thread.currentThread().getName() + " say : " + message);
      
      try {
        TimeUnit.SECONDS.sleep(10);
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }
      return "异步调用成功!";
    });
  }
}

A configuração XML é a mesma que a configuração comum da interface Dubbo RPC, a configuração do módulo xml-provider-service:

<bean id="completableFutureAsyncServiceImpl" class="com.wyz.service.impl.CompletableFutureAsyncServiceImpl" />
<dubbo:service interface="com.wyz.api.CompletableFutureAsyncService" ref="completableFutureAsyncServiceImpl" />

Configuração do módulo XMLConsumer:

<dubbo:reference id="completableFutureAsyncService" interface="com.wyz.api.CompletableFutureAsyncService"/>

Também é muito simples de usar:

CompletableFuture<String> completableFuture = completableFutureAsyncService.async("Hello");
System.out.println(completableFuture.get());

Dicas :

  • Não há diferença entre usar o CompletableFuture no Dubbo e usar o CompletableFuture sozinho~~
  • A finalidade de imprimir o nome da interface na implementação de CompletableFutureAsyncServiceImpl é mostrar claramente o efeito de chamadas assíncronas;
  • CompletableFuture#supplyAsync(Supplier<U> supplier)Por padrão, use ForkJoinPool#commonPool();
  • Os métodos sobrecarregados CompletableFuture#supplyAsync(Supplier<U> supplier, Executor executor)permitem o uso de conjuntos de encadeamentos personalizados.

Use AsyncContext para implementar chamadas assíncronas

Além de usar o CompletableFuture, você também pode implementar chamadas assíncronas por meio do AsyncContext definido pelo Dubbo. Vamos escrever a interface e a implementação da interface primeiro:

public interface RpcContextAsyncService {
  String async(String message);
}

public class RpcContextAsyncServiceImpl implements RpcContextAsyncService {
  
  @Override
  public String async(String message) {
    final AsyncContext asyncContext = RpcContext.startAsync();
    new Thread(() -> {
      asyncContext.signalContextSwitch();
      asyncContext.write(Thread.currentThread().getName() + " say : " + message);
    }).start();
    // 异步调用中,这个返回值完全没有意义
    return null;
  }
}

A configuração do provedor de serviços é a mesma das outras interfaces do Dubbo:

<bean id="rpcContextAsyncServiceImpl" class="com.wyz.service.impl.RpcContextAsyncServiceImpl"/>
<dubbo:service interface="com.wyz.api.RpcContextAsyncService" ref="rpcContextAsyncServiceImpl"/>

A seguir é feita a configuração do consumidor do serviço, que precisa adicionar o parâmetro async:

<dubbo:reference id="rpcContextAsyncService" interface="com.wyz.api.RpcContextAsyncService" async="true"/>

Por fim, chame a interface RPC no consumidor do serviço:

rpcContextAsyncService.async("Thanks");

Future<String> future = RpcContext.getServiceContext().getFuture();
System.out.println(future.get());

chamada de generalização

A chamada generalizada do Dubbo fornece um método de implementação para invocar serviços sem depender da API do provedor de serviços (SDK). O cenário principal está na implementação da plataforma de gateway, normalmente a implementação do gateway não deve depender da API (SDK) de outros serviços.

O Dubbo fornece oficialmente 3 métodos de chamada de generalização:

  • Usando chamadas genéricas por meio da API
  • Usando chamadas genéricas via Spring (formulário XML)
  • Chamada de generalização de objeto Protobuf

Aqui apresentamos a forma de configurar chamadas genéricas na forma de XML.

Preparação

Primeiro, preparamos um projeto GenericProvider para provisionamento de serviços. A estrutura do projeto é a seguinte:

https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/4EZlwNDQ7kPYOxAY/img/834b8cb4-43b8-40e8-a0e4-1f9a30eb6263.png

A interface é definida no projeto, ou seja, as classes de implementação GenericProviderService e GenericProviderServiceImpl, o código é o seguinte:

public interface GenericProviderService {
  String say(String message);
}

public class GenericProviderServiceImpl implements GenericProviderService {
  @Override
  public String say(String message) {
    return "GenericProvider say:" + message;
  }
}

No generic-dubbo-provider.xml, apenas os serviços fornecidos pelo GenericProvider precisam ser configurados normalmente:

<bean id="genericProviderServiceImpl" class="com.wyz.service.impl.GenericProviderServiceImpl"/>
<dubbo:service interface="com.wyz.service.api.GenericProviderService" ref="genericProviderServiceImpl" generic="true"/>

Não entraremos em detalhes sobre a configuração do arquivo application.yml.

Configuração do consumidor de serviço

De volta ao projeto XMLConsumer, configure primeiro a referência do serviço Dubbo e adicione o seguinte conteúdo a xml-dubbo-consumer.xml:

<dubbo:reference id="genericProviderService" generic="true" interface="com.wyz.service.api.GenericProviderService"/>

O parâmetro generic declara que este é um serviço de chamada genérico. Neste momento, o IDEA interface="com.wyz.service.api.GenericProviderService"o marcará em vermelho e solicitará "Não é possível resolver a classe 'GenericProviderService'". Não precisamos prestar atenção a isso, porque a interface GenericProviderService não existe no pacote com.wyz.service.api.

Então vamos usar a interface GenericProviderService:

ApplicationContext context = SpringContextUtils.getApplicationContext();
// genericProviderService是XML中定义的服务id
GenericService genericService = (GenericService) context.getBean("genericProviderService");

// $invoke的3个参数分别为:方法名,参数类型,参数
Object result = genericService.$invoke("say", new String[]{"java.lang.String"}, new Object[]{"wyz"});
System.out.println(result);

Dessa forma, podemos obter os serviços fornecidos pela interface GenericProviderService por meio do ApplicationContext.

Dicas : SpringContextUtils é usado para obter ApplicationContext, o código é o seguinte:

@Component
public class SpringContextUtils implements ApplicationContextAware {
  private static ApplicationContext applicationContext = null;
  
  public static ApplicationContext getApplicationContext() {
    return SpringContextUtils.applicationContext;
  }
  
  @Override
  public void setApplicationContext(@Nonnull ApplicationContext applicationContext) throws BeansException {
    SpringContextUtils.applicationContext = applicationContext;
  }
}

epílogo

Bem, até agora, conhecemos e aprendemos sobre a configuração e o uso de recursos comuns no Dubbo. É claro que, após anos de desenvolvimento, o Dubbo oferece muito mais recursos do que isso. Se você quiser aprender mais, pode visualizar o documento " Apache Dubbo Microservice Framework da entrada ao domínio " fornecido pela Alibaba .

No próximo artigo, iniciaremos oficialmente a exploração dos princípios de implementação do Dubbo a partir da parte de registro do serviço.


Se este artigo for útil para você, dê-lhe muitos elogios e apoio. Se houver algum erro no artigo, por favor, critique e corrija. Por fim, todos estão convidados a prestar atenção em Wang Youzhi, um homem . Até a próxima!

Acho que você gosta

Origin blog.csdn.net/wyz_1945/article/details/131797679
Recomendado
Clasificación