Dubbo快速入门
学习完zookeeper顺便搭建一个dubbo的hello world,在学习的过程中感觉重启后的dubbo好像并没有焕发生机。打算就学到hello world了,如果真用到再深入学习下。
Dubbo的快速开始分为三个项目,一个是通用API主要用来声明服务提供方和服务消费方都会使用的公共接口。一个是服务提供方,一个是服务消费方。
演示项目均使用Maven管理且创建时不使用模板
示例API项目名称:dubbo-common
示例服务方项目名称:dubbo-provider
示例消费方项目名称:dubbo-consumer
开始搭建
开始将会使用Multicast注册中心搭建,随后会将注册中心改为zookeeper
API项目
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wxm</groupId>
<artifactId>dubbo-common</artifactId>
<version>0.1</version>
<packaging>jar</packaging>
</project>
项目结构
服务提供方
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wxm</groupId>
<artifactId>dubbo-provider</artifactId>
<version>0.1</version>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>com.wxm</groupId>
<artifactId>dubbo-common</artifactId>
<version>0.1</version>
</dependency>
</dependencies>
</project>
项目结构
其中log4j.properties和logback.xml根据日志框架任选其一且当前没有实际用处,后文使用zookeeper做注册中心时会用到。
服务消费方
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wxm</groupId>
<artifactId>dubbo-consumer</artifactId>
<version>0.1</version>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>com.wxm</groupId>
<artifactId>dubbo-common</artifactId>
<version>0.1</version>
</dependency>
</dependencies>
</project>
项目结构
同服务提供方一样log4j.properties在当前模式中没有使用,可忽略。
基本配置
API项目
只需提供公共接口
DemoService.java
package service;
public interface DemoService {
String say(String msg);
}
服务提供方
编写provider.xml配置dubbo注册中心信息、通信协议、暴漏服务等。这里复制官方quick start进行修改
<?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">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="hello-world-app" />
<!-- 使用multicast广播注册中心暴露服务地址 -->
<dubbo:registry address="multicast://225.1.1.1:1234?unicast=false" />
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 声明需要暴露的服务接口,即API项目中的接口 -->
<dubbo:service interface="service.DemoService" ref="demoService" />
<!-- 和本地bean一样实现服务 -->
<!-- class指定API接口实现类的全路径 -->
<bean id="demoService" class="provider.service.DemoServiceImp" />
</beans>
这里可以看到暴漏服务地址时有一个unicast=false参数,当两个服务运行在同一台计算机上时需要添加该参数,原因官方文档有解释这里不复制粘贴,转至Multicast注册中心查看原因
DemoServiceImp.java
package provider.service;
import service.DemoService;
public class DemoServiceImp implements DemoService {
public String say(String msg) {
// TODO Auto-generated method stub
return "服务提供:"+msg;
}
}
App.java/服务提供方的
package provider;
import java.io.IOException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.DemoService;
public class App {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext c = new ClassPathXmlApplicationContext("provider.xml");
c.start();
DemoService bean = c.getBean(DemoService.class);
System.out.println(bean.say("serve"));
System.in.read();
}
}
服务消费方
编写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://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">
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="consumer-of-helloworld-app" >
<dubbo:parameter key="qos.port" value="33333"/>
</dubbo:application>
<!-- 使用multicast广播注册中心暴露发现服务地址 -->
<!-- 和服务提供方一样也要写unicast=false -->
<dubbo:registry address="multicast://225.1.1.1:1234?unicast=false" />
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="demoService" interface="service.DemoService" />
</beans>
这里注意多了<dubbo:parameter key="qos.port" value="33333"/>
如果在同一台计算机上需要配置该参数,因为两个服务默认都会尝试绑定22222端口。
App.java/服务消费方的
package consumer;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.DemoService;
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext c = new ClassPathXmlApplicationContext("consumer.xml");
c.start();
DemoService demo = (DemoService)c.getBean("demoService");
System.out.println(demo.say("client"));
}
}
至此配置完成,启动服务提供方的App.java
控制台
五月 27, 2020 5:21:59 下午 org.apache.dubbo.registry.multicast.MulticastRegistry warn
警告: [DUBBO] Ignore empty notify urls for subscribe url provider://192.168.64.1:20880/service.DemoService?anyhost=true&application=hello-world-app&bean.name=service.DemoService&bind.ip=192.168.64.1&bind.port=20880&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=service.DemoService&methods=say&pid=10692®ister=true&release=2.7.3&side=provider×tamp=1590571316867, dubbo version: 2.7.3, current host: 192.168.64.1
服务提供:serve
启动服务消费方App.java
控制台
五月 27, 2020 5:28:41 下午 org.apache.dubbo.config.AbstractConfig info
信息: [DUBBO] Refer dubbo service service.DemoService from url multicast://225.1.1.1:1234/org.apache.dubbo.registry.RegistryService?anyhost=true&application=consumer-of-helloworld-app&check=false&dubbo=2.0.2&generic=false&interface=service.DemoService&lazy=false&methods=say&pid=6720&qos.port=33333®ister.ip=192.168.64.1&release=2.7.3&remote.application=hello-world-app&side=consumer&sticky=false×tamp=1590571718957, dubbo version: 2.7.3, current host: 192.168.64.1
服务提供:client
demo搭建完成,这里记录过程中的坑
-
unicast=false
,同一台机器广播时需要添加此参数 -
multicast://225.1.1.1:1234
官方的为multicast://224.5.6.7:1234
广播地址有可能不可用自己尝试更换,范围224.0.0.0 - 239.255.255.255
将注册中心改为zookeeper
dubbo推荐使用zookeeper作为注册中心。dubbo关于zookeeper做注册中心的官方文档似乎并没有因为dubbo重启维护而更新。甚至文档中还在使用Netflix的curator。在maven仓库中Netflix的curator最后更新时间为2013年。现在的curator似乎隶属于apache。但从报错信息中我们可以得到以下结论。dubbo使用zookeeper作为注册中心时还需要依赖以下项目
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.1</version>
</dependency>
开始我使用的是2.6.3版本的dubbo,该版本不依赖curator-recipes,但2.7.3需要添加此依赖。
修改配置文件
现在分别修改provider.xml和cuonsumer.xml
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://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">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="hello-world-app" />
<!-- 这里发生了变化 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="service.DemoService" ref="demoService" />
<!-- 和本地bean一样实现服务 -->
<bean id="demoService" class="provider.service.DemoServiceImp" />
</beans>
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://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">
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="consumer-of-helloworld-app" >
<dubbo:parameter key="qos.port" value="33333"/>
</dubbo:application>
<!-- 这里发生了变化 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="demoService" interface="service.DemoService" />
</beans>
添加日志配置文件
curator引入了zookeeper而zookeeper又引入了log4j,所以此时项目的日志模块已被log4j接手,所以我们需要为项目添加log4j的配置文件,内容如下
log4j.rootLogger=DEBUG,CONSOLE,file
#log4j.rootLogger=ERROR,CONSOLE,file
#log4j.logger.org.apache.zookeeper.ClientCnxn=error
#log4j.logger.org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator = error
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=error
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern= [%p] %d %c - %m%n
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.DatePattern=yyyy-MM-dd
log4j.appender.file.File=log.log
log4j.appender.file.Append=true
log4j.appender.file.Threshold=error
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-M-d HH:mm:ss}%x[%5p](%F:%L) %m%n
但注意这里打印的日志信息并不包含zookeeper以及curator。什么日志框架是curator引入的不打印curator的日志?????别急先运行示例,看看控制台怎么说。
启动服务
首先要启动zookeeper服务,zookeeper安装部署在另一篇文章中,这里直接用。
重新运行App.java/服务提供方的
控制台
[DEBUG] 2020-05-27 17:30:40,220 org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
[DEBUG] 2020-05-27 17:30:40,221 org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'demoService'
服务提供:serve
重新运行App.java/服务消费方的
控制台
[DEBUG] 2020-05-27 17:32:12,244 io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@239d3e1c
[DEBUG] 2020-05-27 17:32:12,322 org.apache.dubbo.remoting.transport.DecodeHandler - [DUBBO] Decode decodeable message org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcResult, dubbo version: 2.7.3, current host: 192.168.64.1
服务提供:client
虽然不会打印curator和zookeeper的日志但程序是会运行成功的,可以看到远程调用成功。
你可能看到会有许多日志但那都是dobbo打印的
在两个App.java运行过程中可以在控制台看到如下信息
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
SLF4J: Failed to load class "org.slf4j.impl.StaticMDCBinder".
SLF4J: Defaulting to no-operation MDCAdapter implementation.
SLF4J: See http://www.slf4j.org/codes.html#no_static_mdc_binder for further details.
加载StaticLoggerBinder
失败,猜测是缺少slf4j-log4j12库,这时去查看下当前项目的全部依赖
可以看到存在slf4j-api和log4j依赖但缺少slf4j-log4j12猜测正确,但为什么dubbo却可以正常打印日志信息呢,这里应该是dubbo项目本身做了一些处理,例如打包时已经将slf4j-log4j12打包到源码中这里不做深究。
所以现在还需要引入slf4j-log4j12,添加如下依赖至dubbo-provider和dubbo-consumer项目当中。
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
至此以zookeeper为注册中心的demo也完美运行。
自定义Spring版本
dubbo依赖中含有spring且具有传递性,所以说引入dubbo的同时一会一并引入dubbo所需要的spring依赖。
本文使用的dubbo版本为2.7.3内置的spring版本为4.3.16,如果想要自定义spring版本也可以自己引入想要的spring版本。
查看dubbo的pom文件可以看到dubbo仅依赖spring-context即可正常工作,根据maven对相同依赖的选择原则只需要在当前项目中引入想要的spring版本即可,这里以spring5为例,修改后的pom.xml为
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wxm</groupId>
<artifactId>dubbo-provider</artifactId>
<version>0.1</version>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.3</version>
<!--这个exclusions实际上是不需要的,添加目的是为了使意图更为明显以及避免不必要的麻烦。功能 是引入依赖时排除<exclusions></exclusions>中声明的依赖 -->
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
</exclusions>
</dependency>
//添加部分
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
....只显示发生变动的部分。
</dependencies>
</project>
自定义zookeeper版本
替换思路与自定以spring一样,curator中是会引入zookeeper的,默认版本为3.5.3-beta。这里以zookeeper 3.4.6为例,替换后pom.xml为
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wxm</groupId>
<artifactId>dubbo-provider</artifactId>
<version>0.1</version>
<dependencies>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.1</version>
<!--和上文一样,可以省略。目的相同-->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
....只显示发生变动的部分。
</dependencies>
</project>
闲的蛋疼,更换日志框架
排除多余依赖
更换默认日志框架,更换目标logback,首先查看当前项目中都谁引入过log4j相关框架并一一清除,经过排查curator直接或间接引入了log4j和slf4j-log4j12,所以首先排除curator中的log4j相关依赖,修改pom.xml
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.1</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
然后是自定义zookeeper时引入的zookeeper,修改pom.xml
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
这里说明下,以防回来看时有疑惑。根据maven对相同依赖的选择原则(选层数最小那个)实际上当前项目中引入的zookeeper是我自己引入的那个,curator中的zookeeper并没有生效,并且curator中的log4j框架也是它通过zookeeper间接引入的,所以说curator中对log4j相关框架的排除操作在当前项目中依然是可以省略的。还是那句话,为了使意图更加明确且避免不必要的麻烦,所以依然选择保留。
添加目的依赖
添加logback相关依赖,将如下依赖添加到pom.xml中
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-core -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
添加logback配置文件
在classpath下新建logback.xml并键入如下内容
<configuration>
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder
by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<logger name="org.apache.zookeeper.ClientCnxn" level="error" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="o.a.d.c.e.AdaptiveClassCodeGenerator" level="error" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
启动项目即可打印日志,但这里还有一个小坑。可以看到前面的log4j的配置文件有如下两行
log4j.logger.org.apache.zookeeper.ClientCnxn=error
log4j.logger.org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator = error
log4j.logger.org.apache.zookeeper.ClientCnxn=error
用来屏蔽zookeeper心跳日志log4j.logger.org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator = error
用来屏蔽dubbo一个很长的日志,它长下面这样,但这个是由logback打出的
21:49:01.058 [main] DEBUG o.a.d.c.e.AdaptiveClassCodeGenerator - [DUBBO] package org.apache.dubbo.rpc.clusteshishir;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class ConfiguratorFactory$Adaptive implements org.apache.dubbo.rpc.cluster.ConfiguratorFactory {
public org.apache.dubbo.rpc.cluster.Configurator getConfigurator(org.apache.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg0;
String extName = url.getProtocol();
if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.cluster.ConfiguratorFactory) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.cluster.ConfiguratorFactory extension = (org.apache.dubbo.rpc.cluster.ConfiguratorFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.cluster.ConfiguratorFactory.class).getExtension(extName);
return extension.getConfigurator(arg0);
}
}, dubbo version: 2.7.3, current host: 192.168.64.1
但是不知处于什么原因控制台告诉我们该日志是o.a.d.c.e.AdaptiveClassCodeGenerator
打出的,通过原来的log4j可以知道该日志实际是org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator
打出的,所以logback配置文件中logger的name要按找log4j中的日志名进行配置。
将
<logger name="o.a.d.c.e.AdaptiveClassCodeGenerator" level="error" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
修改为
<logger name="com.alibaba.dubbo.common.extension.ExtensionLoader" level="error" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
最后:大幻梦森罗万象狂气断罪眼\ (•◡•) /