0%

ElasticSearch入门教程

ES 基本概念

Cluster: 集群

Node: 节点

Shard: 分片

Replia: 副本

全文检索

MySQL ElasticSearch
Database Index
Table Type
Row Document
Column Field
Schema Mapping
Index Everything is indexed
SQL Query DSL
SELECT * FROM table GET
UPDATE table SET PUT

ELK

ELK = ElasticSearch + Logstash + Kibana

ES特点和优势

  1. 分布式实时文件存储,可将每一个字段存入索引,使其可以被检索到
  2. 实时分析的分布式搜索引擎
    分布式:索引分拆成多个分片,每个分片可有零个或多个副本。集群中的每个数据节点都可承载一个或多个分片,并且协调和处理各种操作。
  3. 可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据
  4. 支持插件机制,分词插件,同步插件,Hadoop 插件,可视化插件。

Linux 下安装ElasticSearch

  1. 下载 ElasticSearch

  2. 安装 ElasticSearch

    1
    tar -zxvf elasticsearch-2.3.3.tar.gz
  3. 启动

    1
    ./elasticsearch 

    查看信息

    1
    curl -X GET 'http://localhost:9200'

    elasticsearch 外网访问

1
2
3
# 修改配置文件 config/elasticsearch.yml

network.host: 0.0.0.0

安装注意点

  1. ElasticSearch 不能使用root帐号启动
  2. 服务器 /etc/sysctl.conf 配置信息 vm.max_map_count = 655360 ,重启命令 sysctl -p
  3. 服务器ulimit -a 查看文件数,配置文件位置/etc/security/limits.conf,修改之后退出当前用户,或者重新登录。
    1
    [1]: max number of threads [1024] for user [sgm] is too low, increase to at least [4096]

###下载安装插件

安装分词插件

1
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.5.1/elasticsearch-analysis-ik-5.5.1.zip
1
./plugin install mobz/elasticsearch-head

基本概念

Node 与 Cluster

Elastic本质是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个Elastic实例。

单个Elastic实例称为一个节点(node).一组节点构成一个集群(cluster)。

Index

Elastic会索引所有字段,经过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。

Elastic数据管理的顶层单位就叫做Index(索引)。它是单个数据库的同义词。每个Index(即数据库)的名字必须是小写。

查看当前节点的所有Index

1
curl -X GET 'uri/_cat/indices?v'

Document

Index 里面单条的记录称为Docuemnt(文档)。许多条Document构成一个Index。

同一个Index里面的Document不要求相同的结构(scheme),但是最好能保持相同,这样有利于提高搜索效率。

Type

Document 可以分组,比如 weather 这个Index里面,可以按城市分组(北京和上海),也可以按气候分组(晴天和雨天)。这种分组就叫做Type,它是虚拟的逻辑分组,用来过滤Document。

不同的Type应该有相似的结构(Schema),举例来说,id字段不能在这个组是字符串,在另一个组是数值。这是与关系型数据库的表的一个区别。性质完全不同的数据(比如products 和 logs)应该存成两个Index,而不是一个Index里面的两个Type

列出每个Index所包含的Type

1
localhost:9200/_mapping?pretty=true

Tip:Elastic 6.x 版只允许每个Index包含一个Type,7.x 版将会彻底移除Type。

新建和删除Index

新建Index,可以直接想Elastic服务器发出PUT请求。
新建一个名叫weather的Index

1
curl -X PUT 'localhost:9200/weather'

发出 Delete 请求删除这个Index

1
curl -X DELETE 'localhost:9200/weather'

中文分词

凡是需要搜索的中文字段,都需要单独设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
curl -X PUT 'url' -d '
{
"mappings":{
"person":{
"properties": {
"user": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"desc": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}
}
}
'

上面代码中, analyzer是字段文本的分词器,search_analyzer是搜索词的分词器。ik_max_word分词器是插件ik提供的,可以对文本进行最大数量的分词。

数据操作

向指定的 /Index/Type 发送PUT请求,就可以在Index里面新增一条记录。
例如:

1
2
3
4
5
6
7
curl -X PUT 'localhost:9200/accounts/persion/1' -d '
{
"user": "张三",
"title": "工程师",
"desc": "数据库管理"
}
'

服务器返回的JSON对象,会给出Index,Type,Id,Version等信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"_index": "accounts",
"_type": "person",
"_id": "1",
"_version": "1",
"result": "create",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}

上代码插入的请求路径是/accounts/person/1,最后的 1是改条记录的Id。它不一定是数字,任意字符串(比如 abc) 都可以。

新增记录的时候,也可以不指定Id,这时要改成POST 请求。

1
2
3
4
5
6
7
curl -X POST 'localhost:9200/accounts/person' -d '
{
"user": "李四",
"title": "工程师",
"desc": "系统管理"
}
'

通过POST发出一个请求,添加一条记录,这时服务器返回的JSON对象里面,_id 字段就是一个随机字符串。

查看记录

/Index/Type/Id 发出GET请求,就可以查看这条记录。

1
curl 'localhost:9200'/accounts/person/1?pretty=true'

请求查看/accounts/persopn/1这条记录,URL的参数 pretty=true表示以易读的格式返回。
返回的数据中,found字段表示查询成功,_source字段返回原始记录。

1
2
3
4
5
6
7
8
9
10
11
12
{
"_index": "accounts",
"_type": "person",
"_id": "1",
"_version": 1,
"found": true,
"_source":{
"user":"",
"title": "",
"desc": ""
}
}

如果Id不正确,就查不到数据,found字段就是false

删除记录

删除记录就是发出DELETE请求

1
curl -X DELETE 'localhost:9200/accounts/person/1'

更新数据

更新记录就是使用PUT请求,重新发送一次数据。

1
2
3
4
5
curl -X PUT 'localhost:9200/accounts/person/1' -d '{
"user": "张三",
"title": "工程师",
"desc": "数据库管理,软件开发"
}'

返回信息

1
2
3
4
5
6
7
8
9
{
"_index": "accounts",
"_type": "person",
"_id": "1",
"_version": 2,
"result": "updated",
"_shards": {"total":2,"successful":1,"failed":0},
"created": false
}

从上面的记录可以看到,记录的Id没变,但是版本(version)从1变成了2,操作类型(result)从created变成updated,created字段变成false,因为这次不是新建记录。

数据查询

返回所有记录

使用GET方法,直接请求/Index/Type/_search,就会返回所有记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
curl 'localhost:9200/accounts/person/_search'

{
"took": 2,
"timed_out": false,
"_shards": {"total":5,"successful":5,"failed":0},
"hits":{
"total":2,
"max_score": 1.0,
"hits":{
{
"_index": "accounts",
"_type": "person",
"_id": "xxxx",
"_score": 1.0,
"_source": {
"user":"",
"title":"",
"desc": ""
}
}
}
}
}

上面代码中,返回结果的took字段表示改操作的耗时(单位是毫秒),timed_out字段表示是否超时,hits字段表示命中的记录,里面子字段的含义如下

1
2
3
total 返回记录数
max_score 最高的匹配程度
hits 返回的记录组成的数组

返回的记录中,每条记录都有一个_score字段,表示匹配的程序,默认是按照这个字段降序排列。

全文搜索

Elastic的查询非常特别,使用自己的查询语法,要求GET请求带有数据体。

1
2
3
4
5
curl 'localhost:9200/accounts/person/_search' -d '
{
"query": {"match": {"desc": "软件"}}
}
'

上面代码使用Match 查询,指定的匹配条件是desc字段里面包含”软件”这个词。返回结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
{
"took": 3,
"time_out": false,
"_shards": {"total":5,"successful":5,"failed":0},
"hits":{
"total": 1,
"max_score": "0.2858",
"hits":[
{}
]
}
}

Elastic默认一次返回10条结果,可以通过size字段改变这个设置。

1
2
3
4
5
6
curl 'localhost:9200/accounts/person/_search' -d '
{
"query": {"match": {"desc":"管理"}},
"size": 1
}
'

上面的代码指定,每次只返回一条结果。

还可以通过from字段,指定位移。

1
2
3
4
5
curl 'localhost:9200/accounts/person/_search' -d '{
"query": {"match": {"desc":"管理"}},
"from": 1,
"size": 1
}'

上面代码指定,从位置1开始(默认是从位置0开始),只返回一条结果。

逻辑运算

如果有多个搜索关键字,Elastic认为它们是or关系。

1
2
3
4
5
curl 'localhost:9200/accounts/person/_search' -d '
{
"query":{"match": {"desc": "系统 软件"}}
}
'

上面的代码搜索的是软件 or 系统
如果要执行多个关键字词的and搜索,必须使用布尔查询。

1
2
3
4
5
6
7
8
9
10
11
12
curl 'localhost:9200/accounts/person/_search' -d '
{
"query": {
"bool":{
"must":[
{"match": {"desc": "软件"}},
{"match": {"desc": "系统"}}
]
}
}
}
'

REST API

ES 学习
ES 学习

ES 在Java 中的使用

Freemarker 入门知识

Configuration 三种模版加载方式

  • void setDirectoryForTemplateLoading(File file);
  • void setClassForTemplateLoading(Class cl,String prefix);
  • void setServletContextForTemplateLoading(Object servletContext,String path);

核心代码

1
2
3
4
Template template = config.getTemplate("test.ftl", "utf-8");
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", "shicc");
template.process(map, out);

构建一个 Template 对象,执行template.process() 进行处理,最后解析 Writer 获取执行完成的字符串。

CentOS 6.5 安装 64位 aapt

通过 aapt 解析 apk包,获取包信息,版本信息等。

aapt在windows、linux、mac分别有对应的文件,我们可以通过解压apktool.jar获取。

aapt文件有32位和64位之分。
在 64位操作系统中安装32位的aapt需要安装glibc.i686、zlib.i686、libstdc,但是官方又没提供64位的aapt文件。我们可以自己在64位操作系统编译。已经编译好的64位aapt文件 aapt_64

把64位aapt文件上传到服务器后,chmod +x aapt添加运行权限,执行./aapt后会发现报libc.so.6: version ‘GLIBC_2.14’ not found,下面需要我们安装glibc2.14。

  1. 下载glibc-2.14.tar.xz上传到服务器
  2. 运行tar -xvf glibc-2.14.tar.xz解压。
  3. 解压完成后,我们进入glibc-2.14目录:cd glibc-2.14。
  4. 创建glibc源码构建目录:mkdir build,然后进入build目录:cd build。在build目录我们运行../configure –prefix=/opt/glibc-2.14进行配置,然后运行make -j4进行编译,最后运行sudo make install进行安装。
  5. 运行strings /lib64/libc.so.6 |grep GLIBC_ 发现还是没有2.14版本
  6. cp -r /etc/ld.so.c* /opt/glibc-2.14/etc/
  7. ln -sf /opt/glibc-2.14/lib/libc-2.14.so /lib64/libc.so.6
  8. 再运行strings /lib64/libc.so.6 |grep GLIBC_可以从结果中找到GLIBC_2.14版本
  9. 运行./aapt出现Android Asset Packaging Tool…信息证明配置成功。

vue 参数:

  1. data
  2. props 依赖父子通信
  3. methods 方法
  4. computed 计算,与methods 的区别,computed带有缓存功能
  5. watch 监控data中的数据
  6. directives 自定义指令
特殊变量

$set vm.$set 实例方法,它只是全局 Vue.set 的别名。

$event 在内联语句处理器中访问原始的 DOM 事件

事件修饰符
  1. .stop
  2. .prevent
  3. .capture
  4. .self
  5. .once
    使用修饰符时,顺序很重要;相应的代码会以相同的顺序产生。因此,用@click.prevent.self 会阻止所有的点击,而@check.self.prevent只会阻止对元素自身的点击。
按键修饰符

keyup.enter 回车事件

定义全局自定义按键修饰别名
Vue.config.keyCodes.f1 = 112

组件组合

DOM 模版解析注意事项

把Vue实例挂载到一个已有内容的元素上,会受到 html 本身的一些限制,因为vue只有在浏览器解析、规范化模版之后才能获取其内容。尤其要注意 <ul> <ol> <table> <select> 这样的元素只能出现在某些特定元素的内部。
解决方法: 使用特殊的 is 特性。

字符串模版

  1. <script type="text/x-template"></script>
  2. JavaScript 内联模版字符串
  3. .vue 组件

父子组件: 组件A在它的模版中使用了组件B,它们之间必然需要相互通信。父组件可能要给子组件下发数据,子组件则可能要将它内部发生的事件告知父组件。
父子组件的关系可以总结为 prop向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。
image

camelCase vs. kebab-case
HTML 特性是不区分大小写,所以当使用的不是字符串模版时,camelCase(驼峰式命名)的prop需要转换为相对应的 kebab-case(短横线分隔式命名)

动态Prop
使用 v-bind 来动态地将prop绑定到父组件的数据。每当父组件的数据变化时,该变化也会传给子组件:

1
2
3
4
5
6
7
8
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>

--- 缩写方式
<child :my-message="parentMsg"></child>

Prop 验证
要指定验证规则,需要用对象的形式来定义 prop ,而不能用字符串数组。

自定义事件

使用v-on绑定自定义事件
  1. 使用$on(eventName) 监听事件
  2. 使用$emit(eventName) 触发事件
    父组件可以在使用子组件的地方直接使用v-on 来监听子组件触发的事件。
事件修饰符
  1. 给组件绑定原生事件
    使用v-on的修饰符.native
    1
    <my-component v-on:click.native="doTheThing"></my-component>
  2. .sync 修饰符
    .sync 对一个prop进行双向绑定。

指令中钩子函数

  1. bind 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
  2. inserted 被绑定元素插入父节点调用(仅保证父节点存在,但不一定已被插入文档)
  3. update 所有组件的VNode更新时调用,但是可能发生其子VNode更新之前。指令的值可能发生了改变,也可能没有。
  4. componentUpdated 指令所在组件的VNode及其子VNode全部更新后调用
  5. unbind 只调用一次,指令与元素解绑时调用

vm对象

vm 是 vue 的一个实例。
vm获取其他属性:

1
2
el = vm.$el
data = vm.$data

状态管理(VUEX)

store中state的改变都放置在store自身的action中去管理。这种集中式状态管理能够被更容易地理解哪种类型的 mutation 将会发生,以及它们是如何被触发。

主要参数

  1. state 定义变量
  2. getter 提供一些方法获取变量值
  3. mutation 修改store 中的状态,并通过commit(‘method’)触发对象风格的提交方式 store.commit({type:”method”,amount:10}) 同步事务
  4. action 处理异步操作,修改store中的状态,并通过commit(‘method’)
  5. module
  6. plugins

Getter

对vuex中 state 数据封装一些公共方法方便使用。

1
2
3
4
5
getters:{
doneTodos:function(state,getter){

}
}

Getter 也可以接受其他 getter 作为第二个参数
使用方法

1
store.getters.methodName

Mutation

更改vuex的store中的状态的唯一方法是提交 mutation 。 vuex 中的mutation非常类似于事件。每个 mutation 都有一个字符类型的事件类型(type)和一个回调函数(handler)
回调函数就是我们实际进行状态更改的地方,并且它会接受一个 state 作为第一个参数。
store.commit 传入的额外的参数,即 mutation 的载荷(payload)

对象风格的提交方式
1
2
3
4
store.commit({
type: 'increment',
amount: 10
})
mutation 必须是同步函数

Action

  1. Action 提交的是 mutation,而不是直接变更状态。
  2. Action 可以包含任意异步操作。
    Action 通过 store.dispatch 方法触发:
    1
    store.dispatch('increment')
组合Action

action 返回的 promises 是异步对象,使用 then 等待异步函数执行完成,并进行下一步操作。

Module

将 store 分割成模块(module),每个模块拥有自己的 state mutation action getter 甚至是嵌套子模块–从上至下进行同样方式的分割。

对于模块中的局部方法,根节点状态会作为第三个参数暴露出来。

vuex 辅助函数

mapGetters

import {mapGetters} form ‘vuex’

getters 辅助函数 mapGetters
mapGetters辅助函数仅仅将store中的getters映射到局部 computed 属性。

mutation 辅助函数 mapMutations

mapMutations 辅助函数将组件中的methods映射为 store.commit 调用

1
2
3
4
5
6
7
8
9
10
11
export default {
methods: {
...mapMutations([
'increment',
'incrementBy'
]),
...mapMutations({
add:'increment' //将 this.add() 映射到 this.$store.commit('increment')
})
}
}
action 辅助函数 mapActions

网站

什么是类的加载

类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

类加载器并不需要等到某个类被“首次主动使用”时再加载它,JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError错误)如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。

加载.class文件的方式

  • 从本地系统中直接加载
  • 通过网络下载.class文件
  • 从zip,jar等归档文件中加载.class文件
  • 从专有数据库中提取.class文件
  • 将Java源文件动态编译为.class文件

类的生命周期

其中类加载的过程包括了加载、验证、准备、解析、初始化五个阶段。在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也成为动态绑定或晚期绑定)。另外注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。

https://www.cnblogs.com/leeSmall/p/7517356.html

理解 zookeeper

Zookeeper 主要用来解决分布式应用中经常遇到的一些数据管理,如统一命名服务器、状态同步服务、集群管理、分布式应用配置项的管理。

节点特性

  1. 同一时刻多台机器创建同一个节点,只有一个会争抢成功。利用这个特性可以做分布式锁。
  2. 临时节点的生命周期与会话一致,会话关闭则临时节点删除。利用这个特性经常来做心跳,动态监控,负载等动作。
  3. 顺序节点保证节点名全局唯一。这个特性可以用来生成分布式环境下的全局自增长id。

zookeeper 提供的服务

  1. 创建节点
  2. 删除节点
  3. 更新节点
  4. 获取节点信息
  5. 权限控制
  6. 事件监听

Zookeeper的集群对server进行了归类:

  • Leader
  • Follower
  • Observer

Zookeeper 作用(使用场景)

  1. 配置中心 – Zookeeper 的目录结构比较特殊,可以这个特性作为分布式的配置中心,当配置内容发生更新可以及时通知各服务器进行更新
  2. 集群选举 – 当某一个服务宕机或者整个服务重启,可根据Zookeeper节点的顺序一致性来选择最大节点或者最小节点作为leader
  3. 分布式锁 – 原理同集群选举,根据节点的顺序一致性来选择最小节点对应的那个服务获得锁,当服务执行完成删除节点就会释放锁,再由其他服务去争取锁。
  4. 注册中心 – Zookeeper的目录以及子节点。主要通过对节点的管理做到发布以及事件监听做到订阅。
  5. 队列管理
    1. 同步队列
      当一个队列的队员都聚气时,队列才可用,否则一直等待所有的成员到达,这种是同步队列
      创建一个父目录 /synchronizing ,每个成员都监控标志(Set Watch)位目录 /synchronizing/start 是否存在,然后每个成员都加入这个队列,加入队列的方式就是创建 /synchronizing/member_i 的临时目录节点,然后每个成员获取 、synchronizing 目录的所有目录节点,也就是 member_i,判断 i 的值是否已经是成员的目录,如果小于成员个数就等待 synchronizing/start 的出现,如果相等就创建synchronizing/start
    2. 异步队列 队列按照FIFO方式进行入队和出队操作,例如实现生产者和消费者模型。
      保证所有成员加入队列时都是有编号的,出队是通过getChildren() 方法可以返回当前所有的队列元素,然后消费其中最小的元素。

Zookeeper 在分布式中的作用:

使用Zookeeper提供分布式锁机制,从而实现分布式的一致性处理。

  • Barrier
  • Queue
  • Lock
  • 2PC

Java Api 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
String create(String path, byte data[], List<ACL> acl, CreateMode createMode)
void create(String path, byte data[], List<ACL> acl, CreateMode createMode, StringCallback cb, Object ctx)

void delete(String path, int version)
void delete(String path, int version, VoidCallback cb, Object ctx)

Stat setData(String path, byte data[], int version)
void setData(String path, byte data[], int version, StatCallback cb, Object ctx)

Stat setACL(String path, List<ACL> acl, int version)
void setACL(String path, List<ACL> acl, int version, StatCallback cb, Object ctx)

Stat exists(String path, Watcher watcher)
Stat exists(String path, boolean watch)
void exists(String path, Watcher watcher, StatCallback cb, Object ctx)
void exists(String path, boolean watch , StatCallback cb, Object ctx)

byte[] getData(String path, Watcher watcher, Stat stat)
byte[] getData(String path, boolean watch , Stat stat)
void getData(String path, Watcher watcher, DataCallback cb, Object ctx)
void getData(String path, boolean watch , DataCallback cb, Object ctx)

List<String> getChildren(String path, Watcher watcher)
List<String> getChildren(String path, boolean watch )
void getChildren(String path, Watcher watcher, ChildrenCallback cb, Object ctx)
void getChildren(String path, boolean watch , ChildrenCallback cb, Object ctx)

List<String> getChildren(String path, Watcher watcher, Stat stat)
List<String> getChildren(String path, boolean watch , Stat stat)
void getChildren(String path, Watcher watcher, Children2Callback cb, Object ctx)
void getChildren(String path, boolean watch , Children2Callback cb, Object ctx)

接口说明

  • 每一种安同步还是异步
  • 添加指定watcher还是默认watcher分为4中。默认watcher在Zookeeper 初始化中进行指定。
  • 如果包含boolean watch 的读方法传入true,则将默认为watcher注册为所关注事件的watch。如果传入false则不注册watch。

CreateMode

  • PERSISTENT 持续的。相比与EPHEMERAL,不会随着client session的close或者expire而消失
  • PERSISTENT_SEQUENTIAL
  • EPHEMERAL 短暂的,生命周期依赖于client session、对应session close/expire 后其znode也会消失。
  • EPHEMERAL_SEQUENTIAL SEQUENTIAL意为顺序的。

Zookeeper 为了解决数据的一致性,使用Watcher的异步回调接口,将服务器znode的变化以事件的形式通知给客户端,主要是一种方向推送的机制,让客户端可以做出及时响应。比如及时更新后端的可用集群服务列表。

参考网站

Zookeeper Api(java)入门与应用(转)

保证分布式系统数据一致性的6种方案

解决分布式系统的一致性问题,我们需要了解哪些理论

分布式系统的事务处理

ZooKeeper典型应用场景一览

zookeeper中的基本概念

Watcher/Callback 参考网站

http://luzengyi.blog.163.com/blog/static/529188201064113744373/
http://luzengyi.blog.163.com/blog/static/529188201061155444869/

ACL

http://rdc.taobao.com/team/jm/archives/947

集群管理

paxos 实现
paxos算法介绍续
zookeeper代码解析

Zookeeper 官方文档

http://zookeeper.apache.org/doc/r3.3.2/recipes.html

TODO

Zookeeper 一致性

延迟执行事务有助于提升性能 因为Redis在执行事务的过程中,会延迟执行已入队的命令直到客户端发送EXEC命令为止。因此很多Python客户端在内的很多Redis客户端都会等到事务包含的所有命令都出现了之后,才一次性的将multi命令、要在事务中执行的一系列命令,以及Exec命令全部发送给Redis,然后等待直到接收到所有命令的回复为止。这种“一次性发送多个命令,然后等待所有回复出现”的做法通常被称为流水线(pipelining),它可以通过介绍客户端与Redis服务器之间的网络通信次数来提升Redis在执行多个命令时的性能。

在需要执行大量命令的情况下,即使命令实际上并不需要放到事务里面执行,但是为了通过一个发送所有命令来减少通信次数并降低延迟值,用户也可能会将命名包裹在MULTI 和 EXEC 里面。遗憾的是 MULTI 和 EXEC 并不是免费的–它们也会消耗资源,并且可能会导致其他重要的命令被延迟执行。

如果Redis和Web服务器通过局域网进行连接,那么它们之间的每次通信往返大概需要耗费一两毫秒,因此需要进行2次或者5次通信往返的update_token() 函数大概需要花费210毫秒,按照此速度,单个Web服务器线程每秒可以处理100500个请求。

iostat

iostat 主要用于监控系统设备的IO负载情况,iostat首次运行时系统启动开始的各项统计信息,之后运行iostat将会显示自上次运行该命令以后的统计信息。用于通过指定统计的次数和时间来获取所需的统计信息。

命令模式

1
iostat [ -c | -d ] [ -k | -m ] [ -t ] [ -V ] [ -x ] [ device [ ... ] | ALL ] [ -p [ device | ALL ]  ] [ interval [ count ] ]

参数说明

1
2
3
4
5
6
7
8
9
10
11
-c 仅显示CPU统计信息.与-d选项互斥.
-d 仅显示磁盘统计信息.与-c选项互斥.
-k 以K为单位显示每秒的磁盘请求数,默认单位块.
-p device | ALL
与-x选项互斥,用于显示块设备及系统分区的统计信息.也可以在-p后指定一个设备名,如:
# iostat -p hda
或显示所有设备
# iostat -p ALL
-t 在输出数据时,打印搜集数据的时间.
-V 打印版本号和帮助信息.
-x 输出扩展信息.

入门

1
2
3
iostat -d -k 2 10
iostat -d -k interval [count]

参数说明

  • -d 表示设备(磁盘)使用状态;
  • -k 某些使用block为单位的列强制使用Kilobytes为单位
  • 2[interval] 表示每隔2秒刷新一次
  • 10[count] 表示总共输出10次
  • -x 用于显示和io相关的扩展数据
  • -c 用来获取cpu部分状态值

输出信息的意义

  • tps 该设备每秒传输的次数。“一次传输”意思是“一次IO请求”。多个逻辑请求可能会被合并为”一次IO请求”。“一次传输”请求的大小是未知的。
  • kB_read/s 每秒从设备读取的数据量
  • kB_wrtn/s 每秒想设备写入的数据量
  • kB_read 读取的总数据量
  • kB_wrtn 写入总数据量

-x 输出信息的含义

  • rrqm/s 每秒这个设备相同的读取请求有多少被Merge了(当系统调用需要读取数据的时候,VFS将请求发到各个FS,如果FS发现不同的读取请求读取的是相同Block的数据,FS就会将这个请求合并Merge)
  • wrqm/s 每秒这个设备相关的写入请求有多少被Merge了
  • rsec/s 每秒读取的扇区数
  • wsec/s 每秒写入的扇区数
  • rKB/s the number of read requests that were issued to device per second
  • wKB/s the number of write requests that were issued to device per second
  • avgrq-sz 请求扇区大小
  • avgqu-sz 是平均请求队列的长度。好无疑问,队列长度越短越好
  • await 每个IO请求的处理的平均时间(单位是毫秒)。这个可以理解为IO的相应时间,一般地系统IO时间应该低于5ms,如果大于10ms就比较大了。这个时间包括队列时间和服务时间,也就是说一般情况下 await大于svctm,他们的差值越小说明队列时间越短,反之差值越大,队列时间越长,说明系统出了问题。
  • svctm 表示平均每次设备IO操作的服务器时间(以毫秒为单位)。如果 svctm的值和await很接近,表示几乎没有IO等待,磁盘性能很好,如果await的值远高于svctm的值,则表示IO队列等待太长,系统运行的应用程序将会变慢。
  • %util 在统计时间内所有IO时间,除以总共统计时间。例如统计间隔1秒,该设备有0.8秒在处理IO,而0.2秒闲置,那么该设备的%util = 0.8/1 = 80%, 所以该参数暗示了设备的繁忙程度。一般地,如果该参数的100%表示该设备已经接近满负荷运行了。(当然如果是多磁盘,即使%util是100%,因为磁盘的并发能力,所以磁盘使用未必就到了瓶颈)

常见用法

iostat -x 1 20
iostat -d -k 1 10 查看TPS和吞吐量信息(磁盘读写速度单位为KB)
iostat -d -m 2 查看TPShe吞吐量信息(磁盘读写速度单位为MB)
iostat -d -x -k 1 10 查看设备使用率(%util),响应时间(await)
iostat -c 1 10 查看cpu状态

实例分析

1
2
3
4
5
6
7
8
ostat -d -k 1 |grep sda10
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
sda10 60.72 18.95 71.53 395637647 1493241908
sda10 299.02 4266.67 129.41 4352 132
sda10 483.84 4589.90 4117.17 4544 4076
sda10 218.00 3360.00 100.00 3360 100
sda10 546.00 8784.00 124.00 8784 124
sda10 827.00 13232.00 136.00 13232 136

磁盘每秒传输次数平均约400;每秒磁盘读取约5MB,写入约1MB。

1
2
3
4
5
iostat -d -x -k 1
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util
sda 1.56 28.31 7.84 31.50 43.65 3.16 21.82 1.58 1.19 0.03 0.80 2.61 10.29
sda 1.98 24.75 419.80 6.93 13465.35 253.47 6732.67 126.73 32.15 2.00 4.70 2.00 85.25
sda 3.06 41.84 444.90 54.08 14204.08 2048.98 7102.04 1024.49 32.57 2.10 4.21 1.85 92.24

磁盘的平均相应时间 < 5ms ,磁盘使用率>80。磁盘相应正常,但是比较繁忙。

使用liunx进行压力测试时出现很多异常,都是java.net.NoRouteToHostException: Cannot assign requested address.

错误原因

由于liunx 分配的客户端连接端口用尽,无法建立socket连接所致,虽然socket正常关闭,但是端口不是立即释放,而是处于 TIME_WAIT 状态,默认等待60s后释放。
查看liunx支持的客户端连接端口范围,也就是 28232 个端口。

1
2
cat /proc/sys/net/ipv4/ip_local_port_range
32768 - 61000

解决方法

  1. 调低端口释放后的等待时间,默认为60s,修改为15~30s。
    echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
  2. 修改 tcp/ip 协议配置,通过配置 /proc/sys/net/ipv4/tcp_tw_reuse,默认为0,修改为1,释放TIME_WAIT端口给新连接使用。
    echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
  3. 修改 ctp/ip 协议配置,快速回收socket资源,默认为0.修改为1。
    echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle

参考网址

木木de果冻儿的博客