SpringBoot系列:9. 分布式系统,Dubbo,Zookeeper服务注册与发现

前言

本章主要对分布式系统,RPC的实现方式和Zookeeper实现做一个详细的概述并通过实战代码加深对他们的了解。

1. 分布式

  1. 什么是分布式系统?
    :“分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”;
  1. 分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是利用更多的机器,处理更多的数据。
    ⑴ 分布式系统(distributed system)是建立在网络之上的软件系统。
    ⑵ 首先需要明确的是,只有当单个节点的处理能力无法满足日益增长的计算、存储任务的时候,且硬件的提升(加内存、加磁盘、使用更好的CPU)高昂到得不偿失的时候,应用程序也不能进一步优化的时候,我们才需要考虑分布式系统。因为,分布式系统要解决的问题本身就是和单机系统一样的,而由于分布式系统多节点、通过网络通信的拓扑结构,会引入很多单机系统没有的问题,为了解决这些问题又会引入更多的机制、协议,带来更多的问题。
    ⑶ 简单来说:分布式系统就是:将多台计算机串联成一台大的计算机,用户访问的就是这一太大的计算机,这一胎大的计算机对于用户来说就像单太计算机,单个服务器一样。

  2. 这多台计算机(服务器)内部都是相互联系的,内部的一台台服务器相互联系都是通过Http(网络)或者RPC(远程调用)来连接起来,这些n多台服务器都是实现同一个目标的,给用户提供服务的
    不可能出现某一个服务器是处理其他事情的,若这样那么此服务就没必要放在这里了。
    —> 即:将多台计算机完成共同的任务(目标:利用更多便宜的机器,实现更多能够处理数据的工作)。

1.1 分布式理解及架构演变过程

1.1.1 分布式使用概述

  1. 分布式是建立在网络之上的,因此没有网络何来分布式
  2. 什么时候考虑分布式的使用?
    ⑴ 使用分布式是在:一台机器满足不了需求的情况下,才考虑分布式。
    ⑵ 使用分布式例子:当我们在淘宝买一件衣服,淘宝商品展示–>用户购买下单成功–>商品走物流–>商品达到–>用户评论等等。这一个过程就涉及了:订单模块,商品库存模块,支付模块,物流模块,评论模块等等。这五个模块中我们可以根据每一个模块占用的资源大小来存放在对应不同的计算机上。(下订单部署在电脑A上,物流和支付分配在另外一台电脑上,这样就将原本在单体系统上部署的模块,分别部署在其他的电脑上了)。
    ------->此时将这些业务拆分到不同到单机电脑上,在利用http Restful来调用,不同单机电脑上的服务模块。
    即:电脑A订单下来了,我给另一台电脑B或电脑C发一个请求让其处理库存(若库存占用资源多可以用两台电脑来存储库存模块)–>这就有效的提升业务处理的效率了。
    ⑶ 因此分布式是建立在:单个节点无法处理大量需求任务的时候,我们才需要去考虑分布式系统部署,不然的话没必要去考虑。因为分布式系统可能还会出现一些“服务崩溃”等等情况产生。

1.1.2 应用架构的演进过程分析

⑴ 单一应用架构(所有业务部署在单台电脑)。
即:将订单模块,用户模块,支付模块等都放在一台电脑上,部署到一个功能里面。

⑵ 垂直应用架构(将不同业务分开存在不同电脑上,但是公用模块无法复用)。
即:当访问量逐渐增大,单一-应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。

⑶ 分布式服务架构(不同业务部署在不同电脑,且可以提供公共模块实现复用)。

⑷ 流式计算架构(服务越来越多,建立一个注册中心来方便管理这些服务,实现访问快捷实时管理)–>就是将所有服务注册到一个"注册中心"来管理,等待需要的此服务的人随时来调用。

2. RPC详解

  1. 在多台单机服务器(电脑)之间的联系(通信),是通过RPC或Http来通信联系的。

  2. 服务器通信的两种方式
    http:http通信协议,基于网络。
    RPC:RPC也是通信协议,远程过程调用。RPC是一种技术的思想,而不是规范。

  3. 既然RPC是一种远程过程调用,那么就存在本地过程调用,以下对这两种调用方式,进行简单解释↓:
    ⑴ 远程过程调用:A()方法和B()方法,一个部署在美国的X电脑,一个部署在中国的Y电脑,他们之间通过网络跨电脑跨地区的调用,就称为"远程过程调用"。
    ⑵ 本地过程调用:A()方法和B()方法部署在同一台电脑,可直接互相引用,这就称为"本地过程调用"。
    ⑶ 因此:RPC这种思想,仅仅是一种进程间的通信方式。所以在使用RPC中调用远程函数就可以理解为在调用本地函数一样。

2.1 RPC思想核心要素-通讯和序列化

  1. RPC的两个核心模块:通讯(dubbo+zookeeper),序列化(实现数据传输)。
    ⑴ 通讯:就是通过"网络"实现双方的交流。
    ⑵ 序列化:双方数据的传输需要进行序列化转换。
    我们序列化就是为了,在传输一个类对象(比如:User)的时候,A电脑将Uer传到B电脑B电脑可以正确解析出这个一个User对象,以及User对象里面包含的字段属性数据。

  2. Dubbo就是一种高可用的RPC框架,利用Dubbo来完成,两台服务器通过网络互相调用对方某一个接口(服务),完成双方数据解析,实现序列化和反序列化。
    因此也可以说:RPC就是一个专门实现通信的框架,而要实现此框架就是利用Dubbo来实现。

2.2 RPC的实现方式Dubbo框架

  1. Dubbo介绍
    ⑴ Dubbo是一款高性能,轻量级的开源Java RPC框架,它提供三大核心能力:
    能力一:面向接口的远程方法调用。
    能力二:智能容错和负载均衡。
    能力三:服务自动注册和发现。

  1. 关于Dubbo共涉及到以下几个对象↓:
    Container:容器
    Provider:生产者
    Registry:注册中心(类似中间商,你所有东西必须经过它)
    Consumer:使用者
    Monitor:监视,监控(这个监控中心,可要可不要)

2.3 Dubbo环境搭建

1. Dubbo的环境搭建
- 在搭建过程中,需要一个注册中心,但是现在网上的注册中心非常
多,我们选择Zookeeper注册中心(Dubbo推荐使用Zookeeper这个
注册中心)。
⑴ 第一步:在Windows下安装Zookeeper注册中心
下载注册中心地址:https://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/
(Windows安装Zookeeper,跟在Linux安装Zookeeper类似)
------->注意:在我们安装ZK后,找到/bin下的'zkServer.cmd'双击启
动后可能会出现闪退,因此需要:在Zookeeper的包下找到conf包下
的"zoo_sample.cfg"复制这个文件-->重命名为zoo_.cfg ==》最后在
bin包下,双击启动zkServer.cmd(服务端),即可实现zookeeper的启
动!-->启动Zookeeper后,我们可以点击bin包下的"zkCli.cmd"(客户
端)连接测试。使用zkCli.cmd测试如图-"图zk1"。

⑵ 第二步:Dobbo搭建
dubbo本身并不是一个服务软件。它其实就是一个jar包,能够帮你的jaw
程序连接到zookeper.并利用okeper消费。提供服务。
但是为了让用户更好的管理监控众多的dubbo服务。官方提供了一个可视
化的监控程序dubbo-adnin,不过这个监控即使不装也不影响使用。
dubbo-adnin可以从页面来管理我们的一些服务,是一个监控管理后台,
它可以查看我们注册了那些服务被消费了。(这里对于'dubbo-adnin'的
使用就不演示了,可自行百度)---->因此我们这里说的'Dubbo搭建'其实就是在项目模块中引入一个Jar
即可。因为Dubbo就是一个jar包,我们在java开发中,直接在项目引入
此jar即可。

--------------------------------------------------

*** 因此 ***:我们实际在开发中只需要Zookeeper(注册中心) + 
Dubbo(服务间通信的框架) 即可。Dubbo-admin:就只是一个网页,方便
用来查看注册了那些服务而已。
2. Dubbo再次说明
Dubbo本身并不是一个服务软件。它其实就是一个jar包,能够帮你
的java程序连接到zookeeper,并利用zookeeper消费、提供服务。

SpringBoot系列:9. 分布式系统,Dubbo,Zookeeper服务注册与发现_第1张图片

图zk1

3. Dubbo+Zookeeper服务注册发现(代码实战)

1. Dubbo+Zookeeper服务注册发现,代码实战演示↓:
⑴ 第一步:IDEA创建一个Empty project(空项目X)。空项目下创建多个模块来实现Dubbo+Zookeeper服务注册与发现。

⑵ 第二步:在空项目X下点击IDEA左上方File中的"New Module"
创建一个模块(使用MavneSpring initializr来创建都可以)
-->模块名字为(Artifact:provide-server(服务提供者)) 
此模块"provide-server"作为服务端,专门创建(服务)接口及接口实
现,并将接口(服务)注册到"Zookeeper"注册中心。

⑶ 第三步:继续创建第二个模块,模块名为"consumer-server"(消
费者)此模块用来作为客户端,接收服务(通过Dubbo来连接ZokkeeperZookeeper中获取"provide-server"注册进来的服务(接口))。

⑷ 第四步:由于服务端和客户端分别在不同模块中。对于处在不同模块,又或者说是处于不同系统中的两个模块要实现项目访问,获取对方资源(接口),如何实现呢?我们可以利用Http或RPC来实现。

⑸ 第五步:以下是服务端的配置↓:
两个"服务端""客户端"服务启动后,服务提供者需要将
自己的服务交给注册中心Registry,因此需要在"服务端"中导入注册中心(Zookeeper)的jar包,并且服务之间需要通信,因此还需要再导入'Dubbo'的依赖,实现服务之间的通信。
--->(服务端的pom.xml中导入依赖)
<!--Dubbo依赖包-->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.7.3</version>
</dependency>
<!--导入Zookeeper依赖包,就是导入zkClient(即:Zookeeper的客户端)-->
<dependency>
    <groupId>com.github.sgroschupf</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.1</version>
</dependency>

-------------------------------------------------

★ 这里注意一下:导入Zookeeper的zkClient客户端,会有一些坑。
-->坑一:Zookeeper的日志会与SpringBoot日志有冲突,因此需要导入剔除日志冲突的依赖
-->并且需要将Zookeeper的服务端及其他依赖也一并导入
<!-- 引入zookeeper -->
<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-framework</artifactId>
   <version>2.12.0</version>
</dependency>
<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-recipes</artifactId>
   <version>2.12.0</version>
</dependency>
<dependency>
   <groupId>org.apache.zookeeper</groupId>
   <artifactId>zookeeper</artifactId>
   <version>3.4.14</version>
   <!--排除这个slf4j-log4j12-->
   <exclusions>
       <exclusion>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-log4j12</artifactId>
       </exclusion>
   </exclusions>
</dependency>

⑹ 第六步:以下是服务端的配置。
在.properties配置文件中配置Zookeeper的相关信息。
# 该模块的端口号
server.port=8001
# 注册当前服务应用名字
dubbo.application.name=provider-server
# 注册中心地址(等会儿我们只要将Zookeeper注册中心打开,服务端中的服务就可以成功注册到Zookeeepr注册中心中去)
dubbo.registry.address=zookeeper://127.0.0.1:2181
# 扫描指定包下服务(表示那些服务所在的地址要被注册到Zookeeper注册中心里面去)
dubbo.scan.base-packages=com.lzk.service


⑺ 第七步:以下是服务端的配置↓:
------>在服务端下的com/lzk/service 下创建具体服务(接口)

//卖票的服务(接口)
public interface TicketService {
    /**
     * 提供票的服务接口:获取票
     */
    public String getTicket();
}

------>在服务端下的com/lzk/service 下实现服务(接口)
/**
 * 
 *
 * 1. 下面使用@Component注解,实现类对象注入于Spring容器中
 *      -->最好不要使用@Service注解来实现容器的注入,因为
 * @Service里可能会有两个意思:一个是引入Spring一个是Dubbo的意
 * 思,我们这里要注入Spring容器的意思,因此避免注入出错,最好使
 * 用@Component注解(@Compent是一个万能注解)
 *
 */
@Service   //这里的导入的是Dubbo表示,被Dubbo扫描到,当项目启动后自动将类下面的服务方法注册到注册中心
@Component //使用了Dubbo后尽量不要用@Service注解,可能会引入Dubbo的依赖
public class TicketServiceImpl implements TicketService {
    @Override
    public String getTicket() {
        return "";
    }
}


⑻ 第八步:首先去Windows系统下,找到Zookeeper/bin下面的zkServer.cmd,双击启动,然后再启动IDEA的provider-server项目。
-->此时就会自动将provider-server模块中指定包下的类下的服务注册到Zookeeper注册中心中去。

⑼ 第九步:此时服务已经注册到zookeeper上面了,就要去消费,获取
zookeeper里面的服务,利用另一个模块"consumer-server"来
消费ZK上的服务↓:
# 步骤一. 模块"consumer-server"的pom.xml中导入依赖
<!--dubbo-->
<!-- Dubbo Spring Boot Starter -->
<dependency>
   <groupId>org.apache.dubbo</groupId>
   <artifactId>dubbo-spring-boot-starter</artifactId>
   <version>2.7.3</version>
</dependency>
<!--zookeeper-->
<!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient -->
<dependency>
   <groupId>com.github.sgroschupf</groupId>
   <artifactId>zkclient</artifactId>
   <version>0.1</version>
</dependency>
<!-- 引入zookeeper -->
<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-framework</artifactId>
   <version>2.12.0</version>
</dependency>
<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-recipes</artifactId>
   <version>2.12.0</version>
</dependency>
<dependency>
   <groupId>org.apache.zookeeper</groupId>
   <artifactId>zookeeper</artifactId>
   <version>3.4.14</version>
   <!--排除这个slf4j-log4j12-->
   <exclusions>
       <exclusion>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-log4j12</artifactId>
       </exclusion>
   </exclusions>
</dependency>


# 步骤二. 配置模块"consumer-server"的配置文件(配置去哪里获取服务)

# 消费模块的server.port=8002
# 当前服务应用名字(当前消费者需要配置自己服务应用的名字)
# 告诉zookeeper注册中心是谁来获取服务的
dubbo.application.name=consumer-server
# 注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181

⑽ 第十步:以下是在消费者模块做的↓:
# 步骤一:编写到zookeeper注册中心获取服务的方法
- 创建一个与服务端相同的接口名(在同一个包下创建)
 //卖票的服务(接口)
public interface TicketService {
    /**
     * 提供票的服务接口
     */
    public String getTicket();
}

# 步骤二:在消费端模块中创建"UserService类",编写代码远程获取'服务端模块'TicketService接口中的数据的实现方法。

/**
 *  -->表示要获取服务端提供的服务(票)
 */
@Service //放到Spring容器中
public class UserService {

    /**
     * 1. User用户想获取provice-server提供的票
     *
     * 2. @Reference表示远程引用
     *      -->远程引用的两种方式: 写pon坐标 + 可以定义路径相
     * 同的接口名
     */
    @Reference
    TicketService ticketService;

    public void bugTicket(){
        String ticket = ticketService.getTicket();
        System.out.println("在注册中心获取"+ticket);
    }
}

⑾ 第十一步:以下是在消费端模块中做的↓:
写一个测试来实现,消费者到zookeeper获取服务。

@SpringBootTest
class ConsumerServerApplicationTests {
    @Autowired
    UserService userService;
    @Test
    void contextLoads() {
        userService.bugTicket();
    }
} 

↑启动上面测试类:查看是否获取到zookeeper注册中心中
刚才在"provider-server"服务端注册到zookeeper的服务。
--->如果上面的测试类打印出服务端"provider-server"里面
的TickServiceImpl类里面的方法,执行的一句话:
<getTicket123456789~~>,就表示成功获取到Zookeeper的服务了。

总结

Dubbo+zookeeper服务注册发现步骤小结
# 提供者提供服务provider
⑴ 第一步:首先有一个提供者,提供服务-->导入依赖
⑵ 第二步:配置注册中心的地址以及服务发现名,要扫描的包
⑶ 第三步:在想要被注册的服务上面,增加一个注解@Service

# 消费者消费,获取服务consumer
⑷ 第四步:导入依赖
⑸ 第五步:配置注册中心的地址,自己的服务名
⑹ 第六步:从远处注入服务!(涉及到一个注解@Reference)

② 上面实现服务注册到Zookeeper注册中心,消费者获取Zookeeper上面
的服务的前提Zookeeper服务已开启。

③ 我们在配置消费者的配置文件中指定的"注册中心的地址",这个地址
可以配置任何电脑的IP地址对应的Zookeeper的ip地址。我只要将2181的
端口号开启就可以输出对应的电脑的ip地址,以此来连接Zookeeper

你可能感兴趣的:(SpringBoot,zookeeper,dubbo,rpc,分布式,spring)