东北某不知名双非本科,四面成功上岸阿里巴巴,在这里把自己整理的面经分享出来,欢迎大家阅读。
恰个饭——>《阿里巴巴 Java 开发手册》,业界普遍遵循的开发规范
本博客内容持续维护,如有改进之处,还望各位大佬指出,感激不尽!
Dubbo部分
第一章 分布式系统相关概念
1.1 大型互联网项目架构目标
 
互联网项目特点:用户多、流量大、并发高、海量数据、易受攻击、功能繁琐、变更快
大型互联网项目架构性能指标:响应时间、并发数、吞吐量
大型互联网项目架构目标:高性能、高可用(网站一直可以正常访问)、可伸缩(通过硬件增加/减少,提高/减低处理能力)、高可扩展(耦合低、方便通过新增/移除方式,增加/减少功能模块)、安全性、敏捷性
1.2 集群和分布式
集群:一个业务模块,部署在多台服务器上。
分布式:一个大的业务系统,拆分成小的业务模块,分别部署在不同的机器上。 
1.3 架构演进

 
  
个人理解:微服务架构是对SOA架构的进一步拆分和细化
Dubbo是SOA时代的产物,SpringCloud是微服务时代的产物
第二章 Dubbo概述
2.1 Dubbo概念
- Dubbo是阿里巴巴公司开源的一个高性能、轻量级的Java RPC框架
- 致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
- 官网:http://dubbo.apache.org
2.2 Dubbo架构
2.2.1 Dubbo架构及调用顺序

节点角色说明
节点 | 角色说明 |
---|
Provider | 暴露服务的服务提供方 | Consumer | 调用远程服务的服务消费方 | Registry | 服务注册与发现的注册中心 | Monitor | 统计服务的调用次数和调用时间的监控中心 | Container | 服务运行容器 |
调用关系说明参考文档
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性
个人理解:Registry就相当于ESB,Consumer和Provider通过其连接
2.2.2 Dubbo特点
连通性
- 注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小
- 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示
- 服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销
- 服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销
- 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
- 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
- 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
- 注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
健壮性
- 监控中心宕掉不影响使用,只是丢失部分采样数据
- 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
- 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
- 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
- 服务提供者无状态,任意一台宕掉后,不影响使用
- 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
伸缩性
- 注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心
- 服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者
升级性
当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力。下图是未来可能的一种架构:

节点角色说明
节点 | 角色说明 |
---|
Deployer | 自动部署服务的本地代理 | Repository | 仓库用于存储服务应用发布包 | Scheduler | 调度中心基于访问压力自动增减服务提供者 | Admin | 统一管理控制台 | Registry | 服务注册与发现的注册中心 | Monitor | 统计服务的调用次数和调用时间的监控中心 |
第三章 Dubbo快速入门
3.1 Zookeeper
3.1.1 Zookeeper概述
注册中心:https://dubbo.apache.org/zh/docs/v2.7/user/references/registry/
Zookeeper是Apache Hadoop的子项目,是一个树形的目录服务,适合作为Dubbo服务的注册中心,工业强度较高,可用于生产环境,并推荐使用。

更详细概述,见我的HSF笔记
3.1.2 Zookeeper安装与配置
下载安装
1、环境准备:Java1.7+
2、上传:将下载的ZooKeeper放到/opt/ZooKeeper目录下
put f:/setup/apache-zookeeper-3.5.6-bin.tar.gz
cd /opt
mkdir zooKeeper
mv apache-zookeeper-3.5.6-bin.tar.gz /opt/zookeeper/
3、解压:将tar包解压到/opt/zookeeper目录下
tar -zxvf apache-ZooKeeper-3.5.6-bin.tar.gz
配置启动
1、配置zoo.cfg
进入conf目录拷贝一个zoo_sample.cfg并完成配置(zoo_sample本质上不起配置作用,只是一个模板,需要自行创建zoo.cfg并修改配置内容。)
cd /opt/zooKeeper/apache-zooKeeper-3.5.6-bin/conf/
cp zoo_sample.cfg zoo.cfg
修改zoo.cfg
cd /opt/zooKeeper/
mkdir zkdata
vim /opt/zooKeeper/apache-zooKeeper-3.5.6-bin/conf/zoo.cfg

修改存储目录:dataDir = /opt/zookeeper/zkdata
2、启动zookeeper
cd /opt/zooKeeper/apache-zooKeeper-3.5.6-bin/bin/
./zkServer.sh start

看到上图表示zookeeper已经成功启动。
3、查看zookeeper状态
./zkServer.sh status
zookeeper启动成功。standalone代表zk没有搭建集群,现在是单节点

zookeeper没有启动成功

3.2 Dubbo快速入门
3.2.1 Spring和SpringMVC整合
代码:1. spring和springmvc整合后代码
实施步骤:
1、创建服务提供者Provider模块
2、创建服务消费者Consumer模块
3、在服务提供者模块编写UserServiceImpl提供服务
4、在服务消费者中的UserController远程调用(提供者是Service,消费者是Controller)
5、UserServiceImpl提供的服务
6、分别启动两服务,测试。
Dubbo作为一个RPC框架,其最核心的功能就是要实现跨网络的远程调用。本小节就是要创建两个应用,一个作为服务的提供方,一个作为服务的消费方。通过Dubbo来实现服务消费方远程调用服务提供方的方法。
0、创建项目、准备环境
首先创建空项目

接下来配置Maven地址

接下来创建Maven的Module  
新建服务方:

新建消费方:

接下来就是编写逻辑代码了。
核心原理:consumer中的pom.xml中添加了servicer的pom依赖,本质上并没有用到dubbo
3.2.2 服务提供者
代码见:2. dubbo快速入门
dubbo的作用: 
服务提供者编写过程:
1、将Servicer的服务注册到Zookeeper中(注册中心)
2、在mvc的xml中编写dubbo的配置,包括配置项 目的名称(唯一标识),配置注册中心(zookeeper)的地址,配置dubbo包扫描的路径
3、配置web.xml
3.2.3 服务消费者
服务消费者编写过程:
1、在Controller中远程注入userService,即获取其url(抛弃Autowired注解,使用Reference注解)
2、在mvc的xml中编写dubbo的配置,包括配置项目的名称(唯一标识),配置注册中心的地址(本机ip+端口),配置dubbo包扫描的路径
3、配置web.xml
服务方和消费方都遵循上述配置,不同的是消费方配置的是Controller,并且不用放到注册中心
**注意:当消费者调用提供者的Service方法时,消费者中也要定义相对应的Service类的接口,因此为了去冗余设计,新定义一个interface的module,来做代码提取。**至于如何调用,直接在Servicer和Consumer的pom.xml中定义即可。
以上,完成了最最最最简单的Dubbo的功能实现。
第四章 Dubbo高级特性
4.1 Dubbo-admin管理平台
简介:
- dubbo-admin管理平台,是图形化的服务管理页面
- 从注册中心获取到所有提供者/消费者进行配置管理
- 路由规则、动态配置、服务降级、访问控制、权重调整、负载均衡等管理功能
- dubbo-admin是一个前后端分离的项目。前端使用vue,后端使用springboot
- 安装dubbo-admin其实就是部署该项目
4.1.1 dubbo-admin安装
1、环境准备
dubbo-admin 是一个前后端分离的项目。前端使用vue,后端使用springboot,安装 dubbo-admin 其实就是部署该项目。我们将dubbo-admin安装到开发环境上。要保证开发环境有jdk,maven,nodejs
安装node**(如果当前机器已经安装请忽略)**
因为前端工程是用vue开发的,所以需要安装node.js,node.js中自带了npm,后面我们会通过npm启动
把node.js理解成更牛逼的tomcat就行
下载地址
https://nodejs.org/en/

2、下载 Dubbo-Admin
进入github,搜索dubbo-admin
https://github.com/apache/dubbo-admin
下载: 
3、把下载的zip包解压到指定文件夹(解压到那个文件夹随意) 
4、修改配置文件
解压后我们进入…\dubbo-admin-develop\dubbo-admin-server\src\main\resources目录,找到 application.properties 配置文件 进行配置修改 
修改zookeeper地址 
admin.registry.address=zookeeper://192.168.149.135:2181
admin.config-center=zookeeper://192.168.149.135:2181
admin.metadata-report.address=zookeeper://192.168.149.135:2181
admin.registry.address注册中心(服务信息的中介(非持久化)) admin.config-center 配置中心(存储 Dubbo 服务的各种治理规则(持久化)) admin.metadata-report.address元数据中心(元数据是指 Dubbo服务对应的方法列表以及参数结构等信息)
5、打包项目
在 dubbo-admin-develop 目录执行打包命令
mvn clean package

6、启动后端
切换到目录
dubbo-Admin-develop\dubbo-admin-distribution\target>
执行下面的命令启动 dubbo-admin,dubbo-admin后台由SpringBoot构建。
java -jar .\dubbo-admin-0.1.jar

7、前台后端
dubbo-admin-ui 目录下执行命令
npm run dev

8、访问
浏览器输入。用户名密码都是root
http://localhost:8081/

4.1.2 dubbo-admin简单使用

注意:Dubbo Admin【服务Mock】【服务统计】将在后续版本发布…
在上面的步骤中,我们已经进入了Dubbo-Admin的主界面,在【快速入门】章节中,我们定义了服务生产者、和服务消费者,下面我们从Dubbo-Admin管理界面找到这个两个服务
1、点击服务查询

2、查询结果

A:输入的查询条件com.itheima.service.UserService
B:搜索类型,主要分为【按服务名】【按IP地址】【按应用】三种类型查询
C:搜索结果
3.1.4 dubo-admin查看详情
我们查看com.itheima.service.UserService (服务提供者)的具体详细信息,包含【元数据信息】
1)点击详情 
从【详情】界面查看,主要分为3个区域
A区域:主要包含服务端 基础信息比如服务名称、应用名称等
B区域:主要包含了生产者、消费者一些基本信息
C区域:是元数据信息,注意看上面的图,元数据信息是空的
我们需要打开我们的生产者配置文件加入下面配置
<dubbo:metadata-report address="zookeeper://192.168.149.135:2181" />
重新启动生产者,再次打开Dubbo-Admin
这样我们的元数据信息就出来了 
4.2 Dubbo常用高级配置
4.2.1 序列化
注意!!!将来所有的pojo类都需要实现Serializable接口!
代码实现见:3. dubbo高级特性-序列化
两台机器之间传输数据,通过序列化和反序列化传输Java对象
- dubbo 内部已经将序列化和反序列化的过程内部封装了
- 我们只需要在定义pojo类时实现Serializable接口即可
- 一般会定义一 个公共的pojo模块,让生产者和消费者都依赖该模块。
User对象未实现Serializable接口
错误信息: 
解决办法:
User implements Serializable
下图为项目的基础架构图:
- interface的pom中写入pojo的依赖。service和web的pom中写入interface的依赖。
- User继承了序列化接口

4.2.2 地址缓存
注册中心挂了,服务是否可以正常访问?
- 可以,因为dubbo服务消费者在第一-次调用时,会将服务提供方地址缓存到本地,以后在调用则不会访问注册中心。
- 当服务提供者地址发生变化时,注册中心会通知服务消费者。

运行后,发现还是可以继续访问的。

4.2.3 超时
参考代码:4. 超时与重试

- 服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,这个时候,服务消费者会直等待下去。
- 在某个峰值时刻,大量的请求都在同时请求服务消费者,会造成线程的大量堆积,势必会造成雪崩。
- dubbo利用超时机制来解决这个问题,设置-个超时时间, 在这个时间段内,无法完成服务访问,则自动断开连接。
- 使用timeout属性配置超时时间,默认值1000,单位毫秒
@Service(timeout = 3000,retries=0)
代码实现:用sleep模拟服务器查询很慢:

4.2.4 重试
- 设置了超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。
- 如果出现网络抖动,则这一-次请求就会失败。
- Dubbo提供重试机制来避免类似问题的发生。
- 通过retries属性来设置重试次数。默认为2次
@Service(timeout = 3000,retries=2)
4.2.5 多版本
参考代码:5. 多版本 
**灰度发布:**当出现新功能时,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。
dubbo中使用version属性来设置和调用同一个接口的不同版本
生产者配置(通过@Service注解)
@Service(version="v2.0")
public class UserServiceImp12 implements UserService {...}
消费者配置(通过@Reference注解)
@Reference(version = "v2.0")
private UserService userService;
代码示例:

4.2.6 负载均衡
解释:当存在多个服务提供者时,消费者应该怎样消费,才能使多个服务提供者的负载均衡。
负载均衡策略(4种) :
-
**Random:**按权重随机,默认值。按权重设置随机概率。 -
RoundRobin: 按权重轮询。 -
LeastActive: 最少活跃调用数,相同活跃数的随机。 -
**ConsistentHash: **一致性Hash,相同参数的请求总是发到同一提供者。
服务提供者配置
@Service(weight = 100)
public class UserServiceImp12 implements UserService {...}
application.xml 配置parameter key
消费者配置
@Reference(loadbalance = "random")
private UserService userService;
代码参考:

4.2.7 集群容错
解释:如果某一个服务器出错了,则选择执行什么策略。是换其他服务器重试,还是直接返回失败等等。
集群容错模式: **Failover Cluster:**失败重试。默认值。当出现失败,重试其它服务器,默认重试2次,使用retries配置。一般用于读操作 **Failfast Cluster 😗*快速失败,发起-次调用,失败立即报错。通常用于写操作。 **Failsafe Cluster:**失败安全,出现异常时,直接忽略。返回一个空结果。 **Failback Cluster:**失败自动恢复,后台记录失败请求,定时重发。 **Forking Cluster 😗*并行调用多个服务器,只要一个成功即返回。 Broadcast Cluster: 广播调用所有提供者,逐个调用,任意一台报错则报错。
消费者配置
@Reference(cluster = "failover")
private UserService userService;
代码示例: 
4.2.8 服务降级
服务降级:当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作
服务降级方式: mock= force:return null:表示消费方对该服务的方法调用都直接返回null值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
mock=fail:return null:表示消费方对该服务的方法调用在失败后,再返回null值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响
消费方配置
@Reference(mock ="force :return null")
private UserService userService;
Zookeeper部分
第一章 初识Zookeeper
1.1 Zookeeper概念
-
Zookeeper 是 Apache Hadoop 项目下的一个子项目,是一个树形目录服务。 -
Zookeeper 翻译过来就是 动物园管理员,他是用来管 Hadoop(大象)、Hive(蜜蜂)、Pig(小 猪)的管理员。简称zk -
Zookeeper 是一个分布式的、开源的分布式应用程序的协调服务。 -
Zookeeper提供的功能主要包括
- 配置管理(增删改查)
- 分布式锁(常规锁无效,因为是不同机器(常规锁由JDK提供),不同集群,因此要采用分布式锁)
- 集群管理
第二章 Zookeeper安装与配置
见3.1
第三章 Zookeeper命令操作
3.1 Zookeeper命令操作数据类型
-
ZooKeeper 是一个树形目录服务,其数据模型和Unix的文件系统目录树很类似,拥有一个层次化结构。 -
这里面的每一个节点都被称为: ZNode,每个节点上都会保存自己的数据和节点信息。 -
节点可以拥有子节点,同时也允许少量(1MB)数据存储在该节点之下。 -
节点可以分为四大类:
- PERSISTENT 持久化节点
- EPHEMERAL 临时节点 :-e (服务端关闭后就消失了)
- PERSISTENT_SEQUENTIAL 持久化顺序节点 :-s (即节点后跟序号)
- EPHEMERAL_SEQUENTIAL 临时顺序节点 :-es

3.2 Zookeeper命令操作服务端命令
进入到Zookeeper/bin中,可执行如下操作
-
启动 ZooKeeper 服务: ./zkServer.sh start -
查看 ZooKeeper 服务状态: ./zkServer.sh status -
停止 ZooKeeper 服务: ./zkServer.sh stop -
重启 ZooKeeper 服务: ./zkServer.sh restart
3.3 Zookeeper客户端常用命令
概述:连接上Server(服务端)后,操作节点。 
./zkCli.sh –server ip:port
quit
help
ls 目录
create /节点path value
例子: create /app1
get /节点path
set /节点path value
delete /节点path
deleteall /节点path
3.4 客户端命令-创建临时有序节点
create -e /节点path value
create -s /节点path value
ls –s /节点path
以上的命令也侧面体现出了Zookeeper的作用,即持久化or非持久化存储数据、配置管理、分布式锁、集群管理(节点储存信息,树形结构、方便查找)
第四章 ZooKeeper JavaAPI 操作
4.1 Curator介绍
其核心就是对节点的操作,对节点的操作贯穿配置管理、分布式锁、集群搭建三大模块
-
Curator 是 Apache ZooKeeper 的Java客户端库。 -
常见的ZooKeeper Java API :
- 原生Java API
- ZkClient
- Curator
-
Curator 项目的目标是简化 ZooKeeper 客户端的使用。 -
Curator 最初是 Netfix 研发的,后来捐献了 Apache 基金会,目前是 Apache 的顶级项目。 -
官网:http://curator.apache.org/
4.2 基本操作(配置管理)
操作-建立连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kF4w6Jb7-1626871686069)(/Users/zhanglong/Library/Application Support/typora-user-images/image-20210720110747301.png)]
具体代码见资料
1、搭建项目
创建项目curator-zk
引入pom和日志文件
资料文件夹下pom.xml和log4j.properties
2、创建测试类,使用curator连接zookeeper
@Before
public void testConnect() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
client = CuratorFrameworkFactory.builder()
.connectString("192.168.200.130:2181")
.sessionTimeoutMs(60 * 1000)
.connectionTimeoutMs(15 * 1000)
.retryPolicy(retryPolicy)
.namespace("itheima")
.build();
client.start();
}
操作-创建节点
@Test
public void testCreate() throws Exception {
String path = client.create().forPath("/app2", "hehe".getBytes());
System.out.println(path);
}
@Test
public void testCreate2() throws Exception {
String path = client.create().forPath("/app1");
System.out.println(path);
}
@Test
public void testCreate3() throws Exception {
String path = client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3");
System.out.println(path);
}
@Test
public void testCreate4() throws Exception {
String path = client.create().creatingParentsIfNeeded().forPath("/app4/p1");
System.out.println(path);
}
操作-查询节点
@Test
public void testGet1() throws Exception {
byte[] data = client.getData().forPath("/app1");
System.out.println(new String(data));
}
@Test
public void testGet2() throws Exception {
List<String> path = client.getChildren().forPath("/");
System.out.println(path);
}
@Test
public void testGet3() throws Exception {
Stat status = new Stat();
System.out.println(status);
client.getData().storingStatIn(status).forPath("/app1");
System.out.println(status);
}
操作-修改节点
@Test
public void testSet() throws Exception {
client.setData().forPath("/app1", "itcast".getBytes());
}
@Test
public void testSetForVersion() throws Exception {
Stat status = new Stat();
client.getData().storingStatIn(status).forPath("/app1");
int version = status.getVersion();
System.out.println(version);
client.setData().withVersion(version).forPath("/app1", "hehe".getBytes());
}
操作-删除节点
@Test
public void testDelete() throws Exception {
client.delete().forPath("/app1");
}
@Test
public void testDelete2() throws Exception {
client.delete().deletingChildrenIfNeeded().forPath("/app4");
}
@Test
public void testDelete3() throws Exception {
client.delete().guaranteed().forPath("/app2");
}
@Test
public void testDelete4() throws Exception {
client.delete().guaranteed().inBackground(new BackgroundCallback(){
@Override
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
System.out.println("我被删除了~");
System.out.println(event);
}
}).forPath("/app1");
}
操作-Watch监听概述
-
ZooKeeper 允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是 ZooKeeper 实现分布式协调服务的重要特性。 -
ZooKeeper 中引入了Watcher机制来实现了发布/订阅功能能,能够让多个订阅者同时监听某一个对象,当一个对象自身状态变化时,会通知所有订阅者。 -
ZooKeeper 原生支持通过注册Watcher来进行事件监听,但是其使用并不是特别方便
? 需要开发人员自己反复注册Watcher,比较繁琐。
监听-NodeCache
@Test
public void testNodeCache() throws Exception {
final NodeCache nodeCache = new NodeCache(client,"/app1");
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
System.out.println("节点变化了~");
byte[] data = nodeCache.getCurrentData().getData();
System.out.println(new String(data));
}
});
nodeCache.start(true);
while (true){
}
}
监听-PathChildrenCache
@Test
public void testPathChildrenCache() throws Exception {
PathChildrenCache pathChildrenCache = new PathChildrenCache(client,"/app2",true);
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() { @Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
System.out.println("子节点变化了~");
System.out.println(event);
PathChildrenCacheEvent.Type type = event.getType();
if(type.equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
System.out.println("数据变了!!!");
byte[] data = event.getData().getData();
System.out.println(new String(data));
}
}
});
pathChildrenCache.start();
while (true){
}
}
监听-TreeCache
@Test
public void testTreeCache() throws Exception {
TreeCache treeCache = new TreeCache(client,"/app2");
treeCache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
System.out.println("节点变化了");
System.out.println(event);
}
});
treeCache.start();
while (true){
}
}
4.3 Zookeeper分布式锁
核心实现原理:临时顺序节点。
分布式锁概念
-
在我们进行单机应用开发,涉及并发同步的时候,我们往往采用synchronized或者Lock的方式来解决多线程间的代码同步问题,这时多线程的运行都是在同一个JVM之下,没有任何问题。 -
但当我们的应用是分布式集群工作的情况下,属于多JVM下的工作环境,跨JVM之间已经无法通过多线程的锁解决同步问题。 -
那么就需要一种更加高级的锁机制,来处理种跨机器的进程之间的数据同步问题——这就是分布式锁。
不同架构层面的分布式锁实现 
分布式锁原理

- 核心思想:当客户端要获取锁,则创建节点,使用完锁,则删除该节点。
- 客户端获取锁时,在lock节点下创建临时顺序节点。
- 然后获取lock下面的所有子节点,客户端获取到所有的子节点之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁。使用完锁后,将该节点删除。
- 如果发现自己创建的节点并非lock所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,同时对其注册事件监听器,监听删除事件。
- 如果发现比自己小的那个节点被删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是lock子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。
原理:以判断是否获取到最后一个节点来决定获取顺序,或者是否可以获取,这样就可以保证在同一时间该数据只被一个进程使用
分布式锁-模拟12306售票案例
总结一下:和正常锁的用法一样,不过调用了不同方法。因为它都为我们封装好了。
Curator实现分布式锁API
-
在Curator中有五种锁方案:
-
InterProcessSemaphoreMutex:分布式排它锁(非可重入锁) -
InterProcessMutex:分布式可重入排它锁 -
InterProcessReadWriteLock:分布式读写锁 -
InterProcessMultiLock:将多个锁作为单个实体管理的容器(单独设置一个容器,把多个锁放到这个容器里管理) -
InterProcessSemaphoreV2:共享信号量(如信号量设为多少,就允许多少人同时访问)
1、创建线程进行加锁设置
public class Ticket12306 implements Runnable{
private int tickets = 10;
private InterProcessMutex lock ;
@Override
public void run() {
while(true){
try {
lock.acquire(3, TimeUnit.SECONDS);
if(tickets > 0){
System.out.println(Thread.currentThread()+":"+tickets);
Thread.sleep(100);
tickets--;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
lock.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
2、创建连接,并且初始化锁
public Ticket12306(){
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("192.168.149.135:2181")
.sessionTimeoutMs(60 * 1000)
.connectionTimeoutMs(15 * 1000)
.retryPolicy(retryPolicy)
.build();
client.start();
lock = new InterProcessMutex(client,"/lock");
}
3、运行多个线程进行测试
public class LockTest {
public static void main(String[] args) {
Ticket12306 ticket12306 = new Ticket12306();
Thread t1 = new Thread(ticket12306,"携程");
Thread t2 = new Thread(ticket12306,"飞猪");
t1.start();
t2.start();
}
}
4.4 ZooKeeper 集群搭建
集群介绍
Leader选举:
搭建要求
真实的集群是需要部署在不同的服务器上的,但是在我们测试时同时启动很多个虚拟机内存会吃不消,所以我们通常会搭建伪集群,也就是把所有的服务都搭建在一台虚拟机上,用端口进行区分。
我们这里要求搭建一个三个节点的Zookeeper集群(伪集群)。
准备工作
重新部署一台虚拟机作为我们搭建集群的测试服务器。
(1)安装JDK 【此步骤省略】。
(2)Zookeeper压缩包上传到服务器(put命令)
(3)将Zookeeper解压 ,建立/usr/local/zookeeper-cluster目录,将解压后的Zookeeper复制到以下三个目录
/usr/local/zookeeper-cluster/zookeeper-1
/usr/local/zookeeper-cluster/zookeeper-2
/usr/local/zookeeper-cluster/zookeeper-3
[root@localhost ~]
[root@localhost ~]
[root@localhost ~]
[root@localhost ~]
(4)创建data目录 ,并且将 conf下zoo_sample.cfg 文件改名为 zoo.cfg
mkdir /usr/local/zookeeper-cluster/zookeeper-1/data
mkdir /usr/local/zookeeper-cluster/zookeeper-2/data
mkdir /usr/local/zookeeper-cluster/zookeeper-3/data
mv /usr/local/zookeeper-cluster/zookeeper-1/conf/zoo_sample.cfg /usr/local/zookeeper-cluster/zookeeper-1/conf/zoo.cfg
mv /usr/local/zookeeper-cluster/zookeeper-2/conf/zoo_sample.cfg /usr/local/zookeeper-cluster/zookeeper-2/conf/zoo.cfg
mv /usr/local/zookeeper-cluster/zookeeper-3/conf/zoo_sample.cfg /usr/local/zookeeper-cluster/zookeeper-3/conf/zoo.cfg
(5) 配置每一个Zookeeper 的dataDir 和 clientPort 分别为2181 2182 2183
修改/usr/local/zookeeper-cluster/zookeeper-1/conf/zoo.cfg
vim /usr/local/zookeeper-cluster/zookeeper-1/conf/zoo.cfg
clientPort=2181
dataDir=/usr/local/zookeeper-cluster/zookeeper-1/data
修改/usr/local/zookeeper-cluster/zookeeper-2/conf/zoo.cfg
vim /usr/local/zookeeper-cluster/zookeeper-2/conf/zoo.cfg
clientPort=2182
dataDir=/usr/local/zookeeper-cluster/zookeeper-2/data
修改/usr/local/zookeeper-cluster/zookeeper-3/conf/zoo.cfg
vim /usr/local/zookeeper-cluster/zookeeper-3/conf/zoo.cfg
clientPort=2183
dataDir=/usr/local/zookeeper-cluster/zookeeper-3/data
配置集群
(1)在每个zookeeper的 data 目录下创建一个 myid 文件,内容分别是1、2、3 。这个文件就是记录每个服务器的ID
echo 1 >/usr/local/zookeeper-cluster/zookeeper-1/data/myid
echo 2 >/usr/local/zookeeper-cluster/zookeeper-2/data/myid
echo 3 >/usr/local/zookeeper-cluster/zookeeper-3/data/myid
(2)在每一个zookeeper 的 zoo.cfg配置客户端访问端口(clientPort)和集群服务器IP列表。
集群服务器IP列表如下
vim /usr/local/zookeeper-cluster/zookeeper-1/conf/zoo.cfg
vim /usr/local/zookeeper-cluster/zookeeper-2/conf/zoo.cfg
vim /usr/local/zookeeper-cluster/zookeeper-3/conf/zoo.cfg
server.1=192.168.149.135:2881:3881
server.2=192.168.149.135:2882:3882
server.3=192.168.149.135:2883:3883
解释:server.服务器ID=服务器IP地址:服务器之间通信端口:服务器之间投票选举端口(根据ip地址来判断是否是同一个集群)
启动集群
启动集群就是分别启动每个实例。
/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh start
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh start
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh start
启动后我们查询一下每个实例的运行状态
/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh status
先查询第一个服务
Mode为follower表示是跟随者(从)
再查询第二个服务Mod 为leader表示是领导者(主)
查询第三个为跟随者(从)

故障测试
(1)首先我们先测试如果是从服务器挂掉,会怎么样
把3号服务器停掉,观察1号和2号,发现状态并没有变化
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh stop
/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status
由此得出结论,3个节点的集群,从服务器挂掉,集群正常
(2)我们再把1号服务器(从服务器)也停掉,查看2号(主服务器)的状态,发现已经停止运行了。
/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh stop
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status
由此得出结论,3个节点的集群,2个从服务器都挂掉,主服务器也无法运行。因为可运行的机器没有超过集群总数量的半数。
(3)我们再次把1号服务器启动起来,发现2号服务器又开始正常工作了。而且依然是领导者。
/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh start
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status
(4)我们把3号服务器也启动起来,把2号服务器停掉,停掉后观察1号和3号的状态。
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh start
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh stop
/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh status
发现新的leader产生了~
由此我们得出结论,当集群中的主服务器挂了,集群中的其他服务器会自动进行选举状态,然后产生新得leader
(5)我们再次测试,当我们把2号服务器重新启动起来启动后,会发生什么?2号服务器会再次成为新的领导吗?我们看结果
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh start
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh status
我们会发现,2号服务器启动后依然是跟随者(从服务器),3号服务器依然是领导者(主服务器),没有撼动3号服务器的领导地位。
由此我们得出结论,当领导者产生后,再次有新服务器加入集群,不会影响到现任领导者。
Zookeeper 核心理论
Zookeepe集群角色
在ZooKeeper集群服中务中有三个角色:
-
Leader 领导者 :
- 处理事务请求
- 集群内部各服务器的调度者
-
Follower 跟随者 :
- 处理客户端非事务请求,转发事务请求给Leader服务器
- 参与Leader选举投票
-
Observer 观察者:
- 处理客户端非事务请求,转发事务请求给Leader服务器
示意图如下:

相关注解
@Service
引入路径:import org.apache.dubbo.config.annotation.Service;
作用:将这个类提供的方法对外发布,将访问的地址、ip、端口、路径注册到注册中心里。
@Reference
翻译:参考的,涉及的。
代替@Autowired。
@Autowired为本地注入,@Reference为远程注入。
储备知识
QoS服务
QoS(Quality of Service,服务质量)指一个网络能够利用各种基础技术,为指定的网络通信提供更好的服务能力,是网络的一种安全机制, 是用来解决网络延迟和阻塞等问题的一种技术。dubbo为用户提供类似的网络服务用来online和offline service来解决网络延迟,阻塞等问题。
QoS配置:
dubbo的QoS是默认开启的,端口为22222,可以通过配置修改端口
<dubbo:application name="demo-provider">
<dubbo:parameter key="qos.port" value="33333"/>
</dubbo:application>
或者关闭服务
<dubbo:application name="demo-provider">
<dubbo:parameter key="qos.enable" value="false"/>
</dubbo:application>
为了安全考虑,dubbo的qos默认是只支持本地连接的,如果要开启任意ip可连接,需做如下配置
<dubbo:application name="demo-provider">
<dubbo:parameter key="qos.port" value="33333"/>
<dubbo:parameter key="qos.accept.foreign.ip" value="false"/>
</dubbo:application>
Node.js是干嘛的
如果你去年注意过技术方面的新闻,我敢说你至少看到node.js不下一两次。那么问题来了“node.js是什么?”。有些人没准会告诉你“这是一种通过JavaScript语言开发web服务端的东西”。如果这种晦涩解释还没把你搞晕,你没准会接着问:“为什么我们要用node.js?”,别人一般会告诉你:node.js有非阻塞,事件驱动I/O等特性,从而让高并发(high concurrency)在的轮询(Polling)和comet构建的应用中成为可能。
当你看完这些解释觉得跟看天书一样的时候,你估计也懒得继续问了。不过没事。我这篇文章就是在避开高端术语的同时,帮助你你理解node.js的。
浏览器给网站发请求的过程一直没怎么变过。当浏览器给网站发了请求。服务器收到了请求,然后开始搜寻被请求的资源。如果有需要,服务器还会查询一下数据库,最后把响应结果传回浏览器。不过,在传统的web服务器中(比如Apache),每一个请求都会让服务器创建一个新的进程来处理这个请求。
后来有了Ajax。有了Ajax,我们就不用每次都请求一个完整的新页面了,取而代之的是,每次只请求需要的部分页面信息就可以了。这显然是一个进步。但是比如你要建一个FriendFeed这样的社交网站(类似人人网那样的刷朋友新鲜事的网站),你的好友会随时的推送新的状态,然后你的新鲜事会实时自动刷新。要达成这个需求,我们需要让用户一直与服务器保持一个有效连接。目前最简单的实现方法,就是让用户和服务器之间保持长轮询(long polling)。
HTTP请求不是持续的连接,你请求一次,服务器响应一次,然后就完了。长轮训是一种利用HTTP模拟持续连接的技巧。具体来说,只要页面载入了,不管你需不需要服务器给你响应信息,你都会给服务器发一个Ajax请求。这个请求不同于一般的Ajax请求,服务器不会直接给你返回信息,而是它要等着,直到服务器觉得该给你发信息了,它才会响应。比如,你的好友发了一条新鲜事,服务器就会把这个新鲜事当做响应发给你的浏览器,然后你的浏览器就刷新页面了。浏览器收到响应刷新完之后,再发送一条新的请求给服务器,这个请求依然不会立即被响应。于是就开始重复以上步骤。利用这个方法,可以让浏览器始终保持等待响应的状态。虽然以上过程依然只有非持续的Http参与,但是我们模拟出了一个看似持续的连接状态
我们再看传统的服务器(比如Apache)。每次一个新用户连到你的网站上,你的服务器就得开一个连接。每个连接都需要占一个进程,这些进程大部分时间都是闲着的(比如等着你好友发新鲜事,等好友发完才给用户响应信息。或者等着数据库返回查询结果什么的)。虽然这些进程闲着,但是照样占用内存。这意味着,如果用户连接数的增长到一定规模,你服务器没准就要耗光内存直接瘫了。
这种情况怎么解决?解决方法就是刚才上边说的:非阻塞和事件驱动。这些概念在我们谈的这个情景里面其实没那么难理解。你把非阻塞的服务器想象成一个loop循环,这个loop会一直跑下去。一个新请求来了,这个loop就接了这个请求,把这个请求传给其他的进程(比如传给一个搞数据库查询的进程),然后响应一个回调(callback)。完事了这loop就接着跑,接其他的请求。这样下来。服务器就不会像之前那样傻等着数据库返回结果了。
如果数据库把结果返回来了,loop就把结果传回用户的浏览器,接着继续跑。在这种方式下,你的服务器的进程就不会闲着等着。从而在理论上说,同一时刻的数据库查询数量,以及用户的请求数量就没有限制了。服务器只在用户那边有事件发生的时候才响应,这就是事件驱动。
FriendFeed是用基于Python的非阻塞框架Tornado (知乎也用了这个框架) 来实现上面说的新鲜事功能的。不过,Node.js就比前者更妙了。Node.js的应用是通过javascript开发的,然后直接在Google的变态V8引擎上跑。用了Node.js,你就不用担心用户端的请求会在服务器里跑了一段能够造成阻塞的代码了。因为javascript本身就是事件驱动的脚本语言。你回想一下,在给前端写javascript的时候,更多时候你都是在搞事件处理和回调函数。javascript本身就是给事件处理量身定制的语言。
Node.js还是处于初期阶段。如果你想开发一个基于Node.js的应用,你应该会需要写一些很底层代码。但是下一代浏览器很快就要采用WebSocket技术了,从而长轮询也会消失。在Web开发里,Node.js这种类型的技术只会变得越来越重要。
总结:
Apache:进程独立,每个进程占用一定资源,无法释放。
ajax:长轮询
node.js:非阻塞、事件驱动。
项目打包的作用
如果用Linux语句运行项目,就一定要先打包,因为我们没有像Idea一样把项目整合在一起的工具, 因此只能把它打成一个Jar包,再运行。
序列化和反序列化
序列化是指将Java对象转换为字节序列的过程,而反序列化则是将字节序列转换为Java对象的过程。
Java对象序列化是将实现了Serializable接口的对象转换成一个字节序列,能够通过网络传输、文件存储等方式传输 ,传输过程中却不必担心数据在不同机器、不同环境下发生改变,也不必关心字节的顺序或其他任何细节,并能够在以后将这个字节序列完全恢复为原来的对象(恢复这一过程称之为反序列化)。
对象的序列化是非常有趣的,因为利用它可以实现轻量级持久性,“持久性”意味着一个对象的生存周期不单单取决于程序是否正在运行,它可以生存于程序的调用之间。通过将一个序列化对象写入磁盘,然后在重新调用程序时恢复该对象,从而达到实现对象的持久性的效果。
本质上讲,序列化就是把实体对象状态按照一定的格式写入到有序字节流,反序列化就是从有序字节流重建对象,恢复对象状态。
|