Zookeeper、Apache Dubbo

Zookeeper

zookeeper概述
ZooKeeper从字面意思理解,【Zoo - 动物园,Keeper - 管理员】动物园中有很多种动物,这里的动物就可以比作分布式环境下多种多样的服务,而ZooKeeper做的就是管理这些服务。
Apache ZooKeeper的系统为分布式协调是构建分布式应用的高性能服务。
ZooKeeper 本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理。从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。
ZooKeeper 适用于存储和协同相关的关键数据,不适合用于大数据量存储。
zookeeper的发展历程
ZooKeeper 最早起源于雅虎研究院的一个研究小组。当时研究人员发现,在雅虎内部很多大型系统基本都需要依赖一个系统来进行分布式协同,但是这些系统往往都存在分布式单点问题。
所以,雅虎的开发人员就开发了一个通用的无单点问题的分布式协调框架,这就是ZooKeeper。ZooKeeper之后在开源界被大量使用,很多著名开源项目都在使用zookeeper,例如:

  • Hadoop:使用ZooKeeper 做Namenode 的高可用。
  • HBase:保证集群中只有一个master,保存hbase:meta表的位置,保存集群中的RegionServer列表。
  • Kafka:集群成员管理,controller 节点选举。
    什么是分布式

集中式系统
集中式系统,集中式系统中整个项目就是一个独立的应用,整个应用也就是整个项目,所有的东西都在一个应用里面。部署到一个服务器上。

分布式系统
随着公司的发展,应用的客户变多,功能也日益完善,加了很多的功能,整个项目在一个tomcat上跑,tomcat说它也很累,能不能少跑点代码,这时候 就产生了。我们可以把大项目按功能划分为很多的模块,比如说单独一个系统处理订单,一个处理用户登录,一个处理后台等等,然后每一个模块都单独在一个tomcat中跑,合起来就是一个完整的大项目,这样每一个tomcat都非常轻松。
在这里插入图片描述
分布式系统的描述总结是:

  • 多台计算机构成
  • 计算机之间通过网络进行通信
  • 彼此进行交互
  • 共同目标
    zookeeper的应用场景
    注册中心
    分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构。通过调用Zookeeper提供的创建节点的API,能够很容易创建一个全局唯一的path,这个path就可以作为一个名称。
    阿里巴巴集团开源的分布式服务框架Dubbo中使用ZooKeeper来作为其命名服务,维护全局的服务地址列表。
    配置中心
    数据发布/订阅即所谓的配置中心:发布者将数据发布到ZooKeeper一系列节点上面,订阅者进行数据订阅,当数据有变化时,可以及时得到数据的变化通知,达到动态获取数据的目的。
    在这里插入图片描述
    ZooKeeper 采用的是推拉结合的方式。
    1、推: 服务端会推给注册了监控节点的客户端 Wathcer 事件通知
    2、拉: 客户端获得通知后,然后主动到服务端拉取最新的数据
    分布式锁(了解)
    分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。
    分布式队列(了解)
    在传统的单进程编程中,我们使用队列来存储一些数据结构,用来在多线程之间共享或传递数据。分布式环境下,我们同样需要一个类似单进程队列的组件,用来实现跨进程、跨主机、跨网络的数据共享和数据传递,这就是我们的分布式队列。
    负载均衡
    负载均衡是通过负载均衡算法,用来把对某种资源的访问分摊给不同的设备,从而减轻单点的压力。
    在这里插入图片描述
    上图中左侧为ZooKeeper集群,右侧上方为工作服务器,下面为客户端。每台工作服务器在启动时都会去ZooKeeper的servers节点下注册临时节点,每台客户端在启动时都会去servers节点下取得所有可用的工作服务器列表,并通过一定的负载均衡算法计算得出一台工作服务器,并与之建立网络连接

zookeeper基本操作
1:Zookeeper的数据结构(树型结构)

2:节点的分类(4个)
-e:表示普通临时节点
-s:表示带序号节点

  • 持久性(带序号、不带序号)
    创建带序号永久节点:create -s /hello “helloworld”
  • 临时性(带序号、不带序号)
    创建普通临时节点:create -e /app3 ‘app3’
    创建带序号临时节点:create -e -s /app4 ‘app4’
    3:客户端命令(创建create、查询get、修改set、删除delete/rmr)

4:Zookeeper的java的api介绍(创建、查询、修改、删除)

  • Curator的客户端
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,3);
    CuratorFramework client = CuratorFrameworkFactory.newClient(“127.0.0.1:2181”, 3000, 3000, retryPolicy);

5:Zookeeper的watch机制

  • NodeCache

  • PathChildrenCache

  • TreeCache(监听和缓存根几点变化和子节点变化)(重点)
    watch机制
    在这里插入图片描述
    zookeeper作为一款成熟的分布式协调框架,订阅-发布功能是很重要的一个。所谓订阅发布功能,其实说白了就是观察者模式。观察者会订阅一些感兴趣的主题,然后这些主题一旦变化了,就会自动通知到这些观察者。

    zookeeper的订阅发布也就是watch机制,是一个轻量级的设计。因为它采用了一种推拉结合的模式。一旦服务端感知主题变了,那么只会发送一个事件类型和节点信息给关注的客户端,而不会包括具体的变更内容,所以事件本身是轻量级的,这就是所谓的“推”部分。然后,收到变更通知的客户端需要自己去拉变更的数据,这就是“拉”部分。watche机制分为添加数据和监听节点。

    Curator在这方面做了优化,Curator引入了Cache的概念用来实现对ZooKeeper服务器端进行事件监听。Cache是Curator对事件监听的包装,其对事件的监听可以近似看做是一个本地缓存视图和远程ZooKeeper视图的对比过程。而且Curator会自动的再次监听,我们就不需要自己手动的重复监听了。

Curator中的cache共有三种

  • NodeCache(监听和缓存根节点变化)
  • PathChildrenCache(监听和缓存子节点变化)
  • TreeCache(监听和缓存根节点变化和子节点变化)

下面我们分别对三种cache详解
NodeCache

  • 介绍
    NodeCache是用来监听节点的数据变化的,当监听的节点的数据发生变化的时候就会回调对应的函数。
  • 增加监听
//创建重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);
//创建客户端
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 1000, 1000, retryPolicy);
//开启客户端
client.start();
System.out.println("连接成功");
//创建节点数据监听对象
final NodeCache nodeCache = new NodeCache(client, "/hello");
//开始缓存
/**
 * 参数为true:可以直接获取监听的节点,System.out.println(nodeCache.getCurrentData());为ChildData{path='/aa', stat=607,765,1580205779732,1580973376268,2,1,0,0,5,1,608
, data=[97, 98, 99, 100, 101]}
 * 参数为false:不可以获取监听的节点,System.out.println(nodeCache.getCurrentData());为null
 */
nodeCache.start(true);
System.out.println(nodeCache.getCurrentData());
//添加监听对象
nodeCache.getListenable().addListener(new NodeCacheListener() {
    //如果节点数据有变化,会回调该方法
    public void nodeChanged() throws Exception {
        String data = new String(nodeCache.getCurrentData().getData());
        System.out.println("数据Watcher:路径=" + nodeCache.getCurrentData().getPath()
                + ":data=" + data);
    }
});
System.in.read();

PathChildrenCache

  • 介绍
    PathChildrenCache是用来监听指定节点 的子节点变化情况
  • 增加监听
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 1000, 1000, retryPolicy);
client.start();
//监听指定节点的子节点变化情况包括͹新增子节点 子节点数据变更 和子节点删除
//true表示用于配置是否把节点内容缓存起来,如果配置为true,客户端在接收到节点列表变更的同时,也能够获取到节点的数据内容(即:event.getData().getData())ͺ如果为false 则无法取到数据内容(即:event.getData().getData())
PathChildrenCache childrenCache = new PathChildrenCache(client,"/hello",true);
/**
 * NORMAL:  普通启动方式, 在启动时缓存子节点数据
 * POST_INITIALIZED_EVENT:在启动时缓存子节点数据,提示初始化
 * BUILD_INITIAL_CACHE: 在启动时什么都不会输出
 *  在官方解释中说是因为这种模式会在start执行执行之前先执行rebuild的方法,而rebuild的方法不会发出任何事件通知。
 */
childrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
System.out.println(childrenCache.getCurrentData());
//添加监听
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
    @Override
    public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
        if(event.getType() == PathChildrenCacheEvent.Type.CHILD_UPDATED){
            System.out.println("子节点更新");
            System.out.println("节点:"+event.getData().getPath());
            System.out.println("数据" + new String(event.getData().getData()));
        }else if(event.getType() == PathChildrenCacheEvent.Type.INITIALIZED ){
            System.out.println("初始化操作");
        }else if(event.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED ){
            System.out.println("删除子节点");
            System.out.println("节点:"+event.getData().getPath());
            System.out.println("数据" + new String(event.getData().getData()));
        }else if(event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED ){
            System.out.println("添加子节点");
            System.out.println("节点:"+event.getData().getPath());
            System.out.println("数据" + new String(event.getData().getData()));
        }else if(event.getType() == PathChildrenCacheEvent.Type.CONNECTION_SUSPENDED ){
            System.out.println("连接失效");
        }else if(event.getType() == PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED ){
            System.out.println("重新连接");
        }else if(event.getType() == PathChildrenCacheEvent.Type.CONNECTION_LOST ){
            System.out.println("连接失效后稍等一会儿执行");
        }
    }
});
System.in.read(); // 使线程阻塞

TreeCache

  • 介绍
    TreeCache有点像上面两种Cache的结合体,NodeCache能够监听自身节点的数据变化(或者是创建该节点),PathChildrenCache能够监听自身节点下的子节点的变化,而TreeCache既能够监听自身节点的变化、也能够监听子节点的变化。
  • 添加监听
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 1000, 1000, retryPolicy);
client.start();
TreeCache treeCache = new TreeCache(client,"/hello");
treeCache.start();
System.out.println(treeCache.getCurrentData("/hello"));
treeCache.getListenable().addListener(new TreeCacheListener() {
    @Override
    public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
            if(event.getType() == TreeCacheEvent.Type.NODE_ADDED){
                System.out.println(event.getData().getPath() + "节点添加");
            }else if (event.getType() == TreeCacheEvent.Type.NODE_REMOVED){
                System.out.println(event.getData().getPath() + "节点移除");
            }else if(event.getType() == TreeCacheEvent.Type.NODE_UPDATED){
                System.out.println(event.getData().getPath() + "节点修改");
            }else if(event.getType() == TreeCacheEvent.Type.INITIALIZED){
                System.out.println("初始化完成");
            }else if(event.getType() ==TreeCacheEvent.Type.CONNECTION_SUSPENDED){
                System.out.println("连接过时");
            }else if(event.getType() ==TreeCacheEvent.Type.CONNECTION_RECONNECTED){
                System.out.println("重新连接");
            }else if(event.getType() ==TreeCacheEvent.Type.CONNECTION_LOST){
                System.out.println("连接过时一段时间");
            }
    }
});
System.in.read();

Apache Dubbo

主流的互联网技术特点
分布式 、高并发、集群、负载均衡、高可用。
分布式:一件事情拆开来做。
集群:一件事情大家一起做。
负载均衡:将请求平均分配到不同的服务器中,达到均衡的目的。
高并发:同一时刻,处理同一件事情的处理能力(解决方案:分布式、集群、负载均衡)
高可用:系统都是可用的。
在这里插入图片描述
架构演变的过程
单一应用架构(all in one)
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
在这里插入图片描述

  • 架构优点:
    架构简单,前期开发成本低、开发周期短,适合小型项目(OA、CRM、ERP)。
  • 架构缺点:
    全部功能集成在一个工程中
    (1)业务代码耦合度高,不易维护。
    (2)维护成本高,不易拓展
    (3)并发量大,不易解决
    (4)技术栈受限,只能使用一种语言开发。

垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
在这里插入图片描述

  • 架构优点:
    (1)业务代码相对解耦
    (2)维护成本相对易于拓展(修改一个功能,可以直接修改一个项目,单独部署)
    (3)并发量大相对易于解决(搭建集群)
    (4)技术栈可扩展(不同的系统可以用不同的编程语言编写)。
  • 架构缺点:
    功能集中在一个项目中,不利于开发、扩展、维护。
    代码之间存在数据、方法的冗余

分布式服务架构

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
在这里插入图片描述

  • 架构优点:
    (1)业务代码完全解耦,并可实现通用
    (2)维护成本易于拓展(修改一个功能,可以直接修改一个项目,单独部署)
    (3)并发量大易于解决(搭建集群)
    (4)技术栈完全扩展(不同的系统可以用不同的编程语言编写)。

  • 架构缺点:
    缺少统一管理资源调度的框架
    流动计算架构(SOA)
    当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
    在这里插入图片描述
    资源调度和治理中心的框架:dubbo、spring cloudy

  • 架构优点:
    (1)业务代码完全解耦,并可实现通用
    (2)维护成本易于拓展(修改一个功能,可以直接修改一个项目,单独部署)
    (3)并发量大易于解决(搭建集群)
    (4)技术栈完全扩展(不同的系统可以用不同的编程语言编写)。

【小结】
1:单体架构
全部功能集中在一个项目内(All in one)。
2:垂直架构
按照业务进行切割,形成小的单体项目。
3:SOA架构(项目一)
面向服务的架构(SOA)是一个组件模型,全称为:Service-Oriented Architecture,它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的系统中的服务可以以一种统一和通用的方式进行交互.
可以使用dubbo作为调度的工具(RPC协议)
4:微服务架构(项目二)
将系统服务层完全独立出来,抽取为一个一个的微服务。
特点一:抽取的粒度更细,遵循单一原则,数据可以在服务之间完成数据传输(一般使用restful请求调用资源)。
特点二: 采用轻量级框架协议传输。(可以使用springcloudy)(http协议)
特点三: 每个服务都使用不同的数据库,完全独立和解耦。

RPC(远程过程调用)
RPC介绍
Remote Procedure Call 远程过程调用,是分布式架构的核心,按响应方式分如下两种:
同步调用:客户端调用服务方方法,等待直到服务方返回结果或者超时,再继续自己的操作。
异步调用:客户端把消息发送给中间件,不再等待服务端返回,直接继续自己的操作。

  • 是一种进程间的通信方式
  • 它允许应用程序调用网络上的另一个应用程序中的方法
  • 对于服务的消费者而言,无需了解远程调用的底层细节,是透明的
    需要注意的是RPC并不是一个具体的技术,而是指整个网络远程调用过程。
    RPC是一个泛化的概念,严格来说一切远程过程调用手段都属于RPC范畴。各种开发语言都有自己的RPC框架。Java中的RPC框架比较多,广泛使用的有RMI、Hessian、Dubbo、spring Cloud等。
    RPC组件
    简单来说一个RPC架构里包含如下4个组件:
    1、 客户端(Client):服务调用者
    2、 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数打包成网络消息,再通过网络发送给服务方
    3、 服务端存根(Server Stub):接受客户端发送过来的消息并解包,再调用本地服务
    4、 服务端(Server):服务提供者。
    在这里插入图片描述
    RPC调用
    在这里插入图片描述
    1、 服务调用方(client)调用以本地调用方式调用服务;

2、 client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体

    在Java里就是序列化的过程

3、 client stub找到服务地址,并将消息通过网络发送到服务端;

4、 server stub收到消息后进行解码,在Java里就是反序列化的过程;

5、 server stub根据解码结果调用本地的服务;

6、 本地服务执行处理逻辑;

7、 本地服务将结果返回给server stub;

8、 server stub将返回结果打包成消息,Java里的序列化;

9、 server stub将打包后的消息通过网络并发送至消费方;

10、 client stub接收到消息,并进行解码, Java里的反序列化;

11、 服务调用方(client)得到最终结果。

Dubbo简介
Apache Dubbo是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。
Dubbo官网地址:http://dubbo.apache.org
Dubbo提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
Dubbo架构
在这里插入图片描述
节点角色说明:

节点角色名称
Provider暴露服务的服务提供方
Consumer调用远程服务的服务消费方
Registry服务注册与发现的注册中心
Monitor统计服务的调用次数和调用时间的监控中心
Container服务运行容器

虚线都是异步访问,实线都是同步访问
蓝色虚线:在启动时完成的功能
红色虚线(实线)都是程序运行过程中执行的功能
调用关系说明:

  1. 服务容器负责启动,加载,运行服务提供者。
  2. 服务提供者在启动时,向注册中心注册自己提供的服务。
  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

什么是长连接?
在这里插入图片描述

Dubbo快速开发
实现步骤:

  1. 环境准备:创建数据库,创建t_user表
  2. 创建父工程,基于maven,打包方式为pom,工程名:dubbo_parent
  3. 创建公共子模块,创建user实体类,打包方式为jar,工程名:dubbo_common
  4. 创建接口子模块,在父工程的基础上,打包方式为jar,模块名:dubbo_interface
  5. 创建服务提供者子模块,在父工程的基础上,打包方式为war,模块名:dubbo_provider
  6. 创建服务消费者模子块,在父工程的基础上,打包方式为war,模块名:dubbo_consumer
  7. 添加watch机制
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值