Dubbo study notes - the generalization to achieve carried out mock

Dubbo RPC Introduction

1. What is Dubbo, is how we normally use?

Apache Dubbo ™ is a high performance Java RPC framework and Alibaba Dubbo where the main difference is that Ali is no longer maintained and developed 2.6.X, Apache 2.7.X development of new metadata Center MetaData and distribution center Conf-center these two functions. Metadata information includes method information service interface, and interfaces. This information will be used to service mock, service testing.

We usually increase @Service comment on instances of exposure to and use of services @Reference done automatically referenced examples, sample view http://dubbo.apache.org/zh-cn/docs/user/configuration/annotation.html

2. What parameters registered an RPC service needs

Quick Start sample given by the official is this: http://dubbo.apache.org/zh-cn/docs/user/quick-start.html this sample is the use of XML, we not commonly used, but can be compared clearly observed what parameters need to register an RPC service. Note the following provider.xml , I added some changes to illustrate

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
 
    <!-- 提供方应用信息,用于计算依赖关系 application.name为应用名,一个接口下可以有多个应用来提供 -->
    <dubbo:application name="hello-world-app"  />
 
    <!-- 使用multicast广播注册中心暴露服务地址  ip:port地址可以为多个,用,分割即可。 zookeeper://101.201.232.80:2181?backup=10.20.153.11:2181,10.20.153.12:2181"-->
    <dubbo:registry address="zookeeper://101.201.232.80:2181" />
 
    <!-- 用dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20880" />
 
    <!-- 声明需要暴露的服务接口 
         interface为接口全类名,
         ref服务对象实现引用,与下面的bean.id对应 
         version=服务版本,升级接口如果不兼容之前接口,就需要升级版本号 
         group=服务分组,当一个接口有多个实现,可以用分组区分
    -->
    <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService" version="1.0.0" group="dubbo"/>
 
    <!-- 声明实现类,使远程RPC像本地bean一样实现服务 id=service.ref class=实现类的全类名 -->
    <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl" />
</beans>

In fact, the difference between targeting a RPC service requires a total of four:

  1. application.name
  2. service.interface. Note: If this interface does not exist, can not be registered and exposed services, and our goal is to mock a fake out of service.
  3. service.version
  4. service.group

Note here: Metadata Service is based on the reflection service.interface get, we have to be compatible and 2.7.X, you must make your own fake metadata up

3. Our Mock realization of ideas

Dubbo with mock offers two api related to: local camouflage Mock and generalization achieved .

本地伪装Mock,XML对应为<dubbo:reference interface="com.foo.BarService" mock="true" .../>用于服务降级,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。

AOP is the vernacular reference to the after-throw, throw an exception after the original service, carried out mock trigger local service degradation. try {//原服务} catch (Exception e) {//执行mock实例}. The pit me a lot of time, because the mock = "true" too confusing, I had to use this mock convention to achieve mock-platform functionality, the use of force mode is forced to call the service on mock platform, but the one overwrites the original local mock; secondly does not comply with the principle of non-invasive. Finally gave up. Posted the following reference usage

<dubbo:reference id="demoService" check="false" interface="com.foo.BarService">
    <!-- force表示强制走mock; return fake是mock执行的语句 -->
    <dubbo:parameter key="sayHello.mock" value="force:return fake"/>
</dubbo:reference>

泛化实现,XML对应为<dubbo:reference interface="com.foo.BarService" generic="true" .../>,用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的远程服务Mock框架,可通过实现GenericService接口处理所有服务请求。

This is to realize that we finally adopted the idea. By specifying a common service interface GenericService achieve complete exposure services.

// 这个就是 提供者dubbo 和标签一一对应,该类很重,因为包括了与注册中心的链接等等,需要自行缓存处理
ServiceConfig<GenericService> service = new ServiceConfig<GenericService>;
// 配置service里注册中心的属性,需要指定address,例"zookeeper://101.201.232.80:2181"。
service.setRegistry(new RegistryConfig("zookeeper://101.201.232.80:2181"));
// 配置应用信息,这里我们将应用名统一表示为luckymock。
ApplicationConfig application = new ApplicationConfig();
application.setDefault(true);
application.setName("luckymock");
service.setApplication(application);
// 指定通信协议和本地通讯端口
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName("dubbo");
protocol.setPort(20880);
service.setProtocol(protocol);
// 说明是否是通用服务 并绑定通用服务接口的实现:GenericServiceImpl
service.setGeneric("true");
GenericService genericService = new GenericServiceImpl();
service.setRef(genericService);
// 配置这个服务特有的的东西
service.setGroup("dubbo");
service.setVersion("1.0.0");
service.setInterface("test")
// 对外暴露这个服务
service.export();
// 由于上面提到的元数据问题,我们手动去提交了一遍元数据。
// 这方面资料极少,我是通过源码ZookeeperMetadataReport的storeMetadata方法追溯到MetadataReportService的publishProviderf方法,所有用法都参考这个方法
// 使用下面这个函数提交服务提供者的元数据,不一定是zookeeper作注册中心
metadataReport.storeProviderMetadata(metadataIdentifier, fullServiceDefinition);

Metadata, MetadataIdentifier class is mainly used for configuration metadata on zookeeper path node and the node name

Example: / dubbo / metadata / service name (full class name Interface) /1.0.0 (version) / dubbo (packet) / provide (side) / demo-provide (application name)

json string after conversion is gson FullServiceDefinition the object, i.e. the content metadata node

  • parameters: service attributes, including version, group, etc.
  • canonicalName: Interface full class name
  • codeSource: Source
  • methods: Method
  • types: all methods into the reference parameters of the type

Sample

GenericServiceImpl this impl xml is required classes in service.ref

import org.apache.dubbo.rpc.service.GenericException;
import org.apache.dubbo.rpc.service.GenericService;

/**
 * @Description: 服务端实现 GenericService
 * @Author: [email protected]
 * @Date: 2019/12/19 15:01
 */
public class GenericServiceImpl implements GenericService {
    @Override
    public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException {
       // 这里只有method方法名可以进行区别,所以该类可能需要动态编译
       if (method.equals("hi")) {
            return "hi, " + args[0];
        } else if (method.equals("hello")) {
            return "hello, " + args[0];
        } else if (method.equals("sayHello")) {
            return "say:hello, " + args[0];
        }
        return "未找到该方法,无法mock";
    }
}

Data Structure: DubboRpcDTO

public class DubboRpcDTO {
    /**
     * 服务接口的全类名
     */
    private String interfaceName;
    /**
     * 分组
     */
    private String group;
    /**
     * 版本
     */
    private String version;
    /**
     * 方法列表
     */
    private List<DubboMethodDTO> methodList;
}

Data Structure: DubboMethodDTO

public class DubboMethodDTO {
    /**
     * 方法名
     */
    private String methodName;
    /**
     * 方法参数
     */
    private String[] arguments;
    /**
     * 返回类型
     */
    private String returnType;
}

Service Implementation

@Service
public class DubboProvideService {
    private static Logger logger = LoggerFactory.getLogger(DubboProvideService.class);
    private MetadataReportFactory metadataReportFactory = ExtensionLoader.getExtensionLoader(MetadataReportFactory.class).getAdaptiveExtension();

    /**
     * 该类很重 自行缓存
     */
    private static ServiceConfig<GenericService> service;

    /**
     * 提供rpc服务
     *
     * @return 是否完成
     */
    Boolean rpcMockProvide(DubboRpcDTO rpcDTO) throws ClassNotFoundException {

        // 注册并对外暴露服务
        ServiceConfig<GenericService> service = getService();
        service.setGroup(rpcDTO.getGroup());
        service.setVersion(rpcDTO.getVersion());
        service.setInterface(rpcDTO.getInterfaceName());
        // 指向自己实现的通用类实例,需要动态化
        GenericService genericService = new GenericServiceImpl();
        service.setGeneric("true");
        service.setRef(genericService);
        service.export();

        // 注册数据源
        FullServiceDefinition fullServiceDefinition = new FullServiceDefinition();
        TypeDefinitionBuilder builder = new TypeDefinitionBuilder();
        List<TypeDefinition> typeDefinitions = new LinkedList<>();
        List<MethodDefinition> methodDefinitions = new LinkedList<>();
        for (DubboMethodDTO method : rpcDTO.getMethodList()) {
            // 记录方法
            MethodDefinition methodDefinition = new MethodDefinition();
            methodDefinition.setName(method.getMethodName());
            methodDefinition.setParameterTypes(method.getArguments());
            methodDefinition.setReturnType(method.getReturnType());
            methodDefinitions.add(methodDefinition);
            // 记录所有入参的type
            for (String argument : method.getArguments()) {
                TypeDefinition td = builder.build(Class.forName(argument), Class.forName(argument));
                typeDefinitions.add(td);
            }
            // 返回值的type也需要记录
            typeDefinitions.add(builder.build(Class.forName(method.getReturnType()), Class.forName(method.getReturnType())));
        }
        // 拼接服务内容
        Map<String, String> parameters = new HashMap<>(16);
        parameters.put("side", "provider");
        parameters.put("release", "2.7.3");
        parameters.put("methods", "*");
        parameters.put("deprecated", "false");
        parameters.put("dubbo", "2.0.2");
        parameters.put("interface", rpcDTO.getInterfaceName());
        parameters.put("version", rpcDTO.getVersion());
        parameters.put("generic", "true");
        parameters.put("application", "luckymock");
        parameters.put("dynamic", "true");
        parameters.put("register", "true");
        parameters.put("group", rpcDTO.getGroup());
        parameters.put("anyhost", "true");
        fullServiceDefinition.setParameters(parameters);
        fullServiceDefinition.setCodeSource(ClassUtils.getCodeSource(this.getClass()));
        fullServiceDefinition.setCanonicalName(rpcDTO.getInterfaceName());
        fullServiceDefinition.setTypes(typeDefinitions);
        fullServiceDefinition.setMethods(methodDefinitions);
        // 拼接服务描述
        MetadataIdentifier metadataIdentifier = new MetadataIdentifier();
        metadataIdentifier.setServiceInterface(rpcDTO.getInterfaceName());
        metadataIdentifier.setVersion(rpcDTO.getVersion());
        metadataIdentifier.setGroup(rpcDTO.getGroup());
        metadataIdentifier.setSide("provider");
        // 应用名统一为luckymock
        metadataIdentifier.setApplication("luckyMock");
        // 写元数据中心
        MetadataReport metadataReport = metadataReportFactory.getMetadataReport(URL.valueOf("zookeeper://101.201.232.80:2181"));
        metadataReport.storeProviderMetadata(metadataIdentifier, fullServiceDefinition);
        logger.info("注册RPC服务成功:{}", rpcDTO.getInterfaceName());
        return true;

    }

    /**
     * 该类很重,缓存
     *
     * @return @Service的信息
     */
    private static ServiceConfig<GenericService> getService() {
        if (null == service) {
            service = new ServiceConfig<>();
            // 注册中心配置
            RegistryConfig registryConfig = new RegistryConfig();
            registryConfig.setAddress("zookeeper://101.201.232.80:2181");
            service.setRegistry(registryConfig);
            // 应用配置
            ApplicationConfig application = new ApplicationConfig();
            application.setName("luckymock");
            service.setApplication(application);
            // 协议配置
            ProtocolConfig protocol = new ProtocolConfig();
            protocol.setName("dubbo");
            protocol.setPort(20880);
            service.setProtocol(protocol);
        }
        return service;
    }
}

After testing, the following test can be completed the same provider.xml function.

@Test
    public void demoService() {
        // 现在用泛化实现,实现com.lucky.demo.api.DemoService
        // 如果想要保证消费者正常调用,消费者处 InterfaceName这个类必须存在。当然,我们作为服务提供者不需要真的有这个类
        // 什么情况消费者也没有这个接口呢?rpc测试平台,dubbo-admin已经做了。
        DubboRpcDTO dubboRpcDTO = new DubboRpcDTO();
        dubboRpcDTO.setGroup("dubbo");
        dubboRpcDTO.setVersion("1.0.0");
        dubboRpcDTO.setInterfaceName("com.lucky.demo.api.DemoService");
        List<DubboMethodDTO> methodList = new LinkedList<>();
        DubboMethodDTO dubboMethodDTO = new DubboMethodDTO();
        dubboMethodDTO.setMethodName("sayHello");
        dubboMethodDTO.setReturnType("java.lang.String");
        dubboMethodDTO.setArguments(new String[]{"java.lang.String"});
        methodList.add(dubboMethodDTO);
        dubboRpcDTO.setMethodList(methodList);

        try {
            dubboProvideService.rpcMockProvide(dubboRpcDTO);
            System.out.println("dubbo service started,enter any keys stop");
            Scanner scanner = new Scanner(System.in);
            scanner.next();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Guess you like

Origin www.cnblogs.com/hyry/p/12113778.html