Advanced Features of Dubbo: Service Control

Wang Youzhi , a mutual gold fisherman who shares hard-core Java technology,
joins the group of Java people running around with buckets: Java people who are rich together

In the previous article , we have introduced the features provided by DUbbo in terms of service governance. Today we will take a look at the features provided by Dubbo in other aspects. Like the service governance article, the purpose of this article is to learn to use the features provided by Dubbo in service management and control, and still does not involve any implementation principles.

engineering structure

hmm~~

This is the case, because the computer is too stretchy, and IDEA is really eating up memory, so I merged the test projects together according to sub-projects. The project structure I am currently using is as follows:

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

The sub-module name consists of two parts: configuration method + function, such as: XMLProvider, which means a service provider based on XML configuration method.

Tips : IDEA is about to catch up with the "memory lion" CLion.

Local stub (Stub)

When using Dubbo, the service consumer only integrates the interface, and all implementations are on the service provider. However, in some scenarios, we hope that the service consumer can complete some logic processing to reduce the performance consumption caused by RPC interaction, for example : Place the parameter verification on the service user to reduce one network interaction with the service caller.

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

In this scenario, we can use the local stub feature provided by Dubbo. We have the following engineering structure of the service provider:

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

The interface XMLProviderService that provides external services is defined in the xml-provider-api module. The code is as follows:

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

And the interface stub XMLProviderServiceStub, the code is as follows:

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

Then we configure the interface stub in the project of the service consumer:

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

Tips : To use a local stub, the implementation class of the stub must have a constructor that passes in the Proxy instance (the Proxy instance generated by the service consumer).

Local camouflage (Mock)

Local camouflage is the service degradation we mentioned in " Dubbo's Advanced Features: Service Governance ", and we will make a little addition today. Local masquerading is a subset of local stubs. Local stubs can handle various errors and exceptions in the RPC call link, while local masquerading focuses on handling RpcException (such as network failure, response timeout, etc.) that require fault-tolerant processing. abnormal.

We add a local masquerade service XMLProviderServiceMock for XMLProviderService, the project structure is as follows:

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

The code of XMLProviderServiceMock is as follows:

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

The configuration file can be configured as follows:

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

In this configuration, the implementation of Mock must be named according to the method of "interface name + Mock suffix"; if you don't want to use this naming method, you can use the fully qualified name:

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

Tips : The reason for "repeating" the Mock again is that there was a mistake in the previous article. I <dubbo:reference>wrote the configuration that should have been done in the label <dubbo:service>, but the reason for the error is still not working. Hey, I really complied with the phrase "what comes on paper is always shallow, but I never know that this matter has to be practiced".

parameter callback

Dubbo supports the parameter callback function, so that the service provider can "reverse" call the service consumer. This function is implemented based on the reverse proxy generated by the long link, and the effect is similar to an asynchronous call. Let's take an example of payment:

Add the PaymentService interface to the xml-provider-api module of the XMLProvider project, and add the PaymentNotifyService to notify the result of the PaymentService:

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

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

The PaymentService interface is implemented in the xml-provider-service module of the XMLProvider project:

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

Execute PaymentService#paymentthe method, and call PaymentNotifyService#paymentNotifythe method to notify the service caller of the execution result.

Implement the PaymentNotifyService interface in the XMLConsumer project:

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

Take a look at the project structure at this time:

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

Next is the XML configuration. In the parameter callback, we need to pay attention to the configuration of the xml-provider-service module of the XMLProvider project of the service provider:

<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"/>The configuration is determined by line 4. PaymentService#paymentThe second parameter in the method (index starts from 0) is a callback parameter; callbacks limits the number of callbacks under the same long link, not the total number of callbacks.

Tips : In the actual payment business scenario, it is more inclined to asynchronous processing. For example, when the service provider receives the payment, it starts a new thread to process the payment business and calls the notification interface, and the main thread returns that the payment request has been successfully received.

asynchronous call

Asynchronous calls allow the service provider to return a response immediately, while the background continues to perform request processing. When the service consumer requests a response result, the service provider returns the result.

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

DUbbo supports two asynchronous calling methods:

  • Use the CompletableFuture interface
  • Use RpcContext

After DUbbo 2.7, DUbbo uses the CompletableFuture interface as the basis for asynchronous programming.

Using CompletableFuture to implement asynchronous calls

Let's first look at how to use CompletableFuture to implement asynchronous calls, and declare the CompletableFutureAsyncService interface:

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

Then comes the interface implementation:

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 "异步调用成功!";
    });
  }
}

The XML configuration is the same as the common Dubbo RPC interface configuration, the configuration of the xml-provider-service module:

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

Configuration of the XMLConsumer module:

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

It is also very simple to use:

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

Tips

  • There is no difference between using CompletableFuture in Dubbo and using CompletableFuture alone~~
  • The purpose of printing the interface name in the implementation of CompletableFutureAsyncServiceImpl is to clearly show the effect of asynchronous calls;
  • CompletableFuture#supplyAsync(Supplier<U> supplier)By default use ForkJoinPool#commonPool();
  • Overloaded methods CompletableFuture#supplyAsync(Supplier<U> supplier, Executor executor)allow use of custom thread pools.

Use AsyncContext to implement asynchronous calls

In addition to using CompletableFuture, you can also implement asynchronous calls through AsyncContext defined by Dubbo. Let's write the interface and interface implementation first:

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

The configuration of the service provider is the same as that of other Dubbo interfaces:

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

Next is the configuration of the service consumer, which needs to add the async parameter:

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

Finally, call the RPC interface in the service consumer:

rpcContextAsyncService.async("Thanks");

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

generalization call

Dubbo's generalized call provides an implementation method of invoking services without relying on the service provider API (SDK). The main scenario lies in the implementation of the gateway platform. Usually, the implementation of the gateway should not depend on the API (SDK) of other services.

Dubbo officially provides 3 generalization calling methods:

  • Using generic calls through the API
  • Using generic calls via Spring (XML form)
  • Protobuf object generalization call

Here we introduce the way to configure generic calls in the form of XML.

Preparation

First, we prepare a GenericProvider project for service provisioning. The project structure is as follows:

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

The interface is defined in the project, that is, the implementation classes GenericProviderService and GenericProviderServiceImpl, the code is as follows:

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

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

In generic-dubbo-provider.xml, only the services provided by GenericProvider need to be configured normally:

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

We will not go into details about the configuration of the application.yml file.

Service consumer configuration

Back to the XMLConsumer project, configure the Dubbo service reference first, and add the following content to xml-dubbo-consumer.xml:

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

The parameter generic declares that this is a generic call service. At this time, IDEA will interface="com.wyz.service.api.GenericProviderService"mark it red and prompt "Cannot resolve class 'GenericProviderService'". We don't need to pay attention to this, because the GenericProviderService interface does not exist under the com.wyz.service.api package.

Then let's use the GenericProviderService interface:

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

In this way, we can obtain the services provided by the GenericProviderService interface through the ApplicationContext.

Tips : SpringContextUtils is used to obtain ApplicationContext, the code is as follows:

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

epilogue

Well, so far, we have known and learned about the configuration and use of common features in Dubbo. Of course, after years of development, Dubbo provides far more features than this. If you want to learn more, You can view the document " Apache Dubbo Microservice Framework from Entry to Mastery " provided by Alibaba .

In the next article, we will officially start the exploration of Dubbo's implementation principles from the service registration part.


If this article is helpful to you, please give it a lot of praise and support. If there are any mistakes in the article, please criticize and correct. Finally, everyone is welcome to pay attention to Wang Youzhi, a financial man . See you next time!

Guess you like

Origin blog.csdn.net/wyz_1945/article/details/131797679