本文章中的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目录下
代码语言:javascript复制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.0WEB-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文件
代码语言:javascript复制<?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层接口和实现类
代码语言:javascript复制import com.alibaba.dubbo.config.annotation.Service;
@Service //发布服务必须使用Dubbo提供的service注解
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "hello";
}
}
web.xml
代码语言:javascript复制<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
代码语言:javascript复制<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应用(服务提供方和消费方)都需要指定一个唯一的名称-->
<dubbo:application name="dubbo_provider"></dubbo:application>
<!--指定Dubbo的注册中心,部署zookeeper的地方-->
<dubbo:registry address="zookeeper://IP:2181"></dubbo:registry>
<!--指定协议和端口,只在提供方配置-->
<dubbo:protocol name="dubbo" port="20880"></dubbo:protocol>
<!--<dubbo:service interface="com.hkrt.service.HelloService" ref="helloService" protocol="dubbo" />-->
<!--1、注解形式:指定包扫描路径,用于发布dubbo服务-->
<dubbo:annotation package="com.hkrt.service"></dubbo:annotation>
</beans>
通过maven插件启动服务provider,查看dubbo-admin发现已经存在HelloService的相关信息。
Dubbo服务消费方
新建modul作为服务提供方。dubbo_consumer
同样和provider相同的pom文件依赖。
创建一个和服务提供方相同的包package,创建controller
代码语言:javascript复制import com.alibaba.dubbo.config.annotation.Reference;
@RestController
@RequestMapping("/hello")
public class HelloController {
@Reference //订阅zookeeper,查找相关服务
private HelloService helloService;
@ResponseBody
@RequestMapping("/say")
public String sayHello(String name){
System.out.println("响应成功");
return helloService.sayHello(name);
}
}
HelloService.class
代码语言:javascript复制public interface HelloService {
public String sayHello(String name);
}
web.xml
代码语言:javascript复制<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
代码语言:javascript复制<?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">
<!-- 自动扫描包,实现支持注解的IOC -->
<context:component-scan base-package="com" />
<!-- Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />
<!-- 支持mvc注解驱动 -->
<mvc:annotation-driven />
<!--每个Dubbo应用(服务提供方和消费方)都需要指定一个唯一的名称-->
<dubbo:application name="dubbo_customer" />
<!--指定Dubbo的注册中心,部署zookeeper的地方-->
<dubbo:registry address="zookeeper://IP:2181" />
<!--1、注解形式:指定包扫描路径,用于发布dubbo服务-->
<dubbo:annotation package="com.hkrt.controller" />
</beans>
通过maven插件启动服务consumer,查看dubbo-admin发现已经存在HelloService的相关信息。
案例回顾
需要在impl类上添加注解@service
注意这里的service已经是 spring提供的 现在要使用Dubbo提供的service注解
代码语言:javascript复制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自动加载
代码语言:javascript复制### 设置###
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
### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
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
### 输出ERROR 级别以上的日志到=E://logs/error.log ###
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相关配置
包扫描
代码语言:javascript复制<dubbo:annotation package="com.hkrt.service"></dubbo:annotation>
表示包扫描,作用是扫描改包下的所有了类,存在相关的注解。
如果不使用包扫描,可以通过配置的方式进行注册发布服务。
服务提供方:
代码语言:javascript复制<!--推荐注解开发,二者选其一-->
<!--1、注解形式:指定包扫描路径,用于发布dubbo服务-->
<dubbo:annotation package="com.hkrt.service"></dubbo:annotation>
<!--2、配置形式:通过bean的方式进行服务的注册(这是服务提供方)-->
<bean class="com.hkrt.service.impl.HelloServiceImpl" id="helloService"></bean>
<dubbo:service interface="com.hkrt.service.HelloService" ref="helloService"/>
服务消费方:
代码语言:javascript复制<!--推荐注解开发,两者只存在其一-->
<!--1、注解形式:指定包扫描路径,用于发布dubbo服务-->
<dubbo:annotation package="com.hkrt.controller" />
<!--2、配置形式:配置reference进行实例,相当于<bean/>-->
<dubbo:reference interface="com.hkrt.service.HelloService" id="helloService"/>
配置的方式只能进行单一的接口配置,如果有多个服务,那么就很繁琐了。
协议
代码语言:javascript复制<dubbo:protocol name="dubbo" port="20880"></dubbo:protocol>
相关协议的介绍请移步博客
作者:xiaojin21cen 名称:dubbo 支持的9种协议 文章地址:https://blog.csdn.net/xiaojin21cen/article/details/79834222
启动时检查
代码语言:javascript复制<dubbo:consumer check="false"/>
这个配置主要是用在服务的消费端,如果不配置则默认为true,Dubbo缺省会在启动的时候检查依赖的服务是否可用,不可用是会抛出异常,阻止Spring进行初始化完成,以便上线时,及时发现问题,可以通过将check的值修改为false关闭检查。
待上线时设置为true
负载均衡
Dubbo内置了4种负载均衡策略:
- RandomLoadBalance:随机负载均衡。随机的选择一个。是Dubbo的默认负载均衡策略。
- RoundRobinLoadBalance:轮询负载均衡。轮询选择一个。
- LeastActiveLoadBalance:最少活跃调用数,相同活跃数的随机。活跃数指调用前后计数差。使慢的 Provider 收到更少请求,因为越慢的 Provider 的调用前后计数差会越大。
- ConsistentHashLoadBalance:一致性哈希负载均衡。相同参数的请求总是落在同一台机器上。
//在服务消费方配置负载均衡
@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") //发布服务必须使用Dubbo提供的service注解
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坐标版本
代码语言:javascript复制<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中添加事务管理器和连接池相关配置
代码语言:javascript复制<!--连接数据库-->
<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
代码语言:javascript复制<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
这样子就使用的是cglib代理。那么启动provider看到有注册信息。
但是它的注册信息是这样的。
代码语言:javascript复制org.springframework.aop.SpringProxy
这样子还是访问不到的。
解决方案
在之前的基础上,需要在注册服务类上添加注解参数。
代码语言:javascript复制@Service(interfaceClass = HelloService.class) //发布服务必须使用Dubbo提供的service注解
@Transactional
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "hello" name;
}
}
明确该服务实现的是HelloService.class的这个类,然后启动,查看dubbo-admin看结果,这时候发现provider注册的就是正确的接口服务了。
代码语言:javascript复制//服务
com.hkrt.service.HelloService
//服务地址
dubbo://ip:20880/com.hkrt.service.HelloService?anyhost=true&application=dubbo_provider&dubbo=2.6.0&generic=false&interface=com.hkrt.service.HelloService&methods=sayHello&pid=24892&side=provider×tamp=1603959532358
另述
前面到在问题展示那里,我指定了dubbo坐标的版本号是2.6.0,博主在这个案例中发现,2.6.0以上的版本,即使使用jdk动态代理,也是能够将被事务管理的服务接口注册到zookeeper上去,并且能够成功访问。
尾言
您的支持和鼓励是我最大的动力,麻烦动动您高贵的小手,点赞,评论和收藏,感谢!