软件结构的演进
软件架构的发展经历了单体架构、垂直架构、SOA架构、微服务架构的演进过程。
1、单体架构
单体架构中,所有功能代码全部都在同一个部署包中。
单体架构优点:
架构简单,前期开发成本低、开发周期短,适合小型项目(OA、CRM、ERP 企业内部应用)
单体架构缺点:
1.1.代码模块边界很混乱,只会越来越乱! 1.2.随着代码的增加,构建和部署的时间也会增加 1.3.技术革新很难,想要引入新的框架或技术平台非常困难。 1.4.团队协作难度高,如多人使用 git 很可能在同一个功能上,多人同时进行了修改,作为一个大而全的项目,可能个人只是需要开发其中一个小的模块的需求,却需要导入整个项目全量的代码
2、垂直架构
按照业务进行切割,形成小的单体项目。
垂直架构优点:技术栈可扩展(不同的系统可以用不同的编程语言编写)
垂直架构缺点:
2.1 垂直架构中每一个系统还是单体架构
2.2 项目之间功能冗余、数据冗余,多个项目中可能要编写重复的功能代码,加载重复的数据。
项目之间没有联系,导致代码重复,比如三个过程都会要用户和商品代码。
3、SOA架构
SOA全称为 "Service-Oriented Architecture",即面向服务的架构。
它可以根据需求通过网络对松散耦合的粗粒度应用组件(服务)进行分布式部署、组合和使用。一个服务通常以独立的形式存在于操作系统进程中。
SOA架构的优点
① 可重用性高 重复功能或模块抽取为服务,提高开发效率。 ② 维护成本低 修改一个功能,可以直接修改一个项目,单独部署。 ③ 可扩展性强 不同的系统可以用不同的语言、不同的技术进行编写。并且可以按照需要,对不同的系统进行集群扩充
SOA架构的缺点
抽取服务的粒度大(相对微服务而言) 各系统之间业务不同,很难确认功能或模块是重复的
4、微服务架构(重要!!!)
微服务是在 SOA 上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”。
原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。
微服务的优点
① 服务拆分粒度更细,遵循单一职责原则,有利于提高开发效率。
② 可采用Http协议进行服务间的调用
③ 可以针对不同服务制定对应的优化方案,适用于互联网时代,产品迭代周期更短。
微服务的缺点
① 对开发团队技术要求高
RPC和Dubbo
1、远程过程调用
RPC(Remote Procedure Call Protocol)——远程过程调用协议,而是指整个网络远程调用过程。
客户端和服务端可以运行在不同的JVM中,Client只需要引入接口,接口的实现以及运行时需要的数据都在Server端,RPC的主要依赖技术是序列化、反序列化和传输协议,JAVA里对应的就是对象的序列化、反序列化以及序列化后数据的传输。
比如两台服务器A和B,A服务器上部署一个应用,B服务器上部署一个应用,A服务器上的应用想调用B服务器上的应用提供的方法,
由于两个应用不在一个内存空间,不能直接调用,所以需要通过网络来表达调用的语义和传达调用的数据。
之前自己写过通过socket的远程调用,,真的很折磨
2、Dubbo中节点角色
Java中的RPC框架比较多,如Dubbo,注意下图的数字
服务容器Container负责启动,加载,运行服务提供者。
服务提供者Provider在启动时,向Registry注册自提供的服务。
服务消费者Consumer在启动时,向Registry订阅自己所需的服务(发现服务),后续直接调用 Provider ,无需经过 Registry。
注册中心Registry返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
服务消费者,从提供者地址列表中,基于负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
监控中心Monitor:统计服务的调用次数和调用时间的监控中心
3、Dubbo
Apache Dubbo是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。
Dubbo官网地址:Apache Dubbo
Dubbo提供了三大核心能力:
- 面向接口的远程方法调用
- 智能容错和负载均衡
- 服务自动注册和发现
Dubbo和Nacos
1、SpringBoot整合Dubbo和Nacos
现在 Dubbo 已经提供了 dubbo-spring-boot-project 项目,集成到 Spring Boot 体系中
springboot 版本和nacos 版本不一致导致项目启动报错,这是我的一种对的版本号可参考
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring-boot.version>2.3.5.RELEASE</spring-boot.version>
<dubbo.version>2.7.5</dubbo.version>
<nacos-client.version>1.4.0</nacos-client.version>
</properties>
<!-- dubbo 2.7.x引入-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!-- nacos 2.7.x引入-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>${nacos-client.version}</version>
</dependency>
创建三个项目
其中 api模块被 consumer 和 provider 两个模块共同依赖,存储 RPC 通信使用的 API 接口。
dubbo-demo-api 项目:服务接口,定义 Dubbo Service API 接口,提供给消费者使用
dubbo-demo-provider 项目:服务提供者,实现 dubbo-demo-api 项目定义的接口,提供相应的服务
dubbo-demo-consumer 项目:服务消费者,会调用dubbo-demo-provider 项目提供的 Dubbo Service 服务。
使用@Service【暴露服务】使用@Reference【引用服务】
2、dubbo-demo-api
dubbo-demo-api 项目:服务接口
@Data public class BookDTO implements Serializable { /** * 书籍编号 */ private Integer id; /** * 名称 */ private String name; /** * 类别 */ private String type; }
要实现 java.io.Serializable 接口。因为,Dubbo RPC 会涉及远程通信,需要序列化和反序列化
public interface BookRpcService { //根据书籍编号,获得书籍信息 BookDTO get(Integer id); }
3、@EnableDubbo
通过 @EnableDubbo
可以在指定的包名下(通过 scanBasePackages
),或者指定的类中(通过 scanBasePackageClasses
)扫描 Dubbo 的服务提供者(以 @Service
标注)以及 Dubbo 的服务消费者(以 Reference
标注)。
扫描到 Dubbo 的服务提供方和消费者之后,对其做相应的组装并初始化,并最终完成服务暴露或者引用的工作。
4、dubbo-demo-provider
dubbo-demo-provider 项目:服务提供者
根据
dubbo
配置项,实现对 Dubbo 的自动化配置。如下:server: port: 8080 dubbo: application: # 服务名称,保持唯一 name: server-provider # 注册中心地址 registry: address: nacos://127.0.0.1:8848 #暴露服务方式 protocol: # dubbo协议,固定写法,更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 name: dubbo # 暴露服务端口 (默认是20880,不同的服务提供者端口不能重复) 使用 -1 表示随机端口。 port: 20880 provider: #远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒 timeout: 1000
1
#开启包扫描,可替代 @EnableDubbo 注解 ##dubbo.scan.base-packages=com.zang.gmall
@Service 只能定义在一个类上,表示一个服务的具体实现。
通过 @Service
上提供的属性,可以进一步的定制化 Dubbo 的服务提供方:如interfaceClass:指定服务提供方实现的 interface 的类
import com.alibaba.dubbo.config.annotation.Service;
@Service(interfaceClass = BookRpcService.class)
@Component
public class BookRpcServiceImpl implements BookRpcService {
@Override
public BookDTO get(Integer id) {
//根据id 查询BOOK。返回BookDTO
return new BookDTO(1, "我不是戏神", "小说");
}
}
@DubboService
@Service
注解从 3.0 版本开始就已经废弃,改用 @DubboService
,以区别于 Spring 的 @Service
注解
提供服务接口的实现逻辑,并用
@DubboService
注解标记,就可以实现 Dubbo 的服务暴露
5、dubbo-demo-consumer
dubbo-demo-consumer 项目:服务消费者
在 resources 目录下, 创建 application.yml 配置文件,添加 Dubbo 相关的配置,如下
server: port: 8081 dubbo: application: # 服务名称,保持唯一 name: dubbo-demo-consumer registry: # 注册中心地址 address: nacos://127.0.0.1:8848 protocol: # dubbo协议,固定写法 name: dubbo consumer: #远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒 timeout: 1000
1
#开启包扫描,可替代 @EnableDubbo 注解 ##dubbo.scan.base-packages=com.zang.gmall
@Reference
用来配置 Dubbo 的服务消费方,比如[@DubboReference]
@RestController
public class HelloController {
@Reference
private BookRpcService bookRpcService;
@GetMapping("/getBook/{bookid}")
public BookDTO hello(@PathVariable Integer bookid) {
BookDTO dto = bookRpcService.get(bookid);
return dto;
}
}
postman测试接口: 127.0.0.1:8081/getBook/1
6、Nacos 控制台
如图所示,服务名前缀为 providers:
的信息为服务提供者的元信息,consumers:
则代表服务消费者的元信息。
点击“详情”可查看服务状态详情:
// 获取最后一次调用的提供方IP地址 String serverIP = RpcContext.getContext().getRemoteHost();
7、负载均衡策略
dubbo在负载沉重时由多台服务器分担负载。具体工作时由负载均衡服务器基于特定算法将每一个具体请求分配到具体服务器上。
负载均衡算法:就是将请求分摊到多个操作单元上进行执行,从而共同完成工作任务
随机、轮询、最少活跃调用数、一致性Hash,默认情况random随机调用
一致性hash: 消费者的ip计算出来hash值,取余。固定的消费者调的提供者是固定的。
配置负载均衡策略:
既可以在服务提供者一方配置,
也可以在服务消费者一方配置,两者选其一即,不要都配置@Reference(loadbalance = "random") //在服务消费者一方配置负载均衡策略 @Service(loadbalance = "random") //在服务提供者一方配置
8、dubbo声明式事务
如果在服务提供者类上加入@Transactional事务控制注解后,服务就发布不成功了。
为什么会失效?声明式事务到底做了啥
2021/10/31 北京 spring【4】 JdbcTemplate,spring声明式事务_£小羽毛的博客-CSDN博客
原因是事务控制的底层原理是为服务提供者类创建代理对象,而默认情况下Spring是基于JDK动态代理方式创建代理对象,而此代理对象的完整类名为com.sun.proxy.$Proxy42(最后两位数字不是固定的),导致Dubbo在发布服务前进行包匹配时无法完成匹配【因为我们在发布服务时扫描的包为com.atguigu.service 】,进而没有进行服务的发布。
Spring底层创建代理对象的规则是:
1、如果类实现了接口,则采用JDK的动态代理,创建代理对象(接口的实现类对象)
2、如果类没有实现接口,则采用CGLIB的动态代理,创建代理对象(类的子类对象)
<!--数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="username" value="root" />
<property name="password" value="root" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/spring-test" />
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务控制的注解支持-->
<tx:annotation-driven transaction-manager="transactionManager"/>
解决方案:
1,让声明式事务底层,不采用JDK的动态代理,而采用CGLIB的动态代理
2.声明当前实现类的接口类型
proxy-target-class="true" ,表示底层使用CGLIB的动态代理
<!--开启事务控制的注解支持-->
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true"/>
@Service(interfaceclass HelloService.class) 指定服务的接口类型【意义??】
@Service(interfaceClass = HelloService.class)
@Transactional
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "8081 hello " + name;
}
}
服务注册中心Zookeeper
Dubbo官方推荐使用Zookeeper作为服务注册中心。
zookeeper为什么能作为注册中心?做注册中心需要有哪些能力?
1.记录数据(消费者与提供者的地址)
2.有监控变化,从而及时推送的功能
1、Zookeeper概述
zookeeper是一个开源的分布式协调服务,提供分布式数据一致性解决方案,分布式应用程序可以实现数据发布订阅、负载均衡、命名服务、集群管理分布式锁、分布式队列等功能。
Zookeeper 是 Apache Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用 。
流程说明:
服务提供者(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 地址
2、安装Zookeeper
下载地址:Index of /dist/zookeeperhttp://archive.apache.org/dist/zookeeper/将压缩包解压到Windows/Linux电脑的没有中文的路径下即可
配置Zookeeper
-
将
conf
目录中的zoo_sample.cfg
文件复制一份到conf
目录并命名为zoo.cfg
-
修改
zoo.cfg
文件: 大概在第12行,设置dataDir=../data
-
在zookeeper的安装目录中,创建
data
目录
启动、停止Zookeeper
-
启动Zookeeper: 进入
bin
目录,双击zkServer.cmd
-
停止Zookeeper: 关闭Zookeeper的DOS窗口
看得出zookper绑定的端口号是2181
因为在zoo.cfg中有配置
3、Zookeeper 客户端命令
启动Zookeeper客户端工具 :进入bin
目录,双击zkServer.cmd
4、zookeeper数据结构
ZooKeeper数据模型的结构整体上可以看作是一棵树,每个节点称做一个ZNode,每个ZNode都可以通过其路径唯一标识
每一个Znode节点类型
1、PERSISTENT-持久节点除非手动删除,否则节点一直存在于 Zookeeper 上2、EPHEMERAL-临时节点临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与zookeeper 连接断开不一定会话失效),那么这个客户端创建的所有临时节点都会被移除。3、PERSISTENT_SEQUENTIAL-持久顺序节点基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。4、EPHEMERAL_SEQUENTIAL-临时顺序节点基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。
5、zookeeper三种角色
Leader事务请求的唯一调度和处理者,保证集群事务处理的顺序性 集群内部各服务的调度者
Follower
处理客户端的非事务请求,转发事务请求给 Leader 服务器 参与事务请求 Proposal 的投票 参与 Leader 选举投票
Observer
3.3.0 版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理 能力 处理客户端的非事务请求,转发事务请求给 Leader 服务器 不参与任何形式的投票
6、zookeeper俩种模式
7、Leader选举算法及流程
第一次启动选举机制
目前有 5 台服务器,每台服务器均没有数据,它们的编号分别是 1,2,3,4,5, 按编号依次启动,它们的选举过程如下:服务器 1 启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1 的状态一直属于 Looking(选举状态)。 服务器 2 启动,给自己投票,同时与之前启动的服务器1 交换结果,由于服务器2 的编号大 所以服务器2 胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。 服务器 3 启动,给自己投票,同时与之前启动的服务器 1,2 交换信息,由于服务器 3 的编号最大所以 服务器 3 胜出,此时投票数正好大于半数,所以服务器 3 成为领导者,服务器 1,2 成为小弟。 服务器 4 启动,给自己投票,同时与之前启动的服务器 1,2,3 交换信息,尽管服务器 4 的编号大,但 之前服务器 3 已经胜出,所以服务器 4 只能成为小弟。 服务器 5 启动,后面的逻辑同服务器 4 成为小弟。
注意的点
1.服务器1和服务器2选举无法完成,保持LOOKING状态
2.服务器2启动,会和服务器1交换选票信息,服务器1把自己的票投给myid大的服务器2
3.服务器3当选Leader后。服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING
4.服务器4和服务器5启动,只要集群中有了leader(服务器3),后来的都得当小弟follower
要是第3台服务器(leader)挂掉了,要重新选举leader
选举Leader规则:
①EPOCH大的直接胜出
②EPOCH相同,事务id大的胜出
③事务id相同,服务器id大的胜出
SID:服务器ID。用来唯一标识一台ZooKeeper集群中的机器,每台机器不能重复,和myid一致。
ZXID:事务ID。ZXID是一个事务ID,用来标识一次服务器状态的变更。在某一时刻,
集群中的每台机器的ZXID值不一定完全一致,这和ZooKeeper服务器对于客户端“更新请求”的处理逻辑有关。Epoch:每个Leader任期的代号。没有Leader时同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加
Dubbo入门案例
Dubbo 和 Spring Cloud 有什么区别?Dubbo 使用的是 RPC 通信,而 Spring Cloud 使用的是 HTTP RESTFul 方式。
1、开发服务提供者
创建maven工程用一种神奇的插件把它改成maven形式的web工程,
然后是在pom文件中导入依赖,,,真的是无语,幸好咱只是复制而已
<packaging>war</packaging> <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>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> <!-- 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> </dependencies> <build> <plugins> <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.4.15.v20190215</version> <configuration> <!-- 如果检测到项目有更改则自动热部署,每隔n秒扫描一次。默认为0,即不扫描--> <scanIntervalSeconds>2</scanIntervalSeconds> <webAppConfig> <!--指定web项目的根路径,默认为/ --> <contextPath>/</contextPath> </webAppConfig> <httpConnector> <!--端口号,默认 8080--> <port>8000</port> </httpConnector> </configuration> </plugin> </plugins> </build>
创建提供者服务的接口
public interface HelloService { public String sayHello(String name); }
创建提供者服务的服务实现类
重点注意这里的@Service注解
注意`Service`注解要导入`com.alibaba.dubbo.config.annotation.Service`,用于对外发布服务
@Service public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello "+name+" , I see you"; } }
2、提供者关于dubbo的配置
创建applicationContext-service.xml
在spring的配置文件,注册服务,创建在src/main/resources下
<?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://code.alibabatech.com/schema/dubbo" 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"> <!--dubbo的配置:向注册中心注册服务--> <!--1.配置应用名:要求每一个应用的应用名唯一--> <dubbo:application name="dubbodemo-provider" /> <!--2.配置注册中心地址--> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <!-- 3.配置rpc协议,name表示协议名,port表示提供者的端口号默认是20880,每一个提供者的端口号应该唯一 --> <dubbo:protocol name="dubbo" port="20881"/> <!-- 4. dubbo的包扫描 --> <dubbo:annotation package="com.atguigu"/> </beans>
创建的位置参考图如下:
通过
jetty
插件启动服务提供者配置web.xml
让它帮我们启动spring的配置文件
<?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"> <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>
3、开发服务消费者
创建服务消费者工程,命名为
dubbodemo-consumer
,通过插件改成javaweb
工程pom文件中导入让人无语到极致的依赖,,
<packaging>war</packaging> <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>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> <!-- 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> </dependencies> <build> <plugins> <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.4.15.v20190215</version> <configuration> <webAppConfig> <!--指定web项目的根路径,默认为/ --> <contextPath>/</contextPath> </webAppConfig> <httpConnector> <!--端口号,默认 8080--> <port>8082</port> </httpConnector> </configuration> </plugin> </plugins> </build>
创建消费者服务接口
因为和提供者服务接口一样,直接复制就好。(这里明显感觉代码重复了,为一会优化做铺垫)
public interface HelloService { public String sayHello(String name); }
创建HelloController
Controller中注入HelloService使用的是Dubbo提供的@Reference注解,而不再是我们以前的@Autowired了
注意这里的@Reference是alibaba包下的
@Controller @RequestMapping("/demo") public class HelloController { @Reference private HelloService helloService; @RequestMapping("/hello") @ResponseBody public String getName(String name){ //基于接口远程调用服务 String result = helloService.sayHello(name); System.out.println(result); return result; } }
4、消费者关于dubbo的配置
创建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:context="http://www.springframework.org/schema/context" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!--1. springmvc的配置--> <!--1.1 包扫描--> <context:component-scan base-package="com.atguigu"/> <!--2. dubbo的配置--> <!--2.1 配置应用名--> <dubbo:application name="dubbodemo-consumer"/> <!--2.2 配置注册中心的地址--> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <!--2.3 配置dubbo的包扫描--> <dubbo:annotation package="com.atguigu"/> <!--2.4 配置启动时不检查:当启动消费者的时候不检查是否有提供者--> <dubbo:consumer check="false"/> </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"> <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:applicationContext-web.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> <!-- 配置过滤器解决 POST 请求的字符乱码问题 --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <!-- encoding参数指定要使用的字符集名称 --> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <!-- 请求强制编码 --> <init-param> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param> <!-- 响应强制编码 --> <init-param> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
使用
jetty
插件启动服务消费者
启动测试
注意顺序:先启动注册中心,再启动提供者、消费者
然后在浏览器输入 http://localhost:8082/demo/hello?name=Alice
查看浏览器输出结果
5、优化及一些思考
记得我前面提过,在服务提供者工程(dubbodemo-provider)和服务消费者工程(dubbodemo-consumer)俩个的HelloService接口是一模一样的。
解决方案:单独创建一个maven工程service-api,将此接口创建在这个maven工程中。需要依赖此接口的工程只需要在自己工程的pom.xml文件中引入maven坐标即可。
在服务消费者工程(dubbodemo-consumer)中只是引用了HelloService接口,并没有提供实现类,Dubbo是如何做到远程调用的?
答:Dubbo底层是基于代理技术为HelloService接口创建代理对象,远程调用是通过此代理对象完成的。
Zookeeper服务这么重要 ,如何防止Zookeeper单点故障呢?
答:Zookeeper其实是支持集群模式的,可以配置Zookeeper集群来达到Zookeeper服务的高可用,防止出现单点故障
创建工程service-api
pom文件也是什么依赖都没有引入,干干净净
创建HelloService接口,参考位置如下:
最终
① 删除服务提供者和服务消费者中的
HelloService
接口② 在服务提供者和服务消费者的
pom.xml
中添加依赖service-api<dependency> <groupId>com.atguigu</groupId> <artifactId>service-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
重新启动服务,测试是没问题的,我还是经得住考验的!
6、Dubbo管理控制台
搭建监控中心,实则就是一个war包。
那怎么运行一个war包呢?
将dubbo-admin-2.6.0.war文件复制到tomcat的webapps目录下,启动Tomcat,此时war文件会自动解压
进入解压的目录中修改WEB-INF下的dubbo.properties文件
1.我们取巧,先启动tomcat,这样就能让tomcat帮我们解压这个war包了,我们再关了tomcat,“X”掉小黑窗,把这个解压的war包删了(这步很重要)
去bin目录下找到,双击启动tomcat
2.进入解压的目录中修改WEB-INF下的dubbo.properties文件
注意dubbo.registry.address对应的值需要对应当前使用的Zookeeper的ip地址和端口号
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=root
浏览器访问http://localhost:8080/dubbo-admin-2.6.0/ ,输入用户名(root)和密码(root),切换简体中文