What is a serial number?

People who do development know the concept of serial numbers, there are business serial numbers, transaction serial numbers, request serial numbers, all kinds of serial numbers.

Whatever the name, the purpose is to give a unique identity to a sequence of actions in a certain dimension. Behind convenient check log, check the problem. System interaction prevents wrangling.

For example, a transaction serial number uniquely identifies a transaction, which can be a request with no business meaning or an accounting transaction. If it is a request with no service meaning. Typically, a 32 – or 64-bit unique code is generated at the beginning of a transaction. This code is passed through the vertical flow of the transaction, either as a parameter, in a Context, or in a ThreadLocal.

In the Singleton Application, the processing of serial numbers is relatively simple, and there is no difficulty in either parameter passing or ThreadLocal.

In A microservice environment, where A transaction passes through multiple interactions between microservices and needs to pass down the serial number, its delivery link might be (service A) –> (service B –>) (service C) –> (Service D) –> (Service E), using Dubbo as the RPC framework. There are two ways to pass a serial number (this is just my idea, and there might be a better way to do it).

Parameter transfer method

This approach is easy to understand by taking the serial number as an argument to the interface (String JNL). This approach is simple in structure and implementation. But there are many problems.

1. Almost all exposed interface services have this parameter. Don’t look good.

2. Transaction serial number is actually less relevant to the business itself. Parameters between microservices should be as business-specific as possible, rather than taking up separate parameter locations to deliver things that are business-neutral.

3. This way, it is troublesome to maintain, write any service have to add such a parameter, portability is also very poor.

I personally don’t like it.

Use dubbo Attachment to do implicit transfer

Again, a 32-bit Jnl is passed between services, mimicking the process just described. (Service A) –> (Service B –>) (Service C)

1.A service is the entry and generates A 32-bit serial number Jnl

2. Service A puts Jnl into ThreadLocal

3. Remove Jnl from ThreadLocal and place it in the Invocation Attachment before invoking system B (this is done by Dubbo Filter)

4.B The system receives the Invocation message and deserializes it. Remove Jnl from Attacment and put it into ThreadLocal (this process is done by Dubbo Filter)

5. When SYSTEM B calls system C, repeat the preceding steps.

Simple to understand: At run time, Jnl is always moved from ThreadLocal of one system to ThreadLocal of another system. The transfer process is accomplished by taking and putting both through the Dubbo Filter.

There are three roles involved in this process.

(1) a ThreadLocal

(2) The Consumer Filter reads the Jnl from ThreadLocal into the Invocation Attachment

(3) Privoder Side Filter, which reads Jnl from the Invocation Attachment into ThreadLocal

Let’s look at the code implementation

public class AccessJnlUtil {

   / / to get
   public static String getAccessJnl(a) {
      return (String) RpcContext.getContext().get("AccessJnl");
   }
	
   / / in the
   public static void setAccessJnl(String accessJnl) {
      RpcContext.getContext().set("AccessJnl", accessJnl);
   }

   public static void deleteAccessJnl(a) {
      RpcContext.getContext().remove("AccessJnl"); }}Copy the code

AccessJnlUtil is used to perform put and set operations on AccessJnl. Use Dubbo RpcContext. RpcContext itself is a ThreadLocal wrapper. If you are not familiar with it, you can trace the source code.

public class AccessJnlConsumerFilter implements Filter {

	@Override
	public Result invoke(Invoker
        invoker, Invocation invocation) throws RpcException {
		invocation.getAttachments().put("AccessJnl", AccessJnlUtil.getAccessJnl());
		returninvoker.invoke(invocation); }}Copy the code

The AccessJnlConsumerFilter is the consumer’s Filter and takes the AccessJnl out of the ThreadLocal and into the Invocation Attachment before the Invoke is actually called.

public class AccessJnlProviderFilter implements Filter {

   @Override
   public Result invoke(Invoker
        invoker, Invocation invocation) throws RpcException {
      AccessJnlUtil.setAccessJnl(RpcContext.getContext().getAttachment("AccessJnl"));
      returninvoker.invoke(invocation); }}Copy the code

The AccessJnlProviderFilter is the consumer’s Filter, and the AccessJnl is removed from the Invocation Attachment and placed in a ThreadLocal before the Invocation is actually invoked.

The next step is to configure the two filters into the system. For details, please refer to the Dubbo documentation

conclusion

Use dubbo Attachment to do implicit transfer. I personally prefer it that way. Relatively elegant.