Dubbo Series (07-5) Cluster fault – Mock

Dubbo series (07-5) cluster fault tolerance-Mock

[toc]

Spring Cloud Alibaba Series Catalog-Dubbo Chapter

1. Background introduction

Recommended documents: p>

  1. Dubbo actual combat-service degradation
  2. Dubbo actual combat-local camouflage
  3. Dubbo actual combat-local stub

Dubbo’s In cluster fault tolerance, MockClusterWrapper is assembled by default, which implements Dubbo’s service degradation and local camouflage.

1.1 Service downgrade

For more information about the service downgrade configuration method, please refer to the official website Dubbo Actual Combat-Service Downgrade

Or write dynamic configuration override rules to the registry:

"override://0.0 .0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"
  • mock=force:return+null means consumer All method calls to this service directly return a null value, and no remote call is initiated. Used to shield the caller when unimportant services are unavailable.
  • mock=fail:return+null means that after the consumer’s method call to the service fails, it returns a null value without throwing an exception. Used to tolerate the impact on the caller when unimportant services are unstable.

1.2 Local camouflage

For more information about the local camouflage configuration method, please refer to the official website Dubbo Actual Combat-Local Camouflage

< dubbo:reference interface="com.foo.BarService" mock="true"/>


The above methods, like mock=fail:return+null, mean that the consumer will execute the mock configuration code after the method call to the service fails.

2. Source code analysis

2.1 Principle analysis

When I explained Dubbo Cluster in the previous article As you can see, Dubbo will wrap MockClusterInvoker with the ClusterInvoker object generated by Cluster#join by default.

Figure 1 Dubbo Mock schematic diagram

graph LR MockClusterWrapper – join –> MockClusterInvoker MockClusterInvoker – selectMockInvoker –> MockInvokersSelector MockInvokersSelector – route –> MockProtocol MockProtocol – refer –> MockInvoker

Summary: The main process of Dubbo Mock is as follows:

  1. MockClusterWrapper: Since this class is a packaging class for Cluster, Dubbo assembles MockClusterWrapper by default to wrap ClusterInvoker.
  2. MockClusterInvoker: The core class, which wraps the ClusterInvoker. The main functions are: one is to determine whether the Mock mechanism needs to be turned on; the other is to filter out the corresponding Mock Invoker according to MockInvokersSelector; the third is to execute MockInvoker.
  3. MockInvokersSelector: Mock routing strategy, because it is @Activate rhetoric, it will be automatically assembled. When Mock is not turned on, it returns to normal Invoker, and when Mock is turned on, it returns to Mock Invoker.
  4. MockProtocol: Create MockInvoker. This MockProtocol can only be referenced, not exposed.
  5. MockInvoker: The core class, which actually performs service degradation, handles mock="return null", mock=”throw com.foo. MockException", mock="com.foo.BarServiceMock".

2.2 MockClusterInvoker

The main function of MockClusterInvoker is to determine whether the Mock mechanism needs to be turned on. If the Mock is turned on, the MockInvoker needs to be filtered out and then the service is degraded. MockClusterWrapper and MockClusterInvoker are located under the dubbo-cluster project.

2.2.1 MockClusterWrapper

MockClusterWrapper is a wrapper class. According to the Dubbo SPI mechanism, the default Cluster will be wrapped.

public class MockClusterWrapper implements Cluster {
private Cluster cluster;
public MockClusterWrapper(Cluster cluster) {
this.cluster = cluster;
}

@Override
public Invoker join(Directory directory) throws RpcException {
return new MockClusterInvoker(directory, this.cluster.join(directory ));
}
}

Summary: Dubbo’s default Cluster is FailoverCluster, which means that MockClusterWrapper will wrap FailoverCluster. Next, take a look at the execution process of Mock’s core MockClusterInvoker.

MockClusterInvoker is the core class of Dubbo Mock. There are three main functions:

  1. Determine whether the Mock mechanism needs to be turned on , Completed by the invoke method.
  2. Filter out the corresponding Mock Invoker according to MockInvokersSelector, which is completed by selectMockInvoker, and actually delegates to MockInvokersSelector to complete the routing.
  3. The execution of MockInvoker is done by the doMockInvoke method, which is actually delegated to MockInvoker.

2.2.2 invoke execution entry

Invoke determines whether the mock mechanism needs to be turned on. If it needs to be turned on, doMockInvoke is called to downgrade the service.

@Override
public Result invoke(Invocation invocation) throws RpcException {
Result result = null;
String value = directory.getUrl().getMethodParameter(invocation.getMethodName (),
MOCK_KEY, Boolean.FALSE.toString()).trim();
if (value.length() == 0 || value.equalsIgnoreCase("false")) {
//no mock
result = this.invoker.invoke(invocation);
} else if (value.startsWith("force")) {
//force:direct mock< br /> result = doMockInvoke(invocation, null);
} else {
//fail-mock
try {
result = this.invoker.invoke(invocation);< br />} catch (RpcException e) {
if (e.isBiz()) {
throw e;
}
result = doMockInvoke(invocation, e);
}
}
return result;
}

Summary: **Invoke pays attention to a question, whether it is necessary to turn on Mock, if it is turned on Mock calls doMockInvoke to execute. **The code comments are already very clear, respectively, for no mock:(normal process), force:(force mock), fail:(failed mock, default ) are processed separately. If mock=false, it will be processed normally. If you configure mock="return null" and mock="fail:return+null", the processing flow is the same.

2.2.3 doMockInvoke

doMockInvoke performs service downgrade.

private Result doMockInvoke(Invocation invocation, RpcException e) {
Result result = null;
Invoker minvoker;

// 1. Filter You can use mockInvokers
List> mockInvokers = selectMockInvoker(invocation);
// 2. If not, create MockInvoker
if (CollectionUtils.isEmpty(mockInvokers)) {
minvoker = (Invoker) new MockInvoker(directory.getUrl(), directory.getInterface());
} else {
minvoker = mockInvokers.get(0);
}
// 3. Perform service downgrade mockInvoker
try {
result = minvoker.invoke(invocation);
} catch (RpcException me) {
if (me .isBiz()) {
result = AsyncRpcResult.newDefaultAsyncResult(me.getCause(), invocation);
} else {
throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause());
}
} catch (Throwable me) {
throw new RpcException(getMockExceptionM essage(e, me), me.getCause());
}
return result;
}

Summary: The final call of doMockInvoke minvoker.invoke(invocation) to downgrade the service, the concern is selectMockInvoker(invocation) to filter the MockInvoker in the cache, if not, you need to create a new MockInvoker.

2.2.4 selectMockInvoker

The selectMockInvoker method is very strange. I haven’t seen how the real MockInvoker filtering is done. In fact, Dubbo’s default routing strategy includes MockInvokersSelector, and this class completes the rule routing.

private List> selectMockInvoker(Invocation invocation) {
List> invokers = null;
if (invocation instanceof RpcInvocation) {
// 1. Set invocation.need.mock=true
((RpcInvocation) invocation).setAttachment(INVOCATION_NEED_MOCK, Boolean.TRUE.toString());
// 2. Call MockInvokersSelector routing rule filtering List of services
invokers = directory.list(invocation);
...
}
return invokers;
}

Summary : The selectMockInvoker method secretly sets the invocation.need.mock property of the invocation to false. This parameter is very useful in MockInvokersSelector. Then use the directory.list(invocation) method to retrieve the service list. When the Dubbo series (07-1) cluster fault tolerance-service dictionary analyzes the RegisterDirectory source code, we know that the list method will call routeChain. route Routing rule filtering service. Let’s take a look at the MockInvokersSelector code.

2.3 MockInvokersSelector

MockInvokersSelector returns to normal Invokers when Mock is not turned on, and MockInvoker when turned on.

@Override
public List> route(final List> invokers,
URL url, final Invocation invocation) throws RpcException {
if (CollectionUtils.isEmpty(invokers)) {
return invokers;
}

if (invocation.getAttachments() == null) {
// 1. Return -> Non-MockedInvoker
return getNormalInvokers(invokers);
} else {
String value = invocation.getAttachments().get(INVOCATION_NEED_MOCK);
if ( value == null) {
return getNormalInvokers(invokers);
// 2. invocation.need.mock=true then return -> MockedInvoker(MockProtocol)
} else if (Boolean.TRUE .toString().equalsIgnoreCase(value)) {
return getMockedInvokers(invokers);
}
}
// 3. invocation.need.mock=false then return -> Non-MockedInvoker + MockedInvoker
// ???
return invokers;
}

**Summary: **directory.list calls MockInvokersSelector In .route, there are three situations:

  1. attachments is null or invocation.need.mock is null, then return non-MockedInvoker.
  2. invocation.need.mock=true then return MockedInvoker.
  3. invocation.need.mock=false then return non-MockedInvoker + MockedInvoker ???

2.4 MockInvoker

MockProtocol and MockInvoker are located under the dubbo-rpc-api project.

In the MockClusterInvoker#doMockInvoke method, if the MockedInvoker filtered out by directory.list is empty, a MockedInvoker will be created directly. The code is as follows:

minvoker = (Invoker) new MockInvoker(directory.getUrl(), directory.getInterface());

In fact, Mock is also a protocol. You can write in the registry/dubbo/com.foo.BarService/providers directory:< /p>

"mock://192.168.139.101/com.foo.BarService"

In this way, consumers will create a corresponding MockedInvoker according to the MockProtocol protocol after subscribing to the com.foo.BarService service.

2.4.1 MockProtocol

MockProtocol can only be imported through reference, and services cannot be exposed through export. In fact, a MockInvoker is created directly.

final public class MockProtocol extends AbstractProtocol {
@Override
public Exporter export(Invoker invoker) throws RpcException {
throw new UnsupportedOperationException ();
}

@Override
public Invoker protocolBindingRefer(Class type, URL url) throws RpcException {
return new MockInvoker<>(url, type);
}
}

Summary: MockProtocol is very simple, so I won’t say more. Let’s take a look at the MockInvoker code.

2.4.2 MockInvoker

MockInvoker performs a service downgrade. After MockClusterInvoker determines whether Mock needs to be turned on, MockInvokersSelector filters out available MockInvokers, and finally performs service downgrading.

  1. According to the service degradation configuration, perform the corresponding service degradation, such as return, throw, xxxServiceMock li>
  2. Direct return needs to parse the return parameters: parseMockValue
  3. To execute xxxServiceMock, you need to find the corresponding implementation class: getInvoker.

Look at the overall execution flow of the invoke method first.

@Override
public Result invoke(Invocation invocation) throws RpcException {
// 1. Get the mock value, the methodname.mock or mock parameter in the URL
String mock = getUrl().getParameter(invocation.getMethodName() + "." + MOCK_KEY);
if (invocation instanceof RpcInvocation) {
((RpcInvocation) invocation).setInvoker(this);
}
if (StringUtils.isBlank(mock)) {
mock = getUrl().getParameter(MOCK_KEY);
}

if (StringUtils.isBlank(mock )) {
throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url));
}

// 2. To mock string Processing, such as removing the prefixes of `force:` and `fail:`
mock = normalizeMock(URL.decode(mock));
// 3. return
if (mock.startsWith( RETURN_PREFIX)) {
mock = mock.substring(RETURN_PREFIX.length()).trim();
try {
Type[] returnTypes = RpcUtils.getReturnTypes(invocation);
Object value = parseMockValue(mock, returnTypes);
return AsyncRpcResult.newDefaultAsyncResult(value, invocation);
} catch (Exception ew) {
throw new RpcException(ew);
}
// 3. throw
} else if (mock.startsWith(THROW_PREFIX)) {
mock = mock.substring(THROW_PREFIX.length()).trim();
if ( StringUtils.isBlank(mock)) {
throw new RpcException("mocked exception for service degradation.");
} else {// user customized class
Throwable t = getThrowable(mock);
throw new RpcException(RpcException.BIZ_EXCEPTION, t);
}
// 5. xxxServiceMock
} else {//impl mock
try {
Invoker invoker = getInvoker(mock);
return invoker.invoke(invocation);
} catch (Throwable t) {
throw new RpcException("Failed to create mock implementation class " + mock, t);
}
}
}

Summary: Invoke performs service degradation, first obtains mock parameters, and processes mock parameters, such as removing force :, fail: prefix. Dubbo service downgrade has three processing situations:

  1. return: Direct return, which can be empty, null, true, false, json format, and parseMockValue is used for parsing.
  2. throw: throw an exception directly. If no exception is specified, an RpcException is thrown, otherwise the specified Exception is thrown.
  3. xxxServiceMock: execute the xxxServiceMock method. If mock=true or mock=defalut, find the xxxServiceMock method and execute it, if mock=com.dubbo.testxxxService, execute the specified method.

The getInvoker method finds the Invoker of the specified convection.

private Invoker getInvoker(String mockService) {
// 1. Cache hit
Invoker invoker = (Invoker) MOCK_MAP.get(mockService) ;
if (invoker != null) {
return invoker;
}

// 2. Find the implementation class of mock according to serviceType, the default is xxxServiceMock
Class serviceType = (Class) ReflectUtils.forName(url.getServiceInterface());
T mockObject = (T) getMockObject(mockService, serviceType);
// 3. Wrapped into Invoker
invoker = PROXY_FACTORY.getInvoker(mockObject, serviceType, url);
if (MOCK_MAP.size() <10000) {
MOCK_MAP.put(mockService, invoker);
}
return invoker;
}

public static Object getMockObject(String mockService, Class serviceType) {
// When mock=true or default, look for xxxServiceMock
if (ConfigUtils.isDefault(mockService)) {
mockService = serviceType.getName() + "Mock";
}
// mock=testxxxService, specify the Mock implementation class< br /> Class moc kClass = ReflectUtils.forName(mockService);
...
return mockClass.newInstance();
}

Summary: Dubbo if Without specifying the Mock implementation class, xxxServiceMock is searched by default. If the implementation class exists, wrap it into Invoker and return.


Record a little bit of heart every day. Content may not be important, but habits are important!

graph LR MockClusterWrapper – join –> MockClusterInvoker MockClusterInvoker – selectMockInvoker –> MockInvokersSelector MockInvokersSelector – route –> MockProtocol MockProtocol – refer –> MockInvoker

< p>

Leave a Comment

Your email address will not be published.