Common Settings for service degradation
- Mock =”force:return NULL “: mock=”force:return NULL “: mock=”force:return NULL “: mock=”force:return NULL “:
- Mock =”fail:return NULL “: mock=”fail:return NULL “: Mock =”fail:return null”: Mock =”fail:return null”
- **mock=”true”:** When a consumer’s method call to the service fails, the corresponding method of the consumer’s service-degraded mock class instance is called. The Mock class is named “Business Interface Name +Mock” and is placed in the same package as the interface.
- ** Mock = Fully qualified class name for the degraded class :** Functions like **mock=”true”** except that the degraded class name in this method can be any name, in any package
InvokerInvocationHandler.java
MockClusterInvoker.java
public Result invoke(Invocation invocation) throws RpcException {
Result result = null;
// Get mock property values
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
// If the mock attribute is not set or the value of the mock attribute is false, the degrade function is not enabled
if (value.length() == 0 || value.equalsIgnoreCase("false")) {
// No mock directly to the remote call (no degradation)
result = this.invoker.invoke(invocation);
} else if (value.startsWith("force")) {
// Enables forced reversion. The remote call is abandoned and the local reversion method is invoked
if (logger.isWarnEnabled()) {
logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
}
//force:direct mock directly
result = doMockInvoke(invocation, null);
} else {
//fail-mock
try { // Remote call
result = this.invoker.invoke(invocation);
} catch (RpcException e) {
// If the remote invocation fails, the service will be degraded
if (e.isBiz()) {
throw e;
}
if (logger.isWarnEnabled()) {
logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
}
// The service is degradedresult = doMockInvoke(invocation, e); }}return result;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private Result doMockInvoke(Invocation invocation, RpcException e) {
Result result = null;
Invoker<T> minvoker;
// Get the latest invoker from ZK again
List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);
// If no invoker is still available, create a MockInvoker, otherwise return the first available invoker
if (CollectionUtils.isEmpty(mockInvokers)) {
minvoker = (Invoker<T>) new MockInvoker(directory.getUrl(), directory.getInterface());
} else {
minvoker = mockInvokers.get(0);
}
try {
// Perform degraded method calls, tracing here
result = minvoker.invoke(invocation);
} catch (RpcException me) {
if (me.isBiz()) {
result = AsyncRpcResult.newDefaultAsyncResult(me.getCause(), invocation);
} else {
throw newRpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause()); }}catch (Throwable me) {
throw new RpcException(getMockExceptionMessage(e, me), me.getCause());
}
return result;
}
Copy the code
MockInvoker.java
public Result invoke(Invocation invocation) throws RpcException {
// Get the mock attribute in
String mock = getUrl().getParameter(invocation.getMethodName() + "." + MOCK_KEY);
if (invocation instanceof RpcInvocation) {
((RpcInvocation) invocation).setInvoker(this);
}
Get the mock in
if the mock is not set in
// The method level is larger than the interface level.
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));
}
// Parse the values in the mock and return. Prepare for the following business
mock = normalizeMock(URL.decode(mock));
// Handle mock cases that start with return
if (mock.startsWith(RETURN_PREFIX)) {
// take the content after return
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("mock return invoke error. method :" + invocation.getMethodName()
+ ", mock:" + mock + ", url: " + url, ew);
}
// Handle the case where a mock begins with 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 newRpcException(RpcException.BIZ_EXCEPTION, t); }}else { // Impl Mock calls custom degraded classes
try {
// Load and instantiate the local degraded class
Invoker<T> invoker = getInvoker(mock);
return invoker.invoke(invocation);
} catch (Throwable t) {
throw new RpcException("Failed to create mock implementation class "+ mock, t); }}}public static String normalizeMock(String mock) {
if (mock == null) { // If mock is null, null is returned
return mock;
}
mock = mock.trim();
if (mock.length() == 0) { // mock is empty string, return empty string
return mock;
}
if (RETURN_KEY.equalsIgnoreCase(mock)) { // Mock returns returnNULL
return RETURN_PREFIX + "null";
}
// If mock is true, default, fail or force, default is returned
if (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock) || "force".equalsIgnoreCase(mock)) {
return "default";
}
// mock starts with fail: and returns the substring after fail
if (mock.startsWith(FAIL_PREFIX)) {
mock = mock.substring(FAIL_PREFIX.length()).trim();
}
// mock starts with force: and returns the substring after force:
if (mock.startsWith(FORCE_PREFIX)) {
mock = mock.substring(FORCE_PREFIX.length()).trim();
}
// Replace backquotes with double quotes if mock uses return or throw
if (mock.startsWith(RETURN_PREFIX) || mock.startsWith(THROW_PREFIX)) {
mock = mock.replace('`.'"');
}
return mock;
}
private Invoker<T> getInvoker(String mockService) {
Invoker<T> invoker = (Invoker<T>) MOCK_MAP.get(mockService);
if(invoker ! =null) {
return invoker;
}
// Load the specified service interface
Class<T> serviceType = (Class<T>) ReflectUtils.forName(url.getServiceInterface());
// Build the degraded class, load it and instantiate it
T mockObject = (T) getMockObject(mockService, serviceType);
// Encapsulate the degraded instance as an 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) {
// If mockService is default, the degraded class name is built
if (ConfigUtils.isDefault(mockService)) {
mockService = serviceType.getName() + "Mock";
}
// Load the degraded classClass<? > mockClass = ReflectUtils.forName(mockService);// If the degraded class does not implement a business interface, an exception is thrown
if(! serviceType.isAssignableFrom(mockClass)) {throw new IllegalStateException("The mock class " + mockClass.getName() +
" not implement interface " + serviceType.getName());
}
try {
/ / instantiate
return mockClass.newInstance();
} catch (InstantiationException e) {
throw new IllegalStateException("No default constructor from mock class " + mockClass.getName(), e);
} catch (IllegalAccessException e) {
throw newIllegalStateException(e); }}Copy the code