美文网首页
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