美文网首页
Dubbo快速入门样例

Dubbo快速入门样例

作者: 树心图物 | 来源:发表于2018-06-02 15:05 被阅读0次

    主要按照Apache Dubbo主页上的demo进行编写,涉及到了具体操作和一些问题。

    官方环境要求(Maven其实可选):

    • JDK: version 6 or higher

    • Maven: version 3 or higher

    基于现在的使用习惯,具体实验使用idea+gradle(4.0)来构建demo

    使用IntelliJ IDEA 新建项目(New Project),选择Gradle


    定义自己项目的GroupId,ArtifactId,Version(GAV三元组),和maven的GAV三元组概念是一样的。

    剩下的新建项目页面不再赘述,一般采用默认即可。

    新建完以后等一会,IDEA会进行一些gradle初始化的操作。初始化完成后项目的目录结构为标准的gradle项目目录结构。

    首先在build.gradle脚本中添加dubbo的依赖,和maven类似。

    在这里贴一下最终版的项目根目录下的build.gradle。里面用到了shadowJar这个gradle的插件,会自动识别的,不需要手动安装。shadowJar是为了将开发的类连同依赖一起打包成一个Jar包(FatJar),方便部署或执行。shadowJar中的配置项是为了解决一些Bug,这个放在后面说。

    group'com.miexample'
    version'1.0-SNAPSHOT'
    
    buildscript{
            repositories{
                    jcenter()
            }
            dependencies{
                    classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
            }
    }
    
    allprojects{
            apply plugin : 'com.github.johnrengelman.shadow'
            apply plugin : 'java'
    
            sourceCompatibility=1.8
            
            repositories{
                mavenCentral()
            }
            
            dependencies{
                compile "com.alibaba:dubbo:2.6.1"
                testCompile group: 'junit', name:' junit', version: '4.12'
            }
            
            shadowJar{
                    transform(com.github.jengelman.gradle.plugins.shadow.transformers
                        .AppendingTransformer){
                            resource='META-INF/spring.schemas'
                    }
                    transform(com.github.jengelman.gradle.plugins.shadow.transformers
                        .AppendingTransformer){
                            resource='META-INF/spring.handlers'
                    }
                    transform(com.github.jengelman.gradle.plugins.shadow.transformers
                        .AppendingTransformer){
                            resource='META-INF/spring.factories'
                    }
            }
    }
    

    官网建议分成RPC接口,服务提供者(Provider),服务调用者(Consumer)这些不同模块,这样符合分成单个服务的开发情景,Provider和Consumer可以共用公共的接口模块。

    使用IDEA 的New -> Module就可以自动帮组创建子模块,本实验过程中创建的子模块也是Gradle模块,既符合IDEA本身管理模块特点也符合Gradle的模块架构。

    创建了三个模块 demo-common, demo-provider和demo-consumer。

    在子模块demo-consumer的build.gradle中增加如下内容(注意主类的包名根据自己的项目目录进行更改)

    dependencies{
            compile project(":demo-common")
    }
    
    processResources{
            exclude "**"
    }
    
    shadowJar{
            from('src/main/resources'){
                    into("META-INF/")
            }
            manifest{
                    attributes "Main-Class":"com.miexample.dubbo.consumer.Consumer"
            }
    }
    

    在子模块demo-provider的build.gradle中增加如下内容(注意主类的包名根据自己的项目目录进行更改)

    dependencies{
            compile project(":demo-common")
    }
    
    processResources{
            exclude"**"
    }
    
    shadowJar{
            from('src/main/resources'){
                    into("META-INF/")
            }
            manifest{
                    attributes "Main-Class":"com.miexample.dubbo.provider.Provider"
            }
    }
    

    在子模块的build.gradle中定义了打包的jar的主类,并且将资源文件统一打包到META-INF目录中。

    接下来就看定义的接口类和实现类吧!

    demo-common模块中定义接口:
    DemoService.java 核心部分(除去包名,导入等语句):

    Public interface DemoService{
            String sayHello(Stringname);
    }
    

    demo-provider模块中定义服务实现并定义启动类及相关配置文件:

    DemoServiceImpl.java核心部分:

    public class DemoServiceImpl implements DemoService {
        @Override
        public String sayHello(String name) {
            return "Hello " + name;
        }
    }
    

    Provider.java核心部分:

    public class Provider {
        public static void main(String[] args) throws Exception {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                    new String[] {"META-INF/spring/dubbo-demo-provider.xml"});
            context.start();
            // press any key to exit
            System.in.read();
        }
    }
    

    dubbo-demo-provider.xml:

    <?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://code.alibabatech.com/schema/dubbo"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
        <dubbo:application name="demo-provider"/>
        <dubbo:registry address="multicast://224.5.6.7:1234"/>
        <dubbo:protocol name="dubbo" port="20880"/>
        <dubbo:service interface="com.miexample.dubbo.common.DemoService" ref="demoService"/>
        <bean id="demoService" class="com.miexample.dubbo.provider.DemoServiceImpl"/>
    </beans>
    

    demo-consumer模块中定义服务调用者:


    Consumer.java核心部分:

    public class Consumer {
        public static void main(String[] args) throws Exception {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                    new String[]{"META-INF/spring/dubbo-demo-consumer.xml"});
            context.start();
            // obtain proxy object for remote invocation
            DemoService demoService = (DemoService) context.getBean("demoService");
            // execute remote invocation
            String hello = demoService.sayHello("world");
            // show the result
            System.out.println(hello);
        }
    }
    

    dubbo-demo-consumer.xml:

    <?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://code.alibabatech.com/schema/dubbo"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
        <dubbo:application name="demo-consumer"/>
        <dubbo:registry address="multicast://224.5.6.7:1234"/>
        <dubbo:reference id="demoService" interface="com.miexample.dubbo.common.DemoService"/>
    </beans>
    

    接下来就是build了,在IDEA的右侧可以方便的使用gradle:

    双击相应的task就可以执行,双击shadowJar来进行统一打包。这里出现了一个问题,因为打好的包中META-INF下的spring.schemas同时出现在dubbo依赖包和sping依赖包中,打包过程会导致文件覆盖(这也是依赖冲突的一种情况),虽然一般不会用什么问题,但是会影响xml文件中的的文件校验,xml引用的阿里巴巴地址已经失效,有可能出现校验失败的问题。为了解决这个冲突,利用了shadow插件的合并文件的功能,对应了在文章前面项目根目录下的build.gradle中的配置。

    build完了就可以在每个模块的build/libs/下找到打包好的jar,可以直接运行。先运行provider的包,然后运行consumer的包。

    运行过程中的问题:

    运行过程中发现provider运行正常,但是consumer报错,错误日志如下:


    这是一个常见错误,Consumer在找不到Provider时都会报这个错误。

    经过改变网络环境和跟踪源码最后发现是由于在同一台机器上运行provider和consumer并且采用组播的registry,并且有虚拟网卡会导致此问题(这种实验方式和网络环境在写demo时会碰到)。

    根本原因是源码中会判断发送给consumer的服务消息是采用单播还是组播,因为虚拟网卡对ip地址的影响导致判断逻辑出错,误认为consumer可以接收到单播,但其实consumer在收到单播消息之前provider自己就把这个消息给抢着处理了(因为provider和consumer在同一台机器上,相当于provider自发自收),导致consumer无法获取服务消息。此问题在linux系统上实验时又无法复现,因为linux和windows的接口对待是否回环是不同的,linux下的consumer可以在provider之先获取到单播消息。以下是源码判断逻辑

    解决方式有多种:

    1、禁用虚拟网卡。

    2、在两台机器上分别部署

    3、采用其它registry

    4、将服务提供者的spring配置文件中改为(注意url后面的参数配置)

    <dubbo:registry address="multicast://224.5.6.7:1234?unicast=false"/>
    

    第四个解决方式在官方文档上有提到,但是经测试2.6.1和2.6.2版本的dubbo配置了以后还是有问题,经查证发现解析URL参数时会将unicast丢失,应该是个bug。

    使用zookeeper当作registry也可能会遇到多IP的问题,参考https://dubbo.gitbooks.io/dubbo-user-book/content/demos/hostname-binding.html进行绑定provider的IP配置。

    相关文章

      网友评论

          本文标题:Dubbo快速入门样例

          本文链接:https://www.haomeiwen.com/subject/hsdrsftx.html