Nginx/LVS/HAProxy负载均衡软件的优缺点详解(转)

Nginx/LVS/HAProxy负载均衡软件的优缺点详解(转)

原文链接:http://www.ha97.com/5646.html,由于工作中遇到负载均衡系统的实现,记录笔记。

PS:Nginx/LVS/HAProxy是目前使用最广泛的三种负载均衡软件,本人都在多个项目中实施过,参考了一些资料,结合自己的一些使用经验,总结一下。

一般对负载均衡的使用是随着网站规模的提升根据不同的阶段来使用不同的技术。具体的应用需求还得具体分析,如果是中小型的Web应用,比如日PV小于1000万,用Nginx就完全可以了;如果机器不少,可以用DNS轮询,LVS所耗费的机器还是比较多的;大型网站或重要的服务,且服务器比较多时,可以考虑用LVS。

一种是通过硬件来进行进行,常见的硬件有比较昂贵的F5和Array等商用的负载均衡器,它的优点就是有专业的维护团队来对这些服务进行维护、缺点就是花销太大,所以对于规模较小的网络服务来说暂时还没有需要使用;另外一种就是类似于Nginx/LVS/HAProxy的基于Linux的开源免费的负载均衡软件,这些都是通过软件级别来实现,所以费用非常低廉。

目前关于网站架构一般比较合理流行的架构方案:Web前端采用Nginx/HAProxy+Keepalived作负载均衡器;后端采用MySQL数据库一主多从和读写分离,采用LVS+Keepalived的架构。当然要根据项目具体需求制定方案。
下面说说各自的特点和适用场合。

一、Nginx

Nginx的优点是:

1、工作在网络的7层之上,可以针对http应用做一些分流的策略,比如针对域名、目录结构,它的正则规则比HAProxy更为强大和灵活,这也是它目前广泛流行的主要原因之一,Nginx单凭这点可利用的场合就远多于LVS了。
2、Nginx对网络稳定性的依赖非常小,理论上能ping通就就能进行负载功能,这个也是它的优势之一;相反LVS对网络稳定性依赖比较大,这点本人深有体会;
3、Nginx安装和配置比较简单,测试起来比较方便,它基本能把错误用日志打印出来。LVS的配置、测试就要花比较长的时间了,LVS对网络依赖比较大。
3、可以承担高负载压力且稳定,在硬件不差的情况下一般能支撑几万次的并发量,负载度比LVS相对小些。
4、Nginx可以通过端口检测到服务器内部的故障,比如根据服务器处理网页返回的状态码、超时等等,并且会把返回错误的请求重新提交到另一个节点,不过其中缺点就是不支持url来检测。比如用户正在上传一个文件,而处理该上传的节点刚好在上传过程中出现故障,Nginx会把上传切到另一台服务器重新处理,而LVS就直接断掉了,如果是上传一个很大的文件或者很重要的文件的话,用户可能会因此而不满。
5、Nginx不仅仅是一款优秀的负载均衡器/反向代理软件,它同时也是功能强大的Web应用服务器。LNMP也是近几年非常流行的web架构,在高流量的环境中稳定性也很好。
6、Nginx现在作为Web反向加速缓存越来越成熟了,速度比传统的Squid服务器更快,可以考虑用其作为反向代理加速器。
7、Nginx可作为中层反向代理使用,这一层面Nginx基本上无对手,唯一可以对比Nginx的就只有lighttpd了,不过lighttpd目前还没有做到Nginx完全的功能,配置也不那么清晰易读,社区资料也远远没Nginx活跃。
8、Nginx也可作为静态网页和图片服务器,这方面的性能也无对手。还有Nginx社区非常活跃,第三方模块也很多。

淘宝的前端使用的Tengine就是基于nginx做的二次开发定制版。

Nginx常规的HTTP请求和响应流程图:

nginx

Nginx的缺点是:
1、Nginx仅能支持http、https和Email协议,这样就在适用范围上面小些,这个是它的缺点。
2、对后端服务器的健康检查,只支持通过端口来检测,不支持通过url来检测。不支持Session的直接保持,但能通过ip_hash来解决。

二、LVS

LVS:使用Linux内核集群实现一个高性能、高可用的负载均衡服务器,它具有很好的可伸缩性(Scalability)、可靠性(Reliability)和可管理性(Manageability)。

LVS的优点是:
1、抗负载能力强、是工作在网络4层之上仅作分发之用,没有流量的产生,这个特点也决定了它在负载均衡软件里的性能最强的,对内存和cpu资源消耗比较低。
2、配置性比较低,这是一个缺点也是一个优点,因为没有可太多配置的东西,所以并不需要太多接触,大大减少了人为出错的几率。
3、工作稳定,因为其本身抗负载能力很强,自身有完整的双机热备方案,如LVS+Keepalived,不过我们在项目实施中用得最多的还是LVS/DR+Keepalived。
4、无流量,LVS只分发请求,而流量并不从它本身出去,这点保证了均衡器IO的性能不会收到大流量的影响。
5、应用范围比较广,因为LVS工作在4层,所以它几乎可以对所有应用做负载均衡,包括http、数据库、在线聊天室等等。

LVS DR(Direct Routing)模式的网络流程图:

lvs_dr

LVS的缺点是:
1、软件本身不支持正则表达式处理,不能做动静分离;而现在许多网站在这方面都有较强的需求,这个是Nginx/HAProxy+Keepalived的优势所在。
2、如果是网站应用比较庞大的话,LVS/DR+Keepalived实施起来就比较复杂了,特别后面有Windows Server的机器的话,如果实施及配置还有维护过程就比较复杂了,相对而言,Nginx/HAProxy+Keepalived就简单多了。

三、HAProxy

HAProxy的特点是:
1、HAProxy也是支持虚拟主机的。
2、HAProxy的优点能够补充Nginx的一些缺点,比如支持Session的保持,Cookie的引导;同时支持通过获取指定的url来检测后端服务器的状态。
3、HAProxy跟LVS类似,本身就只是一款负载均衡软件;单纯从效率上来讲HAProxy会比Nginx有更出色的负载均衡速度,在并发处理上也是优于Nginx的。
4、HAProxy支持TCP协议的负载均衡转发,可以对MySQL读进行负载均衡,对后端的MySQL节点进行检测和负载均衡,大家可以用LVS+Keepalived对MySQL主从做负载均衡。
5、HAProxy负载均衡策略非常多,HAProxy的负载均衡算法现在具体有如下8种:
① roundrobin,表示简单的轮询,这个不多说,这个是负载均衡基本都具备的;
② static-rr,表示根据权重,建议关注;
③ leastconn,表示最少连接者先处理,建议关注;
④ source,表示根据请求源IP,这个跟Nginx的IP_hash机制类似,我们用其作为解决session问题的一种方法,建议关注;
⑤ ri,表示根据请求的URI;
⑥ rl_param,表示根据请求的URl参数’balance url_param’ requires an URL parameter name;
⑦ hdr(name),表示根据HTTP请求头来锁定每一次HTTP请求;
⑧ rdp-cookie(name),表示根据据cookie(name)来锁定并哈希每一次TCP请求。

四、总结

Nginx和LVS对比的总结:
1、Nginx工作在网络的7层,所以它可以针对http应用本身来做分流策略,比如针对域名、目录结构等,相比之下LVS并不具备这样的功能,所以Nginx单凭这点可利用的场合就远多于LVS了;但Nginx有用的这些功能使其可调整度要高于LVS,所以经常要去触碰触碰,触碰多了,人为出问题的几率也就会大。
2、Nginx对网络稳定性的依赖较小,理论上只要ping得通,网页访问正常,Nginx就能连得通,这是Nginx的一大优势!Nginx同时还能区分内外网,如果是同时拥有内外网的节点,就相当于单机拥有了备份线路;LVS就比较依赖于网络环境,目前来看服务器在同一网段内并且LVS使用direct方式分流,效果较能得到保证。另外注意,LVS需要向托管商至少申请多一个ip来做Visual IP,貌似是不能用本身的IP来做VIP的。要做好LVS管理员,确实得跟进学习很多有关网络通信方面的知识,就不再是一个HTTP那么简单了。
3、Nginx安装和配置比较简单,测试起来也很方便,因为它基本能把错误用日志打印出来。LVS的安装和配置、测试就要花比较长的时间了;LVS对网络依赖比较大,很多时候不能配置成功都是因为网络问题而不是配置问题,出了问题要解决也相应的会麻烦得多。
4、Nginx也同样能承受很高负载且稳定,但负载度和稳定度差LVS还有几个等级:Nginx处理所有流量所以受限于机器IO和配置;本身的bug也还是难以避免的。
5、Nginx可以检测到服务器内部的故障,比如根据服务器处理网页返回的状态码、超时等等,并且会把返回错误的请求重新提交到另一个节点。目前LVS中 ldirectd也能支持针对服务器内部的情况来监控,但LVS的原理使其不能重发请求。比如用户正在上传一个文件,而处理该上传的节点刚好在上传过程中出现故障,Nginx会把上传切到另一台服务器重新处理,而LVS就直接断掉了,如果是上传一个很大的文件或者很重要的文件的话,用户可能会因此而恼火。
6、Nginx对请求的异步处理可以帮助节点服务器减轻负载,假如使用apache直接对外服务,那么出现很多的窄带链接时apache服务器将会占用大 量内存而不能释放,使用多一个Nginx做apache代理的话,这些窄带链接会被Nginx挡住,apache上就不会堆积过多的请求,这样就减少了相当多的资源占用。这点使用squid也有相同的作用,即使squid本身配置为不缓存,对apache还是有很大帮助的。
7、Nginx能支持http、https和email(email的功能比较少用),LVS所支持的应用在这点上会比Nginx更多。在使用上,一般最前端所采取的策略应是LVS,也就是DNS的指向应为LVS均衡器,LVS的优点令它非常适合做这个任务。重要的ip地址,最好交由LVS托管,比如数据库的 ip、webservice服务器的ip等等,这些ip地址随着时间推移,使用面会越来越大,如果更换ip则故障会接踵而至。所以将这些重要ip交给 LVS托管是最为稳妥的,这样做的唯一缺点是需要的VIP数量会比较多。Nginx可作为LVS节点机器使用,一是可以利用Nginx的功能,二是可以利用Nginx的性能。当然这一层面也可以直接使用squid,squid的功能方面就比Nginx弱不少了,性能上也有所逊色于Nginx。Nginx也可作为中层代理使用,这一层面Nginx基本上无对手,唯一可以撼动Nginx的就只有lighttpd了,不过lighttpd目前还没有能做到 Nginx完全的功能,配置也不那么清晰易读。另外,中层代理的IP也是重要的,所以中层代理也拥有一个VIP和LVS是最完美的方案了。具体的应用还得具体分析,如果是比较小的网站(日PV小于1000万),用Nginx就完全可以了,如果机器也不少,可以用DNS轮询,LVS所耗费的机器还是比较多的;大型网站或者重要的服务,机器不发愁的时候,要多多考虑利用LVS。

现在对网络负载均衡的使用是随着网站规模的提升根据不同的阶段来使用不同的技术:

第一阶段:利用Nginx或HAProxy进行单点的负载均衡,这一阶段服务器规模刚脱离开单服务器、单数据库的模式,需要一定的负载均衡,但是仍然规模较小没有专业的维护团队来进行维护,也没有需要进行大规模的网站部署。这样利用Nginx或HAproxy就是第一选择,此时这些东西上手快, 配置容易,在七层之上利用HTTP协议就可以。这时是第一选择。

第二阶段:随着网络服务进一步扩大,这时单点的Nginx已经不能满足,这时使用LVS或者商用Array就是首要选择,Nginx此时就作为LVS或者Array的节点来使用,具体LVS或Array的是选择是根据公司规模和预算来选择,Array的应用交付功能非常强大,本人在某项目中使用过,性价比也远高于F5,商用首选!但是一般来说这阶段相关人才跟不上业务的提升,所以购买商业负载均衡已经成为了必经之路。

第三阶段:这时网络服务已经成为主流产品,此时随着公司知名度也进一步扩展,相关人才的能力以及数量也随之提升,这时无论从开发适合自身产品的定制,以及降低成本来讲开源的LVS,已经成为首选,这时LVS会成为主流。
最终形成比较理想的基本架构为:Array/LVS — Nginx/Haproxy — Squid/Varnish — AppServer

lvs_dr

linux使用jmeter进行压力测试

linux使用jmeter进行压力测试

安装

export PATH=/usr/local/jmeter/bin/:$PATH 添加到/etc/profile末尾。

1
`$ wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-3.3.tgz$ tar -xvf apache-jmeter-3.3.tgz$ mv apache-jmeter-3.3.tgz jmeter$ mv jmeter /usr/local`

IK分词器深入学习(1)-分词理论

背景介绍

词是表达语义的最小单位,分词是搜索引擎的基石。

在搜索引擎中,倒排索引就是由一个个Term所构成,分词器通过对自然语言的理解,拆分出Term。分词的输入是一串连续的文字,对于英文”girl is beautiful.boy is handsome.”分词比较容易,可以根据空格和标点符号分割,那最终会分为girl/is/beautiful/boy/is/handsome(一般来说,is作为停用词),是不是很容易?但这种方式在中文中却不行,”女孩美丽,男孩帅气。”这句话应该分为”女孩/美丽/男孩/帅气”,不是简单的靠空格和符号就能实现的。

上例提出了中文分词的背景,那么总要有理论来解决中文分词的问题。经过几十年的研究,中文分词方法也取得了长足的发展,产生了众多的分词方法,主要分为基于字符串匹配的分词方法基于统计的分词方法基于理解的分词方法三大流派。

##基于字符串匹配的分词方法

基于字符串匹配的分词算法也叫做机械分词算法,一般都需要事先建立足够大的分词词典,然后将待分词文本中的字符串与分词词典中的词进行匹配,如果匹配成功,该字符串当做一个词从待分词文本中切分出来,否则不切分。

下面介绍下主流的字符串匹配算法

  • 最大正向匹配分词算法(Forward Maximum Matching,FMM)

    这是字符串匹配分词算法中最基本的一种分词算法,统计结果表明,单纯使用最大正向匹配算法的错误率为1/169。优点是原理简单,切分速度快,易与实现。缺点是歧义处理不精确。

  • 最大逆向匹配分词算法(Reverse Maximum Matching,RMM)

    与FMM算法的基本思想相同,不同的是该方法从待切分的汉字串的末位开始处理,每次不匹配时去掉最前面的一个汉字,继续匹配。优点同FMM一样,而且最大逆向匹配算法对交集型歧义字段的处理精度比正向的要高,错误率在1/245左右,缺点是不能完全排除歧义现象。

  • 双向最大匹配算法(Bi-directction Matching method,BMM)

    双向匹配算法基本原理是同时进行FMM和RMM扫描,然后分析两种扫描的结果。如果两种扫描结果一致,则认为不存在歧义现象;如果不一致,则需要定位到歧义字段处理。优点是提高了分词的准确率,消除了部分歧义现象。缺点是算法执行要做双向扫描,时间复杂度会有所增加。

  • 全切分算法

    全切分算法是指切分出字符串中所有可能的词语,该算法可以大大提高切分的召回率,识别出所有可能的歧义现象。但缺点是算法复杂度比较高。

总结:

字符串匹配分词方法,逻辑简单清晰,易实现,分词性能较高。但是严重依赖字典,如果某些词在字典中未登陆,则会对分词的精度产生较大的影响。另外算法的效率很大层面体现在字典的数据结构上,如果用Hash表的方式,检索最快但是内存占用大,一般情况下会牺牲部分性能来降低词典的容量比如TRIE索引树词典、逐词二分词典、整词二分词典。

基于统计的分词方法

下面介绍下统计分词的常用分词方法

  • N元文法模型(N-gram)

    令C=C1C2…Cm.C 是待切分的汉字串,W=W1W2…Wn.W 是切分的结果。
    设P(WlC)是汉字串C切分为W的某种估计概率。Wa,Wb,⋯.Wk是C的所有可能的切分方案。那么,基于统计的切分模型就是这样的一种分词模型,它能够找到目的词串W ,使得W 满足:
      P(W|C)=MAX(P(Wa|C),P(Wb|C)…P(Wk|C)),
    即估计概率为最大之词串。我们称函数P(W|C)为评价函数。一般的基于统计的分词模型的评价函数,都是根据贝叶斯公式.同时结合系统本身的资源限制,经过一定的简化,近似得来的。

    根据贝叶斯公式, 有:P(W|C)=P(W) P(C|W)/P(C),对于C的多种切分方案,P(C)是一常数,而P(C|W)是在给定词串的条件下出现字串C的概率,故P(C|W)=1。所以 ,我们用P(W)来代替P(W|C)。那么,如何估计P(W)呢?最直接的估计P(W)的方法利用词的n-gram,即:
      P(W)=P(W1) P(W2lW1) P(W3|W1W2)⋯P(Wk|W1,W2…Wk-1)
      但是,由于当前的计算机技术和我们现有的语料资源所限,这种方法存在致命的缺陷:

      ①对于有6万词的词典而言,仅词和词的bigram就可能需要60000 x 60000=3600M的统计空间,这是当前的计算机硬件水平所难以接受的,更不要说更大的n-gram 了。
      ②需要与上述空间相当的熟语料,否则就会产生训练语料不足所产生的数据稀疏问题。
      ③由于不同领域的语料库的用词有所差异,针对某一个领域的语料库统计出来的n-gram,若用于其它领域,其效果难以预料,而目前通过语料库搭配来克服领域差民间的方法尚未成熟。

      因此,利用词的n-gram 直接估计P(W)的方法,在目前是不可行的。基于上述的原因,大多数基于统计的分词模型都没有直接采用上述公式,而是采用各种各样的估计方法,从不同的角度,实现对P(W)的近似。

  • 隐马尔可夫模型(Hidden Markov Model,HMM)

##

Java-Provider一种写法

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class Test {

public static void main(String[] args) {
Map<String, NodeProvider<Node>> map = new HashMap<>();
map.put("ik", TestInner::getNode);

System.out.println(map.get("ik").get("name", 1).getName());
System.out.println(map.get("ik").get("name", 200).getAge());

}
}

interface NodeProvider<T> {
T get(String name, int age);
}

class Node {
private String name;
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

class TestInner {
public static Node getNode(String name, int age) {
Node node = new Node();
node.setAge(age);
node.setName(name);

return node;
}
}

jenkins自动化部署实践

jenkins自动化部署实践

1、依赖技术

  • jenkins:Jenkins是一款开源 CI&CD 软件,用于自动化各种任务,包括构建、测试和部署软件。Jenkins 支持各种运行方式,可通过系统包、Docker 或者通过一个独立的 Java 程序。
  • tomcat:web应用服务器
  • Jdk8
  • nexus:Maven的远程仓库
  • Shell

2、实现流程

1、在一台服务器部署jenkins和maven

2、部署nexus远程仓库

3、mvn clean deploy -P online 打包到maven私服

4、更新最新的shell脚本

5、调用启动脚本

img

参考文献

jenkins官网

nexus官网

nexus搭建maven私服

ElasticSearch之updateByQuery使用

#背景

有时需要通过某些查询条件,更新文档某一个字段,这样的诉求,用SQL表达就是update t_student set class=’优秀的小男生’ where age = 10 and score = 100 and sex = 1。ES有updateByQuery API 和 deleteByQuery API,这样一条DSL就可以搞定类似的需求。

例子

批量更新id = 9627361的文档,admin_rank数值++,address更新成”我是_update_by_query”,name_alias_array字段,更新为一个集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST hotel_20190513/hotel/_update_by_query
{
"script": {
"inline": "ctx._source.admin_rank++;ctx._source['address']='我是_update_by_query';ctx._source['name_alias_array']=['哈哈','hei嘿']",
"lang": "painless"
},
"query": {
"bool": {"must": [
{"term": {
"id": {
"value": 9627361
}
}}
]}
}
}

删除entity_type = hotel_name的所有文档。

1
2
3
4
5
6
7
8
9
10
POST entity_hotel_20190528/entity_hotel/_delete_by_query
{
"query": {
"term": {
"entity_type": {
"value": "hotel_name"
}
}
}
}

golang例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
updateByQuery := client.UpdateByQuery().Query(elastic.NewTermQuery("entity_id", node.TagId)).Script(elastic.NewScriptInline(script)).Index(index).Type(indexType)
out, err := updateByQuery.Do(context.TODO())
if err != nil {
panic(err)
}
b, err := json.Marshal(out)
if err != nil {
panic(err)
}
got := string(b)
updated := tool.GoJson(got).Get("updated").ToString()
if tool.IsEmpty(updated) {
updated = "0"
}
println("执行成功.tagId=" + tool.ToString(node.TagId) + "更新" + updated + "个文档.")

参考文档

官方文档

lucene系列-第一章-lucene包结构及工作流程

lucene包结构及工作流程

Lucene的analysis模块主要负责词法分析及语言处理而形成Term。
Lucene的index模块主要负责索引的创建,里面有IndexWriter。
Lucene的store模块主要负责索引的读写。
Lucene的QueryParser主要负责语法分析。
Lucene的search模块主要负责对索引的搜索。
Lucene的similarity模块主要负责对相关性打分的实现。

img

包名 功能
org.apache.lucene.analysis 语言分析器,主要用于的切词,支持中文主要是扩展此类
org.apache.lucene.document 索引存储时的文档结构管理,类似于关系型数据库的表结构
org.apache.lucene.index 索引管理,包括索引建立、删除等
org.apache.lucene.queryParser 查询分析器,实现查询关键词间的运算,如与、或、非等
org.apache.lucene.search 检索管理,根据查询条件,检索得到结果
org.apache.lucene.store 数据存储管理,主要包括一些底层的I/O操作
org.apache.lucene.util 一些公用类

ElasticSearch集群的搭建

#相关技术点

in-memory buffer是通过refresh操作写到文件缓冲区的,refresh的过程除了在文件缓冲区生成这个文件以外,还会让底层Lucene的index reader打开这个文件,让新生成的这个文件对搜索可见。

周五的时候跟同事讨论下了,不知道这样理解对不对–refresh类似于 outputstream flush(此时数据从 java heap 写到 memory buffer),之后(按照大神上面说的)index reader读取新文件,然后等待触发flush(这个则类似于 outputstream close,此时数据从 memory buffer 写到 disk),请大神赐教

集群配置

集群包括3台物理机共12个节点,每台物理机部署1个Master节点,1个Client节点,2个Data节点。

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
32
33
34
35
36
37
38
# ---------------------------------- Cluster -----------------------------------
#集群名称
cluster.name:
# ------------------------------------ Node ------------------------------------
#节点名称
node.name:
#是否是Master Node
node.master: true
#是否是Data Node
node.data: false
#是否是Ingest Node
node.ingest: false
node.attr.rack: first
# ----------------------------------- Paths ------------------------------------
# Path to log files:
#可以指定es的数据存储目录,默认存储在es_home/data目录下
#path.data: /path/to/data
#可以指定es的日志存储目录,默认存储在es_home/logs目录下
#path.logs: /path/to/logs
# ----------------------------------- Memory -----------------------------------
#锁定物理内存地址,防止elasticsearch内存被交换出去,也就是避免es使用swap交换分区
bootstrap.memory_lock: true
#CentOS下不支持SecComp,故而需要设置成false
bootstrap.system_call_filter: false
# ---------------------------------- Network -----------------------------------
network.host: 192.168.1.97
http.port: 9200
transport.tcp.port: 9300
# --------------------------------- Discovery ----------------------------------
#
discovery.zen.ping.unicast.hosts: ["192.168.1.97:9300","192.168.1.98:9300","192.168.1.113:9300"]
#理论上是 masternum/2+1,防止脑裂
discovery.zen.minimum_master_nodes: 2
# ---------------------------------- Various -----------------------------------
xpack.security.enabled: false
#支持跨域访问
http.cors.enabled: true
http.cors.allow-origin: "*"

参考文档

Linux sandboxing机制

从内核文件系统看文件读写过程

Lucence分词原理研究

#研究背景

​ 近期工作需要优化公司的分词器,需要改写IK源码,故而知其然知其所以然,Lucene分词相关的原理和实现研究透彻之后,可以研究ES IK分词器的源码了。以下源码的研究,是基于Lucene 6.6.0版本,Lucene源码中文档很规范,结合文档和源码学习效率很高。本文的所有内容,均来自于Lucene的文档和代码示例。

#类图

​ 如类图所示,Lucene中和分词相关的最核心的几个类。Analyzer用于构建TokenStream(用于分析文本),提供了用于提取文本生成词项(term)的策略。为了定义哪些Analyzer就绪,子类必须实现createComponents方法,组件分词时,调用tokenStream方法。

​ TokenStream枚举了一系列tokens,它们来自documents.field或者query text。TokenStream是一个抽象类,具体的实现分为Tokenizer和TokenFilter。Tokennizer的输入是Reader,TokenFilter的输入是另一个tokenFilter。

img

TokenStream API工作流程

1、初始化TokenStream时添加对应的属性
2、客户端调用TokenStream.reset()
3、客户端获取需要访问的属性的本地引用
4、客户端调用incrementToken()直到返回false,每次调用就可以获取对应的token的属性
5、客户端调用end()方法让TokenStream执行扫尾操作
6、客户端使用完TokenStream后调用close()释放资源

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
public static void main(String[] args) throws Exception {
Analyzer analyzer = new StandardAnalyzer();
String str = "中华人民";
TokenStream stream = analyzer.tokenStream("content", new StringReader(str));
CharTermAttribute attr = stream.addAttribute(CharTermAttribute.class); //1,3
PositionIncrementAttribute posIncr = stream.addAttribute(PositionIncrementAttribute.class); //1,3
OffsetAttribute offset = stream.addAttribute(OffsetAttribute.class); //1,3
TypeAttribute type = stream.addAttribute(TypeAttribute.class); //1,3
stream.reset(); //2
int position = 0;
while (stream.incrementToken()) { //4
int increment = posIncr.getPositionIncrement();
if (increment > 0) {
position = position + increment;
System.out.print(position + ": ");
}

System.out.println(attr.toString() + "," + offset.startOffset() + "->" + offset.endOffset() + "," + type.type());
}
stream.end(); //5
stream.close(); //6
analyzer.close();
}


执行结果:
1: 中,0->1,<IDEOGRAPHIC>
2: 华,1->2,<IDEOGRAPHIC>
3: 人,2->3,<IDEOGRAPHIC>
4: 民,3->4,<IDEOGRAPHIC>

Charles搭建网络抓包环境

Charles搭建网络抓包环境

Charles 是在 Mac 下常用的网络封包截取工具,在做移动开发时,我们为了调试与服务器端的网络通讯协议,常常需要截取网络封包来分析。Charles 通过将自己设置成系统的网络访问代理服务器,使得所有的网络访问请求都通过它来完成,从而实现了网络封包的截取和分析。除了在做移动开发中调试端口外,Charles 也可以用于分析第三方应用的通讯协议。配合 Charles 的 SSL 功能,Charles 还可以分析 Https 协议。

Charles 是收费软件,可以免费试用 30 天。试用期过后,未付费的用户仍然可以继续使用,但是每次使用时间不能超过 30 分钟,并且启动时将会有 10 秒种的延时。因此,该付费方案对广大用户还是相当友好的,即使你长期不付费,也能使用完整的软件功能。只是当你需要长时间进行封包调试时,会因为 Charles 强制关闭而遇到影响。

Charles 主要的功能包括:

  1. 截取 Http 和 Https 网络封包。
  2. 支持重发网络请求,方便后端调试。
  3. 支持修改网络请求参数。
  4. 支持网络请求的截获并动态修改。
  5. 支持模拟慢速网络。

Charles 4 新增的主要功能包括:

  1. 支持 Http 2。
  2. 支持 IPv6。

IOS http抓包

需要在设置->无线局域网->连接的WIFI下面,设置HTTP代理:

IP为启动Charles的服务器IP,端口默认是8888,如果端口被重新指定,请在Charles -> Proxy -> Proxy Setting中查看。

以上设置好之后,手机的网络请求,便可以在Charles中体现出来了。

IOS https抓包

除了http抓包的设置之外,还需要安装https证书。

Step1: Charles -> Help -> SSL Proxy -> Install Charles Root Certificate on a mobile device or remote browser

Step2: step1执行之后,会出现一个弹窗,需要用apple手机safari访问chls.pro/ssl,安装证书。

Step3: iOS 10.3系统,需要在 设置→通用→关于本机→证书信任设置 里面启用完全信任Charles证书。

基于以上步骤,Charles就能够正常抓取https的包了。

#引用

charles从入门到精通

Charles抓包(IOS的Http/Https请求)