Dubbo-基础入门篇
学习目标
- 分布式系统中的相关概念
- Dubbo概述
- Dubbo快速入门
- Dubbo高级特性
Dubbo
是一款高性能、轻量级的开源Java RPC框架,提供面向接口代理的高性能RPC调用、智能负载均衡、服务自动注册和发现、运行期流量调度、可视化服务治理和运维等功能。
一、分布式系统中的相关概念
1. 大型互联网项目架构目标
(1)特点
互联网项目特点:
- 用户多
- 流量大
- 并发高
- 海量数据
- 易受攻击
- 功能繁琐
- 变更快
(2)衡量网站的性能指标
- 响应时间:指执行一个请求从开始到最后收到响应数据所花费的总体时间。
- 并发数:指系统同时能处理的请求数量。
- 并发连接数:指的是客户端向服务器发起请求,并建立了TCP连接。每秒钟服务器连接的总TCP数量。
- 请求数:也称为QPS(Query Per Second)指每秒多少请求。
- 并发用户数:单位时间内有多少用户。
- 吞吐量:指单位时间内系统能处理的请求数量。
- Qps:Query Per Second每秒查询数。
- TPS:Transactions Per Second每秒事务数。
- 一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数。
- 一个页面的一次访问,只会形成一个TPS;但一次页面请求,可能产生多次对服务器的请求,就会有多个QPS。
- QPS>=并发连接数>= TPS
(3)目标
- 高性能:提供快速的访问体验。
- 高可用:网站服务一直可以正常访问。
- 可伸缩:通过硬件增加/减少,提高/降低处理能力。
- 高可扩展:系统间耦合低,方便的通过新增/移除方式,增加/减少新的功能/模块。
- 安全性:提供网站安全访问和数据加密,安全存储等策略。
- 敏捷性:随需应变,快速响应。
2. 集群和分布式
- 集群:很多“人”一起,干一样的事。
- 一个业务模块,部署在多台服务器上。
- 集群就是同一个业务模块搭载到多个服务器上,配合负载均衡,一个服务器坏了,另一个服务器依旧可以使用这个部署的模块。
- 分布式:很多“人”一起,千不一样的事。这些不一样的事,合起来是一件大事。
- 一个大的业务系统,拆分为小的业务模块,分别部署在不同的机器上。
3. 架构演进
(1)单体架构
单体架构:把所有的功能模块都放在一起,部署在服务器上,形成一个app。
部署在一个tomcat服务器上,叫做单机单体架构;也可以部署在多个服务器上,称为多机单体架构。
优点:
- 简单:开发部署都很方便,小型项目首选。
缺点:
- 项目启动慢:同时启动所有模块,项目启动时间长。
- 可靠性差:所有模块部署在一起,一个功能模块挂了,其他相关模块也跟着挂。
- 可伸缩性差:如果某个模块访问量很大,期望对该模块进行扩展,但所有模块部署在一起没法扩展,只能部署两台服务器才可扩展。
- 扩展性和可维护性差
- 性能低
(2)垂直架构
- 垂直架构是指将单体架构中的多个模块拆分为多个独立的项目。形成多个独立的单体架构。
- 垂直架构存在的问题:重复功能太多。(各模块之间无法通信,无法远程调用)
(3)分布式架构
- 分布式架构是指在垂直架构的基础上,将公共业务模块抽取出来,作为独立的服务,供其他调用者消费,以实现服务的共享和重用。
- 分布式架构:将重复的公共模块服务抽取出来,形成一个独立的服务。需要用到该功能模块的服务直接调用它即可。此时这个公共模块被称为服务提供者,而调用它的app模块被称为服务消费者。
- RPC:Remote Procedure Call 远程过程调用。有非常多的协议和技术来都实现了RPC的过程。比如:HTTPREST风格,JavaRMl规范、WebService SOAP协议.Hession等等。
分布式架构存在的问题:
- 服务提供方一旦产生变更,所有消费方都需要变更。
(4)SOA架构
- SOA:(Service-Oriented Architecture,面向服务的架构)是一个组件模型,它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和契约联系起来。
- ESB:(Enterparise Servce Bus) 企业服务总线,服务中介。主要是提供了一个服务与服务之间的交互和治理功能。ESB包含的功能如:负载均衡,流量控制,加密处理,服务的监控,异常处理,监控告急等等。
(5)微服务架构
- 微服务架构是在SOA上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。
- 微服务架构=80%的SOA服务架构思想+100%的组件化架构思想+80%的领域建模思想
特点:
- 服务实现组件化:开发者可以自由选择开发技术。也不需要协调其他团队。
- 服务之间交互一般使用REST API。
- 去中心化:每个微服务有自己私有的数据库持久化业务数据。
- 自动化部署:把应用拆分成为一个一个独立的单个服务,方便自动化部署、测试、运维。
Dubbo是SOA时代的产物,SpringCloud是微服务时代的产物。
二、Dubbo概述
1. Dubbo概念
Dubbo是阿里巴巴公司开源的一个高性能、轻量级的Java RPC框架。
致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
2. Dubbo架构
节点角色说明:
- Provider:暴露服务的服务提供方
- Container:服务运行容器
- Consumer:调用远程服务的服务消费方
- Registry::服务注册与发现的注册中心
- Monitor:统计服务的调用次数和调用时间的监控中心
3. Dubbo运行流程
扩展:Dubbo是如何解析@Service、@Reference的?
三、dubbo快速入门
Zookeeper是Apacahe Hadoop的子项目,是一个树型的目录服务,支持变更推送,适合作为Dubbo服务的注册中心,工业强度较高,可用于生产环境,并推荐使用。
1. Zookeeper安装
(1)下载安装
① 环境准备
ZooKeeper服务器是用Java创建的,它运行在JVM之上。需要安装JDK 8或更高版本。
② 上传
将下载的ZooKeeper放到/opt/zookeeper目录下
# 上传zookeeper安装包到服务器 put f:/setup/apache-zookeeper-3.5.6-bin.tar.gz # 在/opt目录下创建zookeeper目录 mkdir /opt/zookeeper # 将zookeeper安装包移动到 /opt/zookeeper mv apache-zookeeper-3.5.6-bin.tar.gz /opt/zookeeper/
③ 解压
将tar包解压到/opt/zookeeper目录下
tar -zxvf apache-zookeeper-3.5.6-bin.tar.gz -C /opt/zookeeper/
(2) 配置启动
① 配置zoo.cfg
进入到conf目录拷贝一个zoo_sample.cfg并完成配置
# 进入到conf目录 cd /opt/zookeeper/apache-zookeeper-3.5.6-bin/conf/ # 拷贝 (配置文件必须叫zoo.cfg才能生效) cp zoo_sample.cfg zoo.cfg
修改zoo.cfg
# 打开目录 cd /opt/zookeeper/ # 创建zookeeper存储目录 mkdir zkdata # 修改zoo.cfg vim /opt/zookeeper/apache-zookeeper-3.5.6-bin/conf/zoo.cfg # 修改存储目录 将zk默认存储数据的临时目录dataDir=/tmp/zookeeper,修改为:dataDir=/opt/zookeeper/zkdata
2181端口默认是ZK的客户端端口号,记得防火墙把这个端口给开放出来,不然客户端连接不上。
# 添加2181端口到防火墙允许列表 firewall-cmd --zone=public --add-port=2181/tcp --permanent # 立即生效 firewall-cmd --reload # 查看Linux防火墙允许通过的端口号 firewall-cmd --zone=public --list-ports
② 启动ZooKeeper
cd /opt/zookeeper/apache-zookeeper-3.5.6-bin/bin/ # 启动 ./zkServer.sh start
看到上图表示ZooKeeper已成功启动。
③ 查看ZooKeeper状态
# 查看ZooKeeper状态 ./zkServer.sh status # 停止ZooKeeper ./zkServer.sh stop # 开启ZooKeeper ./zkServer.sh start # 重启ZooKeeper ./zkServer.sh restart
zookeeper没有启动。
zookeeper启动成功。standalone代表zk没有搭建集群,现在是单节点。
2. Spring和SpringMVC整合-本地调用
1)创建一个空项目dubbo-pro,里面新建两个maven项目,dubbo-service (producer) 和 dubbo-web (consumer)
项目结构:
2)复制下面的依赖到dubbo-web和dubbo-service的pom.xml中。注意web模块是最后打成war包部署到tomcat容器上的,需要导入启动tomcat的插件依赖。而service模块打成jar包,不需要启动tomcat,因此不需要复制导入tomcat的插件依赖。
dubbo-web:
<?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.itheima</groupId> <artifactId>dubbo-web</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <!-- 指定编码及版本 --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> <java.version>1.8</java.version> <spring.version>5.1.9.RELEASE</spring.version> <springmvc.version>5.1.9.RELEASE</springmvc.version> <dubbo.version>2.7.4.1</dubbo.version> <zookeeper.version>4.0.0</zookeeper.version> </properties> <dependencies> <!-- servlet3.0规范的坐标 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- spring的坐标 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- springmvc的坐标 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${springmvc.version}</version> </dependency> <!-- 日志 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <!-- Dubbo的起步依赖,版本2.7之后统一为org.apache.dubbo --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> <!-- ZooKeeper客户端实现 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>${zookeeper.version}</version> </dependency> <!-- ZooKeeper客户端实现 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>${zookeeper.version}</version> </dependency> <!-- 依赖service模块 --> <dependency> <groupId>com.itheima</groupId> <artifactId>dubbo-service</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <!-- tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8000</port> <path>/</path> </configuration> </plugin> <!--<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.10.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>${maven.compiler.encoding}</encoding> </configuration> </plugin>--> </plugins> </build> </project>
dubbo-service:
<?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.itheima</groupId> <artifactId>dubbo-service</artifactId> <version>1.0-SNAPSHOT</version> <!-- 指定编码及版本 --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> <java.version>1.8</java.version> <spring.version>5.1.9.RELEASE</spring.version> <springmvc.version>5.1.9.RELEASE</springmvc.version> <dubbo.version>2.7.4.1</dubbo.version> <zookeeper.version>4.0.0</zookeeper.version> </properties> <dependencies> <!-- servlet3.0规范的坐标 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- spring的坐标 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- springmvc的坐标 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- 日志 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <!-- Dubbo的起步依赖,版本2.7之后统一为org.apache.dubbo --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> <!-- ZooKeeper客户端实现 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>${zookeeper.version}</version> </dependency> <!-- ZooKeeper客户端实现 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>${zookeeper.version}</version> </dependency> <!-- 公共接口模块 --> <!--<dependency> <groupId>com.itheima</groupId> <artifactId>dubbo-interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency>--> </dependencies> </project>
3)service模块中创建com.itheima.service.UserService和com.itheima.service.impl.UserServiceImpl:
import org.springframework.stereotype.Service; public interface UserService { public String sayHello(); }
import com.itheima.service.UserService; @Service public class UserServiceImpl implements UserService { public String sayHello() { return "hello dubbo!~"; } }
4)创建Spring的核心配置文件applicationContext.xml和log4j.properties:
<?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" xmlns:context="http://www.springframework.org/schema/context" 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.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd "> <!-- 配置包扫描,为了让Spring加载到@Service,扫描父包即可 --> <context:component-scan base-package="com.itheima.service"/> </beans>
# DEBUG < INFO < WARN < ERROR < FATAL # Global logging configuration log4j.rootLogger=info, stdout,file # My logging configuration... #log4j.logger.com.tocersoft.school=DEBUG #log4j.logger.net.sf.hibernate.cache=debug ## Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=../logs/iask.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
5)dubbo-web模块中要引入dubbo-service模块的依赖,创建com.itheima.controller.UserController,创建SpringMVC核心配置文件springmvc.xml和log4j.properties,创建webapp、WEB-INF和web.xml:
<!-- 依赖service模块 --> <dependency> <groupId>com.itheima</groupId> <artifactId>dubbo-service</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
import com.itheima.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { // 注入Service @Autowired private UserService userService; @RequestMapping("/sayHello") public String sayHello() { return userService.sayHello(); } }
<?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" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" 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://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启mvc注解驱动 --> <mvc:annotation-driven/> <!-- 定义包扫描 --> <context:component-scan base-package="com.itheima.controller"/> </beans>
# DEBUG < INFO < WARN < ERROR < FATAL # Global logging configuration log4j.rootLogger=info, stdout,file # My logging configuration... #log4j.logger.com.tocersoft.school=DEBUG #log4j.logger.net.sf.hibernate.cache=debug ## Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=../logs/iask.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <!-- 加载spring容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/applicationContext*.xml</param-value> </context-param> <!-- 配置监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 加载SpringMVC核心控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
6)先把依赖的service模块通过maven插件进行install,再对web模块进行run
如果出现Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project dubbo-service: Compilation failure错误。
解决办法是 settings -> build,Execution —> compiler -->java compiler 修改jdk版本,可参考:https://blog.csdn.net/qq_39968986/article/details/113461797
如果还不行可在pom.xml指定项目compiler-plugin的版本,jdk的版本:
<!-- 在 </dependencies>标签下方添加 --> <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> </configuration> </plugin> </plugins> </build>
测试访问http://localhost:8000/user/sayHello.do
3. 服务提供者
上面只是做了基本的Spring和SpringMVC整合操作,web模块本地依赖于service模块,利用maven进行了模块拆分和引用。
此时并不是分布式架构,而是对单体架构进行了模块化开发。
因为分布式需要各模块间可以进行RPC远程调用(protocol://ip:port/access_path),每个模块能够独立地启动,独立地对外提供服务。
接下来我们将它们改造成分布式SOA架构,先改造service模块,再改造web模块。
服务之间的调用使用注册中心,同步调用使用Feign,异步调用使用MQ。
1)先引入Dubbo依赖坐标 和 两个Zookeeper注册中心客户端依赖坐标。
<!-- Dubbo的起步依赖,版本2.7之后统一为org.apache.dubbo --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> <!-- ZooKeeper客户端实现 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>${zookeeper.version}</version> </dependency> <!-- ZooKeeper客户端实现 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>${zookeeper.version}</version> </dependency>
2)为了使dubbo-service对外独立启动,需要将默认jar的打包方式改成war,再添加一个tomcat的启动插件,将端口设置为9000。
<groupId>com.itheima</groupId> <artifactId>dubbo-service</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> ... <build> <plugins> <!-- tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>9000</port> <path>/</path> </configuration> </plugin> </plugins> </build>
3)在实现类上使用@Service(org.apache.dubbo.config.annotation.Service),Dubbo 2.7.7以后@Service被@DubboService替代。
import com.itheima.service.UserService; import org.apache.dubbo.config.annotation.Service; //@Service // bean定义,将该类的对象创建出来,放到Spring的IOC容器中。 @Service // 将这个类提供的方法(服务)对外发布。将访问地址(ip、端口、访问路径)注册到ZK注册中心 public class UserServiceImpl implements UserService { public String sayHello() { return "hello dubbo!~"; } }
扩展:Dubbo:@DubboService和@Service、@DubboReference和@Reference的区别和关系,Dubbo学习之DubboService
4)接着在applicationContext.xml中先引入dubbo的约束信息。
<beans xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xsi:schemaLocation=" http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd "> <!-- configuration information... --> </beans>
再配置Dubbo项目的名称、注册中心的地址、dubbo包扫描路径。
<?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" xmlns:context="http://www.springframework.org/schema/context" 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.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd "> <!-- 配置包扫描,为了让Spring加载到@Service,扫描父包即可 --> <context:component-scan base-package="com.itheima.service"/> <!-- dubbo的配置 --> <!-- 1.配置项目的名称(唯一),一般为项目名称 --> <dubbo:application name="dubbo-service"/> <!-- 2.配置注册中心的地址 --> <dubbo:registry address="zookeeper://192.168.8.100:2181"/> <!-- 3.配置dubbo包扫描 --> <dubbo:annotation package="com.itheima.service.impl"/> </beans>
5)将dubbo-web模块的webapp目录复制到dubbo-service模块的main文件夹下,将web.xml中SpringMVC的配置信息去掉。web.xml的version版本如果是4.0,一定要删去Lister监听器。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <!-- 加载spring容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/applicationContext*.xml</param-value> </context-param> <!-- 配置监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
6)运行dubbo-service,不报错即可。
4. 服务消费者
1)之前dubbo-web模块的pom.xml中对service模块的本地调用可以先注释掉了,这里也是需要引入dubbo的那三个依赖坐标。
<!-- 依赖service模块 --> <!--<dependency> <groupId>com.itheima</groupId> <artifactId>dubbo-service</artifactId> <version>1.0-SNAPSHOT</version> </dependency>-->
此时UserController会报错,先创建一个UserService接口不让它报错。
public interface UserService { String sayHello(); }
web.xml中去掉Spring的配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <!-- 加载SpringMVC核心控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
2)使用dubbo远程注入注解@Reference(org.apache.dubbo.config.annotation.Reference)代替本地注入注解@Autowired。
import com.itheima.service.UserService; import org.apache.dubbo.config.annotation.Reference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { // 注入Service //@Autowired // 本地注入 /* dubbo 2.7版本后 @Reference已弃用,建议使用@DubboReference 1.从zookeeper注册中心获取UserService的访问url 2.进行远程调用RPC 3.将结果封装为一个代理对象。给变量赋值 */ @Reference // 远程注入 private UserService userService; @RequestMapping("/sayHello") public String sayHello() { return userService.sayHello(); } }
3)在springmvc.xml中配置dubbo和注册中心地址。
<?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" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" 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://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启mvc注解驱动 --> <mvc:annotation-driven/> <!-- 定义包扫描 --> <context:component-scan base-package="com.itheima.controller"/> <!-- dubbo的配置 --> <!-- 1.配置项目的名称(唯一),一般为项目名称 --> <dubbo:application name="dubbo-web"/> <!-- 2.配置注册中心的地址 --> <dubbo:registry address="zookeeper://192.168.8.100:2181"/> <!-- 3.配置dubbo包扫描 --> <dubbo:annotation package="com.itheima.controller"/> </beans>
4)运行dubbo-web,重新访问http://localhost:8000/user/sayHello.do,可以打印输出结果,但是后台报了一个本地22222端口占用的错。
qos其实是dubbo用于做远程监控的一个组件,而这两个模块都会有一个qos,都部署在同一台机器上就会出现这个错误。但实际生成环境下,这两个模块会部署在不同的服务器上,就不会报错了。
我们可以在核心配置文件springmvc.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" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" 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://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启mvc注解驱动 --> <mvc:annotation-driven/> <!-- 定义包扫描 --> <context:component-scan base-package="com.itheima.controller"/> <!-- dubbo的配置 --> <!-- 1.配置项目的名称(唯一),一般为项目名称 --> <dubbo:application name="dubbo-web"> <dubbo:parameter key="qos.port" value="33333"/> </dubbo:application> <!-- 2.配置注册中心的地址 --> <dubbo:registry address="zookeeper://192.168.8.100:2181"/> <!-- 3.配置dubbo包扫描 --> <dubbo:annotation package="com.itheima.controller"/> </beans>
5)重启dubbo-web服务,重新访问http://localhost:8000/user/sayHello.do,可以打印输出结果,后台也不报错了。
6)思考:在解除了web与service模块的依赖关系后,web中的UserService接口就会报错,解决方法是在web和service中定义同样声明的接口,包名也要一样。但是实际开发环境下,这两个模块可能是不同团队开发的,声明很多个接口就无法保证两边一致。
解决方案:将公共接口抽取出来,形成独立的模块,让web和service分别去依赖这个公共接口。
7)在dubbo-pro模块下新建一个maven模块dubbo-interface,然后在里面创建com.itheima.service.UserService接口。
之后进行修改pom.xml后,对dubbo-interface模块进行maven install。
<?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.itheima</groupId> <artifactId>dubbo-interface</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> <java.version>1.8</java.version> </properties> </project>
8)删除web和service模块中的UserService接口,在各自的pom.xml中添加dubbo-interface公共接口依赖坐标。
dubbo-web的pom.xml:
<?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.itheima</groupId> <artifactId>dubbo-web</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <!-- 指定编码及版本 --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> <java.version>1.8</java.version> <spring.version>5.1.9.RELEASE</spring.version> <springmvc.version>5.1.9.RELEASE</springmvc.version> <dubbo.version>2.7.4.1</dubbo.version> <zookeeper.version>4.0.0</zookeeper.version> </properties> <dependencies> <!-- servlet3.0规范的坐标 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- spring的坐标 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- springmvc的坐标 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${springmvc.version}</version> </dependency> <!-- 日志 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <!-- Dubbo的起步依赖,版本2.7之后统一为org.apache.dubbo --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> <!-- ZooKeeper客户端实现 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>${zookeeper.version}</version> </dependency> <!-- ZooKeeper客户端实现 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>${zookeeper.version}</version> </dependency> <!-- 本地依赖service模块 --> <!--<dependency> <groupId>com.itheima</groupId> <artifactId>dubbo-service</artifactId> <version>1.0-SNAPSHOT</version> </dependency>--> <!-- 公共接口模块 --> <dependency> <groupId>com.itheima</groupId> <artifactId>dubbo-interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <!-- tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8000</port> <path>/</path> </configuration> </plugin> <!--<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.10.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>${maven.compiler.encoding}</encoding> </configuration> </plugin>--> </plugins> </build> </project>
dubbo-service的pom.xml:
<?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.itheima</groupId> <artifactId>dubbo-service</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <!-- 指定编码及版本 --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> <java.version>1.8</java.version> <spring.version>5.1.9.RELEASE</spring.version> <springmvc.version>5.1.9.RELEASE</springmvc.version> <dubbo.version>2.7.4.1</dubbo.version> <zookeeper.version>4.0.0</zookeeper.version> </properties> <dependencies> <!-- servlet3.0规范的坐标 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- spring的坐标 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- springmvc的坐标 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- 日志 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <!-- Dubbo的起步依赖,版本2.7之后统一为org.apache.dubbo --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> <!-- ZooKeeper客户端实现 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>${zookeeper.version}</version> </dependency> <!-- ZooKeeper客户端实现 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>${zookeeper.version}</version> </dependency> <!-- 公共接口模块 --> <dependency> <groupId>com.itheima</groupId> <artifactId>dubbo-interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <!-- tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>9000</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
为UserServiceImpl重新导包,修改返回的字符串,重启两个模块并访问。
import com.itheima.service.UserService; import org.apache.dubbo.config.annotation.Service; //@Service // bean定义,将该类的对象创建出来,放到Spring的IOC容器中。 // 2.7.7提供了@EnableDubbo @Service // 将这个类提供的方法(服务)对外发布。将访问地址(ip、端口、访问路径)注册到ZK注册中心 public class UserServiceImpl implements UserService { public String sayHello() { return "Hello Dubbo RPC Zookeeper!~"; } }
dubbo远程调用服务成功!