docs: delete unused docs

This commit is contained in:
jaysunxiao committed 2026-05-06 17:13:15 +08:00
1 parent 2ac44c8b39
commit af4e65516c
69 files changed
-3708

No files matched your search

-132
View File
@@ -1,132 +0,0 @@
#### 注意
- The main branch is a development version for Java 21 and GraalVM. The stable version is [zfoo-java-11-17](https://github.com/zfoo-project/zfoo/tree/zfoo-java-11-17)
- Upgrade process
- reimport not found classes
- no need to inherit IPacket anymore
- SafeRunnable has been deleted, please use ThreadUtils.safeRunnable()
- rename interfaces and annotations,Storage -> IStorage,@Resource -> @Storage,@ResInjection -> @StorageAutowired
- rename interfaces and annotations,@EntityCachesInjection -> @EntityCacheAutowired,@EntityCaches -> @EntityCache
```
- 主干main是当前是面向java 21和GraalVM的开发版本,稳定版本请使用 [zfoo-java-11-17](https://github.com/zfoo-project/zfoo/tree/zfoo-java-11-17)
- 升级流程
- 重新导入找不到的类
- 不需要再继承IPacket
- SafeRunnable删除了,请使用ThreadUtils.safeRunnable()
- 重命名接口和注解,Storage -> IStorage,@Resource -> @Storage,@ResInjection -> @StorageAutowired
- 重命名接口和注解,@EntityCachesInjection -> @EntityCacheAutowired,@EntityCaches -> @EntityCache
```
#### 为什么部署的时候才用main,平时开发的时候从test启动
- 可以很好的隔离部署环境和开发测试环境
- 可以加上-Dspring.profiles.active=dev就可以从main启动
- 部署的时候从main启动(没有控制台日志),平时开发的时候从test启动(有控制台日志)
- 这样正式环境的配置放在main的resources里,测试环境的配置放在test的resources里,互不干扰
- 从test下启动的程序的配置文件会覆盖main中的配置文件
---
#### logback为什么在main和test都有一份日志配置,为什么弄了两份日志配置
- 隔离生产环境配置和测试环境的日志配置
- test的配置会覆盖main中的配置
- main中没有控制台日志,test中只有控制台日志,控制台日志会加锁在生产环境会导致性能问题
---
#### 为什么没有发布到maven仓库
- 项目非常轻量,容易修改,游戏场景很多都需要自己定制,打算java 21出来才会发布release版本
- zfoo安装很简单,maven install到本地就可以使用了,或者直接将zfoo源代码集成在在项目里当作一个module使用
- 没有用maven的<dependencyManagement>来管理这个root,是为了让下面的module不依赖于任何parent,复制出来就可以用
- 本地开发调试也可以直接用idea的add maven project当作一个第三方库使用
![Image text](image/idea/maven-pom.png)
---
#### 为什么选择 Java 11 而不是 Java 8
- var可以减少非常多的代码,使代码更加简介
- Java 11的版本统一了api,Oracle JDK 和 Open JDK 可以无缝切换
- 11对容器支持友好,现在是容器的时代,反观 Java 8 的一些老版本都不支持容器
- Java 8过两年会停止维护,能早用就早用
- 主流的框架全部都已经支持Java 11
- 下个版本的spring boot直接最低版本要求Java 17
---
#### 为什么不选择基于interface(接口)的形式提供RPC,像dubbo那样的RPC
- 基于interface的不够灵活,不方便
- interface写同步rpc比较方便,不适合异步的写法。现在流行异步调用链的写法,基于interface有点过时了
- 并不是所有编程语言都有interface的,你用了interface的话其它编程语言就没法用了,就无法跨平台了
- 基于packet的容易扩展,可以轻易跨平台
---
#### 为什么不支持SPI
- 大部分编程语言都没有SPI,所以不考虑支持SPI
---
#### zfoo的RPC实现原理
- zfoo的RPC和dubbo实现的原理一样,主要使用CompletableFuture,利用了唯一id
```
zfoo的RPC请求消息体会在IPacket的包后面附加上一个IAttachmet(SignalAttachment)作为同步和异步的信号。
SignalAttachment的signalId就是用于RPC的同步和异步的信号,通过这个唯一ID就可以确定哪个RPC请求对应了哪一个RPC回调。
```
---
#### zfoo的RPC协议格式为什么没有dubbo复杂
- duboo没有自己实现的比较好的协议所以要支持其它协议,zfoo有自己实现的协议已经极快了,只需要专注自己的协议和protobuf,兼容就比较简单
- zfoo作为rpc协议是,length | packet | attachment,当作普通服务器协议就是,length | packet
- attachment能装很多东西,算是协议上的多态,就不需要dubbo那么复杂的设计了
---
#### protocol的实现原理
- 通过递归解析对象的结构,用字节码增强生成顺序的序列化和反序列化函数,可以自己线生成C#协议,就能看到Javassist生成的是什么样的结构
---
#### 为什么选择mongodb
- mongodb可以单机部署也可以分布式部署,是万能的数据库,而且速度足够快
- mongodb,副本集部署(主从模式),可以数据同步,读写分离
- mongodb,分片部署,故障转移(容灾)
---
#### orm为什么不选择Spring的官方驱动SpringData mongodb
- SpringData mongodb 里面会生成一个class字段,比较占内存,而且你移动class类的时候体验不友好,不知道现在版本改了没有
- mongodb的官方驱动轻量,mongodb的官方驱动基本上什么功能都有,SpringData mongodb这个封装太重了
- zfoo的orm接近0封装,简单的封装可以极大提高上层的自由度
---
#### 前端h5的后台管理界面使用的技术栈
- 基础框架使用的,vue 2.6,https://cn.vuejs.org/
- UI框架使用的,vuetify 2.3,https://vuetifyjs.com
- 工具库使用的,lodash 4.17,https://www.lodashjs.com/
- icon使用的material font,mdi 5.3.45,https://pictogrammers.github.io/@mdi/font/5.3.45/
---
#### 手摸手的视频教程可以帮助新手起步
- 新手起步困难的话,直接看手把手的[zfoo 框架视频教程](./doc/video-tutorial.md),新手友好,先从简单的基础使用开始,然后再深入到底层核心代码运行
---
-4
View File
@@ -1,4 +0,0 @@
### protocol
- 考虑用javassist的方式支持protobuf,用约定大于配置的方式,还可以原生的和pojo对象结合起来
- 通过protobuf的proto文件生成java的pojo源代码,通过动态编译java源代码的方式生成其它语言的源代码
-5
View File
@@ -1,5 +0,0 @@
### 静态网页发布流程,主要针对oss的操作
- 创建默认的oss配置,读写权限设置为公共读写
- 静态页面,默认首页和默认404页都设置为:index.html,子目录首页不开通
- 绑定域名,比如bird.godot.fun,绑定到godot-fun-bird.oss....,这个操作会自动将oss域名绑定到指定域名,并且会设置DNS地址
-806
View File
@@ -1,806 +0,0 @@
# 一、索引管理
- es所有的命令都是通过http完成的
- es的和传统的数据库对于规则如下:
```
db es
数据库 索引
表 类型type
行 文档document
列 字段field
表结构schema 映射mapping
索引 全文索引
sql http接口
```
- Text:会分词,然后进行索引,支持模糊、精确查询,不支持聚合
- keyword: 不分词,直接索引,支持模糊、精确查询,支持聚合
## 1.索引的增加
- put blog,索引必须全部小写,es7过后取消type概念,默认为_doc
```json
{
"settings": {
"number_of_shards": 1
},
"mappings": {
"properties": {
"id": {
"type": "long"
},
"content": {
"type": "text"
},
"postTime": {
"type": "date"
},
"title": {
"type": "text"
}
}
}
}
```
## 2.删除
- delete blog
- 全部删除
```
curl -X DELETE http://localhost:9200/_all
```
## 3.改
- put blog/_settings
```json
{
"blocks.read": true
}
```
```
blocks.read,禁止对当前索引进行读操作
blocks.read_only,只允许读,不允许写
blocks.write,禁止写操作
```
- put blog/_mappings
```json
{
"properties": {
"id": {
"type": "text"
},
"content": {
"type": "text"
},
"postTime": {
"type": "date"
},
"title": {
"type": "text"
},
"aaa": {
"type": "text"
}
}
}
```
## 4.查
- 查询全部的索引
```
curl -X GET http://localhost:9200/_cat/indices
```
- get blog/_settings
- get blog/_mappings
## 5.索引打开和关闭
- post blog/_close,已经关闭的索引不能进行任何读写操作
- post blog/_open
- post _all/_close
- post _all/_open,全部打开
# 二、文档管理
## 1.增加文档
- put blog/_doc/1,index/type/id
```json
{
"id": 1,
"title": "Java讲义",
"postTime": "2019-06-09",
"content": "全功能的一门编程语言"
}
```
- post blog/_doc,这种方式id会随机生成
```json
{
"id": 3,
"title": "Java讲义3",
"postTime": "2019-06-09",
"content": "全功能的一门编程语言3"
}
```
## 2.修改文档
- put blog/_doc/1
```json
{
"title": "Java讲义bbb",
"postTime": "2019-06-09",
"content": "全功能的一门编程语言bbb"
}
```
## 3.删除文档
- delete blog/_doc/1
## 4.查询文档
- get blog/_doc/1
## 5.路由机制
- shard = hash(routing) % shard_num
```
routing默认为文档id,也可以指定routing,这样可以避免查询的时候广播查询,如下
get blog/article/1?routing=jay,routing值固定的话,可以让查询集中在一个片上
```
# 三、映射
- get blog/_mapping,查看映射信息,默认是动态映射
- put blog,创建索引时就添加静态映射,添加更精准,更详细的配置信息
```
boost为权重权重,默认为1
format匹配的格式
keyword不会被分词,ignore_above和keyword一起使用,指定分词和索引的最大长度,超过的部分不会被索引
index是否进行到排序索引,false不进行索引,也就不可搜索,默认开启,动态mapping解析出来为数字类型、布尔类型的字段除外
doc_values默认开启,会增加一个列存储索引,对于不需要排序和聚合的字段可以关闭
similarity默认使用BM25评分模型
{
"settings": {
"number_of_shards": 1
},
"mappings": {
"properties": {
"id": {
"type": "long"
},
"title": {
"type": "keyword",
"ignore_above": 20
},
"content": {
"type": "text",
"boost": 2,
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
},
"postTime": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss",
"doc_values": true,
"index": false
}
}
}
}
```
- post _bulk,批量插入,一个document必须在一行,而且还要换行,下面的结构不要改
```
{"index":{ "_index": "blog", "_type": "_doc", "_id": "1" }}
{"id": 1, "title": "Java讲义", "postTime": "2017-06-09 12:30:00", "content": "全功能的一门编程语言,既可以做前端,也可以做后端"}
{"index":{ "_index": "blog", "_type": "_doc", "_id": "2" }}
{"id": 2, "title": "精通C#", "postTime": "2018-06-09 12:30:00", "content": "大部分用来在windows上做前端"}
{"index":{ "_index": "blog", "_type": "_doc", "_id": "3" }}
{"id": 3, "title": "Go实战", "postTime": "2019-06-09 12:30:00", "content": "Google全力主推的一门语言"}
```
# 四、复杂查询
- get/post,blog/_search或者/_search查询全部
```json
{
"query": {
"match_all": {}
}
}
```
- get,/_search,分页查询,_source指定需要返回的字段,version默认不返回,min_score过滤最低的分数,highlight高亮显示,不指定sort默认用评分排序
```json
{
"from": 0,
"size": 100,
"query": {
"term": {
"content": "前端"
}
},
"_source": [
"title",
"content"
],
"version": true,
"min_score": 0.6,
"highlight": {
"fields": {
"content": {
"pre_tags": [
"<strong>"
],
"post_tags": [
"</strong"
]
}
}
},
"sort": [
{
"id": {
"order": "desc"
}
},
{
"title": {
"order": "asc"
}
}
]
}
```
## 1.全文查询
### 1).match
- query会分词,operator分词为或关系
```json
{
"query": {
"match": {
"content": {
"query": "编程语言",
"operator": "or"
}
}
}
}
```
- multi_match是match的升级,可以指定多个字段
```json
{
"query": {
"multi_match": {
"query": "语言",
"fields": [
"title",
"content"
]
}
}
}
```
### 2).match_phrase
- 会分词,分词后的所有词项都要出现在该字段中;字段中的词项顺序要一致
```json
{
"query": {
"match_phrase": {
"content": "编程语言"
}
}
}
```
- match_phrase_prefix与match_phrase类似,只不过支持最后一个词项前缀匹配
```json
{
"query": {
"match_phrase_prefix": {
"content": "语言 既"
}
}
}
```
### 3).simple_query_string
```json
{
"query": {
"simple_query_string": {
"query": "编程语言",
"analyzer": "ik_max_word",
"fields": ["title", "content"],
"default_operator": "or"
}
}
}
```
## 2.词项查询
### 1).terms
- term,查询含有一个词项
```json
{
"query": {
"term": {
"content": "前端"
}
}
}
```
- 查询content中包含语言的多个词项,含有一个词项只能有term
```json
{
"query": {
"terms": {
"content": [
"前端",
"后端"
]
}
}
}
```
### 2).range query
- 查询title,因为keyword类型的不分词,如果查“讲义”是查询不到的,term结构化字段查询,匹配一个值,且输入的值不会被分词器分词。
```json
{
"query": {
"range": {
"id": {
"gt": 2,
"lte": 3
}
}
}
}
```
### 3).exists query
- 查询title,因为keyword类型的不分词,如果查“讲义”是查询不到的,term结构化字段查询,匹配一个值,且输入的值不会被分词器分词。
```json
{
"query": {
"exists": {
"field": "title"
}
}
}
```
### 4).prefix query
- 查询某些以win开头的词项
```json
{
"query": {
"prefix": {
"content": "win"
}
}
}
```
### 5).wildcard query
- 通配符查询,?匹配一个字符,*匹配任意多个字符
```json
{
"query": {
"wildcard": {
"content": "win*"
}
}
}
```
### 6).regexp query
- 正则表达式查询,和java的正则表达式类似
```json
{
"query": {
"regexp": {
"content": "windows"
}
}
}
```
### 7).fuzzy query
- 模糊查询效率不高,如下把windows写错了,仍然可以查到
```json
{
"query": {
"fuzzy": {
"content": "windosw"
}
}
}
```
### 8).type query
- es7中默认的为_doc
```json
{
"query": {
"type": {
"value": "_doc"
}
}
}
```
### 8).ids query
- 查询含有某些_id的文档
```json
{
"query": {
"ids": {
"type": "_doc",
"values": [1, 2]
}
}
}
```
## 3.复合查询
### 1).bool query
- 逻辑查询,must=and,should=or
```json
{
"query": {
"bool": {
"minimum_should_match": 1,
"must": {
"match": {
"title": "Java讲义"
}
},
"should": {
"match": {
"content": "语言"
}
},
"must_not": {
"range": {
"id": {
"gte": 2
}
}
}
}
}
}
```
# 五、聚合查询
## 1.指标聚合
### 1).max 和 min
```json
{
"size": 0,
"aggs": {
"max_id_demo": {
"max": {
"field": "id"
}
}
}
}
```
### 2).avg 和 sum
- avg和sum用法类似
```json
{
"size": 0,
"aggs": {
"avg_id_demo": {
"avg": {
"field": "id"
}
}
}
}
```
### 3).cardinality
- 类似distinct
```json
{
"size": 0,
"aggs": {
"all_title_demo": {
"cardinality": {
"field": "title"
}
}
}
}
```
### 4).stats 和 extended_stats
- 会统计count,max,min,avg,sum,必须是整型,两者用法基本类似,后者返回更多的统计条目如平方差
```json
{
"size": 0,
"aggs": {
"extended_id_demo": {
"stats": {
"field": "id"
}
}
}
}
```
### 5).percentiles
- 百分位统计
```json
{
"size": 0,
"aggs": {
"percentiles_id_demo": {
"percentiles": {
"field": "id"
}
}
}
}
```
### 6).percentiles
- 按字段统计文档数量
```json
{
"size": 0,
"aggs": {
"count_id_demo": {
"value_count": {
"field": "id"
}
}
}
}
```
## 2.分组聚合
### 1).terms aggregation
- 按id分组,统计每组的数量
```json
{
"size": 0,
"aggs": {
"per_count_demo": {
"terms": {
"field": "id"
}
}
}
}
```
- 按id分组,统计每组的数量和每组平均价格
```json
{
"size": 0,
"aggs": {
"per_count_demo": {
"terms": {
"field": "id"
},
"aggs": {
"avg_id_test": {
"avg": {
"field": "id"
}
}
}
}
}
}
```
### 2).filter aggregation
- filter过滤符合条件的文档,将这些文档分到一个桶中,然后统计
```json
{
"size": 0,
"aggs": {
"avg_id_demo": {
"filter": {
"term": {
"content": "前端"
}
},
"aggs": {
"avg_id_test": {
"avg": {
"field": "id"
}
}
}
}
}
}
```
- filters过滤符合条件的文档,将这些文档分到不同的filter桶中,然后统计
```json
{
"size": 0,
"aggs": {
"avg_id_demo": {
"filters": {
"filters": [
{
"match": {
"id": 1
}
},
{
"match": {
"id": 2
}
},
{
"match": {
"id": 3
}
}
]
},
"aggs": {
"avg_id_test": {
"avg": {
"field": "id"
}
}
}
}
}
}
```
### 3).range aggregation
- 数值类型的范围聚合,反映数据的分布情况
```json
{
"size": 0,
"aggs": {
"range_id_demo": {
"range": {
"field": "id",
"ranges": [
{
"to": 2
},
{
"from": 2,
"to": 3
},
{
"from": 3
}
]
}
}
}
}
```
- 日期类型的范围聚合
```json
{
"size": 0,
"aggs": {
"range_date_demo": {
"date_range": {
"field": "postTime",
"format": "yyyy-MM-dd HH:mm:ss",
"ranges": [
{
"to": "2018-01-01 16:00:00"
},
{
"from": "2018-01-01 16:00:00",
"to": "2019-01-01 16:00:00"
},
{
"from": "2019-01-01 16:00:00"
}
]
}
}
}
}
```
-83
View File
@@ -1,83 +0,0 @@
# 一、下载和安装
- 安装Java环境
- 安装ElasticSearch,在安装Java JDK,配置JAVA_HOME环境变量之后,就可以安装Elasticsearch全文搜索引擎了,首先需要下载特定版本的ES安装文件。
- 下载ElasticSearch最新版本,这里是7.x.x,从官方下载中心 ElasticSearch Download 下载ElasticSearch安装包,并解压
# 二、启动
## 1.Windows下ES安装
- 进入解压的bin/目录,双击执行 elasticsearch.bat,该脚本文件执行 ElasticSearch 安装程序
- 稍等片刻,打开浏览器,输入 http://localhost:9200,有回应说明安装成功
## 2.Linux下ES安装
- 将elasticsearch-7.7.0-linux-x86_64.tar.gz下载到/usr/local/目录,这个目录是专门放下载的第三方软件的目录
- tar -xzvf elasticsearch-7.7.0-linux-x86_64.tar.gz 解压
- elasticsearch.yml做如下配置:
```
node.name: node-1
network.host: 0.0.0.0
http.port: 9200
cluster.initial_master_nodes: ["node-1"]
path.data: /data/es/db
path.logs: /data/es/logs
```
- mkdir -p /data/es/db /data/es/logs
- jvm.options做如下配置,单机部署可不用设置:
```
-Xms4g
-Xmx4g
```
- Elasticsearch 不允许root用户直接运行,所以要创建新用户,在root用户中创建新用户,执行如下命令:
```
useradd sun # 新增sun用户
passwd sun # 为sun用户设置密码
chown -R sun:sun /usr/local/elasticsearch-7.7.0 # 为新用户授权,文件夹所有者
chown -R sun:sun /data/es
su sun
./elasticsearch &
```
- 可能遇到的问题
```
启动内存国小,设置jvm内存-xms设置为内存的一半
max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
vim /etc/sysctl.conf # 在最后一行上加上
vm.max_map_count=262144 # 保存退出,加载配置,启动容器,就能够访问了
sysctl -p # 使修改生效
```
- 验证是否生效,curl localhost:9200
# 三、插件和工具
## 1.elasticsearch-analysis-ik插件
- ik是一个对中文比较友好的分词插件
```
download pre-build package from here: https://github.com/medcl/elasticsearch-analysis-ik/releases
create plugin folder cd your-es-root/plugins/ && mkdir ik
unzip plugin to folder your-es-root/plugins/ik
```
## 2.kibanba安装
- kibanba是es中的数据可视化工具
```
https://www.elastic.co/cn/downloads/kibanasu sun
直接下载解压,运行bin目录下的可执行文件kibana.bat
```
-14
View File
@@ -1,14 +0,0 @@
# ElasticSearch
## 一、简介
ES是一个基于Lucene的分布式全文搜索服务器,和SQL Server的全文索引(Fulltext Index)有点类似,都是基于分词和分段的全文搜索引擎, 具有分词,同义词,词干查询的功能,但是ES天生具有分布式和实时的属性。
ElasticSearch官网:http://www.elasticsearch.org
[官方Github](https://github.com/elastic/elasticsearch)
[官方中文权威指南](https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html)
[官方Java API文档](https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/index.html)
[Elasticsearch Java API 手册](https://es.quanke.name/)
-17
View File
@@ -1,17 +0,0 @@
## 1. go安装,windows
- 下载地址:https://golang.google.cn/dl/,这里提供了不同平台的go版本,根据自己的平台选择下载
- 安装完成后,把C:\go\bin目录添加到环境变量,这里就可以使用go了,在命令行输入 go version查看版本
```
最新版本得会自动添加path
```
## 常用命令
- go run xx.go 运行go程序
- go env 打印go得环境变量
- go get mysql 下载包
-195
View File
@@ -1,195 +0,0 @@
# Ⅰ. 环境搭建
## 1. Java安装,直接用Idea自带的(非全局)
```
安装JDK11
配置环境变量:JAVA_HOME D:\Java\JDK (JDK安装的目录,既bin层之上的目录)
Path %JAVA_HOME%\bin (通过JAVA_HOME确定bin的目录)
在cmd命令中运行:java -version ,有结果返回则表示安装成功
```
## 2. git使用默认的安装,直接用Idea自带的(全局)
```
安装好以后,在git bash中输入:git --version,有结果返回则表示安装成功
```
- 参看所有的git配置,git config --list
- git下载代码慢的解决方法|无法下载代码的解决方法,取消git配置使用git config --global --unset http.proxy
```
使用vpn,老版本的是10809端口,新版本的只有一个10809端口
git config --global http.proxy http://127.0.0.1:10809
git config --global https.proxy https://127.0.0.1:10809
```
- 换行符,提交时转换为LF,检出时不转换,强烈推荐设置,可以减少不同电脑和不同系统之间切换,导致换行符不统一的问题
```
git config --global core.autocrlf input
```
- 大小写,默认abc.gd 和 Abc.gd 被认为是同一个文件,在Linux上就会容易出问题,所以给它关闭
```
core.ignorecase = false
```
- idea git log 不同颜色
```
黄色代表HEAD,
绿色表示的是你本地分支,
紫色是远程分支,
```
## 3. nodejs安装(非全局)
- windows直接默认安装,linux到官网 https://nodejs.org/ 查看最新的安装教程
```
查看npm是否安装成功:npm --version;
查看nodejs是否安装成功:node --version;
```
- nodejs设置代理,大部分代理软件在windows的http代理端口是10809,ubuntu的是10808
```
npm config set proxy http://127.0.0.1:10809
```
- npm install -g yarn
- 在package.json文件右键,npm install安装依赖包
```
使用npm install安装需要的node module
安装完成后,在node module,和static上右键->mark Directory as->excluded
然后,File->Invalidate Cache/Restart
```
# Ⅱ. IntelliJ相关设置
## 1 参数配置:Help->edit custom
- 自定义idea参数,edit custom properties
````
# 控制台输出缓存,如果不使用下面的参数,也可以在Setting->Editor->General->Console中设置
# idea.cycle.buffer.size=disabled
# 设置可读文件大小
idea.max.intellisense.filesize=50000
````
- 自定义jvm参数,edit custom vm operation
```
-Xms1024m
-Xmx8192m
-XX:ReservedCodeCacheSize=512m
-Dfile.encoding=UTF-8
-Duser.name=jaysunxiao
```
## 2 界面设置和编码设置(需要上面两步做完)
- 文件编码设置:File->Settings->Editor->File Encodings
```
Global Encoding:UTF-8
Projectt Encoding:UTF-8
Default encoding for properties files:UTF-8
BOM for utf8: No Bom
```
## 3 换行符设置
- 统一用Unix换行符:File->Settings->Editor->Code Style
```
Line separator,Unix and MacOs (\n)
```
## 4 代码模板设置
- 设置类模板:Editor->File and Code Templates->Includes
```
/**
* @author jaysunxiao
*/
```
- 版权信息设置:Editor->Copyright->Copyright Profiles,注意下面的版权信息不要有多余的换行
```
Copyright (C) 2020 The zfoo Authors
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
```
# 5 代理设置
- idea代理设置
![Image text](../image/idea/idea-proxy.jpg)
- maven需要设置代理,把当前目录中的settings.xml复制到.m2文件夹下,settings.xml中配置了proxy网络代理
![Image text](../image/idea/maven-settings-config.png)
- maven代理设置(非全局),-DproxySet=true -DproxyHost=127.0.0.1 -DproxyPort=10809
![Image text](../image/idea/idea-proxy-maven.jpg)
- 设置自动下载源代码,将上图的sources,documentation,annotations勾上
- mvn clean install -U ,强制检查maven库
## 6 使用Idea默认的快捷键设置
- eclipse快捷键设置,个人习惯
```
Run to Cursor,设置为F9
find,设置为ctrl+f
replace,设置为alt+f
close tab, 设置为 [ctrl + w]
folding,expand all设置为 ctrl +,collapse all设置为 ctrl -
在输入法上右键,将win10的简体繁体切换 [ctrl+f] 快捷键键取消,也取消 [ctrl + 空格] 的中英文切换快捷键
```
### 7 插件安装
- translate插件
```
快捷键设置
Show Translation Dialog,设置为ctrl + shift + x
Translate,设置为ctrl + x
```
- Maven Helper,maven依赖查看工具,推荐下载
# Ⅲ 其他软件安装
## 1. mongodb
1. 直接安装MongoDB,用custom自定义安装目录,一般用默认的安装目录即可,不要勾选mongoDB compas
2. 将C:\Program Files\MongoDB\Server\4.2\bin配置到环境变量中,便于全局使用
3. 在MongoDB的官网下载 MongoSh 和 MongoDB Database Tools,解压复制到MongoDB安装目录下的bin目录
4. mongosh # 进入命令行界面,查看安装的版本
5. 下载安装 Navicat for MongoDB
## 2. 中间件安装
- zookeeper安装,解压过后,直接运行zkServer.md,zookeeper运行需要依赖JAVA_HOME环境变量
-16
View File
@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 http://maven.apache.org/xsd/settings-1.2.0.xsd">
<proxies>
<proxy>
<id>my-maven-proxy-config</id>
<active>true</active>
<protocol>http</protocol>
<host>127.0.0.1</host>
<port>10809</port>
</proxy>
</proxies>
</settings>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 992 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

-111
View File
@@ -1,111 +0,0 @@
# 一、卸载java
## 1. 卸载JDK安装路径
- java -version
- javac
```
判断jdk是否安装(显示版本号等信息,说明已经安装)
```
- which java(查看JDK的安装路径)
```
rm -rf JDK的安装路径
```
## 2. 删除环境变量
- java -version
- javac
```
判断jdk是否安装(显示版本号等信息,说明已经安装)
```
- vim /etc/profile,删除配置中的环境变量
- ls /etc,看看有没有别的java配置文件,如果有就全部删除
# 二、安装JDK
```
把JDK下载到/usr/local目录下,在/usr/local下新建文件夹java
```
- tar -zxvf openjdk-17.0.2_linux-x64_bin.tar.gz -C /usr/local
- mv jdk-17.0.2 java
- vim /etc/profile
```
JAVA_HOME=/usr/local/java
PATH=$JAVA_HOME/bin:$PATH
export JAVA_HOME PATH
```
- source /etc/profile,加载环境变量
```
- java -version
- javac
- jps
判断jdk是否安装(显示版本号等信息,说明已经安装)
```
# 三、Linux内核参数设置
- vim /etc/sysctl.conf,然后执行sysctl -p使参数生效,永久生效
```
# ******************* SYN状态的内核参数调优 *******************
# 新建连接如果无响应,内核要发送多少次SYN连接才放弃,默认值为5
net.ipv4.tcp_syn_retries = 2
# 控制回应SYN失败的重试次数,默认值也是5
net.ipv4.tcp_synack_retries = 2
# 当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭
net.ipv4.tcp_syncookies = 1
# 指定所能接受SYN同步包的最大客户端数量,即半连接上限,默认值为128,对于web服务,频繁大量的SYN同步包,应该放大这个值
net.ipv4.tcp_max_syn_backlog = 65535
# ******************* FIN_WAIT_2状态的内核参数调优 *******************
# 如果被动关闭不发送FIN关闭连接,那么这个状态就会一直存在,当然Linux有针对该状态的超时时间,默认为60秒
net.ipv4.tcp_fin_timeout = 8
# ******************* TIME_WAIT状态的内核参数调优 *******************
# 对于web服务器,由于我们需要经常去连接mysql、redis或者一些RPC调用等,会有大量的主动关闭状态(TIME_WAIT),因此可以修改内核参数限制TIME_WAIT的数量
net.ipv4.tcp_max_tw_buckets = 36000
# 限制timewait 的数量,防止大量timewait导致系统负载升高,一旦达到限定值,则强制清理TIME_WAIT状态的连接并在打印系统日志(time wait bucket table overflow),该参数官方文档说明主要用来对抗DDos攻击
net.ipv4.tcp_tw_recycle = 1
# 时间戳,0关闭,1开启。不能和net.ipv4.tcp_tw_recycle参数同时开启,因为一旦开启net.ipv4.tcp_tw_recycle,服务器就会检查包的时间戳,如果对方发来的包的时间戳是乱跳或者说时间戳是滞后的,这样服务器就不会回复,服务器会把带了"倒退"的时间戳包当作是"recycle"的tw连接的重传数据,不是新的请求,于是丢掉不回包,就容易出现syn不响应
net.ipv4.tcp_timestamps = 0
# 开启重用,允许将TIME-WAIT sockets 重新用于新的TCP 连接
net.ipv4.tcp_tw_reuse = 1
# ******************* 长连接(keepalive)的内核参数调整 *******************
# 表示TCP连接在多少秒没有数据报文传输时启动探测报文,探测连接是否正常
net.ipv4.tcp_keepalive_time = 120
# 探测次数,超过设置后丢弃
net.ipv4.tcp_keepalive_probes = 3
# 前后探测报文之间的时间间隔
net.ipv4.tcp_keepalive_intvl = 15
# ******************* TCP/UDP内存参数调整 *******************
net.ipv4.tcp_mem = 786432 1048576 1572864
net.ipv4.tcp_wmem = 8192 131072 16777216
net.ipv4.tcp_rmem = 32768 131072 16777216
# 系统所能处理不属于任何进程的TCP sockets最大数量,默认值8192
net.ipv4.tcp_max_orphans = 32768
# ******************* 其他内核参数 *******************
# 表示用于向外连接的临时端口范围。缺省情况下很小:32768到61000
net.ipv4.ip_local_port_range = 1024 65000
# 表示socket监听(listen)的backlog上限,backlog是socket的监听队列,也就是服务端所能accept(socket编程中accpet()函数为建立TCP连接接受连接状态)即处理数据的最大客户端数量队列,默认值为128,如果队列满了的时候新来一条建立连接,该连接会被拒绝
net.core.somaxconn = 16384
# 设置系统所有进程一共可以打开多少个文件句柄,这是一个系统级的设置,管控的是所有进程总共可以同时打开多少文件句柄
fs.file-max = 6553600
# 每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目
net.core.netdev_max_backlog = 16384
```
-277
View File
@@ -1,277 +0,0 @@
# 一、MongoDB简介
## 1.MongoDB的特点
1. 数据文件为二进制Bson,一种Json的扩展
2. 模式自由,在生产环境可以轻松增加字段
3. 丰富的查询
4. 完整的索引支持
5. 复制和故障恢复
6. 自动分片
7. 客户端支持多种语言
8. 支持内存映射存储引擎
## 2.MongoDB的适用场景
- MongoDB 4.2 以后支持分布式事务,理论上适合所有场景
# 二、MongoDB安装
## Windows
### 1. MongoDB安装配置
1. 直接安装MongoDB,用custom自定义安装目录,一般用默认的安装目录即可,不要勾选mongoDB compass
```text
MongoDB安装出现Verify that you have sufficient privileges to start system services的解决办法如下:
网络服务Network Service账户的权限不够。
找到“我的电脑——右键——管理——本地用户和组——Administrators
双击Administrators后,点击”添加“——”高级“——”立即查找“——”Network Service“,
将Network Service 添加进去后,我们再来重新启动Mongodb service,发现启动成功!
```
2. 将C:\Program Files\MongoDB\Server\7.0\bin配置到环境变量中,便于全局使用
```text
需要另外下载 MongoDB Shell 解压到 C:\Program Files\MongoDB\Server\7.0\bin 目录中
```
3. mongod -version # 查看安装的版本
4. MongoDB的工具
```
需要另外下载 MongoDB Database Tools 解压到 C:\Program Files\MongoDB\Server\7.0\bin 目录中
bsondump # bson转换工具
mongodump # 逻辑备份工具
mongorestore # 逻辑恢复工具
mongoexport # 数据导出工具
mongoimport # 数据导入工具
mongofiles # GriedFS文件工具
mongos # 分片路由工具
mongostat # 状态监控工具
mongotop # 读写监控工具
```
### 2. Navicat for MongoDB
- 客户端自行下载
## Linux
### 1.MongoDB安装配置
- 把MongoDB下载到/usr/local目录下,在/usr/local下新建文件夹MongoDB
```
tar -zxvf mongodb-linux-x86_64-ubuntu2204-6.0.6.tgz -C /usr/local
mv mongodb-linux-x86_64-ubuntu2204-6.0.6 mongodb
vim /usr/local/mongodb/mongodb.config # 创建自定义配置文件,解压的mongodb安装包没有默认的配置文件
```
- wiredTiger
```yaml
#security:
# authorization: enabled
systemLog:
destination: file
path: "/data/mongodb/logs/mongodb.log"
logAppend: true
storage:
engine: wiredTiger
dbPath: "/data/mongodb/db"
directoryPerDB: true
# MongoDB默认端口是27017,为了安全,可以修改这个端口,避免恶意的连接尝试
# 生产环境需要设置ip:bind_ip=127.0.0.1,本机ip
net:
bindIp: 0.0.0.0
port: 27017
maxIncomingConnections: 900
processManagement:
fork: true
```
- inMemory
```yaml
#security:
# authorization: enabled
systemLog:
destination: file
path: "/data/mongodb/logs/mongodb.log"
logAppend: true
storage:
engine: inMemory
dbPath: "/data/mongodb/db"
# MongoDB默认端口是27017,为了安全,可以修改这个端口,避免恶意的连接尝试
# 生产环境需要设置ip:bind_ip=127.0.0.1,本机ip
net:
bindIp: 0.0.0.0
port: 22400
processManagement:
fork: true
```
- mkdir -p /data/mongodb/db /data/mongodb/logs
- touch /data/mongodb/logs/mongodb.log
- vim /etc/profile # 添加命令运行环境变量,在文件最后面添加下面两
```
JAVA_HOME=/usr/local/java
MONGODB_HOME=/usr/local/mongodb
PATH=$JAVA_HOME/bin:$MONGODB_HOME/bin:$PATH
export JAVA_HOME MONGODB_HOME PATH
```
- source /etc/profile,加载环境变量
- mongod --config /usr/local/mongodb/mongodb.config # 启动MongoDB
```
mongod: error while loading shared libraries: libnetsnmpmibs.so.31: cannot open shared object file: No such file or directory
如果启动看到上面这个错误,则是缺少net-snmp安装包,yum install net-snmp
```
- mongosh -port 22400 # 使用mongo客户端链接MongoDB
```
dpkg -i mongodb-mongosh_1.10.1_amd64.deb
mongodb客户端命令行和服务器分开了,需要安装mongosh工具
```
- vim /etc/security/limits.conf 数据库需要大的文件句柄,保存并退出编辑器,重新启动机器使配置生效
```
* soft nofile 65536
* hard nofile 65536
root soft nofile 65536
root hard nofile 65536
# * 表示所有用户
```
### 2.关闭MongoDB服务
- 方式一:进入mongo客户端关闭(推荐方式)
```
mongo -port 22400
use admin
db.shutdownServer()
```
- 方式二:kill -2 pid 或者 kill -15 pid
```
-2 和 -15 都会等MongoDB处理完,释放相应资源再停止。kill -9 会强制服务i停止,会导致数据的顺坏,不建议使用。
```
- 方式三:如果MongoDB注册为service,可以使用服务的命令关闭
```
systemctl stop mongodb
```
- 修复未正常关闭MongoDB,导致无法启动
```
进入/data/mongodb/db,将文件夹下的mongod.lock(也可以使用find命令查找)删除。
如果还有数据顺坏,需要使用mongod --repair命令修复一次,再正常启动。
修复数据库的实际过程很简单:将所有的文档导出后马上导入,忽略无效的文档,完成后会重建索引。
```
### 3.将MongoDB设置为开机自动启动
- vim /etc/systemd/system/mongodb.service,创建启动脚本,systemctl是最新的启动命令,避免用service
- chmod 754 /etc/systemd/system/mongodb.service,赋予启动脚本可执行的权限
```bash
[Unit]
#服务描述
Description=High-performance, schema-free document-oriented database
#指定了在systemd在执行完那些target之后再启动该服务
After=network.target
[Service]
#定义Service的运行类型,一般是forking(后台运行)
Type=forking
LimitNOFILE=65536
ExecStart=/usr/local/mongodb/bin/mongod --config /usr/local/mongodb/mongodb.config
ExecReload=
ExecStop=/usr/bin/pkill mongod
[Install]
#多用户
WantedBy=multi-user.target
```
- systemctl daemon-reload,重新加载服务
- systemctl enable mongodb,会有一行反馈
- systemctl status mongodb,注意看 -> enabled; vendor preset: disabled)
- systemctl start mongodb
### 4.安全和访问控制
#### windows不用设置用户,为了方便便于开发
#### Linux用户设置
- 添加用户之前,要在数据库admin里添加一个拥有userAdminAnyDatabase角色的管理员账号,这个账号可以管理数据库admin和创建其它用户
- 创建管理员帐号,账号信息保存在admin数据库
```
use admin
db.createUser({ user: "root", pwd: "zfoo*5qtWy?cwajCGQl.Jh7o8D%X24A;", roles: [
{ role: "userAdminAnyDatabase", db: "admin" },
{ role: "clusterAdmin", db: "admin" },
{ role: "readWriteAnyDatabase", db: "admin" }
]})
```
- 创建普通帐号,账号信息保存在test数据库
```
use test
db.createUser({ user: "test", pwd: "123456", roles: [ { role: "readWrite", db: "test" }, { role: "read", db: "reporting" }]})
```
- 查看用户权限
```
use test
db.getUser("myTester")
db.getRole( "read", { showPrivileges:true } ) # 查看权限能执行哪些操作,例如要看test数据库中read权限能执行哪些操作
db.grantRolesToUser( "test", [{ role: "readWrite", db: "reporting" }]) # 授权
db.revokeRolesFromUser( "test", [{ role: "readWrite", db: "reporting" }]) # 取消权限
```
- 添加完成后,在配置文件中取消注释,#authorization
- 再重启mongod服务:systemctl restart mongodb
- 用户登录
```
# 启动mongo客户端时登录,--authenticationDatabase "admin"表示myUserAdmin用户在admin数据库下
# 远程用户登录注意打开防火墙的端口,或者直接关闭防火墙
mongo --port 22400 -u "root" -p "123456" --authenticationDatabase "admin"
# 进入mongo客户端后再登录:
mongo --port 22400
use admin
db.auth("root", "123456" )
# 修改密码,将root的密码修改为456789,需要admin管理员权限。
db.changeUserPassword("root", "456789")
# 删除用户,需要admin管理员权限
db.dropUser("root")
```
-222
View File
@@ -1,222 +0,0 @@
# 一、副本集的搭建
## 1.MongoDB环境准备
- 三台虚拟机的静态ip分别设置为
```
192.168.1.30 mongodb0
192.168.1.31 mongodb1
192.168.1.32 mongodb2
```
- 设置好ip后还需要把ip与主机名对应起来
```
hostname # 查看主机名使用命令
vim /etc/sysconfig/network,修改主机名,在最后一行加上HOSTNAME=mongodb0,不同主机的对于关系如下
192.168.1.30 -> HOSTNAME=mongodb0
192.168.1.31 -> HOSTNAME=mongodb1
192.168.1.32 -> HOSTNAME=mongodb2
vim /etc/hosts,修改为我们设置的主机名,不同的主机对应不同的mongodb
127.0.0.1 localhost mongodb0 localhost4 localhost4.localdomain4
::1 localhost mongodb0 localhost6 localhost6.localdomain6
192.168.1.30 mongodb0
192.168.1.31 mongodb1
192.168.1.32 mongodb2
```
- 重启服务器后生效,使用hostname命令验证主机名,使用ping命令验证三个虚拟机之间相互可以使用主机名ping通。
- 开启防火墙端口。
## 2.创建目录和修改配置文件
- mongodb0,mongodb1,mongodb2使用命令
```
mkdir -p /data/mongodb/db # 存放数据的目录
mkdir -p /data/mongodb/logs # 存放日记的目录
mkdir -p /data/mongodb/key # 存放密钥的目录
```
- vim /usr/local/mongodb/mongodb.config,修改配置文件如下
```
dbpath=/data/mongodb/db
logpath=/data/mongodb/logs/mongodb.log
logappend=true
port=22400
fork=true
#keyFile默认会开启auth=true
#auth=true
#keyFile=/data/mongodb/key/myKey.txt
replSet=myReplicaSet
#生产环境需要设置ip:bind_ip=127.0.0.1,本机ip
bind_ip=0.0.0.0
```
## 3.初始化副本集
- 清空一下目录的历史数据,因为可能以前是用的单机节点,集群部署会有不可预知问题,所以先清空数据
```
/data/mongodb/db
/data/mongodb/logs
```
- 分别启动mongodb0,mongodb1,mongodb2
```
systemctl restart mongodb
```
- 创建副本集
```
mongo --port 22400 # 进入mongodb0
config = {_id: "myReplicaSet", members: [{_id: 0, host:"mongodb0:22400"}, {_id: 1, host:"mongodb1:22400"}, {_id: 2, host:"mongodb2:22400", arbiterOnly : true}]}
rs.initiate(config) # 初始化副本集
```
- 观察副本集
```
rs.conf() # 副本集配置
rs.status() # 副本集状态
```
## 4.副本集初始化后,需要给整个副本集创建帐户、密码
- 创建Key,mongodb0,mongodb1,mongodb2使用命令
```
echo "replicaSet key" > /data/mongodb/key/myKey.txt
chmod 600 /data/mongodb/key/myKey.txt
```
- 在主节点上,用客户端连接,创建用户权限(主节点,可以用 rs.status() 查看),创建用户请看mongodb-setup.md
- 关闭副本集,分别关闭mongodb0,mongodb1,mongodb2
```
systemctl stop mongodb
```
- vim /usr/local/mongodb/mongodb.config,修改配置文件如下
```
dbpath=/data/mongodb/db
logpath=/data/mongodb/logs/mongodb.log
logappend=true
port=22400
fork=true
#keyFile默认会开启auth=true
auth=true
keyFile=/data/mongodb/key/myKey.txt
replSet=myReplicaSet
#生产环境需要设置ip:bind_ip=127.0.0.1,本机ip
bind_ip=0.0.0.0
```
- 重新启动副本集,分别启动mongodb0,mongodb1,mongodb2
```
systemctl restart mongodb
```
## 5.数据同步测试
- 首先向PRIMARY(主节点)写入一条数据
```
use test
db.say.insert({"text":"Hello World"})
db.say.find()
```
- 进入SECONDARY(副节点)查看数据是否同步
```
默认情况下SECONDARY节点不能读写,要设定slaveOk为true才可以从SECONDARY节点读取数据。
replSet里只能有一个Primary节点,只能在Primary写数据,不能在SECONDARY写数据。
首先需要在SECONDARY节点设置slaveOk为true
db.getMongo().setSlaveOk();
use test;
db.say.find();
```
## 6.故障切换测试
- 把主节点关点,看看副集点是否能接替主节点进行工作
# 二、原理解析
## 1.副本集实现了数据库的数据同步,故障转移,读写分离
副本集最少应该包含三个节点,其中有一个必须是主节点。
数据同步:从节点会从主节点同步oplog到自己的节点,oplog有自己的大小,可以通过--oplogSize来设置。主从之间的读写对程序员是透明的。
故障转移(容灾):主从节点两秒钟就会有一个心跳,如果发现主节点挂了就从新选取主节点,选取规则是从节点中数据最新的,权重最高的节点。 如果从节点挂了就直接选取从节点,再将主节点的数据同步给从节点。
读写分离:可以设置是优先从主节点读数据,还是优先从从节点读数据。 注:在单个节点中,数据先被写到内存,操作被保存在内存的视图中,内存视图没100ms写到硬盘的journal日志中,mongodb每60s将内存中的数据同步到硬盘。
# 三、副本集的管理
## 1.诊断
- 副本集中的机子,查看主数据库的复制的状态
```
db.printReplicationInfo()
configured oplog size: 990MB
log length start to end: 174676secs (48.52hrs)
oplog first event time: Wed May 01 2019 13:29:38 GMT+0800
oplog last event time: Fri May 03 2019 14:00:54 GMT+0800
now: Fri May 03 2019 14:00:56 GMT+0800
输出信息oplog大小990MB
log length start to end = oplog last event time - oplog first event time
因为oplog是固定大小,有一个循环,所以必须等oplog循环几圈,oplog才和实际相符合。
如果oplog太小,写入的数据太快,oplog中的日志可能会被顶掉,从节点想要同步时候发现找不到切入的oplog,已经跟不上主节点了,就会停止同步。
如果log length start to end和下面的从节点behind the primary非常的接近,说明oplog可能太小了
```
- 查看从数据库的复制的状态
```
db.printSlaveReplicationInfo()
source: mongodb-config1:22400
syncedTo: Fri May 03 2019 13:56:31 GMT+0800
0 secs (0 hrs) behind the primary
source: mongodb-config2:22400
syncedTo: Fri May 03 2019 13:56:31 GMT+0800
0 secs (0 hrs) behind the primary
```
## 2.副本集回滚丢失的数据
```
主节点故障后重新加入副本集时会回滚恢复写操作。
只有在主节点接受了写请求并且从节点没有复制前主节点就故障的时候才需要回滚。
当主节点作为从节点重新加入副本集,他恢复或回滚他的写操作来与其他成员保持数据库一致性。
当回滚确实发生了,MongoDB把回滚数据写入数据库的dbPath的rollback文件夹下的BSON文件中,可以用bsondump读取回滚文件的内容。
回滚文件名如下形式:
<database>.<collection>.<timestamp>.bson
```
-426
View File
@@ -1,426 +0,0 @@
# 一、分片的搭建
## 1.MongoDB环境准备
- config服务器三台,虚拟机的静态ip分别设置为
```
192.168.1.40 mongodb-config0
192.168.1.41 mongodb-config1
192.168.1.42 mongodb-config2
```
- route服务器三台,虚拟机的静态ip分别设置为
```
192.168.1.45 mongodb-route0
192.168.1.46 mongodb-route1
192.168.1.47 mongodb-route2
```
- shard服务器三台,虚拟机的静态ip分别设置为
```
192.168.1.50 mongodb-shard0
192.168.1.51 mongodb-shard1
192.168.1.52 mongodb-shard2
```
- shard副本集服务器三台,虚拟机的静态ip分别设置为
```
192.168.1.55 mongodb-shard3-replica0
192.168.1.56 mongodb-shard3-replica1
192.168.1.57 mongodb-shard3-replica2
```
- 设置好ip后还需要把ip与主机名对应起来
```
hostname # 查看主机名使用命令
vim /etc/sysconfig/network,修改主机名,在最后一行加上HOSTNAME=mongodb-config0,其它的服务器同理
vim /etc/hosts,修改为我们设置的主机名,不同的主机对应不同的mongodb
127.0.0.1 localhost mongodb-config0 localhost4 localhost4.localdomain4
::1 localhost mongodb-config0 localhost6 localhost6.localdomain6
192.168.1.30 mongodb0
192.168.1.31 mongodb1
192.168.1.32 mongodb2
192.168.1.40 mongodb-config0
192.168.1.41 mongodb-config1
192.168.1.42 mongodb-config2
192.168.1.45 mongodb-route0
192.168.1.46 mongodb-route1
192.168.1.47 mongodb-route2
192.168.1.50 mongodb-shard0
192.168.1.51 mongodb-shard1
192.168.1.52 mongodb-shard2
192.168.1.55 mongodb-shard3-replica0
192.168.1.56 mongodb-shard3-replica1
192.168.1.57 mongodb-shard3-replica2
```
- 重启服务器后生效,使用hostname命令验证主机名,使用ping命令验证三个虚拟机之间相互可以使用主机名ping通。
- 开启防火墙端口。
## 2.config服务器
- mongodb-config0,mongodb-config1,mongodb-config2使用命令
```
mkdir -p /data/mongodb/db # 存放数据的目录
mkdir -p /data/mongodb/logs # 存放日记的目录
mkdir -p /data/mongodb/key # 存放密钥的目录
```
- vim /usr/local/mongodb/mongodb.config,修改配置文件如下
```
dbpath=/data/mongodb/db
logpath=/data/mongodb/logs/mongodb.log
logappend=true
port=22400
fork=true
configsvr=true
replSet=mongodbConfigSet
#keyFile默认会开启auth=true
#auth=true
#keyFile=/data/mongodb/key/myKey.txt
#生产环境需要设置ip:bind_ip=127.0.0.1,本机ip
bind_ip=0.0.0.0
```
- 创建副本集
```
mongo --port 22400 # 进入mongodb0
config = {_id: "mongodbConfigSet", members: [
{_id: 0, host:"mongodb-config0:22400"},
{_id: 1, host:"mongodb-config1:22400"},
{_id: 2, host:"mongodb-config2:22400"}
]}
rs.initiate(config) # 初始化副本集
```
- 观察副本集
```
rs.conf() # 副本集配置
rs.status() # 副本集状态
```
## 3.route路由服务器
- mongodb-route0,mongodb-route1,mongodb-route2使用命令
```
mkdir -p /data/mongodb/db # 存放数据的目录
mkdir -p /data/mongodb/logs # 存放日记的目录
mkdir -p /data/mongodb/key # 存放密钥的目录
```
- vim /usr/local/mongodb/mongodb.config,修改配置文件如下
```
logpath=/data/mongodb/logs/mongodb.log
logappend=true
port=22400
fork=true
#必须是1个或则3
configdb=mongodbConfigSet/mongodb-config0:22400,mongodb-config1:22400,mongodb-config2:22400
#keyFile默认会开启auth=true
#keyFile=/data/mongodb/key/myKey.txt
#生产环境需要设置ip:bind_ip=127.0.0.1,本机ip
bind_ip=0.0.0.0
```
- vim /usr/lib/systemd/system/mongodb.service,修改启动脚本
```bash
[Unit]
#服务描述
Description=High-performance, schema-free document-oriented database
#指定了在systemd在执行完那些target之后再启动该服务
After=network.target
[Service]
#定义Service的运行类型,一般是forking(后台运行)
Type=forking
ExecStart=/usr/local/mongodb/bin/mongos --config /usr/local/mongodb/mongodb.config
ExecReload=
ExecStop=/usr/bin/pkill mongos
[Install]
#多用户
WantedBy=multi-user.target
```
- systemctl daemon-reload,重新加载服务
- systemctl restart mongodb
## 4.shard分片服务器
- mongodb-shard0,mongodb-shard1,mongodb-shard2使用命令
```
mkdir -p /data/mongodb/db # 存放数据的目录
mkdir -p /data/mongodb/logs # 存放日记的目录
mkdir -p /data/mongodb/key # 存放密钥的目录
```
- vim /usr/local/mongodb/mongodb.config,修改配置文件如下
```
dbpath=/data/mongodb/db
logpath=/data/mongodb/logs/mongodb.log
logappend=true
port=22400
fork=true
shardsvr=true
#keyFile默认会开启auth=true
#auth=true
#keyFile=/data/mongodb/key/myKey.txt
#生产环境需要设置ip:bind_ip=127.0.0.1,本机ip
bind_ip=0.0.0.0
```
- systemctl restart mongodb
## 5.配置sharding
- mongo客户端进入mongos服务所在的端口,在其中一台机子中进入mongos
```
mongo --port 22400
use admin # 需要先切换到admin数据库,才能进行分片的添加
sh.addShard("mongodb-shard0:22400")
sh.addShard("mongodb-shard1:22400")
sh.addShard("mongodb-shard2:22400")
# 单节点分片的缺点是如果其中一个分片挂了,会导致整个分片集群不可用,所以建议每个分片都使用副本集
sh.addShard("mongodbShard3Set/mongodb-shard3-replica0:22400,mongodb-shard3-replica1:22400,mongodb-shard3-replica2:22400")
```
## 6.使用分片数据库和使用分片集合
- 在任节点中进入mongos
```
mongo --port 22400 # 切换到admin数据库
use admin
sh.enableSharding("test")
sh.shardCollection("test.student", { _id: "hashed" }) # 把test数据库student集合的_id字段设置为哈希片键
sh.shardCollection("test.student", { _id:1 }) # 把test数据库student集合的_id字段设置为区间片键
```
- 标签片键
```
标签片键比较特别,通过对分片节点打标签,再将片键按范围对应到这些标签上,对应片键范围的集合中的数据就会落在这个分片节点上。
首先需要对分片打标签,对分片打标签需要知道分片的_id,在mongos中使用命令查看:
sh.status()
这里分片_id分别为: shard0000, shard0001, shard0002,则对分片打标签命令如下:
sh.addShardTag("shard0000", "tag1")
sh.addShardTag("shard0001", "tag2")
sh.addShardTag("shard0002", "tag3")
然后student的学号字段code设置标签片键范围。
这样设置后学号code为1到600的会存储在分片shard0001或者shard0002中,学号为601到5000的会存储在分片shard0000中。
sh.addTagRange("test.student", { _id: 0 }, { _id: 1000 }, "tag1")
sh.addTagRange("test.student", { _id: 1001 }, { _id: 9000 }, "tag2")
sh.addTagRange("test.student", { _id: 9001 }, { _id: 10000 }, "tag3")
查看标签的分片,在mongos中使用命令
use config
db.shards.find()
查看标签的片键值范围,使用命令
use config
db.tags.find()
移除某个分片的标签使用命令
sh.removeShardTag("shard0000", "tag1")
sh.removeShardTag("shard0001", "tag2")
sh.removeShardTag("shard0002", "tag3")
```
## 7.分片数据测试
- 集合测试
```
mongo --port 22400
use test
for(var i=0; i<10000; i++) db.student.insert({ _id:i, name:"jaysunxiao", addr:"myAddress", country:"myCountry" })
执行可能需要一段会时间,我们直接在工具中查看config集合中的chunks元数据信息,就可以看到随着数据的写入分了多少片,以及片键的最小到最大的范围。
3个shard服务器里可以看到都有了mytest数据库以及student集合。
```
## 8.分片的管理
- 移除Shard Server,回收数据,分片被删除之后,数据被移到其他分片中,不会丢失
```
use admin
# 如果是区间片键,已经指定了区间了,数据迁移会失败,会导致分片一直draining,因为找不到区间分片的节点
db.runCommand( { removeShard: "shard0002" } )
# 检查迁移的状态,再次在 admin 数据库运行 removeShard 命令,因为数据多的话要一处的节点需要很长时间迁移数据
db.runCommand( { removeShard: "shard0002" } )
db.runCommand({"moveprimary": "test", "to": "shard0000"}) # 要是原来分片是一个主分片,则需要执行movePrimary
```
## 9.均衡器
- 均衡器:均衡器负责数据迁移,周期性的检查分片是否存在不均衡。 config.locks集合里的state表示均衡器是否找正在运行,0表示非活动状态,2表示正在均衡。
均衡迁移数据的过程会增加系统的负载:目标分片必须查询源分片的所有文档,将文档插入目标分片中,再清除源分片的数据。 可以关闭均衡器(不建议):关闭会导致各分片数据分布不均衡,磁盘空间得不到有效的利用。
```
sh.setBalancerState(false) #关闭自动均衡器,手动均衡
sh.setBalancerState(true)
use config
db.settings.find() #查看均衡器状态
# 移动块的时候必须关闭均衡器,否则可能导致死循环
use admin
sh.moveChunk("db.collection",{块地址},"新片名称") #手动均衡
sh.moveChunk("test.student",{"_id": "test.student-_id_6148914691236517204"},"shard0000")
use config
db.chunks.find().pretty()
```
## 10.认证分配
- 首先创建账号(Root角色),其中route(mongos)不需要创建账号,需要两种权限
```
userAdminAnyDatabase # 管理员权限,用来管理其它的账号
clusterAdmin # 分片管理员权限
```
- 再生成一个KeyFile文件
- config,shard节点修改配置,vim /usr/local/mongodb/mongodb.config
```
#keyFile默认会开启auth=true
auth=true
keyFile=/data/mongodb/key/myKey.txt
```
- route节点修改配置,vim /usr/local/mongodb/mongodb.config
```
#因为mongos本来就是从config里加载数据的,所以只需要添加keyfile文件即可
keyFile=/data/mongodb/key/myKey.txt
```
- 重新启动各个节点
```
systemctl restart mongodb
```
## 11.分片备份、还原
- 先停止平衡器的工作,并检查没有chunk move动作,保证dump的时候没有进行数据迁移。
```
sh.stopBalancer()
```
- 锁定数据库,保证数据没有写入:在各个分片上和配置服务器上执行。
```
db.fsyncLock()
```
- 执行备份操作mongodump,备份各个分片服务器和配置服务器。
- 解锁数据库,备份完成之后在分片和配置服务器上解锁数据库,允许修改。
```
db.fsyncUnlock()
```
- 备份还原mongorestore各个分片和配置服务器
- 重启各个实例
# 二、原理解析
## 1.分片实现了数据的分布式存储,负载均衡,这些都是海量数据的云存储平台不可或缺的功能
- 配置服务器
```
是一个独立的mongod进程,保存集群和分片的元数据,即各分片包含了哪些数据的信息。
最先开始建立,启用日志功能。像启动普通的mongod一样启动配置服务器,指定configsvr选项。
不需要太多的空间和资源,配置服务器的1KB空间相当于真实数据的200MB。保存的只是数据的分布表。
当服务不可用,则变成只读,无法分块、迁移数据。
```
- 路由服务器
```
即mongos,起到一个路由的功能,供程序连接。
本身不保存数据,在启动时从配置服务器加载集群信息,开启mongos进程需要知道配置服务器的地址,指定configdb选项。
```
- 分片服务器
```
是一个独立普通的mongod进程,保存数据信息。可以是一个副本集也可以是单独的一台服务器。
```
## 2.mongodb什么时候开始分片?
MongoDB的分片是基于范围的,也就是说任何一个文档一定位于指定片键的某个范围。一旦片键选择好后,chunks就会按照片键来将一部分documents从逻辑上组合在一起。
由于chunks的大小默认的阈值是64 MB或100000个文档,一旦达到这个阈值就会根据片键分片。
片键一般是是索引,如果没有指定索引,则用集合默认的_id索引作为片键。
## 3.分片适用的场景
1,机器的磁盘不够用了。使用分片解决磁盘空间的问题。 2,单个mongod已经不能满足写数据的性能要求。通过分片让写压力分散到各个分片上面,使用分片服务器自身的资源。
3,想把大量数据放到内存里提高性能。和上面一样,通过分片使用分片服务器自身的资源。
## 4.集群dump备份恢复策略,对集群的备份就可以转化为对各复制集的备份:
1. 禁用平衡器,命令:sh.stopBalancer();因为分片集群上会有一个balancer进程在后台维护各个片上数据块数量的均衡,如果不禁用平衡器可能会导致备份数据的重复或缺失
2. 停止毎个片(副本集)上的某个secondary节点,利用此节点进行备份;停止其中某个配置服务器(所有配置服务器的数据一样),保证备份时配务器上元数据不会改变,备份可以当作一单节点的实例
3. 重启所有停掉的复制集成员,它们会自动从primary节点上的oplog同步数据,最终数据会达到一致性
4. 重启分片集群的平衡器。通过mongo连接到mongos上,执行命令如下。use config;sh.startBalancer()
## 5.集群恢复流程
1. 停止集群上的所有mongod实例和mongos实例。
2. 利用上面备份的dump文件,依次恢复片中的每个副本集
3. 恢复配置服务器。
4. 重启所有mongod实例与mongos实例。
5. 通过mongo连接上mongos,执行以下命令确保集群是可操作的:db.printShardingStatus()
-66
View File
@@ -1,66 +0,0 @@
# 一、安全写入数据
## 1.MongoDB的四种写入级别
- Unacknowledged 非确认式写入
```
{ writeConcern:{w:0}}
非确认式写入不返回响应结果。
```
- Acknowledged 确认式写入,mogodb客户端默认的写入模式
```
确认式写入返回写入失败的错误信息,比如DuplicateKey Error。使用命令:
{ writeConcern:{w:1}}
```
- Journaled 日志写入
```
一般的写入完成只是写入到内存中,并没有持久化到硬盘,日志写入模式会写入完成之后把记录保存到journal日志后才返回响应结果,这种写入方式能够承受服务器突然断电崩溃,更有效的保障数据的安全。
{ writeConcern:{j:true}}
```
- Replica Acknowledged 复制集确认式写入
```
写操作不仅要得到主节点的写入确认,还需要得到从节点的写入确认,这里还可以设置写入节点的个数。这种方式适用于对写入安全要求更高的场景。
{ writeConcern:{w:2}}
```
# 二、Journal日志
## 1.单机开启Journal,多机器使用副本集
```
Journal是MongoDB中的redolog(MySQL),而Oplog则负责副本集的同步(binlog MySQL)
不开启Journal,数据会写入内存,一般每60秒写入一次到硬盘
开启Journal,每100毫秒左右往Journal日志文件写入一次数据(顺序写速度快),即使宕机,结果Journal恢复也只丢死100毫秒数据
```
## 2.生产环境不要信任repair恢复的数据
```
如果数据库崩溃了,而且没有开启--journal的情况下,千万不要这些数据拿来就用,因为数据库能损坏,索引可能混乱。
如果开启了Journal就可以使用mongod --repair修复一次。修复过程是将所有的文档导出后马上导入,忽略无效的文档,完成后会重建索引。
但是这不是最优的选择,因为可能会丢失数据而且需要大量的磁盘空间。repair是最差的做法,比较好的做法是用以前备份过的数据库恢复,或者使用副本集部署。
```
# 三、锁
- 查看锁的情况
```
db.serverStatus()
db.currentOp()
```
- 解锁
```
db.currentOp() # 找出有锁状态的执行操作,找到opid,根据之前获取的opid
db.killOp(opid) # kill掉对应的操作
```
-695
View File
@@ -1,695 +0,0 @@
# 一、MongoDB常用命令
## 1.数据库常用命令
```
show dbs # 查询数据库列表
use student # 该命令如果数据库不存在,将创建一个新的数据库,否则将返回现有的数据库。
db.dropDatabase() # 删除新的数据库
db.repairDatabase() # 整理数据库,不仅能整理碎片还可以回收磁盘空间,但是会产生锁,建议停服的时候做
db.getName() # 查看当前使用的数据库
db.version() # 当前db的版本
db.getMongo() # 查看当前db的链接机器地址
db.getPrevError() # 查询之前的错误信息
db.resetError() # 清除错误记录
db.help() # 查看数据库帮助文档
```
## 2.集合常用命令
```
show collections # 查看所有的集合
db.createCollection("student") # 创建一个集合
db.createCollection("log", {size: 20, capped:true, max: 100}); # 或者带参数创建固定集合
db.student # 使用集合
db.student.drop() # 删除当前集合
db.student.validate() # 验证集合是否有问题,验证集合内容
db.student.count() # 查询当前集合的数据条数
db.student.dataSize() # 查看集合数据大小
db.student.totalIndexSize() # 查看集合索引大小
db.student.storageSize() # 集合分配的空间大小,包括未使用的空间
db.student.totalSize() # 显示集合总大小,包括索引和数据的大小和分配空间的大小
db.student.getDB() # 显示当前集合所在的db
db.student.stats() # 显示当前集合的状态
db.student.getShardVersion() # 集合的分片版本信息
db.student.renameCollection("students"); # 集合重命名
db.printCollectionStats() # 显示当前db所有集合的状态信息
db.student.help() # 查看集合命令帮助文档
```
- 集合内存
```
在MongoDB中并不需要创建集合。 当插入一些文档 MongoDB 会自动创建集合。
capped: Boolean
(可选)如果为true,它启用上限集合。上限集合是一个固定大小的集合,当它达到其最大尺寸会自动覆盖最老的条目。
如果指定true,则还需要指定参数的大小
size: number (可选)指定的上限集合字节的最大尺寸。如果capped 是true,那么还需要指定这个字段
autoIndexID Boolean (可选)如果为true,自动创建索引_id字段。默认的值是 false
max number (可选)指定上限集合允许的最大文件数
DB.createCollection("student", { capped : true, size : 6142800, autoIndexId : true, max : 10000 })
DB.createCollection("student")
```
## 3. 文档常用命令
### 增
```
db.student.insert({name:"jay", age: 100})
如果我们不指定_id参数插入的文档,那么 MongoDB 将为文档分配一个唯一的ObjectId。_id 是12个字节十六进制数在一个集合的每个文档是唯一的。
0-3时间戳,4-6机器唯一标识的散列标识,7-8进程号PID,9-11计数器。前三个保证同一个机器不同进程产生的标识唯一,后一个保证同一个进程产生不同的标识。
简介说就是在分布式系统中可以在本机产生全局唯一id。
```
### 删
```
db.student.remove({name : "jay"},true); # true是只删除一条,默认是false全部删除
db.student.remove({name : "jay"});
```
### 改
```
db.student.save({name : "jay", age : 20}) # save如果数据库已经有这条数据,则会更新它;如果没有,则写入
db.student.update({name:"jay"},{$set:{age : 20, company: "google"}});
db.student.update({name:"jay"},{$set:{age : 20}},{multi:true}); # $set修改器;multi默认false,只更新找到的第一条记录
db.student.update({name:"jay"},{$unset:{company:1}}); # $unset用于取消字段,去掉文档中的某个字段
db.student.update({name:"jay"},{$inc: {age: 50}}); # $inc用于增加或减少数值
db.student.update({name:"jay"},{$push: {phone: {home:456789}}}); # $push用于把元素追加到数组中,如果字段不存在,会新增
db.student.update({name:"jay"},{$pushAll: {phone: [{home:456789} # $pushAll用于把元素追加到数组中,多个元素
db.student.update({name:"jay"},{$pull: {phone:{home: 456789} }}); # $pull删除数组中满足条件的元素
db.student.update({name:"jay"},{$addToSet: {phone: {home:456789}}}); # $addToSet类似于push,但是只有这个值不在数组中才会增加
db.student.update({name:"jay"},{$pop: {phone:1}}); # $pop删除数组中最后一个元素
db.student.update({name:"jay"},{$rename: {phone:"call"}}); # $rename修改字段名称
```
### 查
```
db.student.find()
db.student.find().pretty(); # 如果需要结构化显示返回的文档可以加上pretty()方法
db.student.find({name:"jay"},{age:1}) # 查询name伟jay,只显示age
db.student.count({$or: [{age: 14}, {age: 28}]}); # 查询满足条件的文档数量
```
## 4.索引
### 创建索引
```
db.student.ensureIndex({age: 1}); # 1表示age索引按升序排序
db.student.ensureIndex({name: 1, age: -1}); # -1表示同组文档按降序排序
例如我们有数据:
{"name":"jay",age:14}
{"name":"ad",age:14}
{"name":"ad",age:38}
{"name":"ad",age:24}
{"name":"ab",age:14}
使用db.student.ensureIndex({name: 1, age: -1})建议索引后,索引中的数据组织为:
{"name":"ab",age:14}
{"name":"ad",age:38}
{"name":"ad",age:24}
{"name":"ad",age:14}
{"name":"jay",age:14}
```
### 创建唯一索引
```
db.student.ensureIndex({name:1},{unique:true}) # 如果创建唯一索引时已经存在重复项,通过dropDups在创建唯一索引时消除重复文档
db.student.ensureIndex({name:1},{unique:true,dropDups:true})
```
### 索引操作
```
db.student.getIndexes(); # 查询集合所有索引
db.student.totalIndexSize(); # 查看集合总索引记录大小
db.student.reIndex(); # 修复索引
db.student.dropIndex("name");# 删除指定索引
```
### 索引调优explain
```
db.student.find({name: "jaysunxiao" }).explain();
```
## 5.查询
### 单个文档大小
```
Object.bsonsize(db.user.findOne({'account':'xxx'})) # 返回文档的bytes数量
```
### 迭代器,一般用于遍历数据集。通过hasNext()判断是否有下一条数据,next()获取下一条数据
```javascript
var iterator= db.student.find();
while(iterator.hasNext()){
let temp=iterator.next();
print(temp.name);
}
// 迭代器还实现了迭代器接口,所以可以使用forEach。
var iterator= db.student.find();
iterator.forEach(function(temp){
print(temp.name);
});
```
### find语法
```
find( query, fields)
query 查询条件,相当于sql的where
fields 查询的结果,字段映射,0不显示,1显示
explain("allPlansExecution") 返回查询计划信息和查询计划的执行统计信息
db.student.find({}, { "_id":0, "name":1}).limit(9).skip(5).sort({"name":-1}).pretty()
```
### 条件查询
```
db.student.find({name:"jay",age:16}) # 与操作
db.student.find({$or: [{age: 14}, {age: 28}]}); # 或操作$or
db.student.find({age: {$gt: 20}}) # 大于$gt
db.student.find({age: {$lt: 20}}) # 小于$lt
db.student.find({age: {$gte: 20}}) # 大于等于$gte
db.student.find({age: {$lte: 20}}) # 小于等于$lte
db.student.find({name: { $type : "string" }}); # 类型查询$type
db.student.find({age: {$exists: true}}) # 是否存在$exists
db.student.find({age: {$mod : [10, 0]}}); # 取模$mod
db.student.find({age: { "$ne" : 23}}) # 不等于$ne
db.student.find({name:{ "$in" : [ "jay" , "ab"]}}) # 包含$in
db.student.find({name:{$not:{"$in":["jay","ab"]}}})# 取非
db.student.find({name:{ "$nin":["jay","ab"]}}) # 不包含$nin
```
### null查询
```
db.student.find({company:null}) # 查询company字段为null的文档
db.student.find({company:{$nin:[null]} }) # 查询company字段不为空的文档\
```
### 正则查询
```
db.student.find({"name":/^j/}) # 以j开头的字符串
```
### 嵌套查询
```
db.student.find({"phone":{"home" : 123321,"mobile" : 15820123123}}) # 指定完整的文档,查询出子文档完全匹配指定文档的文档。
db.student.find({"phone.home":123321}) # 如果我们不知道子文档的完整文档,只知道子文档中一个字段的值,可以通过点查询。
```
### 数组查询
```
db.student.find({favorite_number :[6, 8]}); # 精确匹配查询
db.student.find({favorite_number:6}); # 数组单元素查询
db.student.find({favorite_number : {$all : [6, 8]}}); # 数组多元素查询
db.student.find({favorite_number: {$size: 3}}); # $size数组长度查询
db.student.find({},{favorite_number: {$slice: 2}}); # $slice返回数组子集
db.student.find({},{favorite_number: {$slice: -2}});
db.student.find({phone.home:123456}); # 点查询用于查询更复杂的数组,例如数组中包含的是子文档的情况
db.student.find({phone.home:123456,"phone.mobile":13820123123}); # 数组的子文档如果有多个字段,查询出子文档同时满足两个条件的文档有两种方式
db.student.find( { # 或者,两者等价
phone: {
$elemMatch: {
home :123456,
mobile: 13820123123
}}})
```
### 索引查询
```
db.student.find({favorite_number.0:6}); # 数组都有索引,例如[6,8],6是第0个元素,8是第1个元素(数组索引以0开头)。要查找某个元素指定值的文档可以使用点和索引值
db.student.find({phone.2.home:123456}); # 点查询中只要数组的子文档里有一个home值满足查询值就会返回文档。如果我们要精确到第几个元素也可以用索引查询。
```
### 高级查询$where
- Javascript与$where结合使用,下面的全部等价
```
db.student.find({age: {$gt: 18}});
db.student.find({$where: "this.age > 18"});
db.student.find("this.age > 18");
f = function() {return this.age > 18}; db.student.find(f);
```
## 6.聚合查询
### count和distinct
```
db.student.count({age: {$gte: 18}}) # 数量查询count
db.runCommand({"distinct":"student", "key":"age"}) # 不同值distinct
```
### 分组group
```
db.collection.group({
key:{field:1},
initial:{count:0},
cond:{},
reduce: function ( curr, result ) { },
keyf:function(doc){},
finalize:function(result) {}
})
数据如下:
{ "_id" : ObjectId("58d9b6afa159504ca6c572e0"), "name" : "jay", "age" : 28, "company" : "google", "name" : "a2" }
{ "_id" : ObjectId("58d9ba2d6097167df6313438"), "name" : "jay", "age" : 14 }
{ "_id" : ObjectId("58d9ba2d6097167df6313439"), "name" : "ad", "age" : 14 }
{ "_id" : ObjectId("58d9ba2d6097167df631343a"), "name" : "ad", "age" : 38 }
{ "_id" : ObjectId("58d9ba2d6097167df631343b"), "name" : "ad", "age" : 24 }
{ "_id" : ObjectId("58d9ba2e6097167df631343c"), "name" : "ab", "age" : 14 }
我们这里可以做一个统计:每个年龄有多少人,则使用age字段作为key,在reduce时进行同组的数量计数,finalize中组织结果文档格式。
使用代码如下:
db.student.group({
key:{age:1},
initial:{count:0},
cond:{"age":{$gt:13}},
reduce: function(curr,result) {
result.count += 1;
},
finalize:function(result) {
result.年龄=result.age;
result.人数=result.count;
}
});
使用MapReduce做一个统计:每个年龄有多少人,this表示当前文档,使用this.age的值作为key。在reduce时进行同组的数量计数,finalize中组织结果文档格式。
代码如下:
db.student.mapReduce(
function () {
emit(this.age, {age: this.age, count: 1});
},
function (key, values) {
var count = 0;
values.forEach(function(val) {
count += val.count;
});
return {age: key, count: count};
},
{
out: { inline : 1 },
finalize: function (key, reduced) {
return {"年龄": reduced.age, "人数": reduced.count};
}
}
)
第一个function是map函数,提交两个参数key和value,数据会根据key的值进行分组,把同组的value的值放入values数组中。
第二个function是reduce函数,key和values是第一个map函数的分组结果。
第三个out指定结果集生成在什么地方,inline:1,表示结果集放在内存中,仅适用于结果集符合16MB限制的情况
```
### $project
```
数据如下:
{ "_id" : ObjectId("58d9ba2d6097167df6313438"), "name" : { "first" : "adrian", "last" : "jay" }, "age" : 14, "phone" : 111128912345 }
使用代码:
db.student.aggregate(
[
{ $project: { _id:0, age: 1, phone: "123456789", lastName: "$name.last" } }
]
)
$project映射结果集的文档结构,0表示排除字段,1显示字段,"123456789"直接赋值
```
### $match
```
db.student.aggregate([{$match:{"name.last":"jay"}}])
```
### $limit
```
db.student.aggregate({ $limit : 1 });
```
### $skip
```
db.student.aggregate({ $skip : 2 });
```
### $unwind
```
数据:
db.product.find()
{ "_id" : 1, "item" : "ABC1", "sizes" : [ "S", "M", "L" ] }
db.product.aggregate( [ { $unwind : "$sizes" } ] )
```
### $group
```
数据:
db.student.find()
{ "_id" : ObjectId("58d9ba2d6097167df6313438"), "name" : "jay", "age" : 14 }
{ "_id" : ObjectId("58d9ba2d6097167df6313439"), "name" : "ad", "age" : 14 }
{ "_id" : ObjectId("58d9ba2d6097167df631343a"), "name" : "ad", "age" : 38 }
把数据根据age字段进行分组,使用命令:
db.student.aggregate([{$group : {_id : "$age"}}])
```
### $sort
```
$sort将输入文档排序后输出,1为按字段升序,-1为降序。
我们有数据:
db.student.find()
{ "_id" : ObjectId("58d9ba2d6097167df6313438"), "name" : "jay", "age" : 14 }
{ "_id" : ObjectId("58d9ba2d6097167df6313439"), "name" : "ad", "age" : 14 }
{ "_id" : ObjectId("58d9ba2d6097167df631343a"), "name" : "ad", "age" : 38 }
使用命令:
db.student.aggregate([{$sort : {age :-1}}])
```
### $lookup
```
数据:
db.product.insert({"_id":1,"name":"产品1","price":99})
db.product.insert({"_id":2,"name":"产品2","price":88})
db.order.insert({"_id":1,"pid":1,"name":"订单1"})
db.order.insert({"_id":2,"pid":2,"name":"订单2"})
db.order.insert({"_id":3,"pid":2,"name":"订单3"})
db.order.insert({"_id":4,"pid":1,"name":"订单4"})
db.order.insert({"_id":5,"name":"订单5"})
db.order.insert({"_id":6,"name":"订单6"})
$lookup作左连接组合两个集合的信息使用命令如下:
db.order.aggregate([
{
$lookup:
{
from: "product",
localField: "pid",
foreignField: "_id",
as: "orderDetail"
}
}
])
```
### $geoNear
```
一、平面
我们使用lng和lat的命名,新建数据如下:
db.places.save({name:"肯德基",loc : { lng: 40.739037, lat: 73.992964 },category:"餐饮"})
db.places.save({name:"麦当劳",loc : { lng : 42.739037, lat: 73.992964 },category:"餐饮"})
db.places.save({name:"农行",loc : { lng: 41.739037, lat: 73.992964 },category:"银行"})
db.places.save({name:"地铁站",loc : { lng: 40.639037, lat: 73.992964 },category:"交通"})
2d索引创建方式如下:
db.places.createIndex( { "loc": "2d" } )
我们现在需要查到地铁站附近的文档信息,可以使用:
db.places.find({loc : {$near : { lng: 40.639037, lat:73.992964 }}})
aggregate和$geoNear能指定到范围,比如我们要查范围在坐标值相差2度(平面单位)以内的文档:
db.places.aggregate([
{
$geoNear: {
spherical:false,
distanceMultiplier:1,
near: { lng: 40.639037, lat:73.992964 },
distanceField: "dist.distacnce",
maxDistance: 2,
query: { category: "餐饮" },
includeLocs: "dist.location",
num: 1
}
}
])
二、球面
新建数据如下:
db.places.save({name:"肯德基",loc : { type: "Point", coordinates: [ 40.639037, 73.992964 ] },category:"餐饮"})
db.places.save({name:"麦当劳",loc : { type: "Point", coordinates: [ 42.739037, 73.992964 ] },category:"餐饮"})
db.places.save({name:"农行",loc : { type: "Point", coordinates: [ 41.739037, 73.992964 ] },category:"银行"})
db.places.save({name:"地铁站",loc : { type: "Point", coordinates: [ 40.639037, 73.992964 ] },category:"交通"})
创建2dsphere 索引使用代码:
db.places.createIndex( { loc : "2dsphere" } )
$geoNear用于输出某一地理位置2公里内的文档代码如下:
db.places.aggregate([
{
$geoNear: {
spherical: true,
near: { type: "Point", coordinates: [ 40.639037, 73.992964 ] },
distanceField: "dist.distacnce",
maxDistance: 2000,
query: { category:"餐饮" },
includeLocs: "dist.location",
num: 5
}
}
])
```
### $group和管道表达式
```
测试数据准备:
db.product.insert({"_id":1,"name":"产品1","price":99,"type":"服装"})
db.product.insert({"_id":2,"name":"产品2","price":88,"type":"服装"})
db.product.insert({"_id":3,"name":"产品3","price":29,"type":"饰品"})
db.product.insert({"_id":4,"name":"产品4","price":78,"type":"服装"})
db.product.insert({"_id":5,"name":"产品5","price":9,"type":"饰品"})
db.product.insert({"_id":6,"name":"产品6","price":18,"type":"饰品"})
db.product.aggregate([{$group : {_id : "$type", price : {$sum : "$price"}}}]) # 求和$sum
db.product.aggregate([{$group : {_id : "$type", price : {$avg : "$price"}}}]) # 平均值$avg
db.product.aggregate([{$group : {_id : "$type", price : {$min : "$price"}}}]) # 最小值$min
db.product.aggregate([{$group : {_id : "$type", price : {$max : "$price"}}}]) # 最大值$max
db.product.aggregate([{$group : {_id : "$type", tags : {$push : "$name"}}}]) # 数组添加$push
db.product.aggregate([{$group : {_id : "$type", tags : {$addToSet : "$name"}}}])# 数组添加$addToSet
db.product.aggregate([{$group : {_id : "$type", product : {$first : "$name"}}}])# 首元素$first
db.product.aggregate([{$group : {_id : "$type", product : {$last : "$name"}}}]) # 尾元素$last
```
### 复合使用示例
```
数据如下:
{ "_id" : ObjectId("58d9b6afa159504ca6c572e0"), "name" : "jay", "age" : 28, "company" : "google", "name" : "a2" }
{ "_id" : ObjectId("58d9ba2d6097167df6313438"), "name" : "jay", "age" : 14 }
{ "_id" : ObjectId("58d9ba2d6097167df6313439"), "name" : "ad", "age" : 14 }
{ "_id" : ObjectId("58d9ba2d6097167df631343a"), "name" : "ad", "age" : 38 }
{ "_id" : ObjectId("58d9ba2d6097167df631343b"), "name" : "ad", "age" : 24 }
{ "_id" : ObjectId("58d9ba2e6097167df631343c"), "name" : "ab", "age" : 14 }
使用代码如下:
db.student.aggregate(
[
{ $match: { age:{"$gt":13} } },
{ $sort: { age: 1 } },
{ $limit: 2 },
{ $group: { _id: "$age", "人数": { $sum: 1 } } },
]
)
aggregate默认返回分组信息_id,如果要去掉,可使用$project。
因为aggregate是按顺序处理的管道阶段操作器,所以管道的排序也很重要,下面两端代码使用相同的管道阶段操作器,但是不同的顺序,实现的效果也是不同的:
代码一:
db.student.aggregate(
[
{ $match: { age:{"$gt":13} } },
{ $sort: { age: 1 } },
{ $limit: 2 },
{ $group: { _id: "$age", "人数": { $sum: 1 } } }
]
)
代码二:
db.student.aggregate(
[
{ $match: { age:{"$gt":13} } },
{ $group: { _id: "$age", "人数": { $sum: 1 } } },
{ $sort: { _id: 1 } },
{ $limit: 2 }
]
)
```
## 7.性能监控
- db.stats(),查看当前数据库的状态,可看到数据库的名称,集合(表)数量,索引数量、大小,数据文件大小,存储空间大小和物理文件大小。
- db.serverCmdLineOpts(),查看启动参数
- db.serverStatus(),查询MongoDB的服务状态,有助于了解诊断和性能分析
```
serverStatus命令是静态的监控,MongoDB提供了动态的监控执行工具mongostat。
db.serverStatus().host # 主机名
db.serverStatus().locks # 锁信息
db.serverStatus().globalLock # 全局锁信息
db.serverStatus().mem # 内存信息
db.serverStatus().connections # 连接数信息
db.serverStatus().extra_info # 额外信息
db.serverStatus().indexCounters # 索引统计信息
db.serverStatus().backgroundFlushing # 后台刷新信息
db.serverStatus().cursors # 游标信息
db.serverStatus().network # 网络信息
db.serverStatus().repl # 副本集信息
db.serverStatus().opcountersRepl # 副本集的操作计数器
db.serverStatus().opcounters # 操作计数器
db.serverStatus().asserts # 断言信息Asserts
db.serverStatus().writeBacksQueued # writeBacksQueued
db.serverStatus().dur # 持久化(dur)
db.serverStatus().recordStats # 记录状态信息
db.serverStatus( { workingSet: 1 } ).workingSet # 工作集配置
db.serverStatus().metrics # 指标信息metrics
```
- mongostat,会动态输出一些serverStatus提供的重要信息,每秒输出一次。
```
mongostat会动态输出一些serverStatus提供的重要信息,每秒输出一次。
insert query update delete getmore command flushes mapped vsize res faults qrw arw net_in net_out conn time
*0 *0 *0 *0 0 11|0 0 0B 237M 15.0M 0 0|0 0|0 1.50k 22.7k 1 May 1 14:24:25.425
*0 *0 *0 *0 0 12|0 0 0B 237M 15.0M 0 0|0 0|0 1.55k 23.5k 1 May 1 14:24:26.405
*0 *0 *0 *0 0 11|0 0 0B 237M 15.0M 0 0|0 0|0 1.52k 23.0k 1 May 1 14:24:27.405
*0 *0 *0 *0 0 12|0 0 0B 237M 15.0M 0 0|0 0|0 1.52k 23.1k 1 May 1 14:24:28.404
*0 *0 *0 *0 0 11|0 0 0B 237M 15.0M 0 0|0 0|0 1.52k 23.0k 1 May 1 14:24:29.404
*0 *0 *0 *0 0 12|0 0 0B 237M 15.0M 0 0|0 0|0 1.53k 23.2k 1 May 1 14:24:30.396
insert、query、update、delete、getmore、command 每种对应操作的发生次数。
其中faults表示访问失败数,数据从内存交换出去,放到swap。值越小越好,最好不要大于100。
faults:查询从磁盘读取数据,标志服务器未达到最佳,所需的数据并未完全保存找内存中
qr/qw:队列等待的数目。
ar/aw:活动客户端的数目。
conn:打开的连接数。
flushes:数据刷写到磁盘的数目。
vsize:使用虚拟内存大小。
mapped:隐射的内存大小,约等于数据目录大小。
res:实际使用的内存大小
netId:表示通过网络传输进来的字节数。
netou:t表示通过网络传输出的字节数。
time:表示统计的时间。
```
- mongotop,查看哪个几个最繁忙
- db.currentOP(),可看到当前执行的进程,类似MySQL的show processlist
- show profile
```
db.setProfilingLevel(2) # enable profiling
db.system.profile.find() # show raw profile entries
show profile # 该命令可以查看最近的5条记录
该命令可以查看最近的5条记录:
ts:该命令在何时执行。
millis:执行耗时,以毫秒为单位。
op:什么操作。
query:设置的查询条件。
nReturned:返回的条数。
docsExamined:文档扫描条数。
# 清空还原profile集合使用命令:
db.setProfilingLevel(0)
db.system.profile.drop()
db.createCollection("system.profile", { capped: true, size:4000000 } )
db.setProfilingLevel(1)
```
## 8.备份和恢复
### 1).数据库备份和恢复
- mongodump导出数据库中全部的内容,使用-q参数增加查询条件,注意-q参数值的标点符号,否则会报错 too many positional arguments
```
mongodump --uri mongodb://localhost:27017/robot --username=root --password=123456 --authenticationDatabase=admin -o /home
mongodump --uri mongodb://localhost:27017/robot -u "root" -p "123456" --authenticationDatabase=admin -c user -q "{name:'joe'}" -o /home
```
- mongorestore使用的数据文件就是mongodump备份的数据文件
```
# 使用/home路径下的BSON和JSON文件恢复数据库test,--drop参数表示如果已经存在test数据库则删除原数据库,去掉--drop则与原数据库合并
mongorestore --uri mongodb://localhost:27017/robot -u "root" -p "123456" --authenticationDatabase=admin /home/test --drop
```
### 2).表的备份和恢复
- 官网下载 MongoDB Database Tools Download
- windows下直接拷贝bin可执行文件到MongoDB的安装目录下的bin
- Linux下直接拷贝bin下的文件到MongoDB的安装目录下的bin,并且修改可执行权限
- 导出json格式的备份文件,从test数据库中student集合到/home/test.json文件中
```
mongoexport --uri mongodb://localhost:27017/test -u "root" -p "123456" --authenticationDatabase=admin --collection=student --type=json -o /home/student.json
```
- 导出csv格式的备份文件
```
# -f参数用于指定只导致id和name以及age字段,因为csv是表格类型的,所以对于内嵌文档太深的数据导出效果不是很好,所以一般来说会指定某些字段导出。
mongoexport --uri mongodb://localhost:27017/test -u "root" -p "123456" --authenticationDatabase=admin --collection=student --type=csv -fields="_id,name,age" -o /home/student.csv
```
- json格式导入,使用备份文件/home/test.json导入数据到test数据库的student集合中
```
# --upsert表示更新现有数据,如果不使用—upsert则导入时已经存在的文档会报_id重复,数据不再插入。
# "upsert"是"update"和"insert"的组合,表示如果导入的数据在目标集合中已经存在,则更新该数据;如果数据在目标集合中不存在,则插入一条新的文档。
# --drop在导入之前删除目标集合中的所有文档。
mongoimport --uri mongodb://localhost:27017/test -u "root" -p "123456" -authenticationDatabase=admin --collection=student --type=json /home/test.json --upsert
```
- csv格式导入,--headerline指明不导入第一行,csv格式的文件第一行为列名
```
mongoimport --uri mongodb://localhost:27017/test -u "root" -p "123456" -authenticationDatabase=admin --collection=student --type=csv --headerline --file /home/test.csv
```
-12
View File
@@ -1,12 +0,0 @@
阻塞和非阻塞是针对内核和上层应用来说的;同步和异步是针对应用和开发者来说的来说的。
![Image text](../image/netty/aio-nodejs-0.png)
![Image text](../image/netty/aio-nodejs-1.png)
![Image text](../image/netty/aio-nodejs-2.png)
![Image text](../image/netty/aio-nodejs-3.png)
![Image text](../image/netty/aio-netty-0.png)
![Image text](../image/netty/aio-netty-1.png)
![Image text](../image/netty/aio-netty-2.png)
![Image text](../image/netty/aio-netty-3.png)
![Image text](../image/netty/aio-netty-4.png)
![Image text](../image/netty/aio-netty-5.png)
-98
View File
@@ -1,98 +0,0 @@
# 一、netty模型
## 1.reactor线程驱动模型
```
bossGroup 线程池则只是在 Bind 某个端口后,获得其中一个线程作为 MainReactor,
专门处理端口的 Accept 事件,每个端口对应一个 Boss 线程。
workerGroup 线程池会被各个 SubReactor 和 Worker 线程充分利用。
```
## 2.Netty事件模型
```
核心思路基于主从Reactor模型,一个NioEventLoopGroup包含n个NioEventLoop,
每一个NioEventLoop持有一个Selector和一个线程池(执行器EventExecute,其实是netty ForkJoinPool,并发度为n),
在选择器的NioEventLoop的run方法每次运行后,就会交给NioEventLoop中线程池的另外一个线程。
```
## 3.I/O复用模型
- Netty 的非阻塞 I/O 的实现关键是基于 I/O 复用模型,这里用 Selector 对象表示,基于Java的NIO,netty会有多个Selector
```
DK的NIO类库有一个epoll死循环bug,它会导致Selector空轮询,IO线程CPU达到100%,严重影响系统运行。
Netty的解决策略:
1.对Selector的select操作周期进行统计。
2.每完成一次空的select操作进行一次计数。
3.在某个周期内如果连续N次空轮询,则说明触发了JDK NIO的epoll死循环bug。
4.创建新的Selector,将出现bug的Selector上的channel重新注册到新的Selector上。
5.关闭bug的Selector,使用新的Selector进行替换。
```
# 二、netty设置
## 1.netty有哪些参数设置
1.TCP_NODELAY
解释:是否启用Nagle算法,改算法将小的碎片数据连接成更大的报文来最小化所发送的报文的数量。
使用建议:如果需要发送一些较小的报文,则需要禁用该算法,从而最小化报文传输延时。
只有在网络通信非常大时(通常指已经到100k+/秒了),设置为false会有些许优势,因此建议大部分情况下均应设置为true。
2.SO_REUSEADDR
解释:是否复用处于TIME_WAIT状态连接的端口,适用于有大量处于TIME_WAIT状态连接的场景,如高并发量的Http短连接场景等。
3.SO_SNDBUF
  解释:Socket参数,TCP数据发送缓冲区大小,即TCP发送滑动窗口,linux操作系统可使用命令:cat /proc/sys/net/ipv4/tcp_smem查询其大小。
缓冲区的大小决定了网络通信的吞吐量(网络吞吐量=缓冲区大小/网络时延)。
  使用建议:缓冲区大小设为网络吞吐量达到带宽上限时的值,即缓冲区大小=网络带宽*网络时延。以千兆网卡为例进行计算,假设网络时延为1ms,缓冲区大小=1000Mb/s * 1ms = 128KB。
4.SO_KEEPALIVE
  解释:是否使用TCP的心跳机制;
  使用建议:心跳机制由应用层自己实现;
## 2.netty最佳的Boss线程连接数
```
没有固定的连接数,一切还是看你的场景,连接数在满足传输吞吐量的情况下,越少越好。
2条连接时,只能有40k QPS。
48条连接,升到62k QPS,CPU烧了28%
4条连接,QPS反而上升到68k ,而且CPU降到20%。
```
## 3.netty线程池怎么设置
```
Boss Group用于服务端处理建立连接的请求,WorkGroup用于处理I/O。
EventLoopGroup的默认大小都是是2倍的CPU核数,但这并不是一个恒定的最佳数量,为了避免线程上下文切换,只要能满足要求,这个值其实越少越好。
如果都是长连接,Boss Group平时很闲,好在它也只有忙起来才会多起线程,平时就只占1条。
Netty线程的数量一般固定且较少,所以很怕线程被堵塞,比如同步的数据库查询,
比如下游的服务调用(又来罗嗦,future.get()式的异步在执行future.get()时还是堵住当前线程的啊)。
所以,此时就要把处理放到一个业务线程池里操作,即使要付出线程上下文切换的代价
```
## 4.netty的JVM参数设置
```
-Dio.netty.leakDetectionLevel=disabled把检测关掉
堆外内存大小
```
## 5.netty注意事项
```
1. ctx.writeAndFlush() 与 channel.writeAndFlush()的区别在于,channel要经过整条Pipeline,而ctx直接找下一个outboundHandler。
2. channel.writeAndFlush(buf, channel.voidPromise() )
writeAndFlush不管你用不用默认构造返回一个Promise(Future),有点浪费内存。没有用的话,用一个公共的 voidPromise ,减少大家花费。不过低版本的Netty不能用。
3. 空闲连接管理,因为刚才说的ctx.writeAndFlush()不经Pipeline,所以只监控读空闲就够了。否则每次请求都要READ/WRITE/ALL IDEL三个值算一遍,白白消耗。
4. Handler能共用就标上Shareable Annotation然后共用,不要每个Channel建一个。
```
-135
View File
@@ -1,135 +0,0 @@
### 软件下载地址
- 百度网盘
```
链接:https://pan.baidu.com/s/1TMLtNiNz0aLpgnZkkMNB-g
提取码:zfoo
```
### [15分钟学会zfoo](https://www.bilibili.com/video/BV1TL4y1z7oy?spm_id_from=333)
- 主要就是快速学习zfoo的每一个模块,想深入学习的话,还是得看下面的详细教程
### [第一讲](https://www.bilibili.com/video/BV1Mf4y1b7xA) ,[第二讲](https://www.bilibili.com/video/BV1J64y1b7Px)
- 主要就是环境搭建,软件安装,代理设置,git使用
- 知识点
```
oracle和openjdk的区别
jdk11的优势
```
### [第三讲](https://www.bilibili.com/video/BV1LU4y1V7QD)
- event原理讲解
- 知识点
```
maven本地仓库,maven远程仓库,idea的工程管理
spring自定义标签
spring生命周期,对应的事件
spring自带事件的优先级通过Ordered接口指定
javassist字节码增强,asm,cglib,ByteBuddy,了解
```
### [第四讲](https://www.bilibili.com/video/BV1Aw411Z7ic)
- storage原理讲解
- 知识点
```
通过spring的PathMatchingResourcePatternResolver扫描excel文件位置,有方法可以直接获取Resource
spring的Resource抽象,spring对文件的抽象
apache的poi,poi可以解析excel
通过spring的ConversionService去解析excel单元格的内容,通过ConditionalGenericConverter,Converter接口自定义解析规则
```
### [第五讲](https://www.bilibili.com/video/BV1Xv411H7Qo)
- Tank项目的运行
### [第六讲](https://www.bilibili.com/video/BV1to4y1C77Y)
- scheduler原理讲解
- 知识点
```
cron表达式的用法
spring自带的Scheduled和zfoo的Scheduler
java的线程池,SingleThreadScheduledExecutor,SingleThreadExecutor
通过spring自带的CronExpression计算下一次cron表达式要执行的时间
```
### [第七讲](https://www.bilibili.com/video/BV1rL411W7bo)
- orm原理讲解
- 学习MongoDB优先看官方的文档,https://docs.mongodb.com/manual/
- B站有个稍微权威一点的MongoDB视频教程,https://www.bilibili.com/video/BV13a4y1J7L6
- 知识点
```
Mysql中的数据库,表,一条数据,在Mongodb分别叫做,数据库,集合,文档
Elastic Search的全文搜索引擎
mongodb的map reduce操作
分布式唯一Id,Java自带的UUID,雪花算法(SnowFlake),Redis分布式唯一Id实现,Mongodb分布式唯一Id实现,Zookeeper分布式唯一Id实现
```
### [第八讲](https://www.bilibili.com/video/BV1JL411W7Eo)
- protocol原理讲解
- 知识点
```
netty的ByteBuf
java的序列化和反序列化
kryo的使用,注意不是线程安全
protobuf的使用
juc的CountDownLatch
Zigzag算法压缩,varint数据压缩算法,优先kryo的实现
Javassist字节码增强序列化函数比反射快非常多
```
### [第九讲](https://www.bilibili.com/video/BV1ch411a7GY)
- net原理讲解
- 知识点
```
netty的nio,epoll
同步和异步通过CompletableFuture去实现的
一致性hash算法的负载均衡
zookeeper的用法
```
### [第十讲](https://www.bilibili.com/video/BV13U4y137G7)
- 综合实战
### [第十一讲](https://www.bilibili.com/video/BV1i44y1177r)
- 云服务器部署
- [tank-game-server](https://github.com/zfoo-project/tank-game-server) 网络游戏《进击的坦克(The Fight of Tanks)》
- 快速体验,tank游戏入口 [tank.zfoo.net](http://tank.zfoo.net)
- 快速体验,tank后台入口 [tank-admin.zfoo.net](http://tank-admin.zfoo.net),账号:a,密码:a
```
云服务器的安全组策略
java的安装
zookeeper的安装
mongodb的安装
用maven打包项目的流程
nohup java -XX:InitialHeapSize=300m -XX:MaxHeapSize=300m -XX:+HeapDumpOnOutOfMemoryError -Djdk.attach.allowAttachSelf=true -Dspring.profiles.active=pro -Dfile.encoding=UTF-8 -jar home-1.0.jar >/dev/null 2>&1 &
sh /deploy.sh stopUpdateStart /usr/local/tank/admin/admin-1.0.jar /usr/local/tank/admin
sh /deploy.sh stopUpdateStart /usr/local/tank/single/single-1.0.jar /usr/local/tank/single
sh /deploy.sh stopUpdateStart /usr/local/tank/admin/admin-1.0.jar /usr/local/tank/admin
sh /deploy.sh stopUpdateStart /usr/local/tank/cache/cache-1.0.jar /usr/local/tank/cache
sh /deploy.sh stopUpdateStart /usr/local/tank/gateway/gateway-1.0.jar /usr/local/tank/gateway
sh /deploy.sh stopUpdateStart /usr/local/tank/home/home-1.0.jar /usr/local/tank/home
```
-138
View File
@@ -1,138 +0,0 @@
# Zookeeper命令
## 一、连接Zookeeper
/opt/zookeeper/bin/zkCli.sh -timeout 5000 -server 192.168.238.128:2181 启动zookeeper的客户端
## 二、查看
- h # 查看zookeeper的所有数据结构
- ls / # 列出根目录得所有节点
- ls2 / ls+stat
- get /node_1 # 查询/node_1节点的信息
- stat /node_1 # 查看/node_1节点的状态
```
cZxid # 创建事务的id,create zookeeper transaction id zookeeper中每一次对数据的操作的是一个事务,会分配一个事务的id
ctime # 节点被创建的时间戳(从1970年开始)
mZxid # Mofify 节点最新一次更新发生时的zxid
mtime # 节点最后修改的时间戳(从1970年开始)
pZxid # 是与该节点的子节点(或该节点)的最近一次创建/删除子节点的事务id,修改子节点的数据内容不算
cversion # znode子节点变化号,znode子节点修改次数
dateVersion # znode数据变化号
aclVersion
ephemeralOwner # ephemeral adj. 朝生暮死的, 短暂的, 短命的 临时节点 如果该节点为ephemeral节点, ephemeralOwner值表示与该节点绑定的session id. 如果该节点不是ephemeral节点, ephemeralOwner值为0
dataLength
numChildren
```
## 三、更新
- set /node_1 "set Method" # 更新节点的信息
- create -e /node_2 "hello node_2" # 创建一个node_2的临时节点
- create -s /node_3 "hello node_3" # 创建一个node_3的顺序节点,会自增主键
- delete /node_1 # 删除/node_1节点,如果节点下有另外的节点则不能删除
- rmr /node_1 # 循环删除/node_1
- setquota -n 2 /node_1 # -n设置子节点的数目为2,如果超过了,会记录日志在bin/zookeeper.out中;-b,设置数据长度值(包括子节点)
- delquota -n /node_1 # 删除配额
## 四、权限控制
### 1. auth的授权方式
- addauth digest 用户名:密码明文 # 增加一个认证用户
- create /path
- setAcl /path auth:用户名:密码明文:crwda # 设置权限
- getAcl /path # 查看Acl设置
```
ZK的节点有5种操作权限:
CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、删、改、查、管理权限,这5种权限简写为crwda(即:每个单词的首字符缩写)
注:
这5种权限中,delete是指对子节点的删除权限,其它4种权限指对自身节点的操作权限
Zookeeper对权限的控制是znode级别的,不继承即对父节点设置权限,其子节点不继承父节点的权限。
身份的认证有4种方式:
world:默认方式,相当于全世界都能访问
auth:代表已经认证通过的用户(cli中可以通过addauth digest user:pwd 来添加当前上下文中的授权用户)
digest:即用户名:密码这种方式认证,这也是业务系统中最常用的
ip:使用Ip地址认证
```
### 2. digest授权方式
- create /path digest
- setAcl /path digest:jaysunxiao:F+YQy2Gee3i3tusJucURoYZKurE=:crwda # digest:用户名:密码明文:crwda
- addauth digest jaysunxiao:123456
- get /path
```
digest:jaysunxiao:F+YQy2Gee3i3tusJucURoYZKurE=:crwda
setAcl /digest digest:jaysunxiao:123456:crwda
上下两个只能用编码后的字符串设置密码,避免明文操作,增强安全性。
设置密码需要自己手动去编码,把密码先自己进行sha1编码然后吧结果进行base64编码。
```
# Zookeeper监控工具
- netflix:exhibitor 监控zookeeper,增删改查
- zabbix
# Zookeeper安装
## 一、Windows下的安装
```
1.下载windows下的zookeeper安装包并解压到任意目录
2.加入conf目录,重命名zoo_sample.cfg为zoo.cfg
3.编辑zoo.cfg:
dataDir=../data-zookeeper
admin.serverPort=18385
4.将java命令更换为javaw后台运行
```
## 二、Linux下的安装
```
将zookeeper-3.4.10.tar.gz下载到/opt目录,这个目录是专门放下载的第三方软件的目录
tar -xzvf zookeeper-3.4.10.tar.gz 解压
mv zookeeper-3.4.10 zookeeper 换一个好记一点的目录
cd zookeeper/conf 进入zookeeper配置文件目录
cp zoo_sample.cfg zoo.cfg 建立一个配置文件,zoo_sample.cfg是一个demo
vi zoo.cfg 改变dataDir=/var/zookeeper 这个是zookeeper的共享文件放置的目录,快照文件目录
在最后一行加上,服务器的ip,格式:server.id=host:port:port,
server.1=192.168.238.128:8888:9999
server.2=192.168.238.129:8888:9999
server.3=192.168.238.130:8888:9999
关闭防火墙
getenforce #查看SELinux是否开启,建议关闭,
#enforcing:强制模式,代表 SELinux 运作中;disabled:关闭
vim /etc/selinux/config #设置SELinux的开启和关闭,必须要重新启动
firewall-cmd --zone=public --add-port=2181/tcp --permanent
firewall-cmd --zone=public --add-port=8888/tcp --permanent
firewall-cmd --zone=public --add-port=9999/tcp --permanent
firewall-cmd --reload
开放端口
cd /var
mkdir zookeeper 在var目录创建一个zookeeper文件夹
cd zookeeper
vi myid 在文件里写1
cd /opt/zookeeper/bin
./zkServer.sh start 启动zookeeper服务器 /opt/zookeeper/bin/zkServer.sh start
./zkServer.sh stop
./zkCli.sh timeout 5000 -server 192.168.238.128:2181
yum install telnet
telnet 192.168.238.128 2181
```
-256
View File
@@ -1,256 +0,0 @@
## 一.分布式系统
#### 分布式系统的定义:
- 分布式系统是一个硬件或软件组分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。
#### 分布式系统的特点:
1. 分布性
分布式系统中的多台计算机在空间上随意分布,不同城市,不同机房,不同机柜
2. 对等性
没有主/从之分,没有控制整个系统的主机,也没有被控制的从机,分布式系统所有的计算机节点都是对等的
3. 并发性
银行转账A转B问题
#### 分布式系统的各种问题:
1. 通信异常
单机内存访问的延时在纳秒量级(通常10ns),而一次网络通信的延迟在0.1-1ms左右,相当于内存访问延时的100多倍
2. 网络分区
网络由于发生异常,导致分布式系统中部分节点之间的网络延时不断增大,最终导致分布式系统中只有部分节点能保持正常通行,将这个现象称为网络分区。
这时会出现小集群,在极端情况下,这些小集群需要完成整个分布式系统所要完成的工作。
3. 三态
分布式系统中每一次的请求与相应,都存在三态,成功,失败,超时。 出现超时现象无法确定当前请求是否被成功处理
4. 节点故障
每个节点都有可能会出现故障,并且每天都在发生
5. 缺乏全局时钟
很难定义两个时间谁先谁后
## 二.分布式一致性算法
#### 事务(Transaction)
- ACID
1. 原子性(Atomicity)[ˌætəˈmɪsɪti] 要么做要么不做
2. 一致性(Consistency)事务的执行不能破坏数据库的完整性和一致性
3. 隔离性(Isolation)[ˌaɪsəˈleʃən] 在标准的SQL规范中有4个事务隔离级别
未授权读取,授权读取,可重复读取,串行化。
![Image text](../image/zookeeper/isolation.png)
4. 持久性(Durability)[djʊərə'bɪlətɪ] 事务一旦提交,它对数据库的状态变更就应该是永久性的。
#### CAP定理(CAP theorem /ˈθɪərəm/)
- C:一致性(Consistency),所有节点在同一时间具有相同的数据
- A:可用性(Availability),保证每个请求在一定时间内,不管成功或者失败都有响应
- P:分区容错性(Partition tolerance),系统中任意信息的丢失或失败不会影响系统的继续运作,一般指增加删除节点导致网络分区,不同分区依然可用
```
CA:
CA要求在多个节点满足数据的严格一致性,且延迟较低。
如果要满足P,则可以容忍数据丢包,因为数据在跨节点传输时有可能会丢包,如果丢包就无法满足一致性。
所以CA一般单点集群,传统的数据库系统就是具有CA特征的数据库,所以在可扩展性上不强,水平扩展能力比较差。
CP:
满足一致性,分区容忍性的系统,因为P会导致系统的节点无线延长,也就无法在一定时间内保证可用性。
通常性能不是特别高。
AP:
满足可用性,分区容忍性的系统,就要放弃强一致性,保证最终一致性。因为为了保证可用性,有时候节点就会用自己的本地数据,导致数据不一致性。
因为是分布式系统,所以应用必然需要部署到不同的节点中,因此必然出现子网络,所以P是分布式系统首先需要满足的。
系统架构师往往把精力花在如何根据业务特点在C和A之间寻求平衡。
```
#### BASE理论:
```
Basically Available(基本可用),运行损失部分可用性,如响应时间加长,或者功能上的损失,如双11服务降级
Soft state(软状态),允许在不同节点的数据副本之间进行数据同步的过程中存在延时
Eventually consistency(最终一致性),系统中的副本,在经过一段时间的同步后,最终能够达到一个一致的状态
三个短语的简写即为BASE,BASE是对CAP中一致性和可用性权衡的结果,核心思想是即使无法做到强一致性(Strong consistency),但每个业务可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。
```
#### 分布式一致性协议:
当一个事务需要跨多个分布式节点的时候,为了保持事务处理的ACID特性,就需要引入一个称为“协调者”的组件来统一调度所有分布式节点的执行逻辑,来保证一致性。
2PC:Two Phase Commit,二段提交协议,大多数关系型数据库都是采用的这个协议来完成分布式事务的
![Image text](../image/zookeeper/transaction.png)
3PC:Three Phase Commit,三阶段提交协议
#### Paxos:分布式一致性算法
Proposer(提议者),Acceptor(决策者),Client(产生议题者),Learner(最终决策学习者)
现在通过一则故事来学习paxos的算法的流程(2阶段提交),有2个Client(老板,老板之间是竞争关系)和3个Acceptor(政府官员):
现在需要对一项议题来进行paxos过程,议题是“A项目我要中标!”,这里的“我”指每个带着他的秘书Proposer的Client老板。
Proposer当然听老板的话了,赶紧带着议题和现金去找Acceptor政府官员。作为政府官员,当然想谁给的钱多就把项目给谁。
Proposer-1小姐带着现金同时找到了Acceptor-1~Acceptor-3官员,1与2号官员分别收取了10比特币,找到第3号官员时,没想到遭到了3号官员的鄙视,3号官员告诉她,Proposer-2给了11比特币。
不过没关系,Proposer-1已经得到了1,2两个官员的认可,形成了多数派(如果没有形成多数派,Proposer-1会去银行提款在来找官员们给每人20比特币,这个过程一直重复每次+10比特币,直到多数派的形成),
满意的找老板复命去了,但是此时Proposer-2保镖找到了1,2号官员,分别给了他们11比特币,1,2号官员的态度立刻转变,都说Proposer-2的老板懂事,这下子Proposer-2放心了,搞定了3个官员,
找老板复命去了,当然这个过程是第一阶段提交,只是官员们初步接受贿赂而已。故事中的比特币是编号,议题是value。
这个过程保证了在某一时刻,某一个proposer的议题会形成一个多数派进行初步支持。
===============华丽的分割线,第一阶段结束================ 现在进入第二阶段提交,现在proposer-1小姐使用分身术(多线程并发)
分了3个自己分别去找3位官员,最先找到了1号官员签合同,遭到了1号官员的鄙视,
1号官员告诉他proposer-2先生给了他11比特币,因为上一条规则的性质proposer-1小姐知道proposer-2第一阶段在她之后又形成了多数派(至少有2位官员的赃款被更新了);
此时她赶紧去提款准备重新贿赂这3个官员(重新进入第一阶段),每人20比特币。刚给1号官员20比特币, 1号官员很高兴初步接受了议题,还没来得及见到2,3号官员的时候,
这时proposer-2先生也使用分身术分别找3位官员(注意这里是proposer-2的第二阶段),被第1号官员拒绝了告诉他收到了20比特币,
第2,3号官员顺利签了合同,这时2,3号官员记录client-2老板用了11比特币中标,因为形成了多数派,所以最终接受了Client2老板中标这个议题,对于proposer-2先生已经出色的完成了工作;
这时proposer-1小姐找到了2号官员,官员告诉她合同已经签了,将合同给她看,proposer-1小姐是一个没有什么职业操守的聪明人,觉得跟Client1老板混没什么前途,
所以将自己的议题修改为“Client2老板中标”,并且给了2号官员20比特币,这样形成了一个多数派。顺利的再次进入第二阶段。由于此时没有人竞争了,顺利的找3位官员签合同,
3位官员看到议题与上次一次的合同是一致的,所以最终接受了,形成了多数派,proposer-1小姐跳槽到Client2老板的公司去了。
#### ZAB:原子消息广播协议算法
ZAB:Zookeeper Atomic Broadcast,原子消息广播协议,Zookeeper分布式一致性的核心算法。包括两部分:崩溃恢复和消息广播(类似二阶段的提交过程)。
具体一点可以分为三个阶段:发现(Leader选举),同步(Leader和Follower之间的同步),广播(接受客户端新的事务请求)。
每个节点都可能处在三种状态,looking:leader选举阶段,following:follower服务器和leader保持同步状态,leading:Leader服务器作为主进程领导状态。
Paxos和ZAB的本质区别在于两者的设计目标不太一样。前者用于构建一个分布式的一致性状态机系统,而后者用于构建一个高可用的分布式数据主备系统。
## 三.Zookeeper基本说明
#### Zookeeper的定义:
- Zookeeper是一个高可用的分布式数据管理与协调框架
#### Zookeeper应用范围:
分布式应用可以基于Zookeeper实现如分布式一致性特性,数据发布/订阅,负载均衡,命名服务,分布式协调/通知,集群管理,Master选举,分布式锁,分布式队列
#### Zookeeper特点
- 高性能:Zookeeper将全量数据存储在内存中,并直接服务于客户端的所有非事务请求,因此它尤其适用于以读操作为主的应用场景。Zookeeper性能压测在100%读请求的场景下可以达到12-13W的QPS。
- 顺序访问:对于每一个客户端的请求,Zookeeper都会生成一个全局唯一的递增序号,这个序号反映了所有事务操作的先后顺序,应用程序可以使用这个特性来实现更高层次的同步原语。
- Leader服务器在产生一个新的事务的时候,会生成一个序列号ZXID,Zookeeper Transaction ID,这个ID有64位,前32位代表事务编号,每个事务加1,后32位代表发给客户端的计数器,每发给客户端计数器加1。
![Image text](../image/zookeeper/zookeeper-function.png)
#### Zookeeper的三种选举算法:LeaderElection,AuthFastLeaderElection,FastLeaderElection
默认的算法是FastLeaderElection:
启动时的选举算法如下:
1. 目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:
2. 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking。
3. 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
4. 服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。
5. 服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟。
6. 服务器5启动,后面的逻辑同服务器4成为小弟。
崩溃恢复时的选举算法:
![Image text](../image/zookeeper/zookeeper-leader-election.png)
![Image text](../image/zookeeper/zookeeper-process.png)
## 四.Zookeeper的应用
#### 数据发布/订阅
- 发布/订阅一般有两种设计模式,Zookeeper采用推拉结合的方式,一旦节点数据发生变化,服务端就会向客户端发送Watcher事件通知,客户端自己拉取想要的数据
```
服务器主动推(Push):服务器主动将数据更新发送给所有订阅的客户端,这种数据量在短时间可能会很大
客户端主动拉(Pull):客户端主动发起请求获取最新的数据,通常采用定时轮询的方式,这种缺点就是空轮询,客户端开销大
```
- 试用的场合
```
数据量比较小
数据内容在运行时会发生动态变化
集群中各机器共享,配置一致
```
#### 负载均衡
- 可分为硬件负载均衡和软件负载均衡
四层通过虚拟IP+端口接收请求,然后再分配到真实的服务器,如四层的负载均衡,就是通过发布三层的IP地址(VIP),然后加四层的端口号,来决定哪些流量需要做负载均衡,
对需要处理的流量进行NAT处理,转发至后台服务器,并记录下这个TCP或者UDP的流量是由哪台服务器处理的,后续这个连接的所有流量都同样转发到同一台服务器处理。 以常见的TCP为例,负载均衡设备在接收到第一个来自客户端的SYN
请求时,即通过上述方式选择一个最佳的服务器,并对报文中目标IP地址进行修改(改为后端服务器IP),直接转发给该服务器。
TCP的连接建立,即三次握手是客户端和服务器直接建立的,负载均衡设备只是起到一个类似路由器的转发动作。
在某些部署情况下,为保证服务器回包可以正确返回给负载均衡设备,在转发报文的同时可能还会对报文原来的源地址进行修改。
七层通过虚拟的URL或主机名接收请求,然后再分配到真实的服务器,七层的负载均衡,就是在四层的基础上(没有四层是绝对不可能有七层的),再考虑应用层的特征。
比如同一个Web服务器的负载均衡,除了根据VIP加80端口辨别是否需要处理的流量,还可根据七层的URL、浏览器类别、语言来决定是否要进行负载均衡。
举个例子,如果你的Web服务器分成两组,一组是中文语言的,一组是英文语言的,那么七层负载均衡就可以当用户来访问你的域名时,自动辨别用户语言,然后选择对应的语言服务器组进行负载均衡处理。
以常见的TCP为例,负载均衡设备如果要根据真正的应用层内容再选择服务器,只能先代理最终的服务器和客户端建立连接(三次握手)后,才可能接受到客户端发送的真正应用层内容的报文,
然后再根据该报文中的特定字段,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。负载均衡设备在这种情况下,更类似于一个代理服务器。
负载均衡和前端的客户端以及后端的服务器会分别建立TCP连接。所以从这个技术原理上来看,七层负载均衡明显的对负载均衡设备的要求更高,处理七层的能力也必然会低于四层模式的部署方式。
负载均衡器通常称为四层交换机或七层交换机。四层交换机主要分析IP层及TCP/UDP层,实现四层流量负载均衡。七层交换机除了支持四层负载均衡以外,还有分析应用层的信息,如HTTP协议URI或Cookie信息。
1、负载均衡分为L4 switch(四层交换),即在OSI第4层工作,就是TCP层啦。此种Load Balance不理解应用协议(如HTTP/FTP/MySQL等等)。例子:LVS,F5。
2、另一种叫做L7 switch(七层交换),OSI的最高层,应用层。此时,该Load Balancer能理解应用协议。例子: haproxy,MySQL Proxy。
![Image text](../image/zookeeper/load-balance.png)
#### 命名服务
- 集群中全局唯一ID的生成,用Java自带的UUID生成的是一长串没有意义的ID,在Zookeeper中创建一个顺序生成节点可以方便的生成全局唯一ID
#### 分布式协调/通知
- 在绝大多数的分布式系统中,系统机器间的通讯无外乎心跳检测,工作进度汇报和系统调度这三种类型
1. 心跳检测
```
传统的方案是一段时间A通ping到B,来判断A和B是否相连。
基于Zookeeper的心跳检测,A可以在Zookeeper注册一个临时节点,B监听这个节点的变化判断是否相连,这样可以极大的解耦合
```
2. 工作进度汇报
```
注册一个临时节点,节点的内容为自己的任务
```
3. 系统调度
```
通过临时节点的数据变化,控制其它监听该节点的系统
```
#### 集群管理
- 机器的上下线,机器监控
#### Master选举
- 集群中所有的节点同事注册一个/master的临时节点,哪个注册成功哪个就是master节点
#### 分布式锁
- 排它锁
```
获取锁:所有的客户端同事创建一个/lock临时节点,Zookeeper保证所有的客户端只有一个能创建成功,就认为这个客户端获取了锁,其它客户端注册监听,以便释放锁的时候重新获取锁
释放锁:获取锁的节点宕机,临时节点就会被移除;正常的执行业务逻辑后,主动释放锁,即删除这个临时节点
```
- 共享锁,又称为读锁
```
加上共享锁的对象,只能对锁中的内容进行读取操作,其它的事物也只能对该对象加共享锁
```
#### 分布式队列
- FIFO分布式队列
- Barrier分布式屏障