热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Dubbo学习笔记泛化实现进行mock

DubboRPC介绍目录1.什么是Dubbo,我们正常是怎么使用的?2.注册一个RPC服务需要什么参数3.我们的Mock实现思路样例1.什么是Dubbo,我们正常是怎么使用的?Ap

Dubbo RPC介绍

目录
  • 1. 什么是Dubbo,我们正常是怎么使用的?
  • 2. 注册一个RPC服务需要什么参数
  • 3. 我们的Mock实现思路
  • 样例

1. 什么是Dubbo,我们正常是怎么使用的?

Apache Dubbo™ 是一款高性能Java RPC框架.其中与Alibaba Dubbo的区别主要在于阿里开发的2.6.X且不再维护,Apache开发的2.7.X新增了元数据中心 MetaData 和配置中心 Conf-center 这两个功能。元数据信息包括服务接口,及接口的方法信息。这些信息将被用于服务mock,服务测试。

我们平常是在实例上增加注解@Service暴露服务和使用@Reference来完成自动引用实例,样例查看http://dubbo.apache.org/zh-cn/docs/user/configuration/annotation.html

2. 注册一个RPC服务需要什么参数

官方给出的快速启动样例是这样的: http://dubbo.apache.org/zh-cn/docs/user/quick-start.html 这个样例是使用XML,我们并不常用,但可以比较清楚地观察到注册一个RPC服务需要什么参数。注意下面这个 provider.xml,我增加了一些改动用以说明



 
    
    
 
    
    
 
    
    
 
    
    
 
    
    

其实区别定位一个RPC服务需要的条件一共有四个:

  1. application.name
  2. service.interface。注意:如果这个接口不存在,就无法注册并暴露服务,而我们mock的目标就是个假的服务出来。
  3. service.version
  4. service.group

此处注意:服务的元数据是根据service.interface反射得到的,我们为了和2.7.X兼容,就必须自己造个假的元数据上去

3. 我们的Mock实现思路

Dubbo与mock相关的提供了两种api:本地伪装Mock和泛化实现。

本地伪装Mock,XML对应为用于服务降级,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。

大白话就是参考AOP的after-throw,原服务抛出异常后,触发本地mock进行服务降级。try {//原服务} catch (Exception e) {//执行mock实例}。这个坑了我很多时间,因为mock="true"太具有迷惑性,我原本使用这个mock约定实现mock平台功能,使用force模式强制调用mock平台上的服务,但是一来会覆盖原先的本地mock;二来不符合无侵入原则。最终放弃。贴出参考用法如下


	
    


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

这个就是我们最终采用的实现思路。通过指定一个通用服务接口GenericService的实现,完成服务的暴露。

// 这个就是 提供者dubbo 和标签一一对应,该类很重,因为包括了与注册中心的链接等等,需要自行缓存处理
ServiceConfig service = new ServiceConfig;
// 配置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);

关于元数据,MetadataIdentifier 该类主要是用于配置zookeeper上元数据节点路径和节点名称

例:/dubbo/metadata/服务名(接口全类名)/1.0.0(版本)/dubbo(分组)/provide(side)/demo-provide(应用名)

FullServiceDefinition 该对象进行gson转换后的json串,即为元数据节点内容

  • parameters:服务的属性,包括version,group等等
  • canonicalName:接口全类名
  • codeSource:源代码
  • methods:方法
  • types: 所有方法入参出参的类型

样例

GenericServiceImpl 这个impl就是xml中service.ref所需要的类

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

/**
 * @Description: 服务端实现 GenericService
 * @Author: quanhuan.huang@luckincoffee.com
 * @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";
    }
}

数据结构:DubboRpcDTO

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

数据结构:DubboMethodDTO

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

服务实现

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

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

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

        // 注册并对外暴露服务
        ServiceConfig 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 typeDefinitiOns= new LinkedList<>();
        List 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 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 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;
    }
}

经测试,下面这个test可以完成provider.xml一样的功能。

@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 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();
        }
    }

推荐阅读
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 标题: ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 解决java.lang.IllegalStateException: ApplicationEventMulticaster not initialized错误的方法和原因
    本文介绍了解决java.lang.IllegalStateException: ApplicationEventMulticaster not initialized错误的方法和原因。其中包括修改包名、解决service name重复、处理jar包冲突和添加maven依赖等解决方案。同时推荐了一个人工智能学习网站,该网站内容通俗易懂,风趣幽默,值得一看。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • Python爬虫中使用正则表达式的方法和注意事项
    本文介绍了在Python爬虫中使用正则表达式的方法和注意事项。首先解释了爬虫的四个主要步骤,并强调了正则表达式在数据处理中的重要性。然后详细介绍了正则表达式的概念和用法,包括检索、替换和过滤文本的功能。同时提到了re模块是Python内置的用于处理正则表达式的模块,并给出了使用正则表达式时需要注意的特殊字符转义和原始字符串的用法。通过本文的学习,读者可以掌握在Python爬虫中使用正则表达式的技巧和方法。 ... [详细]
  • iOS超签签名服务器搭建及其优劣势
    本文介绍了搭建iOS超签签名服务器的原因和优势,包括不掉签、用户可以直接安装不需要信任、体验好等。同时也提到了超签的劣势,即一个证书只能安装100个,成本较高。文章还详细介绍了超签的实现原理,包括用户请求服务器安装mobileconfig文件、服务器调用苹果接口添加udid等步骤。最后,还提到了生成mobileconfig文件和导出AppleWorldwideDeveloperRelationsCertificationAuthority证书的方法。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
author-avatar
手机用户2502863643
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有