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