Dubbo series (07-5) cluster fault tolerance-Mock
[toc]
Spring Cloud Alibaba Series Catalog-Dubbo Chapter
1. Background introduction
Recommended documents: p>
- Dubbo actual combat-service degradation
- Dubbo actual combat-local camouflage
- 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.
Summary: The main process of Dubbo Mock is as follows:
MockClusterWrapper
: Since this class is a packaging class for Cluster, Dubbo assembles MockClusterWrapper by default to wrap ClusterInvoker.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.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.MockProtocol
: Create MockInvoker. This MockProtocol can only be referenced, not exposed.MockInvoker
: The core class, which actually performs service degradation, handlesmock="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
publicInvoker 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:
- Determine whether the Mock mechanism needs to be turned on , Completed by the invoke method.
- Filter out the corresponding Mock Invoker according to MockInvokersSelector, which is completed by selectMockInvoker, and actually delegates to MockInvokersSelector to complete the routing.
- 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;
Invokerminvoker;
// 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
publicList > 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:
- attachments is null or invocation.need.mock is null, then return
non-MockedInvoker
. invocation.need.mock=true
then returnMockedInvoker
.invocation.need.mock=false
then returnnon-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
publicExporter export(Invoker invoker) throws RpcException {
throw new UnsupportedOperationException ();
}
@Override
publicInvoker 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.
- According to the service degradation configuration, perform the corresponding service degradation, such as
return
,throw
,xxxServiceMock
li> - Direct return needs to parse the return parameters: parseMockValue
- 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 {
Invokerinvoker = 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:
return
: Direct return, which can be empty, null, true, false, json format, and parseMockValue is used for parsing.throw
: throw an exception directly. If no exception is specified, an RpcException is thrown, otherwise the specified Exception is thrown.xxxServiceMock
: execute the xxxServiceMock method. Ifmock=true
ormock=defalut
, find the xxxServiceMock method and execute it, ifmock=com.dubbo.testxxxService
, execute the specified method.
The getInvoker method finds the Invoker of the specified convection.
private InvokergetInvoker(String mockService) {
// 1. Cache hit
Invokerinvoker = (Invoker ) MOCK_MAP.get(mockService) ;
if (invoker != null) {
return invoker;
}
// 2. Find the implementation class of mock according to serviceType, the default is xxxServiceMock
ClassserviceType = (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>