Typora
Typora是一款 Markdown 编辑器和阅读器。
个人觉得挺好用的,可是已经开启收割模式。
这里附上激活的软件下载地址,下载的可能比较慢,大家耐心等待.
安装使用
下载安装完成后,在mac下会提示如下错误
按照如下配置 “仍要打开” 就可以了
打开后 就可以开心的使用了。
Typora是一款 Markdown 编辑器和阅读器。
个人觉得挺好用的,可是已经开启收割模式。
这里附上激活的软件下载地址,下载的可能比较慢,大家耐心等待.
下载安装完成后,在mac下会提示如下错误
按照如下配置 “仍要打开” 就可以了
打开后 就可以开心的使用了。
缓存是常用的优化数据查询慢的一种方法,数据库出现瓶颈的时候,我们会给服务加上一层缓存,如Redis,命中缓存就不用查询数据库了。减轻数据库压力,提高服务器性能。
引入缓存后,数据出现两份,在数据变更的时候,就需要考虑缓存与数据库的一致性。
由于更新数据库与更新缓存操作 是两个步骤,在高并发的场景下,会出现什么问题呢? 我们来分析一下。
如下图所示,高并发场景下存在数据不一致。
同样也是会出现不一致的场景,如下图所示
所以,无论是先更新数据库还是更新redis,都会存在数据不一致的场景,由于单个操作不是原子操作(并发导致执行数据未知),也没有事物的支持(一个成功一个失败 导致数据不一致),高并发就会存在不可预知的顺序,导致结果与预期不一致。
既然更新有问题,那缓存直接删除缓存呢?在更新的时候直接删除缓存,查询的时候 如果没有缓存就查库,并设置缓存.
如下图所示
读策略步骤
写策略步骤
读取的逻辑比较简单,先读缓存, 再读数据库,但写策略 删除缓存与更新数据库 这两个执行顺序 看似无关紧要,谁先谁后都不影响。我们具体分析一下。
如下图所示,读请求来先查询数据,没查到,这个时候有个更新请求,先删除缓存,之后读请求开始读取数据(数据未更新 旧数据) 并将旧数据写入缓存。更新请求更新数据库为新的数据,这时候数据不一致。
先删除缓存有可能出现不一致的场景,那先更新数据库呢?来跟着我的思路看一下。
同样,一个读请求与一个更新请求,读请求先检查缓存,没数据就从数据库读取数据(这时候还是旧的数据), 在写缓存之前, 更新请求更新了数据,并执行了清理缓存的操作,这个时候,读请求的设置缓存操作执行, 就出现了不一致。
问题的关键还是 非原子操作,无事务支持,导致并发出现未知的执行顺序。
对于比较严格的场景,可以加分布式锁,将更新与删除缓存两步合为一步。也就是,数据更新可以加锁,等更新完成及缓存删除后释放锁,读请求也是加锁,发现有写锁 就等待,读锁就继续读。分布式读写锁可以解决并发导致的不一致问题。
针对「先删除缓存,再更新数据库」可以用延迟双删的操作。更新请求在删除缓存后,等待一段时间,再进行一次缓存删除操作,就可以避免缓存中缓存旧数据。
在面试的过程中,经常会假想,在操作缓存的时候,网络抖动导致缓存操作失败,这个时候很明显数据也是不一致的。
就比如,更新完数据库,删除缓存的时候失败了,怎么保证一致?
要保证强一致,只能多次删除,异步执行删除,失败后重试几次,一直失败可以增加告警机制配合。
也可以记录失败的key,下次读取的时候避开,总之 要保证强一致,大家应该有不少好的方法。
比较高级的一种方案,或者说比较复杂,binlog推送数据变更记录,直接删除缓存。
不过,引入一种机制,就会导致系统越来越复杂,这个就看系统的取舍了。
内置变量存放在 ngx_http_core_module 模块中,变量的命名方式和 apache 服务器变量是一致的。总而言之,这些变量代表着客户端请求头的内容,例如httpuseragent,http_cookie, 等等。
下面是 nginx 支持的所有内置变量:
这个变量是获取链接中参数名为 name 对应的值;
如请求链接: http://service.shiguofu.cn/test?name=100&a=200
argname=′100′,arg_a = ‘200’
这个变量获取链接中所有的参数,即链接问号后面的所有的东西;
如:http://service.shiguofu.cn/test?name=100&a=200
$args = ‘name=100&a=200’
客户端的二进制的 ip 地址;
传输给客户端的字节数,响应头不计算在内;
nginx 返回给客户端的字节数,包括响应头和响应体;
TCP 连接的序列号,并不是一次 http 请求就会更滑一个序列号,http 有 keep-alive 机制,一个序列号会维持
TCP 连接当前的请求数量,服务处理请求的个数,重启后重置为 0
“Content-Length” 请求头字段, 客户端请求的头部中的 content-length 值;
“Content-Type” 请求头字段
获取 cookie 名称为 name 的 cookie 值;
如 cookie:PHP_VERSION: 1.0; NAME:XIAOMING;….
$cookie_NAME = ‘XIAOMING
当前请求的文档根目录或别名,即配置文件中的 root 目录;
即请求的 uri;
如:http://service.shiguofu.cn/test/index?a=1
$document_uri = /test/index
请求的 host, 优先级:HTTP 请求行的主机名 > 请求头中的”HOST”字段 > 符合请求的服务器名
请求的服务主机名
匹配任意请求头字段; 变量名中的后半部分“name”可以替换成任意请求头字段,如在配置文件中需要获取 http 请求头:“Accept-Language”,那么将“-”替换为下划线,大写字母替换为小写,形如:$http_accept_language 即可;
如果开启了 SSL 安全模式,值为“on”,否则为空字符串;
如果请求中有参数,值为“?”,否则为空字符串;
当前的 Unix 时间戳;
nginx 版本;
nginx 进程 pid
如果请求来自管道通信,值为“p”,否则为“.”
获取代理访问服务器的客户端地址,如果是直接访问,该值为空字符串。有些懵懂;
链接中的参数列表,同 $args;
当前请求的文档根目录或别名的真实路径,会将所有符号连接转换为真实路径;
客户端地址
客户端端口
用于 HTTP 基础认证服务的用户名;
#### $request
HTTP 请求的方法/路径及版本;
如: http://service.shiguofu.cn/test/index
$request = GET /test/index HTTP/1.1
客户端的请求主体;post 中的 body 的数据部分
如果请求成功,值为”OK”,如果请求未完成或者请求不是一个范围请求的最后一部分,则为空;
当前连接请求的文件路径,由 root 或 alias 指令与 URI 请求生成;
请求的长度 (包括请求的地址, http 请求头和请求主体);
HTTP 请求方法,通常为“GET”“POST”等
处理客户端请求使用的时间; 从读取客户端的第一个字节开始计时;
客户端请求的 uri;
如:http://service.shiguofu.cn/test/index?a=1&b=200
$request_uri = /test/index?a=1&b=200
请求使用的 Web 协议, “http” 或 “https”
设置任意 http 响应头字段; 变量名中的后半部分“name”可以替换成任意响应头字段,如需要设置响应头 Content-length,那么将“-”替换为下划线,大写字母替换为小写,形如:$sent_http_content_length 4096 即可;
服务器端地址;如 : 172.27.0.15
服务器名;如 service.shiguofu.cn
服务器端口号
服务器的 HTTP 版本, 通常为 “HTTP/1.0” 或 “HTTP/1.1”
HTTP 响应代码
客户端 TCP 连接的具体信息
服务器时间的 ISO 8610 格式
服务器时间(LOG Format 格式)
请求中的当前 URI(不带请求参数,参数位于 $args);
Nginx 是一个高性能的 HTTP 和反向代理服务,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。
其特点是占有内存少,并发能力强,事实上 nginx 的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用 nginx 网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
当 Nginx 作为代理服务,后端可支持的应用也是多种类型的,比如基于 python 的 uwsgi、php 的 fastcgi 以及 TCP、HTTP、UDP 等协议;
1 | upstream service { |
以上配置表示,主要使用 nginx 的指令 uwsgi_pass,使用 Nginx 的 uwsgi 模块将匹配到 location 的路径转发到有 upstream 块级指令代理的 uwsgi 服务,这里默认是轮询的方式;
所有的 uwsgi 服务在 upstream 中由 server 指令完成,server 指令接收 UNIX 套接字、IP 地址、FQDN 名及一些可选参数,参数下文会提及;
1 | upstream service { |
使用 Nginx 的 porxy_pass 指令,将匹配 location 的路径的请求转发到 upstream 块级指令代理的 HTTP 服务,同样采用轮询的方式;
所有的 HTTP 服务在 upstream 中由 server 指令完成,server 指令接收 UNIX 套接字、IP 地址、FQDN 名及一些可选参数,参数下文会提及;
不同的地方在于 proxy_pass 要加上 http,因为 upstream 并没有指定协议;
1 | upstream service { |
使用 Nginx 的 fastcgi_pass 指令,将匹配 location 的路径的请求转发到 upstream 块级指令代理的 HTTP 服务,同样采用轮询的方式;
所有的 fastcgi 服务在 upstream 中由 server 指令完成,server 指令接收 UNIX 套接字、IP 地址、FQDN 名及一些可选参数,参数下文会提及;
1 | stream { |
使用 Nginx 的 stream 块指令,它与 http 指令同一级别,写的时候要注意,在 ubuntu 系统中,http 块写在/etc/nginx/nginx.conf 中;因此笔者当时在/etc/nginx/nginx.conf 中添加的这段配置;
访问服务器的 3307 端口,测试 OK
root@VM-0-15-ubuntu:/etc/nginx# mysql -h 127.0.0.1 -P 3307 -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 27868
Server version: 5.7.23-0ubuntu0.16.04.1-log (Ubuntu)Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the current input statement.
mysql>
Nginx 能够广泛使用,不仅是因为它可以作为代理服务,它还提供了适应于不同业务的负载均衡算法以及判断目标服务的可用性等强大的功能;
最简单的算法,也是 Nginx 默认的负载均衡算法;
1 | upstream service { |
以上配置是在轮询的基础上,增加了权重的配置,在上面示例中,Nginx 会将三个请求中的两个分发到 8889 端口对应的服务,将另一个请求分发到本地的 8888 端口的服务,并将将 tbk.shiguofu.cn 上的服务作为备用,当分发请求失败会启用备份服务;
1 | upstream service { |
上面的 least_conn 指令为所负载的应用服务指定采用最少连接数负载均衡;
它会将访问请求分发到 upstream 所代理的服务中,当前打开连接数最少的应用服务器;它同时支持轮询中的 weight、max_fails、fail_timeout 选项,来决定给性能更好的应用服务器分配更多的访问请求;
1 | upstream service { |
该指令 least_time 仅仅在 NGINX PLUS 版本中支持,不多说。
分为通用散列算法与 ip 散列算法;
1 | upstream service { |
通过 hash 指令实现,根据请求或运行时提供的文本、变量或者其他变量的组合生成散列值;
一般情况, 在需要对访问请求进行负载可控,或将访问请求负载到已经有数据缓存的应用服务的场景下,该算法会非常有用;
需要注意的是,在 upstream 中有应用服务的加入或者删除时,会重新计算散列值进行分发;
1 | upstream service { |
指令 ip_hash 实现,通过计算客服端的 ip 地址来生成散列值。
Goroutine 是Golang语言的一大特色,Goroutine的出现,使得并发得到大幅提升。我们一起看下Goroutine在项目中的实践。
在业务开发中,会碰到几个相互独立的耗时操作,可以并行执行,这个时候Goroutine是很方便派上用场的。如下所示:
1 | // someOperation your work to do |
上面的案例需要我们知道协程的数量,然后等待所有协程结束,那如果我们不确定协程的个数或者我们需要设置固定个数的协程,该如何做呢?
其实也很简单,利用channel的阻塞特性,创建一个固定长度的channel,创建一个协程,在channel中写入一条数据,当channel被填满后,就会阻塞;协程结束后,从channle中消费一条数据,协程就又可以写入数据,如此可固定协程的数量。
1 | // wrapped for wait group |
如上代码所示,创建一个固定长度的channel,添加协程之前先往队列里增加一个占位符(struct{} 结构不占用内存,协程数量大时不会太占用内存),然后再调用真正的WaitGroup增加协程控制,执行完成后调用Done方法,从队列中取出占位符调用真正的WaitGroup的Done函数。
调用如下:
1 | swg := NewSizeWaitGroup(128) |
这样,协程的最大数量会保持在128个。
Golang提供的channel与Goroutine 提供很方便的通信与并发功能,在实际的业务开发中,可以很方便讲相互独立的功能并发处理,提高系统的吞吐量。
在业务开发过程中,经常会碰到需要搜索的需求;结合msyql在模糊搜索的时候,很明显会用到like语句,一般情况下,在数据量比较小的时候,按行检索的效率也不是很明显的低效,但当达到百万级,千万级数据量的时候,查询的效率低下是一回事,很可能把数据库拖垮,严重影响可用性。这个时候提高查询效率就显得很重要!
一般情况,我们在查找时候的写法(field肯定是已经建了索引):
1 | SELECT `column` FROM `table` WHERE `field` like '%keyword%'; |
上面的语句用explain解释来看,SQL语句并未使用索引,而是全表扫描,数据量比较大的时候,这效率可想而知。
对比下面的写法:
1 | SELECT `column` FROM `table` WHERE `field` like 'keyword%'; |
这样的写法用explain解释看到,SQL语句使用了索引,搜索的效率大大的提高了。
但是有的时候,我们在做功能需求的时候,并非要想查询的关键词都在开头,所以如果不是特别的要求,”keywork%”并不能适应所有的查找。
所以,我们需要另一种方法。
Mysql提供LOCATE函数,该方法返回查询字符串在被查询字段下的索引。第一个参数为要查询的字符串,第二个为数据库中的字段名称,第三个代表从字段对应的值的第几个字符串开始查找.
例, 有如下表:
1 | CREATE TABLE `meetings` ( |
以下sql则查询des字段匹配“hello”的行数。这个“hello” 在des中的可以是开头,可以是结尾,也可以是中间,非常方便。
1 | SELECT * from meetings where LOCATE('hello',`des`) > 0; |
LOCTATE这个函数有第三个参数,是查找的起始位置,比如可以在上面的sql中加入:
1 | SELECT * from meetings where LOCATE('hello',`des`, 5) > 0; |
我们使用explain来检查执行是否命中索引,会发现对搜索的字段如果存在索引,确实可以命中。
1 | mysql> explain SELECT * from meetings where LOCATE('hello',`des`) > 0\G |
如上explain的输出语句,确实使用了索引。
field
)position可以看做是locate的别名,功能跟locate一样。其实个人理解,position只是查找是否包含子串,不能指定位置开始。
1 | SELECT * from meetings where POSITION('hello' in `des`); |
str
,’substr’)1 | SELECT `column` FROM `table` WHERE INSTR(`field`, 'keyword' )>0 |
这个INSTR也是子串判断
FIND_IN_SET(str1,str2)
返回str2中str1所在的位置索引,其中str2必须以”,”分割开。
1 | SELECT * FROM person WHERE FIND_IN_SET('apply',name); |
name的内容是以逗号分隔的,如
1 | apple,pear,orange |
就可以使用FIND_IN_SET
1 | select * from `table` where FIND_IN_SET('apple', name); |
个人觉得这个主要是针对sql数组数据的查找; 数组数据以逗号分隔存储到一个字段,FIND_IN_SET可以快速找到包含数组元素的行。
Mysql在进行模糊查找需要注意,前缀匹配的时候会用到索引,前后都模糊,则无法使用索引;
可以使用Mysql提供的函数来“曲线救国”来命中索引。
grpc接口服务端拦截器,一般用来做一些预处理及后处理操作。如下,举两个常用的例子。
如下服务端的拦截器将gRPC client传入的数据放入gRPC的context中,接口中就可以使用ctx.Value去获取该数据。
1 | // MetaDataInterceptor get grpc server info, requestId/traceId/LogId |
如下所示,实现了一个记录接口耗时功能的拦截器,当然实际不会这么low。
1 | // API time elas time get grpc server info |
在golang的gRPC中,普通接口与stream接口的拦截器,需要分别实现。以上的拦截器只用于非stream的接口,对于stream接口,以上拦截器是不生效的。
流式拦截器函数签名如下:
1 | type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error |
查看流式拦截器可知,stream的context是在ServerStream中的,因此stream 要传递context 需要继承ServerStream并覆盖context。如下所示
1 | // WrappedStream wraps around the embedded grpc.ServerStream, and intercepts the Context |
实现该封装之后,就可以将上层的context获取并将元数据写入context后,调用NewWrappedStream传入gRPC的接口调用中。如下所示
1 | // stream method to get meta data |
gRPC客户端拦截器是在调用gRPC接口之前与之后执行的操作。比如,元数据需要在请求接口之前塞入到metaData中(http2.0Header),才会传递到gRPC的服务端。
如下,将当前接口context中的数据放入header中传入服务端。
1 | // request grpc service with requestId/traceId info. |
Stream client的实现也是比较简单的,与服务端不同的是,客户端的流式拦截器不需要封装一层,可以直接使用。
如下,同样实现了元数据传递到服务端的拦截器。
1 | // MetaStreamClientInterceptor get grpc client info, requestId/traceId/LogId for grpc stream server |
拦截器为gRPC的服务端/客户端复用公共模块提供了一种很简单方便的方法,只需要实现对应的拦截器函数,在服务端启动或者客户端连接的时候作为选项传入即可(自行搜索)。
需要注意的是,在Golang中,拦截器分为普通接口与流式接口的拦截器,需要分别实现。
gRPC中拦截器流式接口拦截器需要实现如下签名的函数,有兴趣可深入了解下。例子如上所示
1 | // StreamServerInterceptor provides a hook to intercept the execution of a streaming RPC on the server. |
注意streamServer拦截器如果需要传递context,需要将ServerStream进行封装,覆盖Context 函数
普通方法的拦截器实现比较简单,实现如下签名函数
1 | // @params ctx: grpc context |
golang在调用grpc之前执行的公共的操作,比如要把requestId塞到header中。
1 | // @params method: the RPC name |
实现如下签名的函数即可
1 | // @params desc: contains a description of the stream |
以上即为Golang的拦截器实现,可以分为服务端与客户端的拦截器,两端分别有流式拦截器与普通接口拦截器,在使用的时候可根据自己的业务需求实现。
先搬一个官网的定义。
Elasticsearch is a real-time, distributed storage, search, and analytics engine
Elasticsearch 是一个实时的分布式存储、搜索、分析的引擎。
要想了解它是什么,首先得看他能干什么,概念很清晰: 分布式存储/搜索/分析引擎。
的确,这样做的确可以, mysql也支持全文检索。但是有个问题: like %% 是不走索引的,这就意味着: 数据量非常大的时候,我们的查询肯定是秒级的。
类似搜索引擎,输入往往是多种多样的,不同的人有不同的表达方式,但实际 都是一个含义,数据库的准确性不高,效率低下,高并发下,数据库会被拖垮。
ElasticSearch 是专门做搜索的,就是为了在理解用户输入语义并高效搜索匹配度高的文档记录。
ElasticSearch是基于Lucene库的,Lucene数据只有刷新到磁盘,才可以被检索到,内存缓存中的数据只有刷新到磁盘才可以被检索。ElasticSearch默认是每秒刷新一次,也就是文档的变化会在一秒之后可见。因此近实时搜索。也可根据自己的需求设置刷新频率。
海量数据单机无法存储,就需要使用集群,将多个节点组织在一起,共同维护所有数据,共同提供索引和搜索功能。
一个节点就是集群中的一个服务器,存储部分数据,参与索引与搜索。
一个索引可以存储超出单个结点硬件限制的大量数据,为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。为保证单点故障,一个分片会保存不止一份,可分为一个主分片(primary shard)与多个*复制分片(replica shard) *,复制分片的数量可动态调整,复制分片也可用来提升系统 的读性能。
一个文档是一个可被索引的基础信息单元。文档以JSON(Javascript Object Notation)格式来表示。
一个索引就是一个拥有几分相似特征的文档的集合。
索引类型是在一个索引中,不同类型的数据类型。一条文档中有(type)字段用来区分索引类型,es7.x以上取消同一个索引中存在不同索引类型的数据,也就是说,(_type)字段固定,默认为_doc。
如下,在7.x之前的ES可以在一个索引中创建不同索引类型的数据:
1 | curl -XPOST localhost:9200/indexname/typename -H 'Content-Type:application/json' -d '{"data": 1234}' |
ES对外提供RestFul API来读写集群,设置集群,获取集群状态操作。
集群状态
GET /_cluster/health
1 | curl http://localhost:9200/_cluster/health --user xx:xxxx |
1 | curl http://localhost:9200/_cat/nodes?v --user xxx:xxxx |
结果与_cluster/health一致
1 | curl --user elastic:4j243cNvO1770iCs http://10.1.1.45:9200/_cat/health?v |
1 | curl --user elastic:4j243cNvO1770iCs http://10.1.1.45:9200/_cat/allocation?v |
1 | curl http://localhost:9200/_cat/indices?pretty --user xx:xxxx |
1 | curl http://localhost:9200/[index_name]/_settings |
1 | curl http://localhost:9200/[index_name]/_mapping --user xx:xxx |
1 | curl -H "Content-Type: application/json" -XPUT localhost:9200/blogs -d ' |
- 主分片在索引创建以后就固定了,不可更改,如要修改可重建索引,将数据reindex过去;
- 副本分片最大值是 n-1(n为节点个数),复制分片可随时修改个数
1
2
3
4
5 curl -H "Content-Type: application/json" -XPUT localhost:9200/blogs/_settings -d '
{
"number_of_replicas": 2
}'
1 | curl -H "Content-Type: application/json" -XPOST localhost:9200/_reindex -d ' |
1 | curl -H "Content-Type: application/json" -XDELETE localhost:9200/[indexname] |
1 | POST http://localhost:9200/indexname/_search |
1 | curl -XPOST http://localhost:9200/indexname/_search -H "Content-Type:application/json" -d '{"query":{"match_all":{} } }' |
1 | curl -XPOST http://localhost:9200/indexname/_search -H "Content-Type:application/json" -d '{"query":{"constant_score":{"filter":{"term":{"price":549} } } } }' |
1 | curl -XPOST http://localhost:9200/indexname/_search -H "Content-Type:application/json" -d '{"query":{"term":{"title":"java"} } }' |
1 | curl -XPOST http://localhost:9200/indexname/_search -H "Content-Type:application/json" -d '{"query":{"match":{"title":"Core Java"} } }' |
1 | curl -XPOST http://localhost:9200/indexname/_search -H "Content-Type:application/json" -d |
1 | "dynamic_templates": [ |
1 | put myIndex |
match和unmatch定义应用于filedname的pattern。
定义一个匹配所有以long_开头且不以_text结束的string类型的模板
1 | PUT my_index |
1 | curl -XPOST http://10.1.1.12:9200/_template/default@template --user elastic:b6fBNAapGEcYz2dt -H "Content-Type:application/json" -d '{ |
1 | # register a snapshot repository |
location:my_fs_backup_location 路径必须先在elasticsearch.yaml中配置path.repo
1 | path.repo: /opt/backup_es |
location |
Location of the snapshots. Mandatory. |
compress |
Turns on compression of the snapshot files. Compression is applied only to metadata files (index mapping and settings). Data files are not compressed. Defaults to true . |
chunk_size |
Big files can be broken down into chunks during snapshotting if needed. Specify the chunk size as a value and unit, for example: 1GB , 10MB , 5KB , 500B . Defaults to null (unlimited chunk size). |
max_restore_bytes_per_sec |
Throttles per node restore rate. Defaults to 40mb per second. |
max_snapshot_bytes_per_sec |
Throttles per node snapshot rate. Defaults to 40mb per second. |
readonly |
Makes repository read-only. Defaults to false . |
SLM
elasticsearch.yml增加如下配置
1 | xpack.security.enabled: true |
重新启动es, 执行
1 | bin/elasticsearch-setup-passwords interactive |
这里需要为4个用户分别设置密码,elastic, kibana, logstash_system,beats_system,交互输入密码。
修改密码:
1 | curl -H "Content-Type:application/json" -XPOST -u elastic 'http://127.0.0.1:9200/_xpack/security/user/elastic/_password' -d '{ "password" : "123456" }' |
数据索引后并不会马上搜索到,需要刷新后才能被搜索的,这个选项设置索引后多久会被搜索到。
1 | curl -XGET 'localhost:9200/_cat/shards?h=index,shard,prirep,state,unassigned.reason' | grep UNASSIGNED |
输出:
1 | xxxxx 0 r UNASSIGNED INDEX_CREATED |
展示出所有未分配的分片的列表
1 | curl -XGET 'localhost:9200/_cluster/allocation/explain?pretty' -H 'Content-Type:application/json' -d'{"index": "xxxxx", "shard": 0, "primary":false}' |
输出:(未记录输出)
会给出集群中所有节点不能分配的原因。
如果是磁盘空间不足,删除不必要的索引。对于其他原因,可根据情况解决不能分配的原因。比如下面几个常见的原因。
a. cluster.max_shards_per_node默认为1000,节点分片已经达到最大。
b. 磁盘空间达到配置的阈值,比如磁盘已经达到80%,不会继续分配分片。
c. 分片设置的节点必须是hot节点。
可通过如下接口查看当前磁盘分配配置:
1 | curl -XGET _cluster/settings?include_defaults=true&flat_settings=true&pretty |
输出(输出太多截取一部分):
1 | { |
适用于单索引并不断增长,可设置ILM rollover,根据大小或者文档条数拆分.
对于按天索引,可配置删除阶段规则.
创建ILM策略(hot/warm/cold/delete)
创建索引模板,指定ILM的范围
创建rollover的索引,名称末尾要是数字,这样rollover就会+1, 如:carlshi-00001;配置is_write_index选项
原索引写入数据
For Example:
1 | 创建索引模板 |
创建索引:
1 | 创建第一个索引 |
直接运行elasticsearch,会自动拉去镜像并执行;
1 | docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.5.1 -v /usr/share/elasticsearch/data:/usr/share/elasticsearch/data |
运行成功后,执行curl,获取基本信息
1 | curl localhost:9200 |
ElasticSearch是一款强大的全文检索工具,他提供REST API使得使用ElasticSearch非常简单,对数据做了很强的高可用,也可根据自己的需求配置不同级别的高可用、高性能全文检索工具。
本篇主要讲解对ElasticSearch的常用模块做了简单的介绍,索引的基本属性基本操作(增删改查),动态索引模式模板,快照备份,索引生存周期;还记录了集群黄色的排查方向。以后逐步深入各个模块的配置甚至内部实现原理。
Logstash是一个开源数据收集引擎,具有实时管道功能。Logstash将来自不同数据源的数据统一搜集起来,并根据需求将数据标准化输出到你所选择的目的地。如下图所示。
Logstash可以从多个数据源获取数据,并对其进行处理、转换,最后将其发送到不同类型的“存储”
采集各种样式、大小和来源的数据
分布式系统中,数据往往是以各种各样的形式(结构化、非结构话)存在于不同的节点中。Logstash 支持不同数据源的选择 ,日志、报表、数据库的内容等等。可以在同一时间从众多常用来源捕捉事件。
1 | input{ |
1 | input{ |
主要是接受filebeats的数据导入
1 | input { |
实时解析和转换数据
数据从源传输到存储库的过程中,需要对不同的数据进行不同的存储,Logstash 过滤器能够解析每条记录,识别每条数据的字段内容,并将它们转换成自定义数据,以便进行处理分析计算。
Logstash 动态地转换和解析数据,支持各种格式或复杂度数据的解析:
尽管 ES是logstash的常用输出方向,能够为我们的搜索和分析带来无限可能,但它并非唯一选择。
Logstash 提供众多输出选择,您可以将数据发送到您要指定的地方,并且能够灵活地解锁众多下游用例。
安装比较简单,官网直接有现成的二进制包,下载地址: https://artifacts.elastic.co/downloads/logstash/logstash-7.10.1-linux-x86_64.tar.gz
安装也比较简单,解压设置path即可使用。
本人经常使用,就写了个安装elk的脚本,需要的可以拿去使用:https://github.com/shiguofu2012/scripts/blob/master/install_elk.sh。
Logstash配置有两个必需的元素,输入和输出,以及一个可选过滤器。输入插件从数据源那里消费数据,过滤器插件根据你的期望修改数据,输出插件将数据写入目的地。
1 | [root@VM-145-82-centos ~]# logstash -e 'input { stdin {} } output { stdout {} }' |
从标准输入获取数据,输出到标准输出。
1 | input { |
总体来讲,input/output是比较容易配置的,关键是对数据进行格式化。
grok 匹配非格式化字段,提取字段格式化数据,强大的文本解析工具,支持正则表达式
1 | grok { |
1 | filter { |
解析出来的数据:
1 | { |
1 | filter { |
1 | filter { |
1 | filter { |
这里介绍一个曾经搭建的ELK日志系统。
结构比较简单,kubetnets中filebeat damonSet方式运行,搜集所有container 标准输出的日志,并传入logstash中,logstash将数据导入elasticsearch。结构图如下所示:
下面开始logstash的配置:
input比较简单,使用filebeat搜集日志,传入logstash
1 | input { |
output增加了几个条件判断,根据不同的字段日志类型,索引到不同的es索引中;如下所示
1 | output { |
filter 配置,不同的日志格式,输出格式化的数据
1 | filter { |
总之 ,logstash具备强大的功能,将不同数据源的数据经过清洗格式化,转化为结构化的数据,存储到不同的存储单元。
gRPC是什么可以用官网的一句话来概括
A high-performance, open-source universal RPC framework
所谓RPC(remote procedure call 远程过程调用)框架实际是提供了一套机制,使得应用程序之间可以进行通信,而且也遵从server/client模型。使用的时候客户端调用server端提供的接口就像是调用本地的函数一样。如下图所示就是一个典型的RPC结构图。
既然是server/client模型,那么我们直接用restful api不是也可以满足吗,为什么还需要RPC呢?
gRPC和restful API都提供了一套通信机制,用于server/client模型通信,而且它们都使用http作为底层的传输协议(严格地说, gRPC使用的http2.0,而restful api则不一定)。不过gRPC还是有些特有的优势,如下:
微服务遍地都是,一个功能,一个接口都可能是一个微服务,微服务之间的调用混乱,无法追踪,很难找出瓶颈点,因此迫切需要一种方法来追踪服务之间的调用链路。
Metadata 可以理解为一个 HTTP 请求的 Header(它的底层实现就是 HTTP/2 的 Header),用户可以通过访问和修改每个 gRPC Call 的 Metadata 来传递额外的信息:比如认证信息,比如用于追踪的 Request ID。
Interceptor 有点类似于我们平时常用的 HTTP Middleware,不同的是它可以用在 Client 端和 Server 端。比如在收到请求之后输出日志,在请求出现错误的时候输出错误信息,比如获取请求中设置的 Request ID。
1 | // UnaryInvoker is called by UnaryClientInterceptor to complete RPCs. |
Golang 的实现是把 Metadata 塞在了 context 里面,只需要使用 metadata.FromOutgoingContext(ctx)
和 metadata.FromIncomingContext(ctx)
就能够访问本次请求的 Metadata。概念清楚之后代码应该非常好写了:
1 | const requestIdKey = "requestId" |