Java技术堆栈
2023-12-11 12:28:28 0 举报
AI智能生成
个人学术技术领域的总结,持续不断地在发展中。
作者其他创作
大纲/内容
计算机基础
计算机操作系统
数据结构与算法
计算机组成原理
计算机网络
大数据
流处理
Spark
批处理
Flink
运维与测试
k8s
常用命令
查看POD
kebuctl get pod
kebuctl get pod -o -wide
kebuctl describe pod <pod name>
删除POD
kebuctl detele pod --all/<pod name>
概念
POD
Label
Replication Controller
Horizontal Pod Autoscaler
Volume
Namespace
Service
Docker
虚拟化
虚拟化(英语:Virtualization)是一种资源管理技术,是将计算机的各种
实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来,打破实体结
构间的不可切割的障碍,使用户可以比原本的组态更好的方式来应用这些资源。这些资
源的新虚拟部份是不受现有资源的架设方式,地域或物理组态所限制。一般所指的虚拟
化资源包括计算能力和资料存储。
实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来,打破实体结
构间的不可切割的障碍,使用户可以比原本的组态更好的方式来应用这些资源。这些资
源的新虚拟部份是不受现有资源的架设方式,地域或物理组态所限制。一般所指的虚拟
化资源包括计算能力和资料存储。
介绍
Docker是一个开发,运输和运行应用程序的开放平台。 Docker使您可以将应用程序与基
础架构分离,以便快速交付软件。 使用Docker,您可以像管理应用程序一样管理基础架
构(OS)。 通过利用Docker的方法快速发送,测试和部署代码,您可以显着减少编写代
码和在生产中运行代码之间的延迟。(代码改了)
础架构分离,以便快速交付软件。 使用Docker,您可以像管理应用程序一样管理基础架
构(OS)。 通过利用Docker的方法快速发送,测试和部署代码,您可以显着减少编写代
码和在生产中运行代码之间的延迟。(代码改了)
优缺点
优点
加速本地开发和构建流程,使其更加高效、更加轻量化。
能够让独立的服务或应用程序在不同的环境中,得到相同的运行结果。这一点在面向
服务的架构和重度依赖微型服务的部署由其实用。
服务的架构和重度依赖微型服务的部署由其实用。
用Docker创建隔离的环境来进行测试。
Docker可以让开发者先在本机上构建一个复杂的程序或架构来进行测试,而不是一
开始就在生产环境部署、测试。
开始就在生产环境部署、测试。
构建一个多用户的平台即服务(PaaS)基础设施
为开发、测试提供一个轻量级的独立的沙盒环境
容器和虚拟机的区别
容器
容器是一个应用层抽象,用于将代码和依赖资源打包在一起。 多个容器可以在同一
台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行 。与
虚拟机相比, 容器占用的空间较少(容器镜像大小通常只有几十兆),瞬间就能完
成启动 。
台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行 。与
虚拟机相比, 容器占用的空间较少(容器镜像大小通常只有几十兆),瞬间就能完
成启动 。
虚拟机
容器是一个应用层抽象,用于将代码和依赖资源打包在一起。 多个容器可以在同一
台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行 。与
虚拟机相比, 容器占用的空间较少(容器镜像大小通常只有几十兆),瞬间就能完
成启动 。
台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行 。与
虚拟机相比, 容器占用的空间较少(容器镜像大小通常只有几十兆),瞬间就能完
成启动 。
版本
企业版
社区版
安装
docker对Ubuntu的支持是最好的
如果是 CentOS:安装docker 建议7.x及以上版本
Docker支持在多种平台上使用,包括Mac、Windows、Cloud以及Linux系统上等
常用命令
启动
systemctl status docker
查看docker详细信息
docker info
查看docker版本
docker --version
镜像
介绍
Docker 镜像是容器的基础。镜像是一个有序集合,其中包含根文件系统更改和在容器运
行时中使用的相应执行参数。镜像通常 包含堆叠在彼此之上的联合分层文件系统。镜像
没有状态并且始终不会发生更改。 当运行容器时,使用的镜像如果在本地中不存在,
docker 就会自动从 docker 镜像仓库中下载,默认是从 Docker Hub 公共镜像源下载
行时中使用的相应执行参数。镜像通常 包含堆叠在彼此之上的联合分层文件系统。镜像
没有状态并且始终不会发生更改。 当运行容器时,使用的镜像如果在本地中不存在,
docker 就会自动从 docker 镜像仓库中下载,默认是从 Docker Hub 公共镜像源下载
文件
这些镜像都是存储在Docker宿主机的/var/lib/docker目录下。
配置镜像加速器
阿里云(先加入阿里云开发者平台:https://dev.aliyun.com)
docker中国加速器(https://www.docker‐cn.com
USTC加速器(https://lug.ustc.edu.cn/wiki/ ) 真正的公共服务(无需任何操
作)
作)
操作
sudo vim /etc/docker/daemon.json
{
"registry‐mirrors": ["https://cs913o6k.mirror.aliyuncs.com"]
}
sudo systemctl daemon‐reload
sudo systemctl restart docker
"registry‐mirrors": ["https://cs913o6k.mirror.aliyuncs.com"]
}
sudo systemctl daemon‐reload
sudo systemctl restart docker
命令
列出镜像
docker images
查找镜像
docker search 镜像名称
拉取镜像
docker pull 镜像名称[:version]
删除镜像
一个镜像
docker rmi 镜像名称/id
多个
docker rmi 镜像名称1/id1 镜像名称2/id2 ...
所有
docker rmi `docker images ‐q`
镜像的制作
两种方式
使用docker commit命令
使用docker build和Dockerfile文件
Dockerfile使用基本的基于DSL语法的指令来构建一个Docker镜像,之后使用docker
builder命令基于该Dockerfile中的指令构建一个新的镜像。
builder命令基于该Dockerfile中的指令构建一个新的镜像。
容器
介绍
容器是 docker 镜像的运行时实例
常用命令
创建容器
dcoker run -
‐i:交互式容器
‐t:tty,终端
‐d:后台运行,并且打印容器id
‐t:tty,终端
‐d:后台运行,并且打印容器id
进入容器
docker exec ‐it 容器名称/id /bin/bash (ps:exit,容器不会停止)
查询容器
docker ps:查看正在运行的容器
docker ps ‐a:查看运行过的容器(历史)
docker ps ‐l:最后一次运行的容器
docker ps ‐a:查看运行过的容器(历史)
docker ps ‐l:最后一次运行的容器
停止和启动容器
docker start 容器名称/id
docker stop 容器名称/id
docker stop 容器名称/id
查询容器的元数据
docker inspect 容器/镜像
删除容器
一个容器
docker rm 容器名称/id
多个容器
docker rm 容器名称1/id1 容器名称2/id2 ...
删除所有容器
docker rm `docker ps ‐a ‐q`
PS 无法查看正在运行的容器
查看容器日志
docker logs 容器名称/id
文件拷贝
docker cp 需要拷贝的文件或目录 容器名称:容器目录
例如:docker cp 1.txt c2:/root
例如:docker cp 1.txt c2:/root
目录挂载
我们可以在创建容器的时候,将宿主机的目录与容器内的目录进行映射,这样我们就可
以通过修改宿主机某个目录的文件从而去影响容器。
以通过修改宿主机某个目录的文件从而去影响容器。
创建容器 添加-v参数 后边为 宿主机目录:容器目录
docker run ‐id ‐‐name=c4 ‐v /opt/:/usr/local/myhtml centos
如果你共享的是多级的目录,可能会出现权限不足的提示
这是因为CentOS7中的安全模块selinux把权限禁掉了,我们需要添加参数 --
privileged=true 来解决挂载的目录没有权限的问题
privileged=true 来解决挂载的目录没有权限的问题
docker run ‐id ‐‐privileged=true ‐‐name=c4 ‐v /opt/:/usr/local/myhtml centos
仓库
介绍
Docker仓库(Repository)类似与代码仓库,是Docker集中存放镜像文件的地方
dockerHub
1、打开https://hub.docker.com/
2、注册账号:略
3、创建仓库(Create Repository):略
4、设置镜像标签
docker tag local‐image:tagname new‐repo:tagname(设置tag)
eg:docker tag hello‐world:latest 108001509033/test‐hello‐world:v1
5、登录docker hub
docker login(回车,输入账号以及密码)
6、推送镜像
docker push new‐repo:tagname
eg:docker push 108001509033/test‐hello‐world:v1
2、注册账号:略
3、创建仓库(Create Repository):略
4、设置镜像标签
docker tag local‐image:tagname new‐repo:tagname(设置tag)
eg:docker tag hello‐world:latest 108001509033/test‐hello‐world:v1
5、登录docker hub
docker login(回车,输入账号以及密码)
6、推送镜像
docker push new‐repo:tagname
eg:docker push 108001509033/test‐hello‐world:v1
阿里云
1、创建阿里云账号
2、创建命名空间
3、创建镜像仓库
4、操作指南
$ sudo docker login ‐‐username=[账号名称] registry.cn‐
hangzhou.aliyuncs.com
$ sudo docker tag [ImageId] registry.cn‐
hangzhou.aliyuncs.com/360buy/portal:[镜像版本号]
$ sudo docker push registry.cn‐hangzhou.aliyuncs.com/360buy/portal:[镜像版
本号
2、创建命名空间
3、创建镜像仓库
4、操作指南
$ sudo docker login ‐‐username=[账号名称] registry.cn‐
hangzhou.aliyuncs.com
$ sudo docker tag [ImageId] registry.cn‐
hangzhou.aliyuncs.com/360buy/portal:[镜像版本号]
$ sudo docker push registry.cn‐hangzhou.aliyuncs.com/360buy/portal:[镜像版
本号
私有仓库的搭建
启动Docker Registry,使用Docker官方提供的Registry镜像就可以搭建本地私有镜像
仓库,具体指令如下。
$ docker run ‐d \
‐p 5000:5000 \
‐‐restart=always \
‐‐name registry \
‐v /mnt/registry:/var/lib/registry \
registry:2
仓库,具体指令如下。
$ docker run ‐d \
‐p 5000:5000 \
‐‐restart=always \
‐‐name registry \
‐v /mnt/registry:/var/lib/registry \
registry:2
2、重命名镜像,之前推送镜像时,都是默认推送到远程镜像仓库,而本次是将指定镜像推送
到本地私有镜像仓库。由于推送到本地私有镜像仓库的镜像名必须符合“仓库IP:端口
号/repository”的形式,因此需要按照要求修改镜像名称,具体操作指令如下。
$ docker tag hello‐world:latest localhost:5000/myhellodocker
到本地私有镜像仓库。由于推送到本地私有镜像仓库的镜像名必须符合“仓库IP:端口
号/repository”的形式,因此需要按照要求修改镜像名称,具体操作指令如下。
$ docker tag hello‐world:latest localhost:5000/myhellodocker
3、推送镜像,本地私有镜像仓库搭建并启动完成,同时要推送的镜像也已经准备就绪后,就
可以将指定镜像推送到本地私有镜像仓库了,具体操作指令如下
$ docker push localhost:5000/myhellodocker
可以将指定镜像推送到本地私有镜像仓库了,具体操作指令如下
$ docker push localhost:5000/myhellodocker
4、查看本地仓库镜像
http://localhost:5000/v2/myhellodocker/tags/list (注意:使用该地址时注意镜
像名称)
由于做了目录挂载,因此可以在本地的该目录下查看:
/mnt/registry/docker/registry/v2/repositories
http://localhost:5000/v2/myhellodocker/tags/list (注意:使用该地址时注意镜
像名称)
由于做了目录挂载,因此可以在本地的该目录下查看:
/mnt/registry/docker/registry/v2/repositories
docker compose编排工具
docker可视化工具
数据结构与算法
前端
基础
JS
CSS
HTML
框架
Vue.js
React.JS
Angular.js
基础
面向对象
封装
继承
多态
集合
Map
线程安全
ConcurrentHashMap
初始容量默认为16段(Segment),使⽤分段锁设计
不对整个Map加锁,⽽是为每个Segment加锁
当多个对象存⼊同⼀个Segment时,才需要互斥
最理想状态为16个对象分别存⼊16个Segment,并⾏数量16
HashMap
TreeMap
List
线程安全的集合
CopyOnWriteArrayList
写有锁,读⽆锁,读写之间不阻塞,优于读写锁
线程安全的ArrayList,加强版读写分离
写⼊时,先copy⼀个容器副本、再添加新元素,最后替换引⽤
使用方式与ArrayList一样
写时复制
先从原有的数组中拷⻉⼀份出来,然后在新的数组做写操作,写完之后,
再将原来的数组引⽤指向到新数组
再将原来的数组引⽤指向到新数组
LinkedArrayList
有序
ArrayList
查询快
底层
Set
线程安全
CopyOnWriteArraySet
HashSet
TreeSet
异常
Exception
Error
运算
位运算
逻辑运算
正常计算
IO基础
网络编程
注解
线程
概念
是操作系统能够进行运算调度的最小单位
线程调度
分时调度
所有线程轮流使⽤ CPU 的使⽤权,平均分配每个线程占⽤ CPU 的时间
抢占式调度(JAVA使用)
优先让优先级⾼的线程使⽤ CPU,如果线程的优先级相同,那么会随机选择⼀个(线程随机性)
进程
概念
系统进行资源分配和调度的基本单位
区别
⼀个程序运⾏后⾄少有⼀个进程,⼀个进程中可以包含多个线程
分类
守护线程(后台线程)
setDaemon(true)
如果程序中所有前台线程都执⾏完毕了,后台线程会⾃动结束
垃圾回收器线程属于守护线程
用户线程(前台线程)
并行和并发
并发
指两个或多个事件在同⼀个时间段内发⽣
并行
指两个或多个事件在同⼀时刻发⽣(同时发⽣)
创建线程的方式
继承Thread
直接实例化使用
实现Runnab
直接使用,需借助Thread
实现Callable
有返回值
生命周期
新建状态(New)
实例化线程类,进入该状态
就绪状态(Runnable)
通过线程的start()方法进入就绪状态
执行状态(Running)
线程正在执行一些run()方法中的任务
阻塞状态(Blocked)
等待阻塞
运行过程中调用了Wait()方法
同步阻塞
线程在获取synchronized同步锁失败
其他阻塞
通过调⽤线程的sleep()或join()或发出了I/O请求
死亡状态(Dead)
线程常用的方法
join()
合并线程,当主线程需要用到子线程的结果,可以调用该方法,等子线程执行结束之后,继续主线程
sleep()
执行该方法是指定多长时间之后继续执行后面的程序
yield()
该方法只是让线程从运行状态(Running)回到就绪状态(Runnable),
但线程可能会立马重新抢占资源执行
但线程可能会立马重新抢占资源执行
优先级
设置setPriority()
优先级⾼的线程会获得较多的运⾏机会。优先级 : 只能反
映 线程 的 中或者是 紧急程度 , 不能决定 是否⼀定先执⾏
映 线程 的 中或者是 紧急程度 , 不能决定 是否⼀定先执⾏
线程优先级为1-10,默认为5,优先级越⾼
线程的安全问题
原因
多线程访问统一资源(共享资源、临界资源),产生数据不一致
解决方法
同步代码块
synchronized(对象){原子操作}
同步锁
锁可以是任意对象
多线程需要同一把锁
在任何时候,最多允许⼀个线程拥有同步锁,谁拿到锁就进⼊代码块,
其他的线程只能在外等着(BLOCKED)
其他的线程只能在外等着(BLOCKED)
同步⽅法
使⽤synchronized修饰的⽅法,就叫做同步⽅法,保证A线程执⾏该⽅法的时候,
其他线程只能在⽅法外等着
其他线程只能在⽅法外等着
锁机制
Lock
JDK5加⼊
方法
lock()
获取锁
tryLock()
尝试获取锁,获取锁成功为True,否则False,不阻塞
unLock()
释放锁
线程的通信
多个线程并发执⾏时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成⼀件任
务,并且我们希望他们有规律的执⾏, 那么多线程之间需要⼀些协调通信,以此来帮我们达到多线程共
同操作⼀份数据
务,并且我们希望他们有规律的执⾏, 那么多线程之间需要⼀些协调通信,以此来帮我们达到多线程共
同操作⼀份数据
等待唤醒机制
这是多个线程间的⼀种协作机制
方法
wait()
释放锁,进入等待状态
wait(Long time)
指定时间内进入等待,超过时间自动醒来
notify()
唤醒一个线程
notifyAll()
唤醒所有线程
死锁
多个线程同时被阻塞,它们中的⼀个或者全部都在等待某个资源被释放
线程池
概念
其实就是⼀个容纳多个线程的容器
优点
降低资源消耗
提⾼响应速度
提⾼线程的可管理性
使用
顶层接口
java.util.concurrent.Executor
java.util.concurrent.ExecutorService
java.util.concurrent.Executors
newFixedThreadPool 创建⼀个固定⻓度的线程池
newCachedThreadPool 创建⼀个可缓存的线程池
newScheduledThreadPool 定时线程池
newSingleThreadPoolExecutor 创建⼀个单线程的Executor,确保任务对了,串⾏执⾏
使用步骤
创建线程池对象
创建Runnable接⼝⼦类对象。(task)
提交Runnable接⼝⼦类对象。(take task)
关闭线程池(⼀般不做)
JDK新特性
8
Lambda表达式
匿名内部类
()->{}
特性
函数式接口
@FunctionalInterface注解
java.util.function
只有一个抽象方法
方法引用和构造器引用
Stream Api
常用方法
filter
判断是真的返回为一个新的流
map
将一个集合转换为另外一个集合
distinct
去重
limit
截取保留指定的位置之内的数据,从1开始
skip
跳过指定的条数,从1开始
collect
收集对象
接口中的默认方法和静态方法
新时间API
LocalDate
LocalDateTime
9&10
基本类型
并发编程
JMM模型
Java内存模型(Java Memory Model简称JMM)是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式
JVM比较
不同之处
概念
JMM描述的是一组规则,通过这组规则控制程序中各个变量在共享数据区域和私有数据区域的访问方式
JMM是围绕原子性,有序性、可见性展开
相同之处
共享数据区域和私有数据区域
联系
在JMM中主内存属于共享数据区域,从某个程度上讲应该包括了堆和方法区,而工作内存数据线程私有数据区域,
从某个程度上讲则应该包括程序计数器、虚拟机栈以及本地方法栈
从某个程度上讲则应该包括程序计数器、虚拟机栈以及本地方法栈
内存
主内存
主要存储的是Java实例对象,所有线程创建的实例对象都存放在主内存中,不管该实例
对象是成员变量还是方法中的本地变量(也称局部变量),当然也包括了共享的类信息、常
量、静态变量
对象是成员变量还是方法中的本地变量(也称局部变量),当然也包括了共享的类信息、常
量、静态变量
由于是共享数据区域,多条线程对同一个变量进行访问可能会发生线程安全问题
工作内存
主要存储当前方法的所有本地变量信息(工作内存中存储着主内存中的变量副本拷贝)
每个线程只能访问自己的工作内存
即线程中的本地变量对其它线程是不可见的,就算是两个线程执行的是同一段代码,
它们也会各自在自己的工作内存中创建属于当前线程的本地变量,
当然也包括了字节码行号指示器、相关Native方法的信息
它们也会各自在自己的工作内存中创建属于当前线程的本地变量,
当然也包括了字节码行号指示器、相关Native方法的信息
由于工作内存是每个线程的私有数据,线程间无法相互访问工作内存,
因此存储在工作内存的数据不存在线程安全问题
因此存储在工作内存的数据不存在线程安全问题
数据同步八大原子操作
lock(锁定)
作用于主内存的变量,把一个变量标记为一条线程独占状态
unlock(解锁)
作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后
的变量才可以被其他线程锁定
的变量才可以被其他线程锁定
read(读取)
作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存
中,以便随后的load动作使用
中,以便随后的load动作使用
load(载入)
作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工
作内存的变量副本中
作内存的变量副本中
use(使用)
作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎
assign(赋值)
作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内
存的变量
存的变量
store(存储)
作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存
中,以便随后的write的操作
中,以便随后的write的操作
write(写入)
作用于工作内存的变量,它把store操作从工作内存中的一个变量的值
传送到主内存的变量中
传送到主内存的变量中
特性
原子性
原子性指的是一个操作是不可中断的,即使是在多线程环境下,一个操作一旦开始就不会被其他线程影响
基本数据类型都是安全的,long和double在32位虚拟机中存在不安全
可以通过 synchronized和Lock实现原子性。
因为synchronized和Lock能够保证任一时刻只有一个线程访问该代码块
因为synchronized和Lock能够保证任一时刻只有一个线程访问该代码块
可见性
可见性指的是当一个线程修改了某个共享变量
的值,其他线程是否能够马上得知这个修改的值
的值,其他线程是否能够马上得知这个修改的值
volatile关键字保证可见性
有序性
有序性是指对于单线程的执行代码,我们总是认为代码的执行是按顺序依次执行的,这
样的理解并没有毛病,毕竟对于单线程而言确实如此,但对于多线程环境,则可能出现乱序
现象,因为程序编译成机器码指令后可能会出现指令重排现象,重排后的指令与原指令的顺
序未必一致
样的理解并没有毛病,毕竟对于单线程而言确实如此,但对于多线程环境,则可能出现乱序
现象,因为程序编译成机器码指令后可能会出现指令重排现象,重排后的指令与原指令的顺
序未必一致
可以通过volatile关键字来保证一定的“有序性”
另外可以通过synchronized和Lock来保证有序性,很显然,
synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行
同步代码,自然就保证了有序性
synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行
同步代码,自然就保证了有序性
指令重排序
java语言规范规定JVM线程内部维持顺序化语义。即只要程序的最终结果与它顺序化情况的结果相等,
那么指令的执行顺序可以与代码顺序不一致,此过程叫指令的重排序
那么指令的执行顺序可以与代码顺序不一致,此过程叫指令的重排序
意义
JVM能根据处理器特性(CPU多级缓存系统、多核处理器等)适当的对机器指令进行重排序,
使机器指令能更符合CPU的执行特性,最大限度的发挥机器性能
使机器指令能更符合CPU的执行特性,最大限度的发挥机器性能
as-if-serial语义
不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。
编译器、runtime和处理器都必须遵守as-if-serial语义
编译器、runtime和处理器都必须遵守as-if-serial语义
编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。
但是,如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重排序
但是,如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重排序
happens-before 原则
关键字
volatile
volatile是Java虚拟机提供的轻量级的同步机制
作用
保证被volatile修饰的共享变量对所有线程总数可见的,也就是当一个线程修改
了一个被volatile修饰共享变量的值,新值总是可以被其他线程立即得知
禁止指令重排序优化
volatile无法保证原子性
内存屏障
内存屏障,又称内存栅栏,是一个CPU指令
作用
保证特定操作的执行顺序
保证某些变量的内存可见性(利用该特性实现volatile的内存可见性)
Memory Barrier
如果在指令间插入一条Memory Barrier则会告诉编译器和CPU,不管什么指令都不能和这条Memory Barrier指令重排序,
也就是说通过插入内存屏障禁止在内存屏障前后的指令执行重排序优化
也就是说通过插入内存屏障禁止在内存屏障前后的指令执行重排序优化
强制刷出各种CPU的缓存数据,因此任何CPU上的线程都能读取到这些数据的最新版本
安全问题
所有的并发模式在解决线程安全问题时,采用的方案都是序列化访问临界资源。即在同一时刻,只能有一个线程访问临
界资源,也称作同步互斥访问
界资源,也称作同步互斥访问
synchronized
内置锁是一种对象锁(锁的是对象而非引用),作用粒度是对象,可以用来实现对临界资源的同步互斥访问,是可重入的
加锁的方式
同步实例方法,锁是当前实例对象
同步类方法,锁是当前类对象
同步代码块,锁是括号里面的对象
原理
基于JVM内置锁实现,通过内部对象Monitor(监视器锁)实现,基于进入与退出Monitor对象实现方法与代码
块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁)实现,它是一个重量级锁性能较低
块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁)实现,它是一个重量级锁性能较低
JVM内置锁在1.5之后版本做了重大的优化,如锁粗化(Lock Coarsening)、锁消除(Lock Elimination)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking)、适应性自旋(Adaptive Spinning)等技术来减少锁操作的开销
synchronized关键字被编译成字节码后会被翻译成monitorenter 和 monitorexit 两条指令
分别在同步块逻辑代码的起始位置与结束位置
分别在同步块逻辑代码的起始位置与结束位置
锁
可重入锁
可重入锁的字面意思是“可以重新进入的锁”,即允许同一个线程多次获取同一把锁
synchronized和ReentrantLock
悲观锁
乐观锁
线程池
工作流程
判断核心线程池的线程是否都在执行任务
创建新的工作线程执行
判断阻塞队列是否已满
将新提交的任务存储在阻塞队列中
判断线程池的线程是否处于工作状态
创建新的工作线程执行任务
饱和策略(拒绝策略)处理
ThreadPoolExecutor
原理流程
判断当前的线程少于corePoolSize
创建新的工作线程来执行任务
执行此步需要获取全局锁
判断当前运行的线程大于或等于corePoolSize,而且BlockingQueue未满
添加到BlockingQueue中
如果BlockingQueue已满,而且当前运行的线程小于maximumPoolSize
创建新的工作线程来执行任务
执行这一步骤需要获取全局锁
果当前运行的线程大于或等于maximumPoolSize,任务将被拒绝
调用RejectExecutionHandler.rejectExecution()方法。即调用饱和策略对任务进行处理
工作线程(Worker)
线程池在创建线程时,会将线程封装成工作线程Woker
Woker在执行完任务后,不是立即销毁而是循环获取阻塞队列里的任务来执行
构造参数
new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
7个核心参数
corePoolSize(线程池的基本大小)
提交一个任务到线程池时,线程池会创建一个新的线程来执行任务
即使有空闲的基本线程能执行该任务,也会创建新的线程
如果线程池中的线程数已经大于或等于corePoolSize,则不会创建新的线程
如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程
maximumPoolSize(线程池的最大数量)
阻塞队列已满,线程数小于maximumPoolSize便可以创建新的线程执行任务
如果使用无界的阻塞队列,该参数没有什么效果
workQueue(工作队列)
ArrayBlockingQueue
基于数组结构的有界阻塞队列,按FIFO(先进先出)原则对任务进行排序。使用该队列,线程池中能创建的最大线程数为maximumPoolSize
LinkedBlockingQueue
基于链表结构的无界阻塞队列,按FIFO(先进先出)原则对任务进行排序,吞吐量高于ArrayBlockingQueue。使用该队列,线程池中能创建的最大线程数为corePoolSize。静态工厂方法 Executor.newFixedThreadPool()使用了这个队列
SynchronousQueue
一个不存储元素的阻塞队列。添加任务的操作必须等到另一个线程的移除操作,否则添加操作一直处于阻塞状态。
静态工厂方法 Executor.newCachedThreadPool()使用了这个队列
静态工厂方法 Executor.newCachedThreadPool()使用了这个队列
PriorityBlokingQueue
一个支持优先级的无界阻塞队列。使用该队列,线程池中能创建的最大线程数为corePoolSize
keepAliveTime(线程活动保持时间)
线程池的工作线程空闲后,保持存活的时间。如果任务多而且任务的执行时间比较短,可以调大keepAliveTime,提高线程的利用率
unit(线程活动保持时间的单位)
可选单位有DAYS、HOURS、MINUTES、毫秒、微秒、纳秒
handler(饱和策略,或者又称拒绝策略)
AbortPolicy
无法处理新任务时,直接抛出异常,这是默认策略
CallerRunsPolicy
用调用者所在的线程来执行任务
DiscardOldestPolicy
丢弃阻塞队列中最靠前的一个任务,并执行当前任务
DiscardPolicy
直接丢弃任务
threadFactory
构建线程的工厂类
常见线程池的创建参数
CachedThreadPool核心池为0,最大池为Integer.MAX_VALUE,相当于只使用了最大池;其他线程池,核心池与最大池一样大,因此相当于只用了核心池
FixedThredPool: new ThreadExcutor(n, n, 0L, ms, new LinkedBlockingQueue<Runable>()
SingleThreadExecutor: new ThreadExcutor(1, 1, 0L, ms, new LinkedBlockingQueue<Runable>())
CachedTheadPool: new ThreadExcutor(0, max_valuem, 60L, s, new SynchronousQueue<Runnable>());
ScheduledThreadPoolExcutor: ScheduledThreadPool, SingleThreadScheduledExecutor.
SingleThreadExecutor: new ThreadExcutor(1, 1, 0L, ms, new LinkedBlockingQueue<Runable>())
CachedTheadPool: new ThreadExcutor(0, max_valuem, 60L, s, new SynchronousQueue<Runnable>());
ScheduledThreadPoolExcutor: ScheduledThreadPool, SingleThreadScheduledExecutor.
如果使用的阻塞队列为无界队列,则永远不会调用拒绝策略,因为再多的任务都可以放在队列中
SynchronousQueue是不存储任务的,新的任务要么立即被已有线程执行,要么创建新的线程执行
五种运行状态
running
该状态的线程池既能接受新提交的任务,又能处理阻塞队列中任务
shutdown
该状态的线程池不能接收新提交的任务,但是能处理阻塞队列中的任务
(政府服务大厅不在允许群众拿号了,处理完手头的和排队的政务就下班。)
处于running状态时,调用 shutdown()方法会使线程池进入到该状态
finalize() 方法在执行过程中也会隐式调用shutdown()方法
stop
该状态的线程池不接受新提交的任务,也不处理在阻塞队列中的任务,还会中断正在执行的任务
政府服务大厅不再进行服务了,拿号、排队、以及手头工作都停止了
在线程池处于 RUNNING 或 SHUTDOWN 状态时,调用 shutdownNow() 方法会使线程池进入到该状态
tidying
如果所有的任务都已终止,workerCount (有效线程数)=0
线程池进入该状态后会调用
terminated() 钩子方法
进入TERMINATED 状态
terminated
在terminated()钩子方法执行完后进入该状态,默认terminated()钩子方法中什么也没有做
shutdown或者shutdownNow方法
可以通过调用线程池的shutdown或者shutdownNow方法来关闭线程池:遍历线程池中工作线程,逐个调用interrupt方法来中断线程
特点
shutdown方法将线程池的状态设置为SHUTDOWN状态,只会中断空闲的工作线程
shutdownNow方法
将线程池的状态设置为STOP状态
,会中断所有工作线程,不管工作线程是否空闲 调用两者中任何一种方法,都会使isShutdown方法的返回值为true;线程池中所有的任务都关闭后,isTerminated方法的返回值为true
通常使用shutdown方法关闭线程池,如果不要求任务一定要执行完,则可以调用shutdownNow方法
调优(线程池的合理配置)
几个角度分析任务的特性
任务的性质
CPU 密集型任务、IO 密集型任务和混合型任务
可以通过 Runtime.getRuntime().availableProcessors() 方法获得当前设备的 CPU 个数
CPU 密集型任务配置
尽可能小的线程
,如配置 N c p u + 1 N_{cpu}+1_Ncp**u_+1 个线程的线程池 IO 密集型任务则由于线程并不是一直在执行任务,则配置尽可能多的线程,如2 ∗ N c p u 2_N_{cpu}2∗_Ncp**u*
混合型任务,如果可以拆分,则将其拆分成一个 CPU 密集型任务和一个 IO 密集型任务。只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐率要高于串行执行的吞吐率;如果这两个任务执行时间相差太大,则没必要进行分解
任务的优先级
高、中、低
可以使用优先级队列 PriorityBlockingQueue 来处理,它可以让优先级高的任务先得到执行。但是,如果一直有高优先级的任务加入到阻塞队列中,那么低优先级的任务可能永远不能执行
任务的执行时间
长、中、短
可以交给不同规模的线程池来处理,或者也可以使用优先级队列,让执行时间短的任务先执行
任务的依赖性
是否依赖其他系统资源,如数据库连接。
依赖数据库连接池的任务,因为线程提交 SQL 后需要等待数据库返回结果,线程数应该设置得较大,这样才能更好的利用 CPU
建议使用有界队列,有界队列能
增加系统的稳定性和预警能力
。可以根据需要设大一点,比如几千。使用无界队列
,线程池的队列就会越来越大,有可能会撑满内存,导致整个系统不可用 解决方案
任务性质不同的任务可以用不同规模的线程池分开处理
监控
taskCount
线程池需要执行的任务数量,包括已经执行完的、未执行的和正在执行的
completedTaskCount
线程池在运行过程中已完成的任务数量,completedTaskCount <= taskCount
largestPoolSize
线程池曾经创建过的最大线程数量,通过这个数据可以知道线程池是否满过。如等于线程池的最大大小,则表示线程池曾经满了
getPoolSize
线程池的线程数量。如果线程池不销毁的话,池里的线程不会自动销毁,所以线程池的线程数量只增不减
getActiveCount
获取活动的线程数
通过继承线程池并重写线程池的 beforeExecute,afterExecute 和 terminated 方法,我们可以在任务执行前,执行后和线程池关闭前干一些事情
如监控任务的平均执行时间,最大执行时间和最小执行时间等。这几个方法在线程池里是空方法
protected void beforeExecute(Thread t, Runnable r) { }
常见问题
基础
以ThreadPoolExecutor为切入点,讲解excute()方法中所体现的Java线程池运行流程
工作线程Worker,它的循环工作特点
如何新建线程池:7个参数(重点在阻塞队列和饱和策略)
进阶
线程池五个状态的特点以及如何进行状态之间的切换:running、shutdown、stop、tidying、terminated
如何关闭线程:shutdown方法和shutdownNow方法的特点
线程池的调优(针对任务的不同特性 + 建议使用有界队列)
线程池的
监控参数
以及可以重写的方法
扩展
两种主要的线程池类型:普通的线程池ThreadPoolExecutor,支持延迟或周期性执行的任务的线程池ScheduledThreadPoolExcutor
讲解ThreadPoolExcutor中5个常用参数+2个不常用参数,包含的三种线程池:创建时的参数、运行的流程、各自适合的场景
讲解ScheduledThreadPoolExecutor的阻塞队列的原理、如何更改任务的time
提供了五种定义好的线程池,都可以通过Executors工具类去调用,比如Executors.newFixedThreadPool(12)
具体的场景,如果corePoolSize为x,maximumPoolSize为y,阻塞队列为z,第w个任务进来如何分配?
线程池中的核心参数,超过核心size怎么处理,队列满怎么处理,拒绝策略有哪些?(比较具体)
线程池如何进行调优?
线程池的调优(针对任务的不同特性 + 建议使用有界队列)
JVM
介绍
内存划分
JDK7之前
JDK8之后
底层
常见问题
垃圾回收机制
垃圾回收算法
日志体系
起源
logUtil
起初简单的日志工具类
功能
日志按时间打包处理
日志等级
根据等级高效查看筛选日志
异步追踪
开日志线程,不影响主业务执行时间
日志追踪
出现错误信息可以给指定用户告知错误
常见的日志框架
log4j
Apache的一个开源项目
jul
官方看不惯第三方的日志框架,自己开发了一套日志体系
java.util.logging
log4j2
apache开发升级了log4j
logback
与log4j,Slf4j是同一个作者
门面日志框架
Jcl
jakarta Commons Logging
如果能找到Log4j 则默认使用log4j 实现,如果没有则使用jul(jdk自带的) 实现,再没有则使用jcl内部提供的
SimpleLog 实现。
SimpleLog 实现。
动态加载日志框架的顺序
子主题
commonslogging.properties> 系 统 环 境 变 量 >log4j>jul>simplelog>nooplog
Slf4j
发现jcl不好用,独自开发该门面日志框架
不实现具体的日志功能,适配市面上日志框架
适配器
桥接器
整合主流框架
主流开源框架
SSM
Spring
AOP
解释
应用场景
IOC
DI
解释
事务
特性
事务传播机制7种
源码分析
内容
Bean的生命周期
依赖注入
注入方式
手动
XML
Set方法输入
构造方法注入
自动
XML的autowire自动注入
@Autowired注解的自动注入
注入点
确定Bean
循环依赖
三级缓存
推断构造方法
Spring的启动流程理解
配置类理解
整合Mybatis
AOP
事务
概念
BeanDefinition
BeanDefinitionReader
AnnotatedBeanDefinitionReader
@Conditional,@Scope、@Lazy、@Primary、@DependsOn、
@Role、@Description
@Role、@Description
XmlBeanDefinitionReader
解析<bean/>标签
ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner是扫描器,但是它的作用和BeanDefinitionReader类似,它可以
进行扫描,扫描某个包路径,对扫描到的类进行解析,比如,扫描到的类上如果存在@Component
注解,那么就会把这个类解析为一个BeanDefinition
进行扫描,扫描某个包路径,对扫描到的类进行解析,比如,扫描到的类上如果存在@Component
注解,那么就会把这个类解析为一个BeanDefinition
BeanFactory
BeanFactory表示Bean工厂,所以很明显,BeanFactory会负责创建Bean,并且提供获取Bean的API。
DefaultListableBeanFactory
ApplicationContext
AnnotationConfigApplicationContext
ClassPathXmlApplicationContext
BeanPostProcessor
BeanPostProcess表示Bena的后置处理器,我们可以定义一个或多个BeanPostProcessor
BeanFactoryPostProcessor
FactoryBean
国际化
资源加载
获取运行时环境
事件发布
类型转化
ConversionService
PropertyEditor
JDK
TypeConverter
OrderComparator
OrderComparator是Spring所提供的一种比较器,可以用来根据@Order注解或实现Ordered接口
来执行值进行笔记,从而可以进行排序
来执行值进行笔记,从而可以进行排序
AnnotationAwareOrderComparator
ExcludeFilter和IncludeFilter
MetadataReader、ClassMetadata、AnnotationMetadata
工作流程
SpringMVC
介绍
官网
配置
注解
xml
文件上传
国际化
请求流程
拦截器和过滤器
servlet
源码分析
请求流程源码
父子容器启动原理
Mybatis
缓存
别名
xml
plus
分页
源码分析
体系介绍
配置文件解析
SQL执操作执行流程
Springboot
官网
自动配置原理
整合SSM
基本配置介绍
微服务
SpringCloud
服务注册与发现
Eurka
zookeeper
配置中心
config
服务调用
feign
介绍
Feign是Netflix开发的声明式、模板化的HTTP客户端,Feign可帮助我们更加便捷、优雅地
调用HTTP API
调用HTTP API
优势
它像 Dubbo 一样,consumer 直接调用接口方法调用 provider,而不需要通过常规的 Http Client 构造请求再解析返回数据。它
解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发
解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发
Spring Cloud openfeign对Feign进行了增强,使其支持Spring MVC注解,另外还整合
了Ribbon和Eureka,从而使得Feign的使用更加方便
了Ribbon和Eureka,从而使得Feign的使用更加方便
扩展
日志配置
四种
NONE【性能最佳,适用于生产】:不记录任何日志(默认值)
BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态代码以及执行时间。
HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
FULL【比较适用于开发及测试环境定位问题】:记录请求和响应的header、body和元数据。
通过拦截器实现参数传递
feign.RequestInterceptor
应用场景
1. 统一添加 header 信息;
2. 对 body 中的信息做修改或替换;
2. 对 body 中的信息做修改或替换;
超时时间配置
客户端组件配置
GZIP 压缩配置
dubbo
链路
skywaking
网关
gateway
zuul
服务治理限流熔断
hyst
springcloud Alibaba
服务注册与发现(Nacos)
网关(gateway)
配置中心(Nacos)
服务调用
负载均衡(Ribbon)
feign
dubbo
分布式事务
seata
熔断限流
sentienl
关系型数据库
mysql
版本介绍
搜索引擎
查询语句执行过程
优化
索引
oracle
用户/权限
版本介绍
结构了解
常用查询函数
SQL server
Nginx
介绍
作者
俄罗斯
伊戈尔·赛索耶夫
第一个公开版本0.1.0发布于2004年10月4日
开发语言
c语言
特点
轻量级,占有内存少,并发能力强
官网
中文
https://www.nginx.cn/doc/
英文
http://nginx.org/
应用场景
http 服务器
虚拟主机。可以实现在一台服务器虚拟出多个网站
反向代理,负载均衡,限流
安装
linux
yum install -y pcre-devel openssl-devel gcc curl
wget https://openresty.org/download/openresty-1.17.8.2.tar.gz
tar -zxvf openresty-1.17.8.2.tar.gz
目录在:/usr/local/nginx
./configure
make && make install
window
官网下载ZIP解压皆可使用
配置介绍
#设置用户的权限 root nobody 指定 用户名虚拟机内用户 或者 Ip访问
user nobody;
user nobody;
#设置工作进程数 一般为 Cpu 核心*2 4*2
worker_processes 8;
worker_processes 8;
# 日志输出参数
error_log logs/error.log;
error_log logs/error.log;
# 进程ID
pid logs/nginx.pid;
pid logs/nginx.pid;
events {
#指定运行模型
use epoll;
# 工作连接数 默认512 根据自己的情况调整
worker_connections 1024;
}
#指定运行模型
use epoll;
# 工作连接数 默认512 根据自己的情况调整
worker_connections 1024;
}
http{}
功能使用
代理
正向代理
正向代理代理客户端
反向代理
反向代理代理服务器
动静分离
负载均衡
限流熔断
高级进阶
用户认证管理
主备切换
调优
Netty
介绍
入门
进阶
实战
Linux
环境介绍
Vmware
Centos
环境搭建
常用命令
cd
touch
mv
tail -f 文件
用户
权限
消息中间件(MQ)
介绍
Message Queue(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信
优势
应用解耦
系统的耦合性越高,容错性越低,可维护性就越低
解决系统之间的耦合问题,提高容错性和维护性
异步提速
提高用户的响应时间和系统的吞吐量
削峰填谷
提高系统的运行稳定性
缺点
系统的可用性降低
一旦MQ宕机,便造成了整个系统的瘫痪
系统的复杂度提高
解决消息队列中的重复消费,消息丢失等一系列问题
消息一致性问题
常见MQ对比
常见的MQ
kafka
leader和follower选举规则
ISR中存在的副本,且AR副本排在前面,就可以成为leader
一、入门
1、概述
定义
Kafka是一个开源的分布式事件流平台(Event StreamingPlatform),被数千家公司用于高性能数据管道、流分析、数据集成和关键任务应用。
2、队列模式
点对点模式
消费者主动拉去数据后,确认消息把队列中的数据删除
发布与订阅模式
可以有多个Topic主题
消费数据后,不删除数据
每个消费者独立消费,都可以消费到数据
3、架构
Producer
生产者,向Kafka集群中发消息的客户端
Brocker
一个服务器就是一个Brocker,一个集群由多个Brocker组成,一个Brocker上面可以有多个Topic
Consumer
消费者,从kafka集群中取消息的客户端
Consumer Group
由多个消费者组成,组内每个消费者负责不同分区的消费,一个分区只能由一个组内的消费者消费,消费者组内互不影响
每个消费者都属于一个消费组,一个消费组逻辑上是一个订阅者
每个消费者都属于一个消费组,一个消费组逻辑上是一个订阅者
Topic
可以理解为一个队列,生产者和消费者都面对是topic
Partition
实现扩展性,一个topic分布到多个brocker节点上,一个topic可以分为多个partition,每个partition是个有序的队列
Replica
副本,一个topic分为多个副本,副本分为一个leader和多个follower
Leader
每个分区副本中“主”要的领头者,生产者和消费者收发消息都是通过该副本进行
Follower
每个分区副本中的“从”,会实时从leader中同步数据,如果leader故障,该副本会成为新的leader
4、入门
集群搭建
zookeeper
kafka
基本命令使用
5、生产者
原理
main线程和sender线程,main 线程中创建了一个双端队列 RecordAccumulator,main 线程将消息发送给 RecordAccumulator,
Sender 线程不断从 RecordAccumulator 中拉取消息发送到 Kafka Broker。
Sender 线程不断从 RecordAccumulator 中拉取消息发送到 Kafka Broker。
重要参数
buffer.memory
RecordAccumulator 缓冲区总大小,默认 32m。
batch.size
缓冲区一批数据最大值,默认 16k。适当增加该值,可
以提高吞吐量,但是如果该值设置太大,会导致数据
传输延迟增加。
以提高吞吐量,但是如果该值设置太大,会导致数据
传输延迟增加。
linger.ms
如果数据迟迟未达到 batch.size,sender 等待 linger.time
之后就会发送数据。
单位 ms,默认值是 0ms,表示没有延迟。
生产环境建议该值大小为 5-100ms 之间。
之后就会发送数据。
单位 ms,默认值是 0ms,表示没有延迟。
生产环境建议该值大小为 5-100ms 之间。
acks
0:生产者发送过来的数据,不需要等数据落盘应答。
1:生产者发送过来的数据,Leader 收到数据后应答。
-1(all):生产者发送过来的数据,Leader+和 isr 队列
里面的所有节点收齐数据后应答。默认值是-1,-1 和
all 是等价的。
1:生产者发送过来的数据,Leader 收到数据后应答。
-1(all):生产者发送过来的数据,Leader+和 isr 队列
里面的所有节点收齐数据后应答。默认值是-1,-1 和
all 是等价的。
retries
当消息发送出现错误的时候,系统会重发消息。
retries表示重试次数。默认是 int 最大值,2147483647。
retries表示重试次数。默认是 int 最大值,2147483647。
enable.idempotence
是否开启幂等性,默认 true,开启幂等性。
compression.type
生产者发送的所有数据的压缩方式。默认是 none,也就是不压缩。
支持压缩类型:none、gzip、snappy、lz4 和 zstd。
支持压缩类型:none、gzip、snappy、lz4 和 zstd。
发送方式
异步
同步
在异步的方式基础上加上get()方法
分区
优点
便于合理使用存储资源,每个Partition在一个Broker上存储,可以把海量的数据按照分区切割成一
块一块数据存储在多台Broker上。合理控制分区的任务,可以实现负载均衡的效果。
块一块数据存储在多台Broker上。合理控制分区的任务,可以实现负载均衡的效果。
提高并行度,生产者可以以分区为单位发送数据;消费者可以以分区为单位进行消费数据。
分区策略
默认的分区器 DefaultPartitioner
指明分区,则按照指定分区来进行发送
若没有指明分区,则按照发送key对分区hash取模进行发送
既没有指明分区,又没有key,则采用粘性分区策略
自定义分区器
实现Partitioner,重写partition()
生产经验
提高吞吐量调优
• batch.size:批次大小,默认16k
• linger.ms:等待时间,修改为5-100ms
• compression.type:压缩snappy
• RecordAccumulator:缓冲区大小,修改为64m
数据可靠性
ack应答
Leader维护了一个动态的in-sync replica set(ISR),意为和
Leader保持同步的Follower+Leader集合(leader:0,isr:0,1,2)。
如果Follower长时间未向Leader发送通信请求或同步数据,则
该Follower将被踢出ISR。该时间阈值由replica.lag.time.max.ms参
数设定,默认30s。例如2超时,(leader:0, isr:0,1)。
这样就不用等长期联系不上或者已经故障的节点。
Leader保持同步的Follower+Leader集合(leader:0,isr:0,1,2)。
如果Follower长时间未向Leader发送通信请求或同步数据,则
该Follower将被踢出ISR。该时间阈值由replica.lag.time.max.ms参
数设定,默认30s。例如2超时,(leader:0, isr:0,1)。
这样就不用等长期联系不上或者已经故障的节点。
数据完全可靠条件 = ACK级别设置为-1 + 分区副本大于等于2 + ISR里应答的最小副本数量大于等于2
数据去重
幂等性
幂等性就是指Producer不论向Broker发送多少次重复数据,Broker端都只会持久化一条,保证了不重复。
精确一次(Exactly Once) = 幂等性 + 至少一次( ack=-1 + 分区副本数>=2 + ISR最小副本数量>=2) 。
精确一次(Exactly Once) = 幂等性 + 至少一次( ack=-1 + 分区副本数>=2 + ISR最小副本数量>=2) 。
重复数据的判断标准:具有<PID, Partition, SeqNumber>相同主键的消息提交时,Broker只会持久化一条。其
中PID是Kafka每次重启都会分配一个新的;Partition 表示分区号;Sequence Number是单调自增的。
所以幂等性只能保证的是在单分区单会话内不重复
中PID是Kafka每次重启都会分配一个新的;Partition 表示分区号;Sequence Number是单调自增的。
所以幂等性只能保证的是在单分区单会话内不重复
默认是开启
数据有序
单分区内,是有序
多分区,无序
多分区,无序
数据乱序
1.x之前版本
max.in.flight.requests.per.connection=1(不需要考虑是否开启幂等性)。
1.x之后版本
未开启幂等性
max.in.flight.requests.per.connection需要设置为1。
开启幂等性
max.in.flight.requests.per.connection需要设置为5
原因说明:因为在kafka1.x以后,启用幂等后,kafka服务端会缓存producer发来的最近5个request的元数据,
故无论如何,都可以保证最近5个request的数据都是有序的。
原因说明:因为在kafka1.x以后,启用幂等后,kafka服务端会缓存producer发来的最近5个request的元数据,
故无论如何,都可以保证最近5个request的数据都是有序的。
事务
开启事务,必须开启幂等性。
Producer 在使用事务功能前,必须先
自定义一个唯一的 transactional.id。有
了 transactional.id,即使客户端挂掉了,
它重启后也能继续处理未完成的事务
自定义一个唯一的 transactional.id。有
了 transactional.id,即使客户端挂掉了,
它重启后也能继续处理未完成的事务
6、Broker
Zookeeper 存储的 Kafka 信息
工作流程
重要参数
replica.lag.time.max.ms
ISR 中,如果 Follower 长时间未向 Leader 发送通
信请求或同步数据,则该 Follower 将被踢出 ISR。
该时间阈值,默认 30s。
信请求或同步数据,则该 Follower 将被踢出 ISR。
该时间阈值,默认 30s。
auto.leader.rebalance.enable
默认是 true。 自动 Leader Partition 平衡。
log.retention.hours
Kafka 中数据保存的时间,默认 7 天。
生产调优
节点服役和退役
平衡topic
创建一个要均衡的主题。
生成一个负载均衡的计划。
创建副本存储计划(所有副本存储在 broker0、broker1、broker2、broker3 中)。
执行副本存储计划。
)验证副本存储计划。
副本
副本基本信息
Leader选举规则
Leader和follower故障细节
基本概念
LEO(Log End Offset):每个副本的最后一个offset,LEO其实就是最新的offset + 1。
HW(High Watermark):所有副本中最小的LEO 。
follower故障
等该Follower的LEO大于等于该Partition的HW,即
Follower追上Leader之后,就可以重新加入ISR了
Follower追上Leader之后,就可以重新加入ISR了
leader故障
为保证多个副本之间的数据一致性,其余的Follower会先
将各自的log文件高于HW的部分截掉,然后从新的Leader同步
数据。
将各自的log文件高于HW的部分截掉,然后从新的Leader同步
数据。
生产调优
手动调整分区副本存储
创建执行计划,执行,验证
Leader Partition 负载平衡
正常情况下,Kafka本身会自动把Leader Partition均匀分散在各个机器上,来保证每台机器的读写吞吐量都是均匀的。但是如果某
些broker宕机,会导致Leader Partition过于集中在其他少部分几台broker上,这会导致少数几台broker的读写请求压力过高,其他宕机的
broker重启之后都是follower partition,读写请求很低,造成集群负载不均衡。
些broker宕机,会导致Leader Partition过于集中在其他少部分几台broker上,这会导致少数几台broker的读写请求压力过高,其他宕机的
broker重启之后都是follower partition,读写请求很低,造成集群负载不均衡。
增加副本因子
创建副本执行计划,执行副本计划,验证
文件存储
log、segment
.log、.index、.timeindex
log日志默认是1G
清理策略
Kafka 中默认的日志保存时间为 7 天,
delete 日志删除:将过期数据删除
log.cleanup.policy = delete 所有数据启用删除策略
基于时间:默认打开。以 segment 中所有记录中的最大时间戳作为该文件时间戳。
基于大小:默认关闭。超过设置的所有日志总大小,删除最早的 segment。
log.retention.bytes,默认等于-1,表示无穷大。
log.retention.bytes,默认等于-1,表示无穷大。
ompact 日志压缩
compact日志压缩:对于相同key的不同value值,只保留最后一个版本。
log.cleanup.policy = compact 所有数据启用压缩策略
高效读写数据
Kafka 本身是分布式集群,可以采用分区技术,并行度高
读数据采用稀疏索引,可以快速定位要消费的数据
顺序写磁盘
官网有数据表明,同样的磁盘,顺序写能到 600M/s,而随机写只有 100K/s。
页缓存 + 零拷贝技术
零拷贝
Kafka的数据加工处理操作交由Kafka生产者和Kafka消费者处理。Kafka Broker应用层不关心存储的数据,所以就不用
走应用层,传输效率高。
走应用层,传输效率高。
7、消费者
消费方式
pull模式
push模式
工作流程
子主题
消费者组
Consumer Group(CG):消费者组,由多个consumer组成。形成一个消费者组的条件,是所有消费者的groupid相同。
• 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费。
• 消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
• 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费。
• 消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
重要参数
生产经验
分区的分配以及再平衡
1、一个consumer group中有多个consumer组成,一个 topic有多个partition组成,现在的问题是,到底由哪个consumer来消费哪个
partition的数据。
2、Kafka有四种主流的分区分配策略: Range、RoundRobin、Sticky、CooperativeSticky。
可以通过配置参数partition.assignment.strategy,修改分区的分配策略。默认策略是Range + CooperativeSticky。Kafka可以同时使用
多个分区分配策略。
partition的数据。
2、Kafka有四种主流的分区分配策略: Range、RoundRobin、Sticky、CooperativeSticky。
可以通过配置参数partition.assignment.strategy,修改分区的分配策略。默认策略是Range + CooperativeSticky。Kafka可以同时使用
多个分区分配策略。
漏消费和重复消费
消费者事务
数据积压(消费者如何提高吞吐量)
8、Kafka-Eagle 监控
9、Kafka-Kraft 模式
二、外部系统接入
Flume
生产者
消费者
Flink
消费者
生产者
SpringBoot
生产者
消费者
Spark
生产者
消费者
三、生产环境调优
Kafka 硬件配置选择
100 万日活,每人每天 100 条日志,每天总共的日志条数是 100 万 * 100 条 = 1 亿条。
1 亿/24 小时/60 分/60 秒 = 1150 条/每秒钟。
每条日志大小:0.5k - 2k(取 1k)。
1150 条/每秒钟 * 1k ≈ 1m/s 。
高峰期每秒钟:1150 条 * 20 倍 = 23000 条。
每秒多少数据量:20MB/s。
1 亿/24 小时/60 分/60 秒 = 1150 条/每秒钟。
每条日志大小:0.5k - 2k(取 1k)。
1150 条/每秒钟 * 1k ≈ 1m/s 。
高峰期每秒钟:1150 条 * 20 倍 = 23000 条。
每秒多少数据量:20MB/s。
服务器台数选择
服务器台数= 2 * (生产者峰值生产速率 * 副本 / 100) + 1
= 2 * (20m/s * 2 / 100) + 1
= 3 台
建议 3 台服务器。
= 2 * (20m/s * 2 / 100) + 1
= 3 台
建议 3 台服务器。
磁盘选择
kafka 底层主要是顺序写,固态硬盘和机械硬盘的顺序写速度差不多。
建议选择普通的机械硬盘。
每天总数据量:1 亿条 * 1k ≈ 100g
100g * 副本 2 * 保存时间 3 天 / 0.7 ≈ 1T
建议三台服务器硬盘总大小,大于等于 1T。
建议选择普通的机械硬盘。
每天总数据量:1 亿条 * 1k ≈ 100g
100g * 副本 2 * 保存时间 3 天 / 0.7 ≈ 1T
建议三台服务器硬盘总大小,大于等于 1T。
内存选择
Kafka 内存组成:堆内存 + 页缓存
Kafka 堆内存建议每个节点:10g ~ 15g
页缓存:页缓存是 Linux 系统服务器的内存。我们只需要保证 1 个 segment(1g)中
25%的数据在内存中就好。
每个节点页缓存大小 =(分区数 * 1g * 25%)/ 节点数。例如 10 个分区,页缓存大小
=(10 * 1g * 25%)/ 3 ≈ 1g
建议服务器内存大于等于 11G。
25%的数据在内存中就好。
每个节点页缓存大小 =(分区数 * 1g * 25%)/ 节点数。例如 10 个分区,页缓存大小
=(10 * 1g * 25%)/ 3 ≈ 1g
建议服务器内存大于等于 11G。
CPU 选择
num.io.threads = 8 负责写磁盘的线程数,整个参数值要占总核数的 50%。
num.replica.fetchers = 1 副本拉取线程数,这个参数占总核数的 50%的 1/3
num.network.threads = 3 数据传输线程数,这个参数占总核数的 50%的 2/3。
num.replica.fetchers = 1 副本拉取线程数,这个参数占总核数的 50%的 1/3
num.network.threads = 3 数据传输线程数,这个参数占总核数的 50%的 2/3。
网络选择
网络带宽 = 峰值吞吐量 ≈ 20MB/s 选择千兆网卡即可。
100Mbps 单位是 bit;10M/s 单位是 byte ; 1byte = 8bit,100Mbps/8 = 12.5M/s。
一般百兆的网卡(100Mbps )、千兆的网卡(1000Mbps)、万兆的网卡(10000Mbps)。
100Mbps 单位是 bit;10M/s 单位是 byte ; 1byte = 8bit,100Mbps/8 = 12.5M/s。
一般百兆的网卡(100Mbps )、千兆的网卡(1000Mbps)、万兆的网卡(10000Mbps)。
四、源码分析
源码下载地址
http://kafka.apache.org/downloads
安装 JDK&Scala
加载源码
安装 gradle
RabbitMQ
主要协议
AMQP
Advanced Message Queuing Protocol(高级消息队列协议)
一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。2006年,AMQP 规范发布。类比HTTP
简介
2007年,Rabbit 技术公司基于 AMQP 标准开发的 RabbitMQ 1.0 发布。RabbitMQ 采用 Erlang 语言开发。Erlang 语言由 Ericson 设计,专门为开发高并发和分布式系统的一种语言,在电信领域使用广泛
主要架构
相关概念
Broker
接收和分发消息的应用
Virtual host
出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念
Connection
publisher/consumer 和 broker 之间的 TCP 连接
Channel
如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的 channel 进行通讯,AMQP method 包含了channel id 帮助客户端和message broker 识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的 Connection 极大减少了操作系统建立 TCP connection 的开销
Exchange
message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到queue 中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)
3种类型
Fanout
广播,将消息交给所有绑定到交换机的队列
Direct
定向,把消息交给符合指定routing key 的队列
Topic
通配符,将消息交给符合routing pattern(路由模式) 的队列
只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与 Exchange 绑定,或者没有符合路由规则的队列,那么消息会丢失!
Queue
消息最终被送到这里等待 consumer 取走
Binding
exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息被保存到 exchange 中的查询表中,用于 message 的分发依据
工作模式
简单模式
一对一,有默认的交换机
work queues
对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度
一对多,一个生产对应多个消费者
Publish/Subscribe 发布与订阅模式
绑定交换机(Exchange)
需要设置类型为 fanout 的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列。
Routing 路由模式
交换机和routingKey
需要设置类型为 direct 的交换机,交换机和队列进行绑定,并且指定 routing key,当发送消息到交换机后,交换机会根据 routing key 将消息发送到对应的队列
Topics 主题模式
在路由模式上支持更灵活的方式
需要设置类型为 topic 的交换机,交换机和队列进行绑定,并且指定通配符方式的 routing key,当发送消息到交换机后,交换机会根据 routing key 将消息发送到对应的队列
RPC 远程调用模式
消息确认机制
如何确保消息投入到borker,监听器
Confirm
表示生产者将消息投入到Borker时产生的状态
ack表示borker已经接收消息
nack表示拒收消息,原因,队列满,限流,异常等
Return
表示被Borker接受(ack)以后,但是Borker没有对应的队列进行投递产生的状态,消息退回生产者
以上两种只代表生产者与Borker之间的消息投递状态,与消费者的是否确认和接收无关
安装
下载网址
https://www.rabbitmq.com/download.html
集群搭建
整合
spring
使用 Spring 整合 RabbitMQ 将组件全部使用配置方式实现
提供了RabbitTemplate 简化发送消息 API
使用监听机制简化消费者编码
springboot
基本信息再yml中配置,队列交互机以及绑定关系在配置类中使用Bean的方式配置
生产端直接注入RabbitTemplate完成消息发送
消费端直接使用@RabbitListener完成消息接收
高级特性
消息的可靠投递
confirm 确认模式
return 退回模式
整个消息投递过程
producer--->rabbitmq broker--->exchange--->queue--->consumer
消息从 producer 到 exchange 则会返回一个 confirmCallback
消息从 exchange-->queue 投递失败则会返回一个 returnCallback
Consumer Ack
含义
ack指Acknowledge,确认。 表示消费端收到消息后的确认方式
三种方式
自动确认:acknowledge="none"
当消息一旦被Consumer接收到,则自动确认收到,并将相应 message 从 RabbitMQ 的消息缓存中移除
手动确认:acknowledge="manual"
设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,
如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息
如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息
根据异常情况确认:acknowledge="auto"
可靠性投递总结
持久化
exchenge持久化
queue要持久化
message要持久化
生产方确认Confirm
消费方确认Ack
Broker高可用
消费端限流
配置 prefetch属性设置消费端一次拉取多少消息
消费端的确认模式一定为手动确认。acknowledge="manual"
TTL
解释
Time To Live(存活时间/过期时间)
当消息到达存活时间后,还没有被消费,会被自动清除
RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间
参数
x-message-ttl,单位:ms(毫秒),会对整个队列消息统一过期
expiration。单位:ms(毫秒),当该消息在队列头部时(消费时),会单独判断这一消息是否过期
两者都设置,以时间短的为准
死信队列
DLX
Dead Letter Exchange(死信交换机)
当消息成为Dead message后,可以被重新发送到另一个交换机,这个交换机就是DLX
成为死信的三种情况
队列消息长度到达限制
消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false
原队列存在消息过期设置,消息到达超时时间未被消费
给队列设置参数: x-dead-letter-exchange 和 x-dead-letter-routing-key
延迟队列
即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费
RabbitMQ中没有提供该队列支持,只能通过TTL+死信队列实现
消息幂等性保障
幂等性指一次和多次请求某一个资源,对于资源本身应该具有同样的结果。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同
在MQ中指,消费多条相同的消息,得到与消费该消息一次相同的结果
实现
乐观锁机制
消息挤压
问题
消费者宕机挤压
消费者消费能力不足挤压
发送者发流量太大
解决
创建多个Work(工作队列)进行消费
把消息队列中的消息存放到数据库中,后面写程序进行消费
ActiveMQ
RocketMQ
简介
RocketMQ是阿里巴巴开源的一个消息中间件,在阿里内部历经了双十一等很多高并发场景的考验,能够处理亿万级别的消息。
2016年开源后捐赠给Apache,现在是Apache的一个顶级项目。
2016年开源后捐赠给Apache,现在是Apache的一个顶级项目。
版本
商业版
社区版
java开发
官网
https://rocketmq.apache.org/
组件介绍
NameServer
提供轻量级的Broker路由服务
邮局的管理机构
Broker
实际处理消息存储、转发等服务的核心组件
暂存和传输消息
邮局
Producer
消息生产者集群。通常是业务系统中的一个功能模块
发信者
Consumer
消息消费者集群。通常也是业务系统中的一个功能模块
收信者
Topic
区分消息的种类;一个发送者可以发送消息给一个或者多个Topic;一个消息的接收者可以订阅一个或者多个Topic消息
Message Queue
相当于是Topic的分区;用于并行发送和接收消息
环境部署
环境部署准备
jdk
maven
centos7
单机测试
# 编辑runbroker.sh和runserver.sh修改默认JVM大小
vi runbroker.sh
vi runserver.sh
vi runbroker.sh
vi runserver.sh
先启动NameServer
nohup sh bin/mqnamesrv &
# 2.查看启动日志
tail -f ~/logs/rocketmqlogs/namesrv.log
# 2.查看启动日志
tail -f ~/logs/rocketmqlogs/namesrv.log
启动Broker
nohup sh bin/mqbroker -n localhost:9876 &
# 2.查看启动日志
tail -f ~/logs/rocketmqlogs/broker.log
# 2.查看启动日志
tail -f ~/logs/rocketmqlogs/broker.log
测试
发送消息
# 1.设置环境变量
export NAMESRV_ADDR=localhost:9876
# 2.使用安装包的Demo发送消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
export NAMESRV_ADDR=localhost:9876
# 2.使用安装包的Demo发送消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
接受消息
# 1.设置环境变量
export NAMESRV_ADDR=localhost:9876
# 2.接收消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
export NAMESRV_ADDR=localhost:9876
# 2.接收消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
关闭
# 1.关闭NameServer
sh bin/mqshutdown namesrv
# 2.关闭Broker
sh bin/mqshutdown broker
sh bin/mqshutdown namesrv
# 2.关闭Broker
sh bin/mqshutdown broker
集群搭建
机器
192.168.217.99;192.168.217.88
修改Host文件,配置映射
vim /etc/hosts
# nameserver
192.168.217.99 rocketmq-nameserver1
192.168.217.88 rocketmq-nameserver2
# broker
192.168.217.99 rocketmq-master1
192.168.217.99 rocketmq-slave2
192.168.217.88 rocketmq-master2
192.168.217.88 rocketmq-slave1
192.168.217.99 rocketmq-nameserver1
192.168.217.88 rocketmq-nameserver2
# broker
192.168.217.99 rocketmq-master1
192.168.217.99 rocketmq-slave2
192.168.217.88 rocketmq-master2
192.168.217.88 rocketmq-slave1
systemctl restart network
防火墙
# 关闭防火墙
systemctl stop firewalld.service
# 查看防火墙的状态
firewall-cmd --state
# 禁止firewall开机启动
systemctl disable firewalld.service
systemctl stop firewalld.service
# 查看防火墙的状态
firewall-cmd --state
# 禁止firewall开机启动
systemctl disable firewalld.service
`nameserver` 默认使用 9876 端口
`master` 默认使用 10911 端口
`slave` 默认使用11011 端口
# 开放name server默认端口
firewall-cmd --remove-port=9876/tcp --permanent
# 开放master默认端口
firewall-cmd --remove-port=10911/tcp --permanent
# 开放slave默认端口 (当前集群模式可不开启)
firewall-cmd --remove-port=11011/tcp --permanent
# 重启防火墙
firewall-cmd --reload
firewall-cmd --remove-port=9876/tcp --permanent
# 开放master默认端口
firewall-cmd --remove-port=10911/tcp --permanent
# 开放slave默认端口 (当前集群模式可不开启)
firewall-cmd --remove-port=11011/tcp --permanent
# 重启防火墙
firewall-cmd --reload
环境变量
vim /etc/profile
#set rocketmq
ROCKETMQ_HOME=/usr/local/rocketmq/rocketmq-all-4.4.0-bin-release
PATH=$PATH:$ROCKETMQ_HOME/bin
export ROCKETMQ_HOME PATH
ROCKETMQ_HOME=/usr/local/rocketmq/rocketmq-all-4.4.0-bin-release
PATH=$PATH:$ROCKETMQ_HOME/bin
export ROCKETMQ_HOME PATH
source /etc/profile
创建消息存储路径
mkdir /usr/local/rocketmq/store
mkdir /usr/local/rocketmq/store/commitlog
mkdir /usr/local/rocketmq/store/consumequeue
mkdir /usr/local/rocketmq/store/index
mkdir /usr/local/rocketmq/store/commitlog
mkdir /usr/local/rocketmq/store/consumequeue
mkdir /usr/local/rocketmq/store/index
配置文件
master1
192.168.217.99
vi conf/2m-2s-sync/broker-a.properties
内容
nohup sh mqbroker -c /usr/local/rocketmq/conf/2m-2s-syncbroker-a.properties &
slave2
192.168.217.99
vi conf/2m-2s-sync/broker-b-s.properties
内容
nohup sh mqbroker -c /usr/local/rocketmq/conf/2m-2s-sync/broker-b-s.properties &
master2
192.168.217.88
vi conf/2m-2s-sync/broker-b.properties
内容
nohup sh mqbroker -c /usr/local/rocketmq/conf/2m-2s-sync/broker-b.properties &
slave1
192.168.217.88
vi conf/2m-2s-sync/broker-a-s.properties
内容
nohup sh mqbroker -c /usr/local/rocketmq/conf/2m-2s-sync/broker-a-s.properties &
修改启动脚本中的内存
bin/runbroker.sh
bin/runserver.sh
先启动nameserver
nohup sh mqnamesrv &
在启动broker
查看启动进程
jps
# 查看nameServer日志
tail -500f ~/logs/rocketmqlogs/namesrv.log
# 查看broker日志
tail -500f ~/logs/rocketmqlogs/broker.log
tail -500f ~/logs/rocketmqlogs/namesrv.log
# 查看broker日志
tail -500f ~/logs/rocketmqlogs/broker.log
mqadmin管理工具
可视化页面管理
git clone https://github.com/apache/rocketmq-externals
cd rocketmq-console
mvn clean package -Dmaven.test.skip=true
cd rocketmq-console
mvn clean package -Dmaven.test.skip=true
rocketmq.config.namesrvAddr=192.168.217.99:9876;192.168.217.88:9876
java -jar rocketmq-console-ng-1.0.0.jar
端口是8080
消息分类
基本
消息发送
同步消息
异步消息
单向发送
消息消费
负载均衡模式
广播模式
顺序消息
延迟消息
过滤消息
事务消息
ELK
ElasticSearch
概念
Elasticsearch是用Java开发并且是当前最流行的开源的企业级搜索引擎
官网
https://www.elastic.co/
下载网址
https://www.elastic.co/cn/start
创始人
Shay Banon(谢巴农)
Lucene
关系
Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库(框架)
Es基于Lucene开发
缺点
只能在Java项目中使用,并且要以jar包的方式直接集成项目中
使用非常复杂-创建索引和搜索索引代码繁杂
不支持集群环境-索引数据不同步(不支持大型项目)
索引数据如果太多就不行,索引库和应用所在同一个服务器,共同占用硬盘.共用空间少
Solr
速度
- 当单纯的对已有数据进行搜索时,Solr更快。
- 当实时建立索引时, Solr会产生io阻塞,查询性能较差, Elasticsearch具有明显的优势
区别
- Solr 利用 Zookeeper 进行分布式管理,而Elasticsearch 自身带有分布式协调管理功能
关系型数据库
Database(数据库)
index(索引)
table(表)
Type(类型)
Row(行)
Document(文档)
Column(列)
Field(字段)
Schema(结构)
Mapping(映射)
SQL(语法)
DSL(语法)
倒排索引
索引就类似于目录,平时我们使用的都是索引,都是通过主键定位到某条数据,那么倒排索引呢,刚好相反,数据对应到主键
核心概念
Index(索引)
一个索引就是一个拥有几分相似特征的文档的集合
一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字
Mapping(映射)
mapping是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分词器、是否被索引等等,这些都是映射里面可以设置的
Field(字段)
相当于是数据表的字段|列
Type(类型)
每一个字段都应该有一个对应的类型,例如:Text、Keyword、Byte等
Document(文档)
一个文档是一个可被索引的基础信息单元,类似一条记录。文档以JSON(Javascript Object Notation)格式来表示
Cluster(集群)
一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能
Node(节点)
一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能
一个节点可以通过配置集群名称的方式来加入一个指定的集群
shards&replicas(分片和副本)
分片
一个索引可以存储超出单个结点硬件限制的大量数据
一个索引可以存储超出单个结点硬件限制的大量数据
每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上
至于分片怎么分布,是由ElasticSearch来控制
重要性
允许在分片之上进行分布式的、并行的操作,进而提高性能/吞吐量
允许水平分割/扩展你的内容容量
副本
概念
在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做副本分片,或者直接叫副本
重要性
在分片/节点失败的情况下,提供了高可用性
扩展搜索量/吞吐量,因为搜索可以在所有的副本上并行运行
每个索引可以被分成多个分片。一个索引有0个或者多个副本
一旦设置了副本,每个索引就有了主分片和副本分片,分片和副本的数量可以在索引创建的时候指定
在索引创建之后,可以在任何时候动态地改变副本的数量,但是不能改变分片的数量
安装
linux
ES不能使用root用户来启动,必须使用普通用户来安装启动
创建一个es专门的用户(必须)
分词器
内置分词器
中文分词
IK分词器
下载网址
https://github.com/medcl/elasticsearch-analysis-ik/releases
两种
ik_smart
ik_max_word
config目录
IKAnalyzer.cfg.xml:用来配置自定义词库
可以配置指定的热词
main.dic:ik原生内置的中文词库,总共有27万多条,只要是这些单词,都会被分在一起
quantifier.dic:放了一些单位相关的词
suffix.dic:放了一些后缀
surname.dic:中国的姓氏
stopword.dic:英文停用词
热更新
一种方式,修改IKAnalyzer.cfg.xml
IKAnalyzer.cfg.xml里面配置指定的服务器来更新词语
修改IK源码,通过数据库存储的热词,让IK从数据库读取并实施更新
pinyin分词器
下载网址
https://github.com/medcl/elasticsearch-analysis-pinyin/releases
HanLP
工作流程
预先处理无用的一些信息,比如<Html>
然后根据内容进行分词
分词过根据配置过滤敏感词或者一些其他设置
定制分词器
中英分词
监控和可视化
Head插件
https://github.com/mobz/elasticsearch-head/releases
需要依赖NodeJs
cerebro
https://github.com/lmenezes/cerebro/releases
直接启动
集群搭建
config/elasticsearch.yml
状态
green:每个索引的primary shard和replica shard都是active状态的
yellow:每个索引的primary shard都是active状态的,但是部分replica shard不是active状态,处于不可用的状态
在同一台服务器上部署集群会出现该状态
red:不是所有索引的primary shard都是active状态的,部分索引有数据丢失了
节点介绍
主节点:node.master:true
数据节点: node.data: true
数据节点: node.data: true
默认的配置
在一个生产集群中我们可以对这些节点的职责进行划分,建议集群中设置3台以上的节点作为master节点
避免出现脑裂问题
由于个节点之间通信问题导致
脑裂问题
就是同一个集群中的不同节点,对于集群的状态有了不一样的理解,比如集群中存在两个master
解决
集群中master节点的数量至少3台,三台主节点通过在elasticsearch.yml中配置discovery.zen.minimum_master_nodes: 2,就可以避免脑裂问题的产生
集群节点/2+1
3个节点,该值的配置
3/2+1=2
架构原理
结构
Master
- 管理索引(创建索引、删除索引)、分配分片
- 维护元数据
- 管理集群节点状态
- 不负责数据写入和查询,比较轻量级
一个集群中只有一个
内存可以相对小一点,但机器要稳定
DataNode
一个集群中有N多个节点
数据写入、数据检索
内存最好配置大
7.x以后默认一个索引有一个分片和一个副本
写入
一个请求会任意选一个DataNode节点,作为coordinating node(协调节点)
计算得到文档要写入的分片,shard = hash(routing) % number_of_primary_shards
routing 是一个可变值,默认是文档的 _id
协调节点会把该请求转发到其他的节点上
然后写入到对应的主节点中,然后同步至副本节点
主副节点都保存完毕数据之后,返回结果
检索
请求任意一个DataNode作为协调节点
该节点负责广播到每个DataNode,找到对应查询的Index以后处理
每个分片返回document的分片,节点,文档id到协调节点进行汇总
协调节点向包含这些文档ID的分片发送get请求,对应的分片将文档数据返回给协调节点,最后协调节点将数据返回给客户端
准实时检索
分片收到ES的数据,首先会写入到内存
然后通过内存的buffer生成一个segment
并刷到文件系统缓存中,数据可以被检索
每秒一次
在写入到内存中的同时,也会记录translog日志,在refresh期间出现异常,会根据translog来进行数据恢复
等到文件系统缓存中的segment数据都刷到磁盘中,清空translog文件
等到文件系统缓存中的segment数据都刷到磁盘中,清空translog文件
ES默认每隔30分钟会将文件系统缓存的数据刷入到磁盘
Segment太多时,ES定期会将多个segment合并成为大的segment,减少索引查询时IO开销,此阶段ES会真正的物理删除(之前执行过的delete的数据)
_score 计算原理
根据用户的query条件,先过滤出包含指定term的doc
relevance score算法,简单来说,就是计算出,一个索引中的文本,与搜索文本,他们之间的关联匹配程度
TF/IDF算法
term frequency/inverse document frequency
TF
搜索文本中的各个词条在field文本中出现了多少次,出现次数越多,就越相关
IDF
搜索文本中的各个词条在整个索引的所有文档中出现了多少次,出现的次数越多,就越不相关
Kibana
概念
Kibana是世界上最受欢迎的开源日志分析平台ELK Stack中的“K” ,它为用户提供了一个工具,
用于在存储于Elasticsearch集群中的日志数据进行检索,可视化和构建仪表板
用于在存储于Elasticsearch集群中的日志数据进行检索,可视化和构建仪表板
下载
https://www.elastic.co/cn/downloads/kibana
安装
bin目录中,首先修改config目录下的kibana.yml文件
elasticsearch.hosts: ["http://ip1:9200", "http://ip2:9200"]
基本操作
DevTools
查询所有的索引
GET _cat/indices
创建索引
PUT /es_db
查询索引
GET /es_db
删除索引
DELETE /es_db
文档操作
添加
PUT /索引名称/类型/ID
PUT /es_db/_doc/1
POST和PUT都能起到创建/更新的作用
1、需要注意的是==PUT==需要对一个具体的资源进行操作也就是要确定id才能进行==更新/创建,而==POST==是可以针对整个资源集合进行操作的,如果不写id就由ES生成一个唯一id进行==创建==新文档,如果填了id那就针对这个id的文档进行创建/更新
2、PUT只会将json数据都进行替换, POST只会更新相同字段的值
3、PUT与DELETE都是幂等性操作, 即不论操作多少次, 结果都一样
1、需要注意的是==PUT==需要对一个具体的资源进行操作也就是要确定id才能进行==更新/创建,而==POST==是可以针对整个资源集合进行操作的,如果不写id就由ES生成一个唯一id进行==创建==新文档,如果填了id那就针对这个id的文档进行创建/更新
2、PUT只会将json数据都进行替换, POST只会更新相同字段的值
3、PUT与DELETE都是幂等性操作, 即不论操作多少次, 结果都一样
查询
GET /索引名称/类型/id
GET /es_db/_doc/1
删除
DELETE /索引名称/类型/id
DELETE /es_db/_doc/1
查询
简单查询
查询当前类型中的所有文档 _search
GET /索引名称/类型/_search
GET /es_db/_doc/6
条件查询
GET /索引名称/类型/_search?q=*:***
GET /es_db/_doc/_search?q=age:18
范围查询
GET /索引名称/类型/_search?q=***[25 TO 26]
GET /es_db/_doc/_search?q=age[26 TO 26]
批量查询
根据多个ID进行批量查询 _mget
GET /索引名称/类型/_mget
GET /es_db/_doc/_mget
{
"ids":["1","2"]
}
{
"ids":["1","2"]
}
分页查询
GET /索引名称/类型/_search?q=age[25 TO 26]&from=0&size=1
GET /es_db/_doc/_search?q=age[25 TO 26]&from=0&size=1
过滤查询结果字段
GET /索引名称/类型/_search?_source=字段,字段
GET /es_db/_doc/_search?_source=name,age
排序
GET /索引名称/类型/_search?sort=字段 desc
GET /es_db/_doc/_search?sort=age:desc
批量查询
在URL中不指定index和type
请求方式:GET
请求地址:_mget
功能说明 : 可以通过ID批量获取不同index和type的数据
请求参数:
docs : 文档数组参数
_index : 指定index
_type : 指定type
_id : 指定id
_source : 指定要查询的字段
请求方式:GET
请求地址:_mget
功能说明 : 可以通过ID批量获取不同index和type的数据
请求参数:
docs : 文档数组参数
_index : 指定index
_type : 指定type
_id : 指定id
_source : 指定要查询的字段
GET _mget
{
"docs": [
{
"_index": "es_db",
"_type": "_doc",
"_id": 1
},
{
"_index": "es_db",
"_type": "_doc",
"_id": 2
}
]
}
{
"docs": [
{
"_index": "es_db",
"_type": "_doc",
"_id": 1
},
{
"_index": "es_db",
"_type": "_doc",
"_id": 2
}
]
}
批量操作(增删改)
增加
批量创建文档create
POST _bulk
{"create":{"_index":"article", "_type":"_doc", "_id":3}}
{"id":3,"title":"好家伙","content":"好家伙666","tags":["java", "面向对象"],"create_time":1554015482530}
{"create":{"_index":"article", "_type":"_doc", "_id":4}}
{"id":4,"title":"好家伙1","content":"好家伙3","tags":["java", "面向对象"],"create_time":1554015482530}
{"create":{"_index":"article", "_type":"_doc", "_id":3}}
{"id":3,"title":"好家伙","content":"好家伙666","tags":["java", "面向对象"],"create_time":1554015482530}
{"create":{"_index":"article", "_type":"_doc", "_id":4}}
{"id":4,"title":"好家伙1","content":"好家伙3","tags":["java", "面向对象"],"create_time":1554015482530}
如果原文档不存在,则是创建
如果原文档存在,则是替换(全量修改原文档)
修改
POST _bulk
{"update":{"_index":"article", "_type":"_doc", "_id":3}}
{"doc":{"title":"ES大法必修内功"}}
{"update":{"_index":"article", "_type":"_doc", "_id":4}}
{"doc":{"create_time":1554018421008}}
{"update":{"_index":"article", "_type":"_doc", "_id":3}}
{"doc":{"title":"ES大法必修内功"}}
{"update":{"_index":"article", "_type":"_doc", "_id":4}}
{"doc":{"create_time":1554018421008}}
删除
POST _bulk
{"delete":{"_index":"article", "_type":"_doc", "_id":3}}
{"delete":{"_index":"article", "_type":"_doc", "_id":4}}
{"delete":{"_index":"article", "_type":"_doc", "_id":3}}
{"delete":{"_index":"article", "_type":"_doc", "_id":4}}
DSL语言高级查询
Domain Specific Language
DSL由叶子查询子句和复合查询子句两种子句组成
查询
记录查询(query)
无条件
match
有条件
叶子查询(单条件)
模糊匹配
match
profix
regexp
精确匹配
term
terms
range
exists
ids
组合查询(多条件)
bool
must
filter
must_not
should
constant_score
dis_max
mult_match
type
most_fields:在多字段中匹配的越多排名越靠前
best_fields: 能完全匹配的文档,排名越靠前
cross_fields: 查询越分散,排名越靠前
连接查询
父子文档查询
has_child
type
指定子文档名称
query
子文档的查询条件
inner_hits
内层过滤
has_parent
parent_type
指定父文档名称
query
父文档查询条件
inner_hits
内层过滤
嵌套文档查询
nested
path
嵌套字段路径
query
嵌套文档查询条件
聚合查询(aggs)
bucket
metric
对一个bucket数据执行的统计分析
求和,最大值,最小值,平均值
示例
无条件查询所有
GET /es_db/_doc/_search
{
"query":{
"match_all":{}
}
}
{
"query":{
"match_all":{}
}
}
推荐搜索
suggest_mode
当词典中没有找到对应的索引信息,才去推荐
popular
即使我们去搜索一个被索引了的单词,但是还是会去给我们推荐类似的但是出现频率很高的词
always
无论在任何情况下,都给出推荐
高亮显示
highlight中的field,必须跟query中的field一一对齐
常用的highlight介绍
plain
lucene
posting
index_options=offsets
性能比plain highlight要高,因为不需要重新对高亮文本进行分词
对磁盘的消耗少
fast vector
对大field而言(大于1mb),性能更高
强制使用某种highlighter
设置高亮html标签,默认是<em>标签
高亮片段fragment的设置
fragment_size: 你一个Field的值,比如有长度是1万,不可能在页面上显示这么长,设置要显示出来的fragment文本判断的长度,默认是100
number_of_fragments:你可能你的高亮的fragment文本片段有多个片段,你可以指定就显示几个片段
Logstash
介绍
Logstash是一个开源的服务器端数据处理管道,可以同时从多个数据源获取数据,并对其进行转换,然后将其发送到你最喜欢的“存储”。创建于2009年,于2013年被elasticsearch收购
下载
https://www.elastic.co/cn/downloads/logstash
资源
http://files.grouplens.org/datasets/movielens/
开发工具
git
官方网站
安装和部署
gitlab搭建
常用命令
设置用户和邮箱
git config --global user.name "chenjingbo"
git config --global user.email "chenjb1024@aliyun.com"
git config --global user.email "chenjb1024@aliyun.com"
在c盘下面用户名文件夹下面有一个.gitconfig,里面可以查看配置的文件
git的分支管理
创建分支:git branch 分支名;
切换分支:git chectout 分支名
切换分支:git chectout 分支名
8.1.切换到主分支
8.2.git pull
8.3.git checkout -b 分支名字
8.4.git push --set-upstream origin 分支名字
8.4.git push
8.2.git pull
8.3.git checkout -b 分支名字
8.4.git push --set-upstream origin 分支名字
8.4.git push
删除分支
git branch -d 分支名字
git push origin --delete 分支名字
git push origin --delete 分支名字
合并分支
git checkout 需要合并分支
git merge 合并的分支
取消合并 git merge --abort
git merge 合并的分支
取消合并 git merge --abort
从一个分支的某个提交合并到主分支
git checkout 主分支上
git cherry-pick commitId
git cherry-pick A、B
git cherry-pick A^..B
git cherry-pick commitId
git cherry-pick A、B
git cherry-pick A^..B
更新某个版本下的一个文件
git checkout -m 68e95237 pom.xml
本地没有远程的分支,切换远程的分支至本地
git checkout -b <分支名> origin/<分支名>
配置gitLab公私玥生成
ssh-keygen -t rsa -C '你注册的邮箱'
位置保存在:C:\Users\本人用户\.ssh
位置保存在:C:\Users\本人用户\.ssh
回滚上个版本并更新到远程
git reset --hard HEAD^;
git push -f origin 分支名称;
git push -f origin 分支名称;
常用分支
master
dev
bugFix
future
test
maven
生命周期
Jenkins
svn
gland
UML类图
基本介绍
UML——Unified modeling language UML (统一建模语言),是一种用于软件系统分析和设计的语言工具,它用
于帮助软件开发人员进行思考和记录思路的结果
于帮助软件开发人员进行思考和记录思路的结果
UML 本身是一套符号的规定,就像数学符号和化学符号一样,这些符号用于描述软件模型中的各个元素和他
们之间的关系,比如类、接口、实现、泛化、依赖、组合、聚合等
们之间的关系,比如类、接口、实现、泛化、依赖、组合、聚合等
分类
用例图(use case)
静态结构图:类图、对象图、包图、组件图、部署图
动态行为图:交互图(时序图与协作图)、状态图、活动图
UML 类图
说明
用于描述系统中的类(对象)本身的组成和类(对象)之间的各种静态关系。
类之间的关系:依赖、泛化(继承)、实现、关联、聚合与组合。
依赖关系(Dependence)
只要是在类中用到了对方,那么他们之间就存在依赖关系。如果没有对方,连编绎都通过不了。
泛化关系(generalization)
泛化关系实际上就是继承关系,他是依赖关系的特例
实现关系(Implementation)
实现关系实际上就是 A 类实现 B 接口,他是依赖关系的特例
关联关系(Association)
类与类之间的联系,他也是依赖关系的特例
聚合关系(Aggregation)
表示的是整体和部分的关系,整体与部分可以分开。聚合关系是关联关系的特例,所
以他具有关联的导航性与多重性。
以他具有关联的导航性与多重性。
组合关系(Composition)
组合关系:也是整体与部分的关系,但是整体与部分不可以分开。
设计模式
设计模式七大原则
目的
编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等多方面的
挑战,设计模式是为了让程序(软件),具有更好
挑战,设计模式是为了让程序(软件),具有更好
1)代码重用性 (即:相同功能的代码,不用多次编写)
2)可读性 (即:编程规范性, 便于其他程序员的阅读和理解)
3)可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
4)可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
5)使程序呈现高内聚,低耦合的特性
2)可读性 (即:编程规范性, 便于其他程序员的阅读和理解)
3)可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
4)可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
5)使程序呈现高内聚,低耦合的特性
意义
设计模式原则,其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式为什么
这样设计的依据)
这样设计的依据)
原则
1)单一职责原则
对类来说的,即一个类应该只负责一项职责。如类 A 负责两个不同职责:职责 1,职责 2。当职责 1 需求变更
而改变 A 时,可能造成职责 2 执行错误,所以需要将类 A 的粒度分解为 A1,A2
而改变 A 时,可能造成职责 2 执行错误,所以需要将类 A 的粒度分解为 A1,A2
2)接口隔离原则
1)客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
3)依赖倒转(倒置)原则
高层模块不应该依赖低层模块,二者都应该依赖其抽象
抽象不应该依赖细节,细节应该依赖抽象
依赖倒转(倒置)的中心思想是面向接口编程
依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架
构比以细节为基础的架构要稳定的多。在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类
构比以细节为基础的架构要稳定的多。在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类
使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
依赖关系传递的三种方式
接口传递
构造方法传递
setter方法传递
4)里氏替换原则
基本介绍
里氏替换原则(Liskov Substitution Principle)在 1988 年,由麻省理工学院的以为姓里的女士提出的。
所有引用基类的地方必须能透明地使用其子类的对象。
在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来解决问题。
5)开闭原则
开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则
一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节
当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
6)迪米特法则
一个对象应该对其他对象保持最少的了解
类与类关系越密切,耦合度越大
迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于
被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的 public 方法,不对外泄露任何信息
被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的 public 方法,不对外泄露任何信息
迪米特法则还有个更简单的定义:只与直接的朋友通信
直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间
是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返
回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变
量的形式出现在类的内部。
是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返
回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变
量的形式出现在类的内部。
7)合成复用原则
原则是尽量使用合成/聚合的方式,而不是使用继承
核心思想
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
针对接口编程,而不是针对实现编程。
为了交互对象之间的松耦合设计而努力
概述
层次
第 1 层:刚开始学编程不久,听说过什么是设计模式
第 2 层:有很长时间的编程经验,自己写了很多代码,其中用到了设计模式,但是自己却不知道
第 3 层:学习过了设计模式,发现自己已经在使用了,并且发现了一些新的模式挺好用的
第 4 层:阅读了很多别人写的源码和框架,在其中看到别人设计模式,并且能够领会设计模式的精妙和带来的好处。
第 5 层:代码写着写着,自己都没有意识到使用了设计模式,并且熟练的写了出来。
介绍
设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验,模式不是代码,而是某类问题的通
用解决方案,设计模式(Design pattern)代表了最佳的实践。这些解决方案是众多软件开发人员经过相当长的
一段时间的试验和错误总结出来的。
用解决方案,设计模式(Design pattern)代表了最佳的实践。这些解决方案是众多软件开发人员经过相当长的
一段时间的试验和错误总结出来的。
设计模式的本质提高 软件的维护性,通用性和扩展性,并降低软件的复杂度。
类型
设计模式分为三种类型,共 23 种
创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、
解释器模式(Interpreter 模式)、状态模式、策略模式、职责链模式(责任链模式)。
解释器模式(Interpreter 模式)、状态模式、策略模式、职责链模式(责任链模式)。
Redis
常用数据结构
String
Hash
Set
Zset
List
持久化方式
RDB
AOF
区别
集群搭建
并发产生的问题以及对应的解决方案
哈希槽
底层实现模型
MongoDB
介绍
MongoDB是一个文档数据库(以 JSON 为数据模型),由C++语言编写,旨在为WEB应用提供可扩展的
高性能数据存储解决方案。
高性能数据存储解决方案。
概念
数据库(database)
最外层的,逻辑上命名
集合(collection)
相当于SQL中的表,一个集合可以存放多个不同的文档
文档(document)
一个文档相当于数据表中的一行,由多个不同的字段组成
字段(field)
文档中的一个属性,等同于列(column)
索引(index)
独立的检索式数据结构,与SQL概念一致
id
每个文档中都拥有一个唯一的id字段,相当于SQL中的主键(primary key)
视图(view)
可以看作一种虚拟的(非真实存在的)集合,与SQL中的视图类似。从MongoDB
3.4版本开始提供了视图功能,其通过聚合管道技术实现。
3.4版本开始提供了视图功能,其通过聚合管道技术实现。
聚合操作($lookup)
MongoDB用于实现“类似”表连接(tablejoin)的聚合操作符
与传统数据库的对比
半结构化,在一个集合中,文档所拥有的字段并不需要是相同的,而且也不需要对所用的字段进行
声明。因此,MongoDB具有很明显的半结构化特点
声明。因此,MongoDB具有很明显的半结构化特点
弱关系,MongoDB没有外键的约束,也没有非常强大的表连接能力。类似的功能需要使用聚合管
道技术来弥补。
道技术来弥补。
优势
MongoDB基于灵活的JSON文档模型,非常适合敏捷式的快速开发
其与生俱来的高可用、高水平扩展能力使得它在处理海量、高并发的数据应用时颇具优势
复制集提供99.999%高可用
多中心容灾的能力
分片架构支持海量数据和无缝扩容
横向扩展能力强,支持TB、PB级别数据
JSON 结构和对象模型接近,开发代码量低
JSON的动态模型意味着更容易响应新的业务需求
应用场景
游戏场景
用户信息,游戏的积分,用户的装备
物流场景
海量订单信息,实时更新,内嵌存储,一次性可以查询出所有信息
社交场景
存储朋友圈信息,方便地理位置,附近的人功能的实现
物联网场景
智能设备,并且汇总日志信息,可对这些内容进行多维度的分析
视频直播
用 MongoDB 存储用户信息、礼物信息等
大数据应用
使用云数据库MongoDB作为大数据的云存储系统,随时进行数据提取分析,掌握行
业动态。|
业动态。|
应用
官网
https://www.mongodb.com/
下载地址
https://www.mongodb.com/try/download/community
学习文档地址
https://www.mongodb.com/docs/v4.4/introduction/
安装
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.4.9.tgz
tar -zxvf mongodb-linux-x86_64-rhel70-4.4.9.tgz
tar -zxvf mongodb-linux-x86_64-rhel70-4.4.9.tgz
启动与关闭
#创建dbpath和logpath
mkdir -p /mongodb/data /mongodb/log /mongodb/conf
#进入mongodb目录,启动mongodb服务
bin/mongod --port=27017 --dbpath=/mongodb/data --logpath=/mongodb/log/mongodb.log --bind_ip=0.0.0.0 --fork
mkdir -p /mongodb/data /mongodb/log /mongodb/conf
#进入mongodb目录,启动mongodb服务
bin/mongod --port=27017 --dbpath=/mongodb/data --logpath=/mongodb/log/mongodb.log --bind_ip=0.0.0.0 --fork
添加环境变量
修改/etc/profile,添加环境变量,方便执行MongoDB命令
export MONGODB_HOME=/usr/local/soft/mongodb
PATH=$PATH:$MONGODB_HOME/bin
PATH=$PATH:$MONGODB_HOME/bin
然后执行source /etc/profile 重新加载环境变量
增加配置文件并启动
systemLog:
destination: file
path: /mongodb/log/mongod.log # log path
logAppend: true
storage:
dbPath: /mongodb/data # data directory
engine: wiredTiger #存储引擎
journal: #是否启用journal日志
enabled: true
net:
bindIp: 0.0.0.0
port: 27017 # port
processManagement:
fork: true
destination: file
path: /mongodb/log/mongod.log # log path
logAppend: true
storage:
dbPath: /mongodb/data # data directory
engine: wiredTiger #存储引擎
journal: #是否启用journal日志
enabled: true
net:
bindIp: 0.0.0.0
port: 27017 # port
processManagement:
fork: true
mongod -f /mongodb/conf/mongo.conf
关闭
mongod --port=27017 --dbpath=/mongodb/data --shutdown
mongo shell
use admin
db.shutdownServer()
db.shutdownServer()
mongo shell的使用
mongo --port=27017
常用的命令
show dbs | show databases
展示数据库
use 数据库名
切换数据库,如果不存在创建数据库
db.dropDatabase()
删除数据库
show collections | show tables
显示当前数据库的集合列表
db.集合名.stats()
查询集合的详情
db.集合名.drop()
删除集合
show users
显示当前数据库的用户列表
show roles
显示当前数据库下的用户角色
show profile
显示最近发生的操作
load("xxx.js")
加载对应的js脚本
exit | quit()
退出当前shell
db.help()
查看当前数据库支持的方法
db.集合名.help()
db.集合名.help()
db.version()
查看数据库的版本
#创建集合
db.createCollection("emp")
db.createCollection("emp")
安全认证
创建管理员账号
# 设置管理员用户名密码需要切换到admin库
use admin
#创建管理员
db.createUser({user:"root",pwd:"root",roles:["root"]})
# 查看所有用户信息
show users
#删除用户
db.dropUser("root")
use admin
#创建管理员
db.createUser({user:"root",pwd:"root",roles:["root"]})
# 查看所有用户信息
show users
#删除用户
db.dropUser("root")
创建应用数据库的账号
use test
db.createUser({user:"test",pwd:"test",roles:["dbOwner"]})
db.createUser({user:"test",pwd:"test",roles:["dbOwner"]})
默认情况下,MongoDB不会启用鉴权,以鉴权模式启动MongoDB
mongod -f /mongodb/conf/mongo.conf --auth
mongo localhost:27017 -u test -p test --authenticationDatabase=test
文档常用操作
插入
3.2 版本之后新增了 db.collection.insertOne() 和 db.collection.insertMany()
insert: 若插入的数据主键已经存在,则会抛 DuplicateKeyException 异常,提示主键重复,不保
存当前数据。
存当前数据。
save: 如果 _id 主键存在则更新数据,如果不存在就插入数据。
insertMany:向指定集合中插入多条文档数据
查询
查询一条
db.collection.findOne(query, projection)
条件查询
db.collection.find({字段:条件})
正则表达式匹配查询
//使用正则表达式查找type包含 so 字符串的book
db.books.find({type:{$regex:"so"}})
//或者
db.books.find({type:/so/})
db.books.find({type:{$regex:"so"}})
//或者
db.books.find({type:/so/})
分页&排序
#指定按收藏数(favCount)降序返回
db.books.find({type:"travel"}).sort({favCount:-1})
db.books.find({type:"travel"}).sort({favCount:-1})
skip用于指定跳过记录数,limit则用于限定返回结果数量。可以在执行find命令的同时指定skip、limit
参数,以此实现分页的功能。比如,假定每页大小为8条,查询第3页的book文档:
参数,以此实现分页的功能。比如,假定每页大小为8条,查询第3页的book文档:
db.books.find().skip(8).limit(4)
更新
db.collection.update(query,update,options)
query
描述更新的查询条件
update
描述更新的动作及新的内容
options
描述更新的选项
upsert
可选,如果不存在update的记录,是否插入新的记录。默认false,不插入
multi
可选,是否按条件查询出的多条记录全部更新。 默认false,只更新找到的第一条记录
writeConcern
决定一个写操作落到多少个节点上才算成功
更新多条
db.books.update({type:"novel"},{$set:{publishedDate:new Date()}},{"multi":true})
updateMany
更新单个
updateOne
替换单个文档
replaceOne
upsert是一种特殊的更新,其表现为如果目标文档不存在,则执行插入命令。
db.books.update(
{title:"my book"},
{$set:{tags:["nosql","mongodb"],type:"none",author:"fox"}},
{upsert:true}
)
{title:"my book"},
{$set:{tags:["nosql","mongodb"],type:"none",author:"fox"}},
{upsert:true}
)
nMatched、nModified都为0,表示没有文档被匹配及更新,nUpserted=1提示执行了upsert动作
findAndModify命令
findAndModify兼容了查询和修改指定文档的功能,findAndModify只能更新单个文档
findAndModify会返回修改前的“旧”数据。如果希望返回修改后的数据,则可以指定new选
项
项
findOneAndUpdate
更新单个文档并返回更新前(或更新后)的文档
findOneAndReplace
替换单个文档并返回替换前(或替换后)的文档
删除
remove
db.user.remove({age:28})// 删除age 等于28的记录
db.user.remove({age:{$lt:25}}) // 删除age 小于25的记录
db.user.remove( { } ) // 删除所有记录
db.user.remove() //报错
db.user.remove({age:{$lt:25}}) // 删除age 小于25的记录
db.user.remove( { } ) // 删除所有记录
db.user.remove() //报错
只删除一条文档
remove命令会删除匹配条件的全部文档,如果希望明确限定只删除一个文档,则需要指定justOne参
数,命令格式如下:
数,命令格式如下:
db.books.remove({type:"novel"},true)
删除满足条件的首条记录
delete
db.books.deleteMany ({}) //删除集合下全部文档
db.books.deleteMany ({ type:"novel" }) //删除 type等于 novel 的全部文档
db.books.deleteOne ({ type:"novel" }) //删除 type等于novel 的一个文档
db.books.deleteMany ({ type:"novel" }) //删除 type等于 novel 的全部文档
db.books.deleteOne ({ type:"novel" }) //删除 type等于novel 的一个文档
remove、deleteOne等命令在删除文档后只会返回确认性的信息,如果希望获得被删除的文档,则可以
使用findOneAndDelete命令
使用findOneAndDelete命令
db.books.findOneAndDelete({type:"novel"})
除了在结果中返回删除文档,findOneAndDelete命令还允许定义“删除的顺序”,即按照指定顺序删除找
到的第一个文档
到的第一个文档
db.books.findOneAndDelete({type:"novel"},{sort:{favCount:1}})
remove、deleteOne等命令只能按默认顺序删除,利用这个特性,findOneAndDelete可以实现队列的先进先出。
聚合操作
单一聚合
db.collection.estimatedDocumentCount()
忽略查询条件,返回集合或视图中所有文档的计数
db.collection.count()
返回与find()集合或视图的查询匹配的文档计数 。
等同于 db.collection.find(query).count()构造
等同于 db.collection.find(query).count()构造
db.collection.distinct()
在单个集合或视图中查找指定字段的不同值,并在
数组中返回结果。
数组中返回结果。
聚合管道
管道(Pipeline)
整个聚合运算过程
阶段(Stage)
常用管道操作
$project
投影
db.books.aggregate([{$project:{name:"$title"}}])
db.books.aggregate([{$project:{name:"$title",_id:0,type:1,author:1}}])
$match
筛选条件
db.books.aggregate([{$match:{type:"technology"}}])
$count
统计
db.books.aggregate([ {$match:{type:"technology"}}, {$count: "type_count"}])
$lookup
连接
db.customer.aggregate([
{$lookup: {
from: "order",
localField: "customerCode",
foreignField: "customerCode",
as: "customerOrder"
}
}
])
{$lookup: {
from: "order",
localField: "customerCode",
foreignField: "customerCode",
as: "customerOrder"
}
}
])
$group
分组
$sort
排序
$skip/$limit
分页
$unwind
展开数组
视图
介绍
MongoDB视图是一个可查询的对象,它的内容由其他集合或视图上的聚合管道定义
作用
数据抽象
保护敏感数据的一种方法
将敏感数据投射到视图之外
只读
基于角色,给与访问权限
应用
创建
删除
db.orderInfo.drop();
修改
索引
介绍
索引是一种用来快速查询数据的数据结构。B+Tree就是一种常用的数据库索引数据结构,MongoDB
采用B+Tree 做索引,索引创建在colletions上。MongoDB不使用索引的查询,先扫描所有的文档,再
匹配符合条件的文档。 使用索引的查询,通过索引找到文档,使用索引能够极大的提升查询效率。
采用B+Tree 做索引,索引创建在colletions上。MongoDB不使用索引的查询,先扫描所有的文档,再
匹配符合条件的文档。 使用索引的查询,通过索引找到文档,使用索引能够极大的提升查询效率。
分类
按照索引包含的字段数量,可以分为单键索引和组合索引(或复合索引)
按照索引字段的类型,可以分为主键索引和非主键索引。
按照索引节点与物理记录的对应方式来分,可以分为聚簇索引和非聚簇索引,其中聚簇索引是指索
引节点上直接包含了数据记录,而后者则仅仅包含一个指向数据记录的指针
引节点上直接包含了数据记录,而后者则仅仅包含一个指向数据记录的指针
按照索引的特性不同,又可以分为唯一索引、稀疏索引、文本索引、地理空间索引等
应用
创建索引
db.collection.createIndex(keys, options)
查询索引
#查看索引信息
db.books.getIndexes()
#查看索引键
db.books.getIndexKeys()
db.books.getIndexes()
#查看索引键
db.books.getIndexKeys()
查询索引占用大小
db.collection.totalIndexSize([is_detail])
is_detail:可选参数,传入除0或false外的任意数据,都会显示该集合中每个索引的大小及总大
小。如果传入0或false则只显示该集合中所有索引的总大小。默认值为false。
小。如果传入0或false则只显示该集合中所有索引的总大小。默认值为false。
删除索引
#删除集合指定索引
db.col.dropIndex("索引名称")
#删除集合所有索引
db.col.dropIndexes()
db.col.dropIndex("索引名称")
#删除集合所有索引
db.col.dropIndexes()
操作
单键索引(Single Field Indexes)
在某一个特定的字段上建立索引 mongoDB在ID上建立了唯一的单键索引,所以经常会使用id来进行查
询; 在索引字段上进行精确匹配、排序以及范围查找都会使用此索引
询; 在索引字段上进行精确匹配、排序以及范围查找都会使用此索引
复合索引(Compound Index)
复合索引是多个字段组合而成的索引,其性质和单字段索引类似。但不同的是,复合索引中字段的顺
序、字段的升降序对查询性能有直接的影响,因此在设计复合索引时则需要考虑不同的查询场景。
序、字段的升降序对查询性能有直接的影响,因此在设计复合索引时则需要考虑不同的查询场景。
多键索引(Multikey Index)
在数组的属性上建立索引。针对这个数组的任意值的查询都会定位到这个文档,既多个索引入口或者键值
引用同一个文档
引用同一个文档
多键索引很容易与复合索引产生混淆,复合索引是多个字段的组合,而多键索引则仅仅是在一个字段上
出现了多键(multi key)。而实质上,多键索引也可以出现在复合字段上
MongoDB并不支持一个复合索引中同时出现多个数组字段
地理空间索引(Geospatial Index)
在移动互联网时代,基于地理位置的检索(LBS)功能几乎是所有应用系统的标配。MongoDB为地理空
间检索提供了非常方便的功能。地理空间索引(2dsphereindex)就是专门用于实现位置检索的一种特
殊索引。
间检索提供了非常方便的功能。地理空间索引(2dsphereindex)就是专门用于实现位置检索的一种特
殊索引。
db.restaurant.createIndex({location : "2dsphere"})
全文索引(Text Indexes)
MongoDB支持全文检索功能,可通过建立文本索引来实现简易的分词检索。
db.reviews.createIndex( { comments: "text" } )
Hash索引(Hashed Indexes)
不同于传统的B-Tree索引,哈希索引使用hash函数来创建索引。在索引字段上进行精确匹配,但不支持范
围查询,不支持多键hash; Hash索引上的入口是均匀分布的,在分片集合中非常有用;
围查询,不支持多键hash; Hash索引上的入口是均匀分布的,在分片集合中非常有用;
db.users. createIndex({username : 'hashed'})
通配符索引(Wildcard Indexes)
MongoDB的文档模式是动态变化的,而通配符索引可以建立在一些不可预知的字段上,以此实现查询
的加速。MongoDB 4.2 引入了通配符索引来支持对未知或任意字段的查询。
的加速。MongoDB 4.2 引入了通配符索引来支持对未知或任意字段的查询。
db.products.createIndex( { "product_attributes.$**" : 1 } )
属性
唯一索引(Unique Indexes)
部分索引(Partial Indexes)
稀疏索引(Sparse Indexes)
TTL索引(TTL Indexes)
隐藏索引(Hidden Indexes)
建议
为每一个查询建立合适的索引
创建合适的复合索引,不要依赖于交叉索引
复合索引字段顺序:匹配条件在前,范围条件在后(Equality First, Range After)
尽可能使用覆盖索引(Covered Index)
建索引要在后台运行
在对一个集合创建索引时,该集合所在的数据库将不接受其他读写操作。对大数据量的集合建索引,建
议使用后台运行选项 {background: true}
议使用后台运行选项 {background: true}
explain执行计划详解
db.collection.find().explain(<verbose>)
verbose 可选参数,表示执行计划的输出模式,默认queryPlanner
# 未创建title的索引
db.books.find({title:"book-1"}).explain("queryPlanner")
executionStats
#创建索引
db.books.createIndex({title:1})
db.books.find({title:"book-1"}).explain("executionStats")
db.books.createIndex({title:1})
db.books.find({title:"book-1"}).explain("executionStats")
allPlansExecution
allPlansExecution返回的信息包含 executionStats 模式的内容,且包含allPlansExecution:[]块
Zookeeper
介绍
分布式协调框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同
步服务、集群管理、分布式应用配置项的管理等。
步服务、集群管理、分布式应用配置项的管理等。
核心概念
文件系统数据结构
每个子目录项都被称作为 znode(目录节点)
PERSISTENT持久化目录节点
客户端与zookeeper断开连接后,该节点依旧存在,只要不手动删除该节点,他将永远存在
PERSISTENT_SEQUENTIAL持久化顺序编号目录节点
客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
EPHEMERAL临时目录节点
客户端与zookeeper断开连接后,该节点被删除
EPHEMERAL_SEQUENTIAL临时顺序编号目录节点
客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
Container 节点
3.5.3 版本新增,如果Container节点下面没有子节点,则Container节点
在未来会被Zookeeper自动清除,定时任务默认60s 检查一次
在未来会被Zookeeper自动清除,定时任务默认60s 检查一次
TTL 节点
默认禁用,只能通过系统配置 zookeeper.extendedTypesEnabled=true 开启,不稳定
监听通知机制
客户端注册监听它关心的任意节点,
或者目录节点及递归子目录节点
或者目录节点及递归子目录节点
如果注册的是对某个节点的监听,则当这个节点被删除,或者被修改时,对应的客户端将被通知
如果注册的是对某个目录的监听,则当这个目录有子节点被创建,或者有子节点被删除,对应
的客户端将被通知
的客户端将被通知
如果注册的是对某个目录的递归子节点进行监听,则当这个目录下面的任意子节点有目录结构
的变化(有子节点被创建,或被删除)或者根节点有数据变化时,对应的客户端将被通知。
的变化(有子节点被创建,或被删除)或者根节点有数据变化时,对应的客户端将被通知。
注意:所有的通知都是一次性的,及无论是对节点还是对目录进行的监听,一旦触发,对应的监
听即被移除。递归子节点,监听是对所有子节点的,所以,每个子节点下面的事件同样只会被触
发一次。
听即被移除。递归子节点,监听是对所有子节点的,所以,每个子节点下面的事件同样只会被触
发一次。
应用场景
分布式配置中心
分布式注册中心
分布式锁
分布式队列
集群选举
分布式屏障
发布/订阅
应用
安装
配置JAVA环境
java ‐version
下载
wget https://mirror.bit.edu.cn/apache/zookeeper/zookeeper‐3.5.8/apache‐zookeeper‐3.5.8‐bin.tar.gz
tar ‐zxvf apache‐zookeeper‐3.5.8‐bin.tar.gz
cd apache‐zookeeper‐3.5.8‐bin
重命名配置文件
cp zoo_sample.cfg zoo.cfg
启动zookeeper
# 可以通过 bin/zkServer.sh 来查看都支持哪些参数
bin/zkServer.sh start conf/zoo.cfg
bin/zkServer.sh start conf/zoo.cfg
监测是否启动成功
echo stat | nc 192.168.109.200 // 前提是配置文件中中讲 stat 四字命令设置了了白名单
如:
4lw.commands.whitelist=stat
如:
4lw.commands.whitelist=stat
连接服务器
bin/zkCli.sh -server ip:port
命令操作
help
查询所有命令
创建zookeeper 节点命令
create [-s] [-e] [-c] [-t ttl] path [data] [acl]
中括号为可选项,没有则默认创建持久化节点
-s: 顺序节点
-e: 临时节点
-c: 容器节点
-t: 可以给节点添加过期时间,默认禁用,需要通过系统参数启用
(-Dzookeeper.extendedTypesEnabled=true, znode.container.checkIntervalMs : (Java system property only) New in 3.5.1: The time interval in milliseconds for each check of candidate container and ttl nodes. Default is "60000".)
-e: 临时节点
-c: 容器节点
-t: 可以给节点添加过期时间,默认禁用,需要通过系统参数启用
(-Dzookeeper.extendedTypesEnabled=true, znode.container.checkIntervalMs : (Java system property only) New in 3.5.1: The time interval in milliseconds for each check of candidate container and ttl nodes. Default is "60000".)
创建
create /test niubi
查看节点
get /test
修改节点
set /test niubi1
查看节点状态
stat /test
Stat
cZxid:创建znode的事务ID(Zxid的值)。
mZxid:最后修改znode的事务ID。
pZxid:最后添加或删除子节点的事务ID(子节点列表发生变化才会发生改变)。
ctime:znode创建时间。
mtime:znode最近修改时间。
dataVersion:znode的当前数据版本。
cversion:znode的子节点结果集版本(一个节点的子节点增加、删除都会影响这个版本)。
aclVersion:表示对此znode的acl版本。
ephemeralOwner:znode是临时znode时,表示znode所有者的 session ID。 如果znode不是临时znode,则该字段设置为零。
dataLength:znode数据字段的长度。
numChildren:znode的子znode的数量。
cZxid:创建znode的事务ID(Zxid的值)。
mZxid:最后修改znode的事务ID。
pZxid:最后添加或删除子节点的事务ID(子节点列表发生变化才会发生改变)。
ctime:znode创建时间。
mtime:znode最近修改时间。
dataVersion:znode的当前数据版本。
cversion:znode的子节点结果集版本(一个节点的子节点增加、删除都会影响这个版本)。
aclVersion:表示对此znode的acl版本。
ephemeralOwner:znode是临时znode时,表示znode所有者的 session ID。 如果znode不是临时znode,则该字段设置为零。
dataLength:znode数据字段的长度。
numChildren:znode的子znode的数量。
创建子节点
create /test/test1
查看子节点信息,比如根节点下面的所有子节点, 加一个大写 R 可以查看递归子节点列表
ls -R /test
监听
针对节点的监听:一定事件触发,对应的注册立刻被移除,所以事件监听是一次性的
get -w /path // 注册监听的同时获取数据
stat -w /path // 对节点进行监听,且获取元数据信息
stat -w /path // 对节点进行监听,且获取元数据信息
针对目录的监听
ls -w /path
针对递归子目录的监听
ls -R -w /path : -R 区分大小写,一定用大写
Zookeeper事件类型
None: 连接建立事件
NodeCreated: 节点创建
NodeDeleted: 节点删除
NodeDataChanged:节点数据变化
NodeChildrenChanged:子节点列表变化
DataWatchRemoved:节点监听被移除
ChildWatchRemoved:子节点监听被移除
NodeCreated: 节点创建
NodeDeleted: 节点删除
NodeDataChanged:节点数据变化
NodeChildrenChanged:子节点列表变化
DataWatchRemoved:节点监听被移除
ChildWatchRemoved:子节点监听被移除
ACL 权限控制( Access Control List )
可以控制节点的读写操作,保证数据的安全性,Zookeeper ACL 权限设置分为 3 部分组成,分别是:权限模式(Scheme)、授权对象(ID)、权限信息(Permission)。最终组成一条例如“scheme:id:permission”格式的 ACL 请求信息
Scheme(权限模式)
用来设置 ZooKeeper 服务器进行权限验证的方式
种是范围验证。所谓的范围验证就是说 ZooKeeper 可以针对一个 IP 或者一段 IP 地址授予某种权限。比如我们可以让一个 IP 地址为“ip:192.168.0.110”的机器对服务器上的某个数据节点具有写入的权限。或者也可以通过“ip:192.168.0.1/24”给一段 IP 地址的机器赋权。
另一种权限模式就是口令验证,也可以理解为用户名密码的方式。在 ZooKeeper 中这种验证方式是 Digest 认证,而 Digest 这种认证方式首先在客户端传送“username:password”这种形式的权限表示符后,ZooKeeper 服务端会对密码 部分使用 SHA-1 和 BASE64 算法进行加密,以保证安全性。
还有一种Super权限模式, Super可以认为是一种特殊的 Digest 认证。具有 Super 权限的客户端可以对 ZooKeeper 上的任意数据节点进行任意操作。
授权对象(ID)
授权对象就是说我们要把权限赋予谁,而对应于 4 种不同的权限模式来说,如果我们选择采用 IP 方式,使用的授权对象可以是一个 IP 地址或 IP 地址段;而如果使用 Digest 或 Super 方式,则对应于一个用户名。如果是 World 模式,是授权系统中所有的用户。
生成授权ID的两种方式
@Test
public void generateSuperDigest() throws NoSuchAlgorithmException {
String sId = DigestAuthenticationProvider.generateDigest("gj:test");
System.out.println(sId);// gj:X/NSthOB0fD/OT6iilJ55WJVado=
}
public void generateSuperDigest() throws NoSuchAlgorithmException {
String sId = DigestAuthenticationProvider.generateDigest("gj:test");
System.out.println(sId);// gj:X/NSthOB0fD/OT6iilJ55WJVado=
}
在xshell 中生成
echo -n <user>:<password> | openssl dgst -binary -sha1 | openssl base64
权限信息(Permission)
权限就是指我们可以在数据节点上执行的操作种类
数据节点(c: create)创建权限,授予权限的对象可以在数据节点下创建子节点;
数据节点(w: wirte)更新权限,授予权限的对象可以更新该数据节点;
数据节点(r: read)读取权限,授予权限的对象可以读取该节点的内容以及子节点的列表信息;
数据节点(d: delete)删除权限,授予权限的对象可以删除该数据节点的子节点;
数据节点(a: admin)管理者权限,授予权限的对象可以对该数据节点体进行 ACL 权限设置。
数据节点(w: wirte)更新权限,授予权限的对象可以更新该数据节点;
数据节点(r: read)读取权限,授予权限的对象可以读取该节点的内容以及子节点的列表信息;
数据节点(d: delete)删除权限,授予权限的对象可以删除该数据节点的子节点;
数据节点(a: admin)管理者权限,授予权限的对象可以对该数据节点体进行 ACL 权限设置。
设置ACL有两种方式
节点创建的同时设置ACL
create [-s] [-e] [-c] path [data] [acl]
create [-s] [-e] [-c] path [data] [acl]
create /zk-node datatest digest:gj:X/NSthOB0fD/OT6iilJ55WJVado=:cdrwa
setAcl /zk-node digest:gj:X/NSthOB0fD/OT6iilJ55WJVado=:cdrwa
访问前需要添加授权信息
addauth digest gj:test
get /zk-node
datatest
get /zk-node
datatest
auth 明文授权
使用之前需要先
addauth digest username:password 注册用户信息,后续可以直接用明文授权
addauth digest username:password 注册用户信息,后续可以直接用明文授权
addauth digest u100:p100
create /node-1 node1data auth:u100:p100:cdwra
这是u100用户授权信息会被zk保存,可以认为当前的授权用户为u100
get /node-1
node1data
create /node-1 node1data auth:u100:p100:cdwra
这是u100用户授权信息会被zk保存,可以认为当前的授权用户为u100
get /node-1
node1data
IP授权模式
setAcl /node-ip ip:192.168.109.128:cdwra
create /node-ip data ip:192.168.109.128:cdwra
create /node-ip data ip:192.168.109.128:cdwra
多个指定IP可以通过逗号分隔, 如 setAcl /node-ip ip:IP1:rw,ip:IP2:a
Super 超级管理员模式
这是一种特殊的Digest模式, 在Super模式下超级管理员用户可以对Zookeeper上的节点进行任何的操作。
需要在启动了上通过JVM 系统参数开启
DigestAuthenticationProvider中定义
-Dzookeeper.DigestAuthenticationProvider.superDigest=super:<base64encoded(SHA1(password))
-Dzookeeper.DigestAuthenticationProvider.superDigest=super:<base64encoded(SHA1(password))
getAcl:获取某个节点的acl权限信息
setAcl:设置某个节点的acl权限信息
addauth: 输入认证授权信息,相当于注册用户信息,注册时输入明文密码,zk将以密文的形式存储
setAcl:设置某个节点的acl权限信息
addauth: 输入认证授权信息,相当于注册用户信息,注册时输入明文密码,zk将以密文的形式存储
可以通过系统参数zookeeper.skipACL=yes进行配置,默认是no,可以配置为true, 则配置过的ACL将不再进行权限检测
内存数据和持久化
内存数据
public class DataTree {
private final ConcurrentHashMap<String, DataNode> nodes =
new ConcurrentHashMap<String, DataNode>();
private final WatchManager dataWatches = new WatchManager();
private final WatchManager childWatches = new WatchManager();
private final ConcurrentHashMap<String, DataNode> nodes =
new ConcurrentHashMap<String, DataNode>();
private final WatchManager dataWatches = new WatchManager();
private final WatchManager childWatches = new WatchManager();
DataNode 是Zookeeper存储节点数据的最小单位
public class DataNode implements Record {
byte data[];
Long acl;
public StatPersisted stat;
private Set<String> children = null;
byte data[];
Long acl;
public StatPersisted stat;
private Set<String> children = null;
事务日志
针对每一次客户端的事务操作,Zookeeper都会将他们记录到事务日志中,当然,Zookeeper也会将数据变更应用到内存数据库中。我们可以在zookeeper的主配置文件zoo.cfg 中配置内存中的数据持久化目录,也就是事务日志的存储路径 dataLogDir. 如果没有配置dataLogDir(非必填), 事务日志将存储到dataDir (必填项)目录,
zookeeper提供了格式化工具可以进行数据查看事务日志数据
org.apache.zookeeper.server.LogFormatter
zookeeper提供了格式化工具可以进行数据查看事务日志数据
org.apache.zookeeper.server.LogFormatter
java -classpath .:slf4j-api-1.7.25.jar:zookeeper-3.5.8.jar:zookeeper-jute-3.5.8.jar org.apache.zookeeper.server.LogFormatter /usr/local/zookeeper/apache-zookeeper-3.5.8-bin/data/version-2/log.1
Zookeeper进行事务日志文件操作的时候会频繁进行磁盘IO操作,事务日志的不断追加写操作会触发底层磁盘IO为文件开辟新的磁盘块,即磁盘Seek。因此,为了提升磁盘IO的效率,Zookeeper在创建事务日志文件的时候就进行文件空间的预分配- 即在创建文件的时候,就向操作系统申请一块大一点的磁盘块。这个预分配的磁盘大小可以通过系统参数 zookeeper.preAllocSize 进行配置。
数据快照
数据快照用于记录Zookeeper服务器上某一时刻的全量数据,并将其写入到指定的磁盘文件中
可以通过配置snapCount配置每间隔事务请求个数,生成快照,数据存储在dataDir 指定的目录中,
可以通过如下方式进行查看快照数据( 为了避免集群中所有机器在同一时间进行快照,实际的快照生成时机为事务数达到 [snapCount/2 + 随机数(随机数范围为1 ~ snapCount/2 )] 个数时开始快照)
可以通过如下方式进行查看快照数据( 为了避免集群中所有机器在同一时间进行快照,实际的快照生成时机为事务数达到 [snapCount/2 + 随机数(随机数范围为1 ~ snapCount/2 )] 个数时开始快照)
java -classpath .:slf4j-api-1.7.25.jar:zookeeper-3.5.8.jar:zookeeper-jute-3.5.8.jar org.apache.zookeeper.server.SnapshotFormatter /usr/local/zookeeper/apache-zookeeper-3.5.8-bin/data-dir/version-2/snapshot.0
快照事务日志文件名为: snapshot.<当时最大事务ID>,日志满了即进行下一次事务日志文件的创建
有了事务日志,为啥还要快照数据。
快照数据主要时为了快速恢复,事务日志文件是每次事务请求都会进行追加的操作,而快照是达到某种设定条件下的内存全量数据。所以通常快照数据是反应当时内存数据的状态。事务日志是更全面的数据,所以恢复数据的时候,可以先恢复快照数据,再通过增量恢复事务日志中的数据即可
快照数据主要时为了快速恢复,事务日志文件是每次事务请求都会进行追加的操作,而快照是达到某种设定条件下的内存全量数据。所以通常快照数据是反应当时内存数据的状态。事务日志是更全面的数据,所以恢复数据的时候,可以先恢复快照数据,再通过增量恢复事务日志中的数据即可
集群搭建
这是模板水印,可删除
这是模板水印,可删除
0 条评论
下一页