本文章中的Demo案例已经同步至gitee,供各位参考。
Demo地址:https://gitee.com/lemon_ant/dubbo-demo.git
zookeeper&dubbo简介
架构

节点角色说明
节点 | 角色说明 |
---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
调用关系说明
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性。
zookeeper安装
下载zookeeper.tar.gz
https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.6.2/apache-zookeeper-3.6.2-bin.tar.gz
上传到centos上、
解压使用命令解压在/usr/local目录下
1
| tar -zxvf zookeeper.tar.gz -C /usr/local
|
zookeeper目录下创建data
文件夹存放数据,修改配置conf
下的zoo_sample.cfg
名为zoo.cfg
(默认加载zoo.cfg)。
修改zoo.cfg
的dataDir
参数为刚才创建的目录,保存退出。
zookeeper启动
启动的前提是需要安装jdk
作者:疯子加天才
标题:centos安装jdk1.8的三种方法
文章链接:https://www.cnblogs.com/telwanggs/p/11546751.html
在zookeeper目录下使用 ./zkServer.sh start
启动
./zkServer.sh status
查看运行状态 standalone
代表单机版
./zkServer.sh stop
停止
dubbo-admin可视化界面
dubbo-admin:https://github.com/apache/incubator-dubbo/releases/tag/dubbo-2.6.0
安装dubbo-admin.war
放在本机tomcat中的wabapps目录,启动tomcat,待dubbo-admin.war
解压后,进入目录dubbo-admin-2.6.0\WEB-INF\
找到dubbo.properties
配置文件,修改配置为zookeeper的IP地址,重新启动tomcat。
浏览器访问地址:http://127.0.0.1:8080/dubbo-admin-2.6.0 账号信息:root root
里面就是所有信息home > governance > applications
能够看到注册上去的接口信息。
Dubbo服务提供方
创建一个空的maven项目,新建modul作为服务提供方。dubbo_provider。
导入pom文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
| <?xml version="1.0" encoding="UTF-8"?> <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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.hkrt</groupId> <artifactId>dubbo_provider</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>dubbo Maven Webapp</name> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring.version>5.0.5.RELEASE</spring.version> </properties>
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.2</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.9</version> </dependency> <dependency> <groupId>com.github.sgroschupf</groupId> <artifactId>zkclient</artifactId> <version>0.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> </dependencies>
<build> <finalName>dubbo</finalName> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <port>8081</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
|
创建service层接口和实现类
1 2 3 4 5 6 7 8 9
| import com.alibaba.dubbo.config.annotation.Service; @Service public class HelloServiceImpl implements HelloService {
@Override public String sayHello(String name) { return "hello"; } }
|
web.xml
1 2 3 4 5 6 7 8 9 10 11 12
| <web-app> <display-name>Archetype Created Web Application</display-name>
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param>
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
|
applicationContext-service.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <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" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<dubbo:application name="dubbo_provider"></dubbo:application>
<dubbo:registry address="zookeeper://IP:2181"></dubbo:registry>
<dubbo:protocol name="dubbo" port="20880"></dubbo:protocol> <dubbo:annotation package="com.hkrt.service"></dubbo:annotation> </beans>
|
通过maven插件启动服务provider,查看dubbo-admin发现已经存在HelloService的相关信息。
Dubbo服务消费方
新建modul作为服务提供方。dubbo_consumer
同样和provider相同的pom文件依赖。
创建一个和服务提供方相同的包package,创建controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import com.alibaba.dubbo.config.annotation.Reference;
@RestController @RequestMapping("/hello") public class HelloController {
@Reference private HelloService helloService;
@ResponseBody @RequestMapping("/say") public String sayHello(String name){ System.out.println("响应成功"); return helloService.sayHello(name); } }
|
HelloService.class
1 2 3
| public interface HelloService { public String sayHello(String name); }
|
web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <web-app> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
<servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
|
springmvc-servlet.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd 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">
<context:component-scan base-package="com" />
<mvc:default-servlet-handler />
<mvc:annotation-driven />
<dubbo:application name="dubbo_customer" />
<dubbo:registry address="zookeeper://IP:2181" />
<dubbo:annotation package="com.hkrt.controller" /> </beans>
|
通过maven插件启动服务consumer,查看dubbo-admin发现已经存在HelloService的相关信息。
案例回顾
需要在impl类上添加注解@service
注意这里的service已经是 spring提供的 现在要使用Dubbo提供的service注解
1
| import com.alibaba.dubbo.config.annotation.Service
|
问题:案例中服务提供方和服务消费方里面都会存在HelloService接口,这样做是否合理?有没有更好的方法?
答:这种做法不好,同一个接口复制了两份,放于两个服务中,不利于维护,更好的方式是单独创建一个maven工程,将接口全部放到这个maven工程中,需要依赖此接口的工程只需要在自己的pom.xml中引入坐标即可。
问题:在服务的消费方里面只是引用了HelloService接口,并没有实现类,那么Dubbo是如何实现远程调用的?
答:Dubbo底层是基于代理技术为HelloService接口创建代理对象,远程调用时通过此代理对象完成的,可以通过开发工具的Debug功能查看此代理对象的内部结构,另外,Dubbo实现网络传输是基于Netty框架完成的。
问题:使用zookeeper作为服务的注册中心,默认单机版运行,如何防止zookeeper单点故障呢?
答:zookeeper支持集群模式,可以配置zookeeper集群来达到zookeeper服务的高可用,防止单点故障的发生。
加入log4j日志
创建log4j.properties文件放于resources下,spring自动加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| log4j.rootLogger = debug, stdout
log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Threshold = DEBUG log4j.appender.stdout.Target = System.err log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender log4j.appender.D.File = C:/Users/Surpass/Desktop/log.log log4j.appender.D.Append = true log4j.appender.D.Threshold = DEBUG log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender log4j.appender.E.File =E://logs/error.log log4j.appender.E.Append = true log4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayout log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
|
Dubbo相关配置
包扫描
1
| <dubbo:annotation package="com.hkrt.service"></dubbo:annotation>
|
表示包扫描,作用是扫描改包下的所有了类,存在相关的注解。
如果不使用包扫描,可以通过配置的方式进行注册发布服务。
服务提供方:
1 2 3 4 5 6 7
|
<dubbo:annotation package="com.hkrt.service"></dubbo:annotation>
<bean class="com.hkrt.service.impl.HelloServiceImpl" id="helloService"></bean> <dubbo:service interface="com.hkrt.service.HelloService" ref="helloService"/>
|
服务消费方:
1 2 3 4 5 6
|
<dubbo:annotation package="com.hkrt.controller" />
<dubbo:reference interface="com.hkrt.service.HelloService" id="helloService"/>
|
配置的方式只能进行单一的接口配置,如果有多个服务,那么就很繁琐了。
协议
1
| <dubbo:protocol name="dubbo" port="20880"></dubbo:protocol>
|
相关协议的介绍请移步博客
作者:xiaojin21cen
名称:dubbo 支持的9种协议
文章地址:https://blog.csdn.net/xiaojin21cen/article/details/79834222
启动时检查
1
| <dubbo:consumer check="false"/>
|
这个配置主要是用在服务的消费端,如果不配置则默认为true,Dubbo缺省会在启动的时候检查依赖的服务是否可用,不可用是会抛出异常,阻止Spring进行初始化完成,以便上线时,及时发现问题,可以通过将check的值修改为false关闭检查。
待上线时设置为true
负载均衡
Dubbo内置了4种负载均衡策略:
- RandomLoadBalance:随机负载均衡。随机的选择一个。是Dubbo的默认负载均衡策略。
- RoundRobinLoadBalance:轮询负载均衡。轮询选择一个。
- LeastActiveLoadBalance:最少活跃调用数,相同活跃数的随机。活跃数指调用前后计数差。使慢的 Provider 收到更少请求,因为越慢的 Provider 的调用前后计数差会越大。
- ConsistentHashLoadBalance:一致性哈希负载均衡。相同参数的请求总是落在同一台机器上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Reference(check=false,loadbalance="random") private HelloService helloService;
@ResponseBody @RequestMapping("/say") public String sayHello(String name){ System.out.println("响应成功"); return helloService.sayHello(name); }
@Service(loadbalance = "random") public class HelloServiceImpl implements HelloService {
@Override public String sayHello(String name) { return "hello"+name; } }
|
更多负载均衡,请移步Dubbo官方文档:
http://dubbo.apache.org/zh-cn/blog/dubbo-loadbalance.html
这个案例里面,修改端口号来模拟多台并行的服务提供方,启动服务provider和consumer,访问consumer。
解决Dubbo无法发布被事务代理的service问题
如果在服务提供者注册的类上面添加事务注解@Transactinal事务控制后,发现服务就发布不成功了,原因是因为事务控制的底层原理是为服务提供者类创建代理对象,而默认情况下Spring是基于JDK动态代理方式创建代理对象,而代理对象的完成类名为com.sunproxy.$Proxy–(后面是数字),导致Dubbo在发布服务的时候进行包匹配匹配不到。
问题展示
使用dubbo坐标版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.9</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.40</version> </dependency>
|
在applicationContext-service.xml中添加事务管理器和连接池相关配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://127.0.0.1:3306/dow_jones"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean>
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager"/>
|
然后在HelloServiceImpl类添加@Transactinal,完成后启动provider,在dubbo-admin中发现并没有服务提供者进行注册,只有一个服务消费者。
如果修改成cglib动态代理,回事什么样子?
修改配置applicationContext-service.xml
1
| <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
|
这样子就使用的是cglib代理。那么启动provider看到有注册信息。
但是它的注册信息是这样的。
1
| org.springframework.aop.SpringProxy
|
这样子还是访问不到的。
解决方案
在之前的基础上,需要在注册服务类上添加注解参数。
1 2 3 4 5 6 7 8 9
| @Service(interfaceClass = HelloService.class) @Transactional public class HelloServiceImpl implements HelloService {
@Override public String sayHello(String name) { return "hello"+name; } }
|
明确该服务实现的是HelloService.class的这个类,然后启动,查看dubbo-admin看结果,这时候发现provider注册的就是正确的接口服务了。
1 2 3 4 5
| com.hkrt.service.HelloService
dubbo:
|
另述
前面到在问题展示那里,我指定了dubbo坐标的版本号是2.6.0,博主在这个案例中发现,2.6.0以上的版本,即使使用jdk动态代理,也是能够将被事务管理的服务接口注册到zookeeper上去,并且能够成功访问。
尾言
您的支持和鼓励是我最大的动力,麻烦动动您高贵的小手,点赞,评论和收藏,感谢!