分布式软件架构(二)RPC框架dubbo
RPC框架
什么是RPC
RPC(remote procedure cal)l,即远程过程调用。比如两台服务器A和B,A服务器上部署一个应用,B服务器上部署一个应用,A服务器上的应用想调用B服务器上的应用提供的方法,由于两个应用不在一个内存空间,不能直接调用,所以需要通过网络来表达调用的语义和传达调用的数据。RPC并不是一个具体的技术,而是指整个网络远程调用过程。
RPC是一个泛化的概念,严格来说一切远程过程调用手段都属于RPC范畴。各种开发语言都有自己的RPC框架。 Java中的RPC框架比较多,广泛使用的有RMI、Hessian、Dubbo等。 *即dubbo 是javaRPC框架成员之一。
dubbo的简介
Apache Dubbo是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。Dubbo 提供了六大核心能力:面向接口代理的高性能RPC调用,智能容错和负载均衡,服务自动注册和发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。
dubbo 架构
节点 | 角色名称 |
---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心(监控中心并不是必须的) |
Container | 服务运行容器 |
虚线都是异步访问,实线都是同步访问
蓝紫色色虚线:在启动时完成的功能
蓝绿色虚线(实线)都是程序运行过程中执行的功能
执行步骤
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
服务注册中心
Zookeeper 是 Apache Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用 。
zookeeper 目录架构
基本工作流程
- 服务提供者(Provider)启动时: 向
/dubbo/com.foo.BarService/providers
目录下写入自己的 URL 地址 - 服务消费者(Consumer)启动时: 订阅
/dubbo/com.foo.BarService/providers
目录下的提供者 URL 地址。并向/dubbo/com.foo.BarService/consumers
目录下写入自己的 URL 地址 - 监控中心(Monitor)启动时: 订阅
/dubbo/com.foo.BarService
目录下的所有提供者和消费者 URL 地址
zookeeper的下载
zookeeper是常用的apache工具,下载可以直接上镜像即可。
zookeeper安装
上传到linux
rz
查看是否有zookeeper运行
ps -ef | grep zookeeper
若果界面干净就可以安装zookeeper
将压缩包解压解压到指定目录
tar -zxvf apache-zookeeper-3.6.2-bin.tar.gz -C /usr/local/
在zookeeper中建立data文件夹,便于存储数据
mkdir data
进入conf
cd conf
重命名 zoo_sample.cfg为 zoo.cfg。
mv zoo_sample.cfg zoo.cfg
### 或者采用
cp zoo_sample.cfg zoo.cfg #### 源文件不删除
编辑 zoo.cfg
vim zoo.cfg
保存 esc+wq!即可。
zookeeper的启动和停止
进入zookeeper的bin目录
cd bin
启动本zookeeper
./zkServer.sh start
查看zookeeper是否运行
ps -ef | grep zookeeper
####或者采用
./zkServer.sh status
停止zookeeper再次查看状态
./zkServer.sh stop
zookeeper重启
dubbo管理控制台
我们在开发时,需要知道Zookeeper注册中心都注册了哪些服务,有哪些消费者来消费这些服务。我们可以通过部署一个管理中心来实现。其实管理中心就是一个web应用,部署到tomcat即可。除了这种思路外,我们还可以参考 dubbo管理工具之dubbo-admin安装使用,这篇文章也写得非常详细,这篇文章是直接运行在将dubbo-admin直接在idea中运行。
控制台安装
如何制作war包和安装控制台也可以参考这篇文章
(1)将dubbo-admin-2.6.0.war文件复制到tomcat的webapps目录下
(2)启动tomcat,此war文件会自动解压
(3)修改WEB-INF下的dubbo.properties文件,注意dubbo.registry.address对应的值需要对应当前使用的Zookeeper的ip地址和端口号
dubbo.registry.address=zookeeper://192.168.168.128:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=guest
(4)重启tomcat
使用
默认用户root和guest,直接采用root登录即可默认密码也是root。
小猿的服务一致处于开启状态,所以在服务列表中会自动展现。
doubbo的应用
案例目录结构
父工程pom文件。
<?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.feitian</groupId>
<artifactId>mavendubbo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>dubboprovider</module>
<module>dubboconsumer</module>
<module>dubbodao</module>
</modules>
<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>
<log4j.version>1.2.17</log4j.version>
<slf4j-log4j12.version>1.7.7</slf4j-log4j12.version>
<commons-logging.version>1.2</commons-logging.version>
<mysql-connector-java.version>5.1.47</mysql-connector-java.version>
<druid.version>1.1.6</druid.version>
<mybatis-spring.version>1.3.2</mybatis-spring.version>
</properties>
<dependencyManagement>
<dependencies>
<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-core</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-aop</artifactId>
<version>${ spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${ spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${ mysql-connector-java.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${ druid.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${ mybatis-spring.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
注意父工程可能内导入的jar包原型可能不是全部都有用。
dubboprovider
目录结构
dubboprovider pom文件
<?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">
<parent>
<artifactId>mavendubbo</artifactId>
<groupId>com.feitian</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubboprovider</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>com.feitian</groupId>
<artifactId>dubbodao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 指定端口 -->
<port>8081</port>
<!-- 请求路径 -->
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
dubboprovider 需要依赖dubbodao 的jar包。
HiPyService
public interface HiPyService {
public String hiPyService(String name);
}
HiPyServiceImpl
/*@Service(protocol = "dubbo", interfaceClass = HiPyService.class, loadbalance="random")*/
@Service
public class HiPyServiceImpl implements HiPyService {
@Override
public String hiPyService(String name) {
return "port8081hello" + name;
}
}
此处需要注意的@Service注解需要用dubbo下面注解。
applicationContext-service.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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样 -->
<dubbo:application name="dubbo_provider" />
<!-- 连接服务注册中心zookeeper ip为zookeeper所在服务器的ip地址-->
<dubbo:registry address="zookeeper://192.168.168.128:2181"/>
<!-- 注册 协议和port 端口默认是20880 -->
<dubbo:protocol name="dubbo" port="20881"></dubbo:protocol>
<!-- 扫描指定包,加入@Service注解的类会被发布为服务 -->
<dubbo:annotation package="com.feitian.service" />
<!--<bean id="hiPyService" class="com.feitian.service.impl.HiPyServiceImpl"/> <dubbo:service interface="com.feitian.service.HiPyService" ref="hiPyService"/>-->
</beans>
需要服务系统者将服务提供给注册中心。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
<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>
<!-- 防止Spring内存溢出监听器 -->
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
</web-app>
在服务提供者tomcat服务器启动时,需要用监听器将服务添加到注册中心。
log4j配置文件
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ ABSOLUTE} %5p %c{ 1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ ABSOLUTE} %5p %c{ 1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
需要观察日志,加入日志配置文件,zookeeper中已经导入日志依赖,无需再次导入。
dubboconsummer
目录结构
pom文件
pom文件与dubboprovider基本一致,需要改tomcat插件端口
<?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">
<parent>
<artifactId>mavendubbo</artifactId>
<groupId>com.feitian</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubboconsumer</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- dubbo相关 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.7</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<!--<dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 指定端口 -->
<port>8083</port>
<!-- 请求路径 -->
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
controller
@Controller
@RequestMapping("/hiPPyController")
public class PyController {
/*@Reference(check = false,loadbalance = "random")*/
/* @Autowired*/
@Reference
private HiPyService hiPyService;
@RequestMapping("/hiPython")
@ResponseBody
public String hiPython(String name){
return hiPyService.hiPyService(name);
}
}
HiPyService
public interface HiPyService {
public String hiPyService(String name);
}
需要将dubboprovider 的HiPyService接口拷贝一份,但拷贝是否构成了整个案例的代码冗余呢?
applicationContext_web.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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样 -->
<dubbo:application name="dubbo_consummer" />
<!-- 连接服务注册中心zookeeper ip为zookeeper所在服务器的ip地址-->
<dubbo:registry address="zookeeper://192.168.168.128:2181"/>
<!-- 扫描指定包,加入@Service注解的类会被发布为服务 -->
<dubbo:annotation package="com.feitian.controller" />
<!--直接注入即可相当于替代了包扫描和reference标签-->
<!--<dubbo:reference id="hiPyService" interface="com.feitian.service.HiPyService" />-->
<!--启动不检查-->
<dubbo:consumer check="false"/>
</beans>
此处注意的是不再需要声明远程调用协议,如果开启了启动不检查,那么就无需顾忌provider和consummer的顺序了。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext_web.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
一般在前端控制器初始化时,将服务需求注册到服务中心。
日志文件
日志文件保持与provider一致即可。
dubbodao
文件目录
dubbodao pom配置文件
<?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">
<parent>
<artifactId>mavendubbo</artifactId>
<groupId>com.feitian</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbodao</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- dubbo相关 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.7</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
</dependencies>
</project>
applicationContext-dao.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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc" 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://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="jdbc.properties"/>
<!--配置数据源信息,使用druid连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务控制的注解支持-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
此处要加入事务管理器和数据库的一些配置,注意此处只是为了生成数据源进行事务管理,而非真正的操作数据库。
properties配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
可以看出案例成功运行。
dubbo配置文件解析
包扫描
<!-- 扫描指定包,加入@Service注解的类会被发布为服务 -->
<dubbo:annotation package="com.feitian.service" />
服务提供者和服务消费者都需要配置,表示包扫描,作用是扫描指定包(包括子包)下的类。如果不使用包扫描,也可以通过如下配置的方式来发布服务:
<bean id="hiPyService" class="com.feitian.service.impl.HiPyServiceImpl"/>
<dubbo:service interface="com.feitian.service.HiPyService" ref="hiPyService"/>
相当于替代了
<dubbo:annotation package="com.feitian.service" />
作为服务消费者,可以通过如下配置来引用服务:
<dubbo:reference id="hiPyService" interface="com.feitian.service.HiPyService" />
在接口上加上@Autowired
@Autowired
private HiPyService hiPyService;
相当于替代了了dubbo的@Reference标签。
上面这种方式发布和引用服务,一个配置项(dubbo:service、dubbo:reference)只能发布或者引用一个服务,如果有多个服务,这种方式就比较繁琐了。推荐使用包扫描方式。
dubbo协议
一般dubbo的默认protocol为dubbo,一般建议在provider方指定协议。
<!-- 注册 协议和port 端口默认是20880 -->
<dubbo:protocol name="dubbo" port="20881"></dubbo:protocol>
Dubbo支持的协议有:dubbo、rmi、hessian、http、webservice、rest、redis等。推荐使用的是dubbo协议。
dubbo 协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。 不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。也可以在同一个工程中配置多个协议,不同服务可以使用不同的协议,例如:
<!-- 多协议配置 -->
<dubbo:protocol name="dubbo" port="20881" />
<dubbo:protocol name="rmi" port="1099" />
<!-- 使用dubbo协议暴露服务 -->
<dubbo:service interface="com.feitian.service.HelloService" ref="helloService" protocol="dubbo" />
<!-- 使用rmi协议暴露服务 -->
<dubbo:service interface="com.feitian.service.DemoService" ref="demoService" protocol="rmi" />
也可以通过在注解中配置协议,指定所要的RPC协议
@Service(protocol = "rmi")
public class HiPyServiceImpl implements HiPyService {
@Override
public String hiPyService(String name) {
return "hello" + name;
}
}
启动时检查
默认情况下 服务提供方和服务消费方启动有先后顺序,会检查对应的服务提供方有没有注册服务,若没有就会报错,这种情况就给开发者带来了困扰,所以在开发阶段建议停止检查则在服务消费方引入一下配置:
<dubbo:consumer check="false"/>
也可用 @Reference(check = false)来替代删除xml配置文件。如果不配置默认check值为true。Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题。可以通过将check值改为false来关闭检查。
建议在开发阶段将check值设置为false,在生产环境下改为true。
负载均衡
负载均衡(Load Balance):本质上就是将请求分摊到多个操作单元上进行执行,从而共同完成工作任务。
在集群负载均衡时,Dubbo 提供了多种均衡策略(包括随机、轮询、最少活跃调用数、一致性Hash),缺省为random随机调用。
配置负载均衡策略,既可以在服务提供者一方配置,也可以在服务消费者一方配置,如下:
负载均衡小案例
服务消费方
@Controller
@RequestMapping("/hiPPyController")
public class PyController {
@Reference(check = false,loadbalance = "random")
/* @Autowired*/
private HiPyService hiPyService;
@RequestMapping("/hiPython")
@ResponseBody
public String hiPython(String name){
return hiPyService.hiPyService(name);
}
}
服务提供方
@Service(protocol = "dubbo",loadbalance="random")
/*@Service*/
@Transactional
public class HiPyServiceImpl implements HiPyService {
@Override
public String hiPyService(String name) {
return "port8081hello" + name;
}
}
注意要修改dubbo协议端口
由此就可以是此案consumer随机调用provider提供的service。
Dubbo的发布与事务代理问题
如果在服务提供者类上加入@Transactional事务控制注解后,服务就发布不成功了。 原因是事务控制的底层原理是为服务提供者类创建代理对象,而默认情况下Spring是基于JDK动态代理方式创建代理对象,而此代理对象的完整类名为com.sun.proxy.$Proxy42(最后两位数字不是固定的),导致Dubbo在发布服务前进行包匹配时无法完成匹配,进而没有进行服务的发布。
修改案例,引入dao
修改dubboprovider的HiPyServiceImpl
@Service(protocol = "dubbo", loadbalance="random")
/*@Service*/
@Transactional
public class HiPyServiceImpl implements HiPyService {
@Override
public String hiPyService(String name) {
return "port8081hello" + name;
}
}
启动provider和consummer
访问报错
其奥妙就在这个类中AnnotationBean这个类中。
dubbo对于服务的发布最终是通过AnnotationBean的postProcessAfterInitialization方法进行处理。
当以debug方式启动时,首先会进入下年的方法
然后会进入
在两种方法中都调用了下面的方法
private boolean isMatchPackage(Object bean) {
if (this.annotationPackages != null && this.annotationPackages.length != 0) {
String beanClassName = bean.getClass().getName();
String[] arr$ = this.annotationPackages;
int len$ = arr$.length;
for(int i$ = 0; i$ < len$; ++i$) {
String pkg = arr$[i$];
if (beanClassName.startsWith(pkg)) {
return true;
}
}
return false;
} else {
return true;
}
}
调试过程中发现有如下问题
解决问题的关键
指定cglib代理
指定代理接口
@Service(protocol = “dubbo”, interfaceClass = HiPyService.class, loadbalance=”random”)
@Transactional
public class HiPyServiceImpl implements HiPyService {@Override
public String hiPyService(String name) {
return "port8081hello" + name;
}
}
发布成功
访问发信可以正常调用。
dubbo常见问题
思考一: 上面的Dubbo入门案例中我们是将HiPyService接口从服务提供者工程(dubboprovider)复制到服务消费者工程(dubboconsumer)中,这种做法是否合适?还有没有更好的方式?
答: 同一个接口被复制了两份,项目代码冗余严重,不利于后期维护。更好的方式是单独创建一个maven工程,将此接口创建在这个maven工程中。需要依赖此接口的工程只需要在自己工程的pom.xml文件中引入maven坐标即可。
思考二: 在服务消费者工程(dubboconsumer)中只是引用了HiPyService接口,并没有提供实现类,Dubbo是如何做到远程调用的?
答: Dubbo底层是基于代理技术为HiPyService接口创建代理对象,远程调用是通过此代理对象完成的。可以通过开发工具的debug功能查看此代理对象的内部结构。另外,Dubbo实现网络传输底层是基于Netty框架完成的。
思考三: 上面的Dubbo入门案例中我们使用Zookeeper作为服务注册中心,服务提供者需要将自己的服务信息注册到Zookeeper,服务消费者需要从Zookeeper订阅自己所需要的服务,此时Zookeeper服务就变得非常重要了,那如何防止Zookeeper单点故障呢?
答: Zookeeper其实是支持集群模式的,可以配置Zookeeper集群来达到Zookeeper服务的高可用,防止出现单点故障。
还没有评论,来说两句吧...