ElasticSearch面试题
大苹果

ElasticSearch面试题

为什么要使用Elasticsearch系统中的数据,随着业务的发展,时间的推移,将会非常多,而业务中往往采用模糊查询进行数据的搜索,而模糊查询会导致查询引擎放弃索引,导致系统查询数据时都是全表扫描,在百万级别的数据库中,查询效率是非常低下的,而我们使用ES做一个全文索引,将经常查询的系统功能的某些字段,比如说电商系统的商品表中商品名,描述、价格还有id这些字段我们放入ES索引库里,可以提高查询速度。Elasticsearch的master选举流程Elasticsearch的选主是ZenDiscovery模块负责的,主要包含Ping(节点之间通过这个RPC来发现彼此)和Unicast(单播模块包含一个主机列表以控制哪些节点需要ping通)这两部分对所有可以成为master的节点(node.master:true、node.roles:[master,data])根据nodeId字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点如果对某个节点的投票数达到一定的值(可以成为master节点数n/2+1)并且该节点自己也选举自己,那这个节点就是master。否则重新选举一直到满足上述条件master节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data节点可以关闭http功能Elasticsearch集群脑裂问题「脑裂」问题可能的成因网络问题:集群间的网络延迟导致一些节点访问不到master,认为master挂掉了从而选举出新的master,并对master上的分片和副本标红,分配新的主分片节点负载:主节点的角色既为master又为data,访问量较大时可能会导致ES停止响应造成大面积延迟,此时其他节点得不到主节点的响应认为主节点挂掉了,会重新选取主节点内存回收:data节点上的ES进程占用的内存较大,引发JVM的大规模内存回收,造成ES进程失去响应脑裂问题解决方案减少误判:discovery.zen.ping_timeout节点状态的响应时间,默认为3s,可以适当调大如果master在该响应时间的范围内没有做出响应应答,判断该节点已经挂掉了。调大参数(如6s,discovery.zen.ping_timeout:6),可适当减少误判选举触发:修改参数为1,discovery.zen.minimum_master_nodes:1该参数是用于控制选举行为发生的最小集群主节点数量。当备选主节点的个数大于等于该参数的值,且备选主节点中有该参数个节点认为主节点挂了,进行选举。官方建议为(n/2)+1,n为主节点个数(即有资格成为主节点的节点个数)角色分离:即master节点与data节点分离,限制角色主节点配置为:node.master:truenode.data:false/node.roles:[master]从节点配置为:node.master:falsenode.data:true/node.roles:[data]Elasticsearch索引文档的流程协调节点默认使用文档ID参与计算(也支持通过routing),以便为路由提供合适的分片:shard=hash(document_id)%(num_of_primary_shards)当分片所在的节点接收到来自协调节点的请求后,会将请求写入到MemoryBuffer,然后定时(默认是每隔1秒)写入到FilesystemCache,这个从MemoryBuffer到FilesystemCache的过程就叫做refresh当然在某些情况下,存在MomeryBuffer和FilesystemCache的数据可能会丢失,ES是通过translog的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到translog中,当Filesystemcache中的数据写入到磁盘中时,才会清除掉,这个过程叫做flush在flush过程中,内存中的缓冲将被清除,内容被写入一个新段,段的fsync将创建一个新的提交点,并将内容刷新到磁盘,旧的translog将被删除并开始一个新的translogflush触发的时机是定时触发(默认30分钟)或者translog变得太大(默认为512M)时Elasticsearch更新和删除文档的流程删除和更新也都是写操作,但是Elasticsearch中的文档是不可变的,因此不能被删除或者改动以展示其变更磁盘上的每个段都有一个相应的.del文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del文件中被标记为删除的文档将不会被写入新段在新的文档被创建时,Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉Elasticsearch搜索的流程搜索被执行成一个两阶段过程,我们称之为QueryThenFetch在初始查询阶段时,查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。每个分片在本地执行搜索并构建一个匹配文档的大小为from+size的优先队列。PS:在搜索的时候是会查询FilesystemCache的,但是有部分数据还在MemoryBuffer,所以搜索是近实时的每个分片返回各自优先队列中所有文档的ID和排序值给协调节点,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表接下来就是取回阶段,协调节点辨别出哪些文档需要被取回并向相关的分片提交多个GET请求。每个分片加载并丰富文档,如果有需要的话,接着返回文档给协调节点。一旦所有的文档都被取回了,协调节点返回结果给客户端QueryThenFetch的搜索类型在文档相关性打分的时候参考的是本分片的数据,这样在文档数量较少的时候可能不够准确,DFSQueryThenFetch增加了一个预查询的处理,询问Term和Documentfrequency,这个评分更准确,但是性能会变差Elasticsearch在部署时,对Linux的设置有哪些优化方法64GB内存的机器是非常理想的,但是32GB和16GB机器也是很常见的。少于8GB会适得其反如果你要在更快的CPUs和更多的核心之间选择,选择更多的核心更好。多个内核提供的额外并发远胜过稍微快一点点的时钟频率如果你负担得起SSD,它将远远超出任何旋转介质。基于SSD的节点,查询和索引性能都有提升。如果你负担得起,SSD是一个好的选择即使数据中心们近在咫尺,也要避免集群跨越多个数据中心。绝对要避免集群跨越大的地理距离请确保运行你应用程序的JVM和服务器的JVM是完全一样的。在Elasticsearch的几个地方,使用Java的本地序列化通过设置gateway.recover_after_nodes、gateway.expected_nodes、gateway.recover_after_time可以在集群重启的时候避免过多的分片交换,这可能会让数据恢复从数个小时缩短为几秒钟Elasticsearch默认被配置为使用单播发现,以防止节点无意中加入集群。只有在同一台机器上运行的节点才会自动组成集群。最好使用单播代替组播不要随意修改垃圾回收器(CMS)和各个线程池的大小把你的内存的(少于)一半给Lucene(但不要超过32GB),通过ES_HEAP_SIZE环境变量设置内存交换到磁盘对服务器性能来说是致命的。如果内存交换到磁盘上,一个100微秒的操作可能变成10毫秒。再想想那么多10微秒的操作时延累加起来。不难看出swapping对于性能是多么可怕Lucene使用了大量的文件。同时,Elasticsearch在节点和HTTP客户端之间进行通信也使用了大量的套接字。所有这一切都需要足够的文件描述符。你应该增加你的文件描述符,设置一个很大的值,如64000补充:索引阶段性能提升方法使用批量请求并调整其大小:每次批量数据5MB–15MB大是个不错的起始点存储:使用SSD段和合并:Elasticsearch默认值是20MB/s,对机械磁盘应该是个不错的设置。如果你用的是SSD,可以考虑提高到100–200MB/s。如果你在做批量导入,完全不在意搜索,你可以彻底关掉合并限流。另外还可以增加index.translog.flush_threshold_size设置,从默认的512MB到更大一些的值,比如1GB,这可以在一次清空触发的时候在事务日志里积累出更大的段如果你的搜索结果不需要近实时的准确度,考虑把每个索引的index.refresh_interval改到30s如果你在做大批量导入,考虑通过设置index.number_of_replicas:0关闭副本GC方面,在使用Elasticsearch时要注意什么倒排词典的索引需要常驻内存,无法GC,需要监控datanode上segmentmemory增长趋势各类缓存,fieldcache,filtercache,indexingcache,bulkqueue等等,要设置合理的大小,并且要应该根据最坏的情况来看heap是否够用,也就是各类缓存全部占满的时候,还有heap空间可以分配给其他任务吗?避免采用clearcache等「自欺欺人」的方式来释放内存避免返回大量结果集的搜索与聚合。确实需要大量拉取数据的场景,可以采用scan&scrollapi来实现clusterstats驻留内存并无法水平扩展,超大规模集群可以考虑分拆成多个集群通过tribenode连接想知道heap够不够,必须结合实际应用场景,并对集群的heap使用情况做持续的监控Elasticsearch对于大数据量(上亿量级)的聚合如何实现Elasticsearch提供的首个近似聚合是cardinality度量。它提供一个字段的基数,即该字段的distinct或者unique值的数目。它是基于HLL算法的。HLL会先对我们的输入作哈希运算,然后根据哈希运算的结果中的bits做概率估算从而得到基数。其特点是:可配置的精度,用来控制内存的使用(更精确=更多内存);小的数据集精度是非常高的;我们可以通过配置参数,来设置去重需要的固定内存使用量。无论数千还是数十亿的唯一值,内存使用量只与你配置的精确度相关。在并发情况下,Elasticsearch如果保证读写一致可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突另外对于写操作,一致性级别支持quorum/one/all,默认为quorum,即只有当大多数分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建对于读操作,可以设置replication为sync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置replication为async时,也可以通过设置搜索请求参数_preference为primary来查询主分片,确保文档是最新版本如何监控Elasticsearch集群状态elasticsearch-head插件通过Kibana监控Elasticsearch。你可以实时查看你的集群健康状态和性能,也可以分析过去的集群、索引和节点指标。Elasticsearch中的集群、节点、索引、文档、类型是什么集群是一个或多个节点(服务器)的集合,它们共同保存您的整个数据,并提供跨所有节点的联合索引和搜索功能。群集由唯一名称标识,默认情况下为my-es-application。此名称很重要,因为如果节点设置为按名称加入群集,则该节点只能是群集的一部分节点是属于集群一部分的单个服务器。它存储数据并参与群集索引和搜索功能索引就像关系数据库中的「数据库」。它有一个定义多种类型的映射。索引是逻辑名称空间,映射到一个或多个主分片,并且可以有零个或多个副本分片。MySQL=>数据库;Elasticsearch=>索引文档类似于关系数据库中的一行。不同之处在于索引中的每个文档可以具有不同的结构(字段),但是对于通用字段应该具有相同的数据类型。MySQL=>Databases=>Tables=>Columns/Rows;Elasticsearch=>Indices=>Types=>具有属性的文档类型是索引的逻辑类别/分区,其语义完全取决于用户Elasticsearch中的倒排索引是什么倒排索引是搜索引擎的核心。搜索引擎的主要目标是在查找发生搜索条件的文档时提供快速搜索。ES中的倒排索引其实就是lucene的倒排索引,区别于传统的正向索引,倒排索引会在存储数据时将关键词和数据进行关联,保存到倒排表中,然后查询时,将查询内容进行分词后在倒排表中进行查询,最后匹配数据即可。

ElasticSearch 41 1月前
ElasticSearch集群搭建
大苹果

ElasticSearch集群搭建

概述单机&集群单台Elasticsearch服务器提供服务,往往都有最大的负载能力,超过这个阈值,服务器性能就会大大降低甚至不可用,所以生产环境中,一般都是运行在指定服务器集群中。除了负载能力,单点服务器也存在其他问题:单台机器存储容量有限单服务器容易出现单点故障,无法实现高可用单服务的并发处理能力有限配置服务器集群时,集群中节点数量没有限制,大于等于2个节点就可以看做是集群了。一般出于高性能及高可用方面来考虑集群中节点数量都是3个以上。集群Cluster一个集群就是由一个或多个服务器节点组织在一起,共同持有整个的数据,并一起提供索引和搜索功能。一个Elasticsearch集群有一个唯一的名字标识,这个名字默认就是elasticsearch。这个名字是重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群。节点Node集群中包含很多服务器,一个节点就是其中的一个服务器。作为集群的一部分,它存储数据,参与集群的索引和搜索功能。一个节点也是由一个名字来标识的,默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做elasticsearch的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做elasticsearch的集群中。在一个集群里,只要你想,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做elasticsearch的集群。集群搭建说明集群搭建同样会区分7.x版本及8.x版本,其中,Windows下的集群搭建,8.x版本就不搞证书之类的了,linux下我们会使用https证书,毕竟实际生产环境大部分是linux。Windows集群搭建7.X(7.8.0)集群搭建集群准备1、首先创建es-cluster文件夹,将elasticsearch-7.8.0复制三份,并修改文件名2、修改集群文件目录下,每一个节点的config/elasticsearch.yml配置文件node-1001节点node-1002节点node-1003节点#节点1的配置信息:#----------------------------------Cluster-----------------------------------#集群名称,节点之间要保持一致cluster.name:my-elasticsearch#------------------------------------Node------------------------------------#节点名称,集群内要唯一node.name:node-1001node.master:truenode.data:true#----------------------------------Network-----------------------------------#ip地址network.host:localhost#http端口http.port:7001#tcp监听端口transport.tcp.port:9301#---------------------------------Discovery----------------------------------#集群内的可以被选为主节点的节点列表cluster.initial_master_nodes:["node-1001","node-1002","node-1003"]#----------------------------------Various-----------------------------------#跨域配置#action.destructive_requires_name:truehttp.cors.enabled:truehttp.cors.allow-origin:"*"#节点2的配置信息:#----------------------------------Cluster-----------------------------------#集群名称,节点之间要保持一致cluster.name:my-elasticsearch#------------------------------------Node------------------------------------#节点名称,集群内要唯一node.name:node-1002node.master:truenode.data:true#----------------------------------Network-----------------------------------#ip地址network.host:localhost#http端口http.port:7002#tcp监听端口transport.tcp.port:9302#---------------------------------Discovery----------------------------------discovery.seed_hosts:["localhost:9301"]discovery.zen.fd.ping_timeout:1mdiscovery.zen.fd.ping_retries:5#集群内的可以被选为主节点的节点列表cluster.initial_master_nodes:["node-1001","node-1002","node-1003"]#----------------------------------Various-----------------------------------#跨域配置#action.destructive_requires_name:truehttp.cors.enabled:truehttp.cors.allow-origin:"*"#节点3的配置信息:#----------------------------------Cluster-----------------------------------#集群名称,节点之间要保持一致cluster.name:my-elasticsearch#------------------------------------Node------------------------------------#节点名称,集群内要唯一node.name:node-1003node.master:truenode.data:true#----------------------------------Network-----------------------------------#ip地址network.host:localhost#http端口http.port:7003#tcp监听端口transport.tcp.port:9303#---------------------------------Discovery----------------------------------discovery.seed_hosts:["localhost:9301","localhost:9302"]discovery.zen.fd.ping_timeout:1mdiscovery.zen.fd.ping_retries:5#集群内的可以被选为主节点的节点列表cluster.initial_master_nodes:["node-1001","node-10022","node-1003"]#----------------------------------Various-----------------------------------#跨域配置#action.destructive_requires_name:truehttp.cors.enabled:truehttp.cors.allow-origin:"*"启动集群启动前先删除每个节点中的data目录中所有内容(如果存在)进入bin目录,分别双击执行bin/elasticsearch.bat,启动节点服务器,启动后,会自动加入指定名称的集群,谁先启动就会成为master8.X(8.3.3)集群搭建集群准备1、同样的,我们创建es-cluster文件夹,将elasticsearch-8.3.3复制三份,并修改文件名2、分别修改每一个节点的config/elasticsearch.yml配置文件node-1001节点node-1002节点node-1003节点#节点1的配置信息:#----------------------------------Cluster-----------------------------------#集群名称,节点之间要保持一致cluster.name:my-es-application#------------------------------------Node------------------------------------#节点名称,集群内要唯一node.name:node-1001#节点角色[主节点、数据节点]:注意这一点同7.x版本的区别配置node.roles:[master,data]#----------------------------------Network-----------------------------------#发布ipnetwork.host:localhost#http端口http.port:7001#tcp监听端口:注意这一点同7.x版本的区别配置transport.port:9301#---------------------------------Discovery----------------------------------#初始主节点cluster.initial_master_nodes:["node-1001","node-1002","node-1003"]#----------------------------------Various-----------------------------------#删除索引是否需要指定索引全名(false就是可以用正则)#action.destructive_requires_name:false#跨域配置http.cors.enabled:truehttp.cors.allow-origin:"*"#节点2的配置信息:#----------------------------------Cluster-----------------------------------#集群名称,节点之间要保持一致cluster.name:my-es-application#------------------------------------Node------------------------------------#节点名称,集群内要唯一node.name:node-1002#节点角色[主节点、数据节点]node.roles:[master,data]#----------------------------------Network-----------------------------------#发布ipnetwork.host:localhost#http端口http.port:7002#tcp监听端口transport.port:9302#---------------------------------Discovery----------------------------------#自动发现集群节点ipdiscovery.seed_hosts:["localhost:9301"]discovery.zen.fd.ping_timeout:1mdiscovery.zen.fd.ping_retries:5#集群内的可以被选为主节点的节点列表cluster.initial_master_nodes:["node-1001","node-1002","node-1003"]#----------------------------------Various-----------------------------------#跨域配置http.cors.enabled:truehttp.cors.allow-origin:"*"#节点3的配置信息:#----------------------------------Cluster-----------------------------------#集群名称,节点之间要保持一致cluster.name:my-es-application#------------------------------------Node------------------------------------#节点名称,集群内要唯一node.name:node-1003#节点角色[主节点、数据节点]node.roles:[master,data]#----------------------------------Network-----------------------------------#发布ipnetwork.host:localhost#http端口http.port:7003#tcp监听端口transport.port:9303#---------------------------------Discovery----------------------------------#自动发现集群节点ipdiscovery.seed_hosts:["localhost:9301","localhost:9302"]discovery.zen.fd.ping_timeout:1mdiscovery.zen.fd.ping_retries:5#集群内的可以被选为主节点的节点列表cluster.initial_master_nodes:["node-1001","node-1002","node-1003"]#----------------------------------Various-----------------------------------#跨域配置http.cors.enabled:truehttp.cors.allow-origin:"*"启动集群启动前先删除每个节点中的data目录中所有内容(如果存在)进入bin目录,分别双击执行bin/elasticsearch.bat,启动节点服务器测试集群查看集群状态:浏览器直接访问:http://127.0.0.1:7001/_cluster/healthstatu:当前集群在总体上是否工作正常,有三种颜色green:所有的主分片和副本分片都正常运行yellow:所有的主分片都正常运行,但不是所有副本分片都正常运行red:有主分片没能正常运行numnber_of_nodes:集群里多少个节点numnber_of_data_nodes:集群里多少个节点可以存储数据向集群中的node-7001节点增加索引在集群的node-1002节点查询刚刚添加的索引Linux集群7.X(7.8.0)集群搭建集群准备1、首先需要安装好ElasticSearch,Linux安装ES请参考2、安装完成后,同样的我们复制三份ES服务,最好是新建一个es-cluster文件夹来存放创建用户因为安全问题,Elasticsearch不允许root用户直接运行,所以要在每个节点中创建新用户,在root用户中创建新用户useraddes#新增用户passwdes#设置密码userdel-res#如果错了,可以删除再加#对文件夹赋权给es用户chown-Res/usr/local/es-cluster/node-7001chown-Res/usr/local/es-cluster/node-7002chown-Res/usr/local/es-cluster/node-7003修改ES配置文件分别修改各节点的config/elasticsearch.yml文件node-7001#加入如下配置#集群名称cluster.name:cluster-es#节点名称,每个节点的名称不能重复node.name:node-7001#ip地址,每个节点的地址不能重复network.host:linux1#是不是有资格主节点node.master:truenode.data:truehttp.port:9200#head插件需要这打开这两个配置http.cors.allow-origin:"*"http.cors.enabled:truehttp.max_content_length:200mb#es7.x之后新增的配置,初始化一个新的集群时需要此配置来选举mastercluster.initial_master_nodes:["node-7001"]#es7.x之后新增的配置,节点发现discovery.seed_hosts:["linux1:9300","linux2:9300","linux3:9300"]gateway.recover_after_nodes:2network.tcp.keep_alive:truenetwork.tcp.no_delay:truetransport.tcp.compress:true#集群内同时启动的数据任务个数,默认是2个cluster.routing.allocation.cluster_concurrent_rebalance:16#添加或删除节点及负载均衡时并发恢复的线程个数,默认4个cluster.routing.allocation.node_concurrent_recoveries:16#初始化数据恢复时,并发恢复线程的个数,默认4个cluster.routing.allocation.node_initial_primaries_recoveries:16node-7002#加入如下配置#集群名称cluster.name:cluster-es#节点名称,每个节点的名称不能重复node.name:node-7002#ip地址,每个节点的地址不能重复network.host:linux1#是不是有资格主节点node.master:truenode.data:truehttp.port:9200#head插件需要这打开这两个配置http.cors.allow-origin:"*"http.cors.enabled:truehttp.max_content_length:200mb#es7.x之后新增的配置,初始化一个新的集群时需要此配置来选举mastercluster.initial_master_nodes:["node-7001"]#es7.x之后新增的配置,节点发现discovery.seed_hosts:["linux1:9300","linux2:9300","linux3:9300"]gateway.recover_after_nodes:2network.tcp.keep_alive:truenetwork.tcp.no_delay:truetransport.tcp.compress:true#集群内同时启动的数据任务个数,默认是2个cluster.routing.allocation.cluster_concurrent_rebalance:16#添加或删除节点及负载均衡时并发恢复的线程个数,默认4个cluster.routing.allocation.node_concurrent_recoveries:16#初始化数据恢复时,并发恢复线程的个数,默认4个cluster.routing.allocation.node_initial_primaries_recoveries:16node-7003#加入如下配置#集群名称cluster.name:cluster-es#节点名称,每个节点的名称不能重复node.name:node-7003#ip地址,每个节点的地址不能重复network.host:linux1#是不是有资格主节点node.master:truenode.data:truehttp.port:9200#head插件需要这打开这两个配置http.cors.allow-origin:"*"http.cors.enabled:truehttp.max_content_length:200mb#es7.x之后新增的配置,初始化一个新的集群时需要此配置来选举mastercluster.initial_master_nodes:["node-7001"]#es7.x之后新增的配置,节点发现discovery.seed_hosts:["linux1:9300","linux2:9300","linux3:9300"]gateway.recover_after_nodes:2network.tcp.keep_alive:truenetwork.tcp.no_delay:truetransport.tcp.compress:true#集群内同时启动的数据任务个数,默认是2个cluster.routing.allocation.cluster_concurrent_rebalance:16#添加或删除节点及负载均衡时并发恢复的线程个数,默认4个cluster.routing.allocation.node_concurrent_recoveries:16#初始化数据恢复时,并发恢复线程的个数,默认4个cluster.routing.allocation.node_initial_primaries_recoveries:16修改系统配置文件1、修改系统中允许应用最多创建多少文件等的限制权限。Linux默认来说,一般限制应用最多创建的文件是65535个。但是ES至少需要65536的文件创建权限。2、修改系统中允许用户启动的进程开启多少个线程。默认的Linux限制root用户开启的进程可以开启任意数量的线程,其他用户开启的进程可以开启1024个线程。必须修改限制数为4096+。因为ES至少需要4096的线程池预备。ES在5.x版本之后,强制要求在Linux中不能使用root用户启动ES进程。所以必须使用其他用户启动ES进程才可以。3、Linux低版本内核为线程分配的内存是128K。4.x版本的内核分配的内存更大。如果虚拟机的内存是1G,最多只能开启3000+个线程数。至少为虚拟机分配1.5G以上的内存。ES可以对每个进程的文件数进行限制等,如果服务器内存或空间不足,可以通过修改配置文件,进行「裁剪」修改/etc/security/limits.conf:vim/etcsecurity/limits.conf#在文件末尾中增加下面内容#每个进程可以打开的文件数的限制essoftnofile65536eshardnofile65536修改/etc/security/limits.d/20-nproc.conf:vim/etc/security/limits.d/20-nproc.conf#在文件末尾中增加下面内容#每个进程可以打开的文件数的限制essoftnofile65536eshardnofile65536#操作系统级别对每个用户创建的进程数的限制*hardnproc4096#注:*带表Linux所有用户名称修改/etc/sysctl.conf:vim/etc/sysctl.conf#在文件末尾中增加下面内容#一个进程可以拥有的VMA(虚拟内存区域)的数量,默认值为65536vm.max_map_count=655360重新加载配置sysctl-p启动集群使用ES用户分别在不同节点上启动ES服务器,-d代表后台启动#节点1cd/usr/local/es-cluster/node-7001bin/elasticsearch-d#节点2cd/usr/local/es-cluster/node-7002bin/elasticsearch-d#节点3cd/usr/local/es-cluster/node-7003bin/elasticsearch-d8.X(8.3.3)集群搭建集群准备前期准备同7.x一致,这里就不再重复叙述准备三台机器或者单台都可以示例中ES在/opt/module目录下集群规划IP节点名称节点角色节点功能182.61.xx.43es-node-1Master,data主+数据节点182.61.xx.44es-node-2Master,data主+数据节点182.61.xx.45es-node-3Master,data主+数据节点搭建集群1、创建新用户es,数据文件,证书目录,并修改Elasticsearch文件拥有者#新增es用户useraddes#为es用户设置密码passwdes#创建数据文件目录mkdir/opt/module/elasticsearch-8.3.3/data#创建证书目录mkdir/opt/module/elasticsearch-8.3.3/config/certs#切换目录cd/opt/module/elasticsearch-8.3.3#修改文件拥有者chown-Res:es/opt/module/elasticsearch-8.3.32、在第一台服务器节点es-node-1设置集群多节点通信密钥#切换用户sues#签发ca证书,过程中需按两次回车键bin/elasticsearch-certutilca#用ca证书签发节点证书,过程中需按三次回车键bin/elasticsearch-certutilcert--caelastic-stack-ca.p12#将生成的证书文件移动到config/certs目录中mvelastic-stack-ca.p12elastic-certificates.p12config/certs3、在第一台服务器节点es-node-1设置集群多节点HTTP证书#签发Https证书bin/elasticsearch-certutilhttp#以下是每次要求输入时,需要输入的内容(注意看关键字词)1、GenerateaCSR?是否生成CSR,输入`【n】`2、useanexistingCA?是否使用已经存在的CA证书,输入`【y】`3、CAPath:certs/elastic-stack-ca.p12指定证书路径4、Passwordforelastic-stack-ca.p12:为证书输入密码,不用密码直接回车5、Forhowlongshouldyourcertificatebevalid?[5y]20y输入证书过期时间,输入20年`【20y】`6、generateacertificatepernode?是否为每个节点单独生成证书,输入`【n】`7、Isthiscorrect[Y/n]y输出连接到第一个节点的所有主机名称`【y】`8、Isthiscorrect[Y/n]y输出连接到第一个节点的所有主机IP地址`【y】`9、Doyouwishtochangeanyoftheseoptions?[y/N]n不改变证书选项配置`【n】`10、Whatpassworddoyouwantforyouprivatekey(s)?不给证书加密,按键输入两次回车4、解压刚刚生成的zip包#解压文件unzipelasticsearch-ssl-http.zipzip内容/elasticsearch|_README.txt|_http.p12|_sample-elasticsearch.yml/kibana|_README.txt|_elasticsearch-ca.pem|_sample-kibana.yml5、将解压后的证书文件移动到config/certs目录中#移动文件:其中kibana中的证书是提供给kibana通信使用的,具体配置查看目录中的README.txtmvelasticsearch/http.p12kibana/elasticsearch-ca.pemconfig/certs6、修改主配置文件:config/elasticsearch.yml#设置ES集群名称cluster.name:es-cluster#设置集群中当前节点名称node.name:es-node-1#节点角色[主节点、数据节点]node.roles:[master,data]#设置数据,日志文件路径path.data:/opt/module/elasticsearch-8.3.3/datapath.logs:/opt/module/elasticsearch-8.3.3/log#设置网络访问节点network.host:linux1#设置网络访问端口http.port:9200#初始节点discovery.seed_hosts:["linux1"]#安全认证xpack.security.enabled:truexpack.security.enrollment.enabled:truexpack.security.http.ssl:enabled:truekeystore.path:/opt/module/elasticsearch-8.3.3/config/certs/http.p12truststore.path:/opt/module/elasticsearch-8.3.3/config/certs/http.p12#集群间安全通信配置xpack.security.transport.ssl:enabled:trueverification_mode:certificatekeystore.path:/opt/module/elasticsearch-8.3.3/config/certs/elastic-certificates.p12truststore.path:/opt/module/elasticsearch-8.3.3/config/certs/elastic-certificates.p12#此处需注意,es-node-1为上面配置的节点名称cluster.initial_master_nodes:["es-node-1"]http.host:[_local_,_site_]ingest.geoip.downloader.enabled:falsexpack.security.http.ssl.client_authentication:none7、启动ES服务#启动ES软件bin/elasticsearch注意1:第一次成功启动后,会显示密码,请记住,访问时需要。如果没记住,就通过ES的rest-password重置密码,详细操作请查看ElasticSearch安装中重置密码一节注意2:9300端口为Elasticsearch集群间组件的通信端口,9200端口为浏览器访问的http协议RESTful端口。8、访问服务器节点https://虚拟机地址:9200。因为配置了安全协议,所以使用https协议进行访问,但由于证书是自己生成的,并不可靠,所以会有安全提示9、修改集群中其他节点的配置文件:config/elasticsearch.ymllinux2:证书直接拷贝,其他步骤完全相同,配置文件中修改如下内容即可#设置节点名称node.name:es-node-2#设置网络访问主机network.host:linux2linux3:证书直接拷贝,其他步骤完全相同,配置文件中修改如下内容即可#设置节点名称node.name:es-node-3#设置网络访问主机network.host:linux310、依次启动集群的三台服务器节点,不要忘记切换用户后再启动#linux1:##后台启动服务bin/elasticsearch-d#linux2:##后台启动服务bin/elasticsearch-d#linux3:##后台启动服务bin/elasticsearch-d

ElasticSearch 59 1月前
ElasticSearch-8.X的JAVA-API
大苹果

ElasticSearch-8.X的JAVA-API

前述在8.x版本中,ES出了一个新的ElasticsearchJavaAPIClient,点击查看(opensnewwindow),如果你要在Springboot高版本中使用,可以直接使用该API。ES7.X版本的JAVA-API请查看🔎以下示例,首先需要将elasticsearch.yml中的有关ssl的配置全部改为false,本地测试就不搞HTTPS那么麻烦了示例代码已经上传到gitee(opensnewwindow)项目配置依赖引入<dependency><groupId>co.elastic.clients</groupId><artifactId>elasticsearch-java</artifactId><version>8.3.3</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.3</version></dependency><!--Itmayhappenthataftersettingupthedependencies,yourapplicationfailswithClassNotFoundException:jakarta.json.spi.JsonProvider.--><dependency><groupId>jakarta.json</groupId><artifactId>jakarta.json-api</artifactId><version>2.1.1</version></dependency>注意:elasticsearch的两个依赖要和elasticsearch服务器版本一致。客户端对象@TestvoidElasticsearchClientBuild()throwsIOException{//Createthelow-levelclientRestClientrestClient=RestClient.builder(newHttpHost("127.0.0.1",9200)).build();//CreatethetransportwithaJacksonmapperElasticsearchTransporttransport=newRestClientTransport(restClient,newJacksonJsonpMapper());//AndcreatetheAPIclientElasticsearchClientelasticsearchClient=newElasticsearchClient(transport);System.out.println("elasticsearchClient="+elasticsearchClient);restClient.close();}结果输出elasticsearchClient=co.elastic.clients.elasticsearch.ElasticsearchClient@6e4f263e配置获取客户端对象yml配置elasticsearch:host:127.0.0.1port:9200http:http配置ElasticClient/***@version1.0.0*@className:ElasticClient*@description:配置获取ES客户端对象*@author:smartzeng*@create:2022/8/89:58*/@ComponentpublicclassElasticClient{@Value("${elasticsearch.host}")privateStringhost;@Value("${elasticsearch.port}")privateIntegerport;@Value("${elasticsearch.http}")privateStringhttp;/***获取elasticsearch客户端**@return{@linkElasticsearchClient}*/@BeanpublicElasticsearchClientgetElasticsearchClient(){RestClientrestClient=RestClient.builder(newHttpHost(host,port,http)).build();ElasticsearchTransporttransport=newRestClientTransport(restClient,newJacksonJsonpMapper());returnnewElasticsearchClient(transport);}}索引操作创建索引@SpringBootTest@Slf4jpublicclassIndexTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***创建索引**@throwsIOExceptionioexception*/@TestvoidcreateIndex()throwsIOException{CreateIndexResponseresponse=elasticsearchClient.indices().create(c->c.index("products"));//响应状态booleanacknowledged=response.acknowledged();booleanshardsAcknowledged=response.shardsAcknowledged();Stringindex=response.index();log.info("创建索引状态:{}",acknowledged);log.info("已确认的分片:{}",shardsAcknowledged);log.info("索引名称:{}",index);}}结果[main]c.e.e.ElasticsearchApplicationTests:elasticsearchClient:co.elastic.clients.elasticsearch.ElasticsearchClient@6f911326[main]c.e.e.ElasticsearchApplicationTests:创建索引状态:true[main]c.e.e.ElasticsearchApplicationTests:已确认的分片:true[main]c.e.e.ElasticsearchApplicationTests:索引名称:products查看索引/***@version1.0.0*@className:IndexTest*@description:索引测试*@author:smartzeng*@create:2022/8/810:03*/@SpringBootTest@Slf4jpublicclassIndexTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***获取索引*/@TestvoidgetIndex()throwsIOException{//查看指定索引GetIndexResponsegetIndexResponse=elasticsearchClient.indices().get(s->s.index("products"));Map<String,IndexState>result=getIndexResponse.result();result.forEach((k,v)->log.info("key={},value={}",k,v));//查看全部索引IndicesResponseindicesResponse=elasticsearchClient.cat().indices();//返回对象具体查看co.elastic.clients.elasticsearch.cat.indices.IndicesRecordindicesResponse.valueBody().forEach(info->log.info("health:{}\nstatus:{}\nuuid:{}\n",info.health(),info.status(),info.uuid()));}}结果key=products,value=IndexState:{"aliases":{},"mappings":{},"settings":{"index":{"number_of_shards":"1","number_of_replicas":"1","routing":{"allocation":{"include":{"_tier_preference":"data_content"}}},"provided_name":"products","creation_date":"1659923692276","uuid":"WN0uQLLvQ1SdFsWE2bhlgw","version":{"created":"8030399"}}}}删除索引/***@version1.0.0*@className:IndexTest*@description:索引测试*@author:smartzeng*@create:2022/8/810:03*/@SpringBootTest@Slf4jpublicclassIndexTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***删除索引**@throwsIOExceptionioexception*/@TestvoiddeleteIndex()throwsIOException{DeleteIndexResponsedeleteIndexResponse=elasticsearchClient.indices().delete(s->s.index("products"));log.info("删除索引操作结果:{}",deleteIndexResponse.acknowledged());}}结果[main]com.example.elasticsearch.IndexTest:删除索引操作结果:true总结关于索引的请求,用到elasticsearchClient.indices().xxx(s->s.index("索引名")),其中xxx代表增删查文章操作新增实体类:UserpublicclassUser{privateStringid;privateStringname;privateIntegerage;privateStringsex;publicStringgetId(){returnid;}publicvoidsetId(Stringid){this.id=id;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicIntegergetAge(){returnage;}publicvoidsetAge(Integerage){this.age=age;}publicStringgetSex(){returnsex;}publicvoidsetSex(Stringsex){this.sex=sex;}publicUser(){}publicUser(Stringid,Stringname,Integerage,Stringsex){this.id=id;this.name=name;this.age=age;this.sex=sex;}@OverridepublicStringtoString(){return"User{"+"id='"+id+'\''+",name='"+name+'\''+",age="+age+",sex='"+sex+'\''+'}';}}新增文档/***@version1.0.0*@className:DocTest*@description:文档操作测试*@author:smartzeng*@create:2022/8/810:19*/@SpringBootTest@Slf4jpublicclassDocTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***添加一个文档*@see:https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/8.3/indexing.html#indexing*@throwsIOExceptionioexception*/@TestvoidaddOneDocument()throwsIOException{//1、usingthefluentDSLUseruser=newUser("1","王五",28,"男");IndexResponseindexResponse=elasticsearchClient.index(s->//索引s.index("users")//ID.id(user.getId())//文档.document(user));log.info("result:{}",indexResponse.result().jsonValue());//2、YoucanalsoassignobjectscreatedwiththeDSLtovariables.JavaAPIClientclasseshaveastaticof()methodforthis,thatcreatesanobjectwiththeDSLsyntax.IndexRequest<User>request=IndexRequest.of(i->i.index("users").id(user.getId()).document(user));IndexResponseresponse=elasticsearchClient.index(request);log.info("Indexedwithversion"+response.version());//3、UsingclassicbuildersIndexRequest.Builder<User>indexReqBuilder=newIndexRequest.Builder<>();indexReqBuilder.index("users");indexReqBuilder.id(user.getId());indexReqBuilder.document(user);IndexResponseresponseTwo=elasticsearchClient.index(indexReqBuilder.build());log.info("Indexedwithversion"+responseTwo.version());}}结果[main]com.example.elasticsearch.DocTest:user.id:e051445c-ae8c-47ef-ab18-97b34025d49a[main]com.example.elasticsearch.DocTest:result:created查询文档/***@version1.0.0*@className:DocTest*@description:文档操作测试*@author:smartzeng*@create:2022/8/810:19*/@SpringBootTest@Slf4jpublicclassDocTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***获取文档*https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/8.3/reading.html#reading*@throwsIOExceptionioexception*/@TestvoidgetDocument()throwsIOException{//co.elastic.clients.elasticsearch.core.get.GetResult<TDocument>GetResponse<User>getResponse=elasticsearchClient.get(s->s.index("users").id("e051445c-ae8c-47ef-ab18-97b34025d49a"),User.class);log.info("getResponse:{}",getResponse.source());//Readingadomainobjectif(getResponse.found()){Useruser=getResponse.source();assertuser!=null;log.info("username={}",user.getName());}//ReadingrawJSON//if(getResponse.found())//{//ObjectNodejson=getResponse.source();//Stringname=json.get("name").asText();//log.info("Productname"+name);//}//判断文档是否存在BooleanResponsebooleanResponse=elasticsearchClient.exists(s->s.index("users").id("e051445c-ae8c-47ef-ab18-97b34025d49a"));log.info("判断Document是否存在:{}",booleanResponse.value());}}结果getResponse:User{id='e051445c-ae8c-47ef-ab18-97b34025d49a',name='王五',age=28,sex='男'}更新文档/***@version1.0.0*@className:DocTest*@description:文档操作测试*@author:smartzeng*@create:2022/8/810:19*/@SpringBootTest@Slf4jpublicclassDocTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***更新文档**@throwsIOExceptionioexception*/@TestvoidupdateDocument()throwsIOException{//构建需要修改的内容,这里使用了MapMap<String,Object>map=newHashMap<>();map.put("name","liuyife");//构建修改文档的请求UpdateResponse<Test>response=elasticsearchClient.update(e->e.index("users").id("33").doc(map),Test.class);//打印请求结果log.info(String.valueOf(response.result()));}}结果[main]com.example.elasticsearch.DocTest:Updated删除文档/***@version1.0.0*@className:DocTest*@description:文档操作测试*@author:smartzeng*@create:2022/8/810:19*/@SpringBootTest@Slf4jpublicclassDocTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***删除文档**@throwsIOExceptionioexception*/@TestvoiddeleteDocument()throwsIOException{DeleteResponsedeleteResponse=elasticsearchClient.delete(s->s.index("users").id("e051445c-ae8c-47ef-ab18-97b34025d49a"));log.info("删除文档操作结果:{}",deleteResponse.result());}}结果[main]com.example.elasticsearch.DocTest:删除文档操作结果:Deleted批量新增/***@version1.0.0*@className:DocTest*@description:文档操作测试*@author:smartzeng*@create:2022/8/810:19*/@SpringBootTest@Slf4jpublicclassDocTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***批量添加文档**@throwsIOExceptionioexception*/@TestvoidbatchAddDocument()throwsIOException{//1、useBulkOperationList<User>users=newArrayList<>();users.add(newUser("1","赵四",20,"男"));users.add(newUser("2","阿旺",25,"男"));users.add(newUser("3","刘菲",22,"女"));users.add(newUser("4","冬梅",20,"女"));List<BulkOperation>bulkOperations=newArrayList<>();users.forEach(u->bulkOperations.add(BulkOperation.of(b->b.index(c->c.id(u.getId()).document(u)))));BulkResponsebulkResponse=elasticsearchClient.bulk(s->s.index("users").operations(bulkOperations));bulkResponse.items().forEach(i->log.info("i={}",i.result()));log.error("bulkResponse.errors()={}",bulkResponse.errors());//2、useBulkRequestBulkRequest.Builderbr=newBulkRequest.Builder();for(Useruser:users){br.operations(op->op.index(idx->idx.index("users").id(user.getId()).document(user)));}BulkResponseresult=elasticsearchClient.bulk(br.build());//Logerrors,ifanyif(result.errors()){log.error("Bulkhaderrors");for(BulkResponseItemitem:result.items()){if(item.error()!=null){log.error(item.error().reason());}}}}}结果[main]com.example.elasticsearch.DocTest:i=created[main]com.example.elasticsearch.DocTest:i=created[main]com.example.elasticsearch.DocTest:i=created[main]com.example.elasticsearch.DocTest:i=created[main]com.example.elasticsearch.DocTest:bulkResponse.errors()=false批量删除/***@version1.0.0*@className:DocTest*@description:文档操作测试*@author:smartzeng*@create:2022/8/810:19*/@SpringBootTest@Slf4jpublicclassDocTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***批量删除文档**@throwsIOExceptionioexception*/@TestvoidbatchDeleteDocument()throwsIOException{//1、useBulkOperationList<String>list=newArrayList<>();list.add("1");list.add("2");list.add("3");list.add("4");List<BulkOperation>bulkOperations=newArrayList<>();list.forEach(a->bulkOperations.add(BulkOperation.of(b->b.delete(c->c.id(a)))));BulkResponsebulkResponse=elasticsearchClient.bulk(a->a.index("users").operations(bulkOperations));bulkResponse.items().forEach(a->log.info("result={}",a.result()));log.error("bulkResponse.errors()={}",bulkResponse.errors());//2、useBulkRequestBulkRequest.Builderbr=newBulkRequest.Builder();for(Strings:list){br.operations(op->op.delete(c->c.id(s)));}BulkResponsebulkResponseTwo=elasticsearchClient.bulk(br.build());bulkResponseTwo.items().forEach(a->log.info("result={}",a.result()));log.error("bulkResponse.errors()={}",bulkResponseTwo.errors());}}结果[main]com.example.elasticsearch.DocTest:result=deleted[main]com.example.elasticsearch.DocTest:result=deleted[main]com.example.elasticsearch.DocTest:result=deleted[main]com.example.elasticsearch.DocTest:result=deleted[main]com.example.elasticsearch.DocTest:bulkResponse.errors()=false高级查询查询准备通过批量添加文档准备几条数据先/***@version1.0.0*@className:SearchTest*@description:查询测试*@author:smartzeng*@create:2022/8/811:04*/@SpringBootTest@Slf4jpublicclassSearchTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***批量添加准备数据**@throwsIOExceptionioexception*/@TestvoidbatchAddDocument()throwsIOException{List<User>users=newArrayList<>();users.add(newUser("11","zhaosi",20,"男"));users.add(newUser("22","awang",25,"男"));users.add(newUser("33","liuyifei",22,"女"));users.add(newUser("44","dongmei",20,"女"));users.add(newUser("55","zhangya",30,"女"));users.add(newUser("66","liuyihu",32,"男"));BulkRequest.Builderbr=newBulkRequest.Builder();for(Useruser:users){br.operations(op->op.index(idx->idx.index("users").id(user.getId()).document(user)));}BulkResponseresult=elasticsearchClient.bulk(br.build());//Logerrors,ifanyif(result.errors()){log.error("Bulkhaderrors");for(BulkResponseItemitem:result.items()){if(item.error()!=null){log.error(item.error().reason());}}}}}简单的搜索查询/***@version1.0.0*@className:SearchTest*@description:查询测试*@author:smartzeng*@create:2022/8/811:04*/@SpringBootTest@Slf4jpublicclassSearchTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***条件查询方式1**@throwsIOExceptionioexception*/@TestvoidsearchOne()throwsIOException{StringsearchText="liuyihu";SearchResponse<User>response=elasticsearchClient.search(s->s//我们要搜索的索引的名称.index("users")//搜索请求的查询部分(搜索请求也可以有其他组件,如聚合).query(q->q//在众多可用的查询变体中选择一个。我们在这里选择匹配查询(全文搜索).match(t->t//name配置匹配查询:我们在字段中搜索一个词.field("name").query(searchText))),//匹配文档的目标类User.class);TotalHitstotal=response.hits().total();booleanisExactResult=total.relation()==TotalHitsRelation.Eq;if(isExactResult){log.info("Thereare"+total.value()+"results");}else{log.info("Therearemorethan"+total.value()+"results");}List<Hit<User>>hits=response.hits().hits();for(Hit<User>hit:hits){Useruser=hit.source();assertuser!=null;log.info("FounduserId"+user.getId()+",name"+user.getName());}}}嵌套搜索查询/***@version1.0.0*@className:SearchTest*@description:查询测试*@author:smartzeng*@create:2022/8/811:04*/@SpringBootTest@Slf4jpublicclassSearchTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***嵌套搜索查询*/@TestvoidsearchTwo()throwsIOException{StringsearchText="liuyihu";intmaxAge=30;//byName、byMaxAge:分别为各个条件创建查询QuerybyName=MatchQuery.of(m->m.field("name").query(searchText))//MatchQuery是一个查询变体,我们必须将其转换为Query联合类型._toQuery();QuerybyMaxAge=RangeQuery.of(m->m.field("age")//Elasticsearch范围查询接受大范围的值类型。我们在这里创建最高价格的JSON表示。.gte(JsonData.of(maxAge)))._toQuery();SearchResponse<User>response=elasticsearchClient.search(s->s.index("users").query(q->q.bool(b->b//搜索查询是结合了文本搜索和最高价格查询的布尔查询.must(byName)//.should(byMaxAge).must(byMaxAge))),User.class);List<Hit<User>>hits=response.hits().hits();for(Hit<User>hit:hits){Useruser=hit.source();assertuser!=null;log.info("FounduserId"+user.getId()+",name"+user.getName());}}}模板化搜索模板化搜索是存储的搜索,可以使用不同的变量运行它。搜索模板让您无需修改应用程序代码即可更改搜索。在运行模板搜索之前,首先必须创建模板。这是一个返回搜索请求正文的存储脚本,通常定义为Mustache模板/***@version1.0.0*@className:SearchTest*@description:查询测试*@author:smartzeng*@create:2022/8/811:04*/@SpringBootTest@Slf4jpublicclassSearchTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***模板化搜索*模板化搜索是存储的搜索,可以使用不同的变量运行它。搜索模板让您无需修改应用程序代码即可更改搜索。*在运行模板搜索之前,首先必须创建模板。这是一个返回搜索请求正文的存储脚本,通常定义为Mustache模板*/@TestvoidtemplatedSearch()throwsIOException{//事先创建搜索模板elasticsearchClient.putScript(r->r//要创建的模板脚本的标识符.id("query-script").script(s->s.lang("mustache").source("{\"query\":{\"match\":{\"{{field}}\":\"{{value}}\"}}}")));//开始使用模板搜索Stringfield="name";Stringvalue="liuyifei";SearchTemplateResponse<User>response=elasticsearchClient.searchTemplate(r->r.index("users")//要使用的模板脚本的标识符.id("query-script")//模板参数值.params("field",JsonData.of(field)).params("value",JsonData.of(value)),User.class);List<Hit<User>>hits=response.hits().hits();for(Hit<User>hit:hits){Useruser=hit.source();assertuser!=null;log.info("FounduserId"+user.getId()+",name"+user.getName());}}}分页&排序查询根据条件搜索的分页&排序/***@version1.0.0*@className:SearchTest*@description:查询测试*@author:smartzeng*@create:2022/8/811:04*/@SpringBootTest@Slf4jpublicclassSearchTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***分页+排序条件搜索**@throwsIOExceptionioexception*/@TestvoidpaginationSearch()throwsIOException{intmaxAge=20;QuerybyMaxAge=RangeQuery.of(m->m.field("age").gte(JsonData.of(maxAge)))._toQuery();SearchResponse<User>response=elasticsearchClient.search(s->s.index("users").query(q->q.bool(b->b.must(byMaxAge)))//分页查询,从第0页开始查询4个document.from(0).size(4)//按age降序排序.sort(f->f.field(o->o.field("age").order(SortOrder.Desc))),User.class);List<Hit<User>>hits=response.hits().hits();for(Hit<User>hit:hits){Useruser=hit.source();assertuser!=null;log.info("FounduserId"+user.getId()+",name"+user.getName());}}}结果[{"id":"66","name":"liuyihu","age":32,"sex":"男"},{"id":"55","name":"zhangya","age":30,"sex":"女"},{"id":"c49fa7a2-f8dc-45a4-8ea8-340f10a6162e","name":"zhangSan","age":28,"sex":"男"},{"id":"1","name":"王五","age":28,"sex":"男"}]查询所有并进行分页&排序/***@version1.0.0*@className:SearchTest*@description:查询测试*@author:smartzeng*@create:2022/8/811:04*/@SpringBootTest@Slf4jpublicclassSearchTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***分页+排序条件搜索**@throwsIOExceptionioexception*/@TestvoidpaginationSearch()throwsIOException{intmaxAge=20;QuerybyMaxAge=RangeQuery.of(m->m.field("age").gte(JsonData.of(maxAge)))._toQuery();SearchResponse<User>response=elasticsearchClient.search(s->s.index("users").query(q->q.matchAll(m->m)).from(0).size(6).sort(f->f.field(o->o.field("age").order(SortOrder.Desc))),User.class);List<Hit<User>>hits=response.hits().hits();for(Hit<User>hit:hits){Useruser=hit.source();assertuser!=null;log.info("FounduserId"+user.getId()+",name"+user.getName());}}}结果[{"id":"66","name":"liuyihu","age":32,"sex":"男"},{"id":"55","name":"zhangya","age":30,"sex":"女"},{"id":"c49fa7a2-f8dc-45a4-8ea8-340f10a6162e","name":"zhangSan","age":28,"sex":"男"},{"id":"1","name":"王五","age":28,"sex":"男"},{"id":"22","name":"awang","age":25,"sex":"男"},{"id":"33","name":"liuyifei","age":22,"sex":"女"},{"id":"11","name":"zhaosi","age":20,"sex":"男"},{"id":"44","name":"dongmei","age":20,"sex":"女"}]过滤字段/***@version1.0.0*@className:SearchTest*@description:查询测试*@author:smartzeng*@create:2022/8/811:04*/@SpringBootTest@Slf4jpublicclassSearchTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***过滤字段**@throwsIOExceptionioexception*/@TestvoidfilterFieldSearch()throwsIOException{SearchResponse<User>response=elasticsearchClient.search(s->s.index("users").query(q->q.matchAll(m->m)).sort(f->f.field(o->o.field("age").order(SortOrder.Desc))).source(source->source.filter(f->f.includes("name","id").excludes(""))),User.class);List<Hit<User>>hits=response.hits().hits();List<User>userList=newArrayList<>(hits.size());for(Hit<User>hit:hits){Useruser=hit.source();userList.add(user);}log.info("过滤字段后:{}",JSONUtil.toJsonStr(userList));}}结果[{"id":"66","name":"liuyihu"},{"id":"55","name":"zhangya"},{"id":"c49fa7a2-f8dc-45a4-8ea8-340f10a6162e","name":"zhangSan"},{"id":"1","name":"王五"},{"id":"22","name":"awang"},{"id":"33","name":"liuyifei"},{"id":"11","name":"zhaosi"},{"id":"44","name":"dongmei"}]模糊查询/***@version1.0.0*@className:SearchTest*@description:查询测试*@author:smartzeng*@create:2022/8/811:04*/@SpringBootTest@Slf4jpublicclassSearchTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***模糊查询**@throwsIOExceptionioexception*/@TestvoidfuzzyQuerySearch()throwsIOException{SearchResponse<User>response=elasticsearchClient.search(s->s.index("users").query(q->q//模糊查询.fuzzy(f->f//需要判断的字段名称.field("name")//需要模糊查询的关键词//目前文档中没有liuyi这个用户名.value("liuyi")//fuzziness代表可以与关键词有误差的字数,可选值为0、1、2这三项.fuzziness("2"))).source(source->source.filter(f->f.includes("name","id").excludes(""))),User.class);List<Hit<User>>hits=response.hits().hits();List<User>userList=newArrayList<>(hits.size());for(Hit<User>hit:hits){Useruser=hit.source();userList.add(user);}log.info("过滤字段后:{}",JSONUtil.toJsonStr(userList));}}结果[{"id":"66","name":"liuyihu"}]高亮查询/***@version1.0.0*@className:SearchTest*@description:查询测试*@author:smartzeng*@create:2022/8/811:04*/@SpringBootTest@Slf4jpublicclassSearchTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***高亮查询**@throwsIOExceptionioexception*/@TestvoidhighlightQueryQuery()throwsIOException{SearchResponse<User>response=elasticsearchClient.search(s->s.index("users").query(q->q.term(t->t.field("name").value("zhaosi"))).highlight(h->h.fields("name",f->f.preTags("<fontcolor='red'>").postTags("</font>"))).source(source->source.filter(f->f.includes("name","id").excludes(""))),User.class);List<Hit<User>>hits=response.hits().hits();List<User>userList=newArrayList<>(hits.size());for(Hit<User>hit:hits){Useruser=hit.source();userList.add(user);for(Map.Entry<String,List<String>>entry:hit.highlight().entrySet()){System.out.println("Key="+entry.getKey());entry.getValue().forEach(System.out::println);}}log.info("模糊查询结果:{}",JSONUtil.toJsonStr(userList));}}结果Key=name<fontcolor='red'>zhaosi</font>聚合查询最大值/***@version1.0.0*@className:SearchTest*@description:聚合查询测试*@author:smartzeng*@create:2022/8/811:04*/@SpringBootTest@Slf4jpublicclassPartyTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***获取最大年龄用户*/@TestvoidgetMaxAgeUserTest()throwsIOException{SearchResponse<Void>response=elasticsearchClient.search(b->b.index("users").size(0).aggregations("maxAge",a->a.max(MaxAggregation.of(s->s.field("age")))),Void.class);MaxAggregatemaxAge=response.aggregations().get("maxAge").max();log.info("maxAge.value:{}",maxAge.value());}}分组统计LongTermsAggregate类型/***@version1.0.0*@className:SearchTest*@description:聚合查询测试*@author:smartzeng*@create:2022/8/811:04*/@SpringBootTest@Slf4jpublicclassPartyTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***年龄分组**@throwsIOExceptionioexception*/@TestvoidgroupingTest()throwsIOException{SearchResponse<Void>response=elasticsearchClient.search(b->b.index("users").size(0).aggregations("groupName",a->a.terms(TermsAggregation.of(s->s.field("age")))),Void.class);LongTermsAggregatelongTermsAggregate=response.aggregations().get("groupName").lterms();log.info("multiTermsAggregate:{}",longTermsAggregate.buckets());}}结果[{"doc_count":2,"key":20},{"doc_count":2,"key":28},{"doc_count":1,"key":22},{"doc_count":1,"key":25},{"doc_count":1,"key":30},{"doc_count":1,"key":32}]StringTermsAggregate类型/***@version1.0.0*@className:SearchTest*@description:聚合查询测试*@author:smartzeng*@create:2022/8/811:04*/@SpringBootTest@Slf4jpublicclassPartyTest{@AutowiredprivateElasticsearchClientelasticsearchClient;/***性别分组**@throwsIOExceptionioexception*/@TestvoidgroupBySexTest()throwsIOException{SearchResponse<Void>response=elasticsearchClient.search(b->b.index("users").size(0).aggregations("groupSex",a->a.terms(TermsAggregation.of(s->s//⚠️特别注意这一块,我们加上了.keyword,下面会说明.field("sex.keyword")))),Void.class);StringTermsAggregatestringTermsAggregate=response.aggregations().get("groupSex").sterms();log.info("stringTermsAggregate:{}",stringTermsAggregate.buckets());}}结果[{"doc_count":5,"key":"男"},{"doc_count":3,"key":"女"}]问题在性别分组代码中,有一块地方我们要注意下。我们根据性别分组,按理来说,field直接写sex就行,但是我们发现,如果不加.keyword,运行测试用例会报错,错误如下:原因分析:这个错误的原因是因为我的分组聚合查询的字符串sex类型是text类型。当使用到term查询的时候,由于是精准匹配,所以查询的关键字在es上的类型,必须是keyword而不能是text。所以想要分组查询,指定根据分组字段的keyword属性就可以了。

ElasticSearch 47 1月前
ElasticSearch安装
大苹果

ElasticSearch安装

Windows上安装ES下载软件Elasticsearch的官方地址(opensnewwindow)下载地址(opensnewwindow)安装软件Windows版的Elasticsearch的安装很简单,解压即安装完毕,这里我下载的目前最新版8.3.3,解压后的Elasticsearch的目录结构如下目录含义bin可执行脚本config配置目录jdk内置JDK目录lib类库logs日志目录modules模块目录plugins插件目录解压后,我们先来修改一些配置:1、配置启动内存,修改配置文件conf/jvm.options##################################################################IMPORTANT:JVMheapsize##################################################################配置启动内存,默认是4G-Xms1g-Xmx1g################################################################2、暂时禁止掉再次启动时更新地图的一些数据库操作,修改conf/elasticsearch.yml配置#添加配置:暂时禁止掉再次启动时更新地图的一些数据库操作ingest.geoip.downloader.enabled:false3、测试时可以暂时关闭用户密码和SSL,将下图中的true全部改为false:4、进入bin文件目录,点击elasticsearch.bat文件启动服务启动完成后,打开浏览器,输入:http://localhost:9200/重置密码如果安装步骤中第三步,你没有修改elasticsearch.yml配置为false,那么你第一次启动ES服务的时候,控制台会首先出现一些用户名及密码,切记要先保存下来!启动完成后,访问:https://127.0.0.1:9200/,输入用户名及密码,用户名为elastic,密码就是第一次启动时控制台显示的密码如果你第一次启动控制台,忘记保存密码了,那么也别担心,进入es安装目录的bin文件中执行一下命令会返回最新的密码G:\java\ElasticSearch\elasticsearch-8.3.3\bin>elasticsearch-reset-password-uelasticwarning:ignoringJAVA_HOME=G:\java\java8\jdk;usingbundledJDKThistoolwillresetthepasswordofthe[elastic]usertoanautogeneratedvalue.Thepasswordwillbeprintedintheconsole.Pleaseconfirmthatyouwouldliketocontinue[y/N]yPasswordforthe[elastic]usersuccessfullyreset.Newvalue:-PPR2=1zsthCZ0gCYz3ILinux安装ES准备工作首先Linux上需要先安装了jdk1.8以上版本前往官网下载Linux版本(opensnewwindow)在Linux里,进入/usr/local/目录,创建一个目录elastic-stack将下载好的文件上传到新建目录下安装解压ES文件cdelastic-stacktar-zxvfelasticsearch-8.3.3-linux-x86_64.tar.gzmvelasticsearch-8.3.3es解压完成后,先不要运行,因为不可使用root用户直接启动elasticsearch,我们需要添加用户,并赋予对es目录的权限useraddes#设置的密码passwdes#赋予新用户权限chown-Res/usr/local/elastic-stack/es修改conf/elasticsearch.yml#-----------------------------------Paths------------------------------------##Pathtodirectorywheretostorethedata(separatemultiplelocationsbycomma):#path.data:/data/es##Pathtologfiles:#path.logs:/var/log/es##集群名cluster.name:elasticsearch#节点名node.name:node-1#允许外界访问的ipnetwork.host:0.0.0.0#http访问端口http.port:9200#集群节点的mastercluster.initial_master_nodes:["node-1"]修改/etc/security/limits.conf,在文件末尾添加修改系统中允许应用最多创建多少文件等的限制权限。Linux默认来说,一般限制应用最多创建的文件是65535个。但是ES至少需要65536的文件创建权限。#每个进程可以打开的文件数的限制essoftnofile65536eshardnofile65536#操作系统级别对每个用户创建的进程数的限制*hardnproc4096#注:*带表Linux所有用户名称修改/etc/sysctl.conf,在文件末尾中增加下面内容:#一个进程可以拥有的VMA(虚拟内存区域)的数量,默认值为65536vm.max_map_count=655360配置修改完成后,重新加载sysctl-p切换用户并启动#切换用户sues#进入安装目录cd/usr/local/elastic-stack/es#-d代表后台启动./bin/elasticsearch-dWindows上安装KibanaKibana是一个免费且开放的用户界面,能够让你对Elasticsearch数据进行可视化,并让你在ElasticStack中进行导航。你可以进行各种操作,从跟踪查询负载,到理解请求如何流经你的整个应用,都能轻松完成。下载时尽量下载与ElasicSearch一致的版本。Windows版本前往官网下载Windows版本(opensnewwindow)下载后进行解压,目录如图:启动kibana启动Kibana之前要启动Elasticsearch进入bin目录,双击kibana.bat启动服务启动成功后,复制控制台的地址进行访问:http://localhost:5601/?code=9057268.x版本访问时,需要先生成一个token,我们在ES的bin目录下执行以下命令生成tokenG:\java\ElasticSearch\elasticsearch-8.3.3\bin>elasticsearch-create-enrollment-token.bat--scopekibanawarning:ignoringJAVA_HOME=G:\java\java8\jdk;usingbundledJDKeyJ2ZXIiOiI4LjMuMyIsImFkciI6WyIxNzIuMjAuMTAuNDo5MjAwIl0sImZnciI6ImUzOWZlYzRmM2MyMDg2ZTEyOGIxOTU3OTAyOTVlZTc5OTlkZmI1MDlmMzM5NjNiZWE2Y2Q1ZTRjMWVhOTdlNmMiLCJrZXkiOiJfbHdsYklJQkpWZm9XX1lLNlY5NTo2czNnMFJZYVRfYXNCX19BZVZ6MDVnIn0=将token复制到kibana登录页中登录ES的elastic用户修改界面语言访问界面是英文,可修改成中文,进入根目录下的config目录,打开kibana.yml文件,在最末尾处加入以下配置,添加完成后,重新启动即可#默认端口#server.port:5601#ES服务器的地址,经过上一步的操作,这些会自动生成,所以不需要改动#elasticsearch.hosts:["https://127.0.0.1:9200"]#索引名:可改可不改#kibana.index:".kibana"#支持中文i18n.locale:"zh-CN"Linux上安装Kibana通过文件安装前往官网下载Linux版本(opensnewwindow)同样将压缩包上传到Linux指定目录下通过wgetwgethttps://artifacts.elastic.co/downloads/kibana/kibana-8.3.3-linux-x86_64.tar.gztar-xvfkibana-8.0.0-linux-x86_64.tar.gzmvkibana-8.3.3kibanacdkibana#赋予es用户对kibana目录的权限chown-Res/usr/local/kibana#切换至es用户sues#启动./bin/kibana修改配置修改kibana.yml配置,允许远程访问server.host:"0.0.0.0"

ElasticSearch 31 1月前
nodejs最新版本安装相关依赖问题
大苹果

nodejs最新版本安装相关依赖问题

近期经常遇到使用源码编译的部署方式进行应用部署,在GCC编译环境上遇到各种问题,本文对升级部署GCC编译环境的流程以及遇到的一些问题进行记录。一、python3gcc11.2安装需要python3yuminstall-ypython3-devel二、升级Make查看版本make-v#查看版本make-v#下载wgethttp://ftp.gnu.org/pub/gnu/make/make-4.3.tar.gz#解压tar-zxvfmake-4.3.tar.gzcdmake-4.3#编译./configure--prefix=/usr/local/makemakemakeinstall#应用#找一下都有哪些makewhereismakecd/usr/bin/#把默认的make改名mvmakemake.bak#建立一个软连接ln-sv/usr/local/make/bin/make/usr/bin/make#查看一下大功告成make-v三、安装GMP、PMFR、MPC升级GCC时需要yum-yinstallgmp-develyum-yinstallmpfr-develyum-yinstalllibmpc-devel四、升级GCCGCC和GLIBC版本有关联,升级GCC前需要先确定GLIBC的版本,如Glibc2.28对应要求的GCC版本为8.2#查看版本gcc-v#下载wgethttp://ftp.gnu.org/gnu/gcc/gcc-11.2.0/gcc-11.2.0.tar.gz#解压tar-zxvfgcc-11.2.0.tar.gzcdgcc-11.2.0#下载四个依赖包./contrib/download_prerequisites#编译mkdirbuildcdbuild/../configure-enable-checking=release-enable-languages=c,c++-disable-multilibmake-j16makeinstall#卸载旧版本yum-yremovegccg++#配置新版本全局可用ln-s/usr/local/bin/gcc/usr/bin/gccln-s/usr/local/bin/g++/usr/bin/g++#更新动态库#查看当前的动态库strings/usr/lib64/libstdc++.so.6|grepCXXABI#更新rm-f/usr/lib64/libstdc++.so.6ln-s/usr/local/lib64/libstdc++.so.6.0.29/usr/lib64/libstdc++.so.6#查看更新后的动态库strings/usr/lib64/libstdc++.so.6|grepCXXABI五、GLIBCGLIBC下载地址:http://ftp.gnu.org/gnu/glibc/nodejs最新版本服务需要2.18、2.25、2.28#查看已安装的glibcstrings/usr/lib64/libc.so.6|grep^GLIBC_#下载wgethttp://ftp.gnu.org/gnu/glibc/glibc-2.33.tar.xz#又重新安装回了GCC貌似覆盖了原先的升级yumgroupinstall"DevelopmentTools"-y#解压xz-dglibc-2.33.tar.xztar-xvfglibc-2.33.tar#安装cdglibc-2.33mkdirbuildcdbuild../configure--prefix=/usr--disable-profile--enable-add-ons--with-headers=/usr/include--with-binutils=/usr/binmake-j16sed-i'128i\&&$namene"nss_test2"'../scripts/test-installation.plmakeinstall报错:glibc/usr/bin/ld:cannotfind-lnss_test2原因在于找不到lnss_test2文件:解决:cd../回到上级目录,viscripts/test-installation.pl打开文本定位到128行&&$namene"db1"&&$namene"thread_db"&&$namene"nss_test1"文本,在中间加一行&&$namene"nss_test2",保存makeinstall

centos,nodejs 59 2月前
【NodeJS】GLIBC_2.28 not found CentOS7不兼容Node高版本
大苹果

【NodeJS】GLIBC_2.28 not found CentOS7不兼容Node高版本

提前部署安装编译环境devtoolset-8编译安装glibc-2.28编译安装gcc高版本并配置LD_LIBRARY_PATH参考链接:https://github.com/nodejs/build/issues/2815https://cloud.tencent.com/developer/article/2021784操作步骤1、安装编译环境devtoolset-8sudoyuminstallcentos-release-sclsudoyuminstalldevtoolset-8sclenabledevtoolset-8bashgcc--version2、编译安装glibc-2.28wgethttps://ftp.gnu.org/gnu/glibc/glibc-2.28.tar.gz--no-check-certificatetar-xzvfglibc-2.28.tar.gzcdglibc-2.28mkdirbuild&&cdbuild../configure--prefix=/usr--disable-profile--enable-add-ons--with-headers=/usr/include--with-binutils=/usr/bin--enable-obsolete-nslconfigure增加--enable-obsolete-nsl,解决编译错误undefinedreferenceto'_nsl_default_nss@GLIBC_PRIVATE'。修改scripts/test-installation.pl128行,增加&&$namene"nss_test2",解决编译错误nss_test2报错。sed-i'128i\&&$namene"nss_test2"'../scripts/test-installation.pl126if($namene"nss_ldap"&&$namene"db1"127&&$namene"thread_db"128&&$namene"nss_test2"129&&$namene"nss_test1"&&$namene"libgcc_s"){130$link_libs.="-l$name";131$versions{$name}=$version;132}执行编译安装:make-j4makeinstall查看glibc支持的版本:strings/lib64/libc.so.6|grepGLIBC3、编译安装gcc高版本并配置LD_LIBRARY_PATHsudoyuminstallgmp-develmpfr-devellibmpc-develwgethttps://ftp.gnu.org/gnu/gcc/gcc-8.5.0/gcc-8.5.0.tar.gz--no-check-certificatetar-xvfgcc-8.5.0.tar.gzcdgcc-8.5.0mkdirbuildcdbuild../configure--disable-multilib--enable-languages=c,c++--prefix=$HOME/localmake-j8makeinstall在~/.bashrc中增加语句:exportLD_LIBRARY_PATH=$HOME/local/lib64

yum,nodejs,Linux,centos 224 2月前
前端开发
大苹果

前端开发

前端开发当前以及未来的前端开发,一定是:组件化、模块化有利于团队协作开发便于组件的复用:提高开发效率、方便后期维护、减少页面中冗余代码如何规划组件业务组件:针对项目需求封装的普通业务组件“没有啥复用性,只是单独拆出来的一个模块“通用业务组件“具备复用性”功能组件:适用于多个项目“例如:UI组件库的组件”通用功能组件因为组件化开发,必然会带来“工程化”的处理也就是基于webpack等工具“vite/rollup/turbopack...”实现组件的合并、压缩、打包等代码编译、兼容、校验等React的工程化/组件化开发我们可以基于webpack自己去搭建一套工程化的架子,但是这样非常的麻烦、复杂;React官方,为我们提供了一个脚手架;create-react-app脚手架:基于它创建项目,默认就是把webpack的打包规则已经处理好,把一些项目需要的基本文件也都创建好了!!create-react-app基本运用安装脚手架npmicreate-react-app-g#mac前面需要设置sudo#检查安装情况create-react-app--version基于脚手架创建React工程化的项目create-react-app项目名称项目名称要遵循npm包命名规范:使用“数字、小写字母、_”命名项目目录|-src:所有后续编写的代码,几乎都放在src下“打包的时候,一般只对这个目录下面的代码进行处理”-->|-index.js|-public:放页面模板-->|index.html|-package.json|-...react常用的版本很早之前是15版本(太早了)16版本:一些项目用的最多的17版本:最大的升级就是看不出升级“语法没变啥,只是底层处理机制上升级了”18版本:新版本“机制和语法上都有区别”一个React项目中,默认会安装:react:React框架的核心react-dom:React视图渲染的核心“基于React构建WebApp(html页面)”--->react-native:构建和渲染App的react-scripts:脚手架为了让项目目录看起来干净一些,把webpac打包的规则及相关的插件/LOADER等都隐藏到了node_modules目录下,react-scripts就是脚手架中自己对打包命令的一种封装、基于它的打包,会调用node_modules中的webpack等进行处理!!如图web-vitals:性能检测eslintbrowserslist

React,前端 86 2月前
Node.js cluster模块相关笔记
大苹果

Node.js cluster模块相关笔记

clusterNode.js的单个实例在单个线程中运行。要利用多核系统,用户有时会想启动一个Node.js进程集群来处理负载。cluster模块可以轻松创建全部共享服务器端口的子进程。constcluster=require('cluster');consthttp=require('http');constnumCPUs=require('os').cpus().length;if(cluster.isMaster){console.log(`Master${process.pid}isrunning`);//Forkworkers.for(leti=0;i<numCPUs;i++){cluster.fork();}cluster.on('exit',(worker,code,signal)=>{console.log(`worker${worker.process.pid}died`);});}else{//WorkerscanshareanyTCPconnection//InthiscaseitisanHTTPserverhttp.createServer((req,res)=>{res.writeHead(200);res.end('helloworld\n');}).listen(8000);console.log(`Worker${process.pid}started`);}运行Node.js,所有的工作进程将共享8000端口$nodeserver.jsMaster3596isrunningWorker4324startedWorker4520startedWorker6056startedWorker5644started请注意,在Windows上,尚不可能在工作进程中设置命名管道服务。cluster如何工作使用child_process.fork()方法产生工作进程,以便它们可以通过IPC与父进程通信并来回传递服务器句柄。cluster模块支持两种分配传入连接的方法。第一种方法(除Windows以外的所有平台上的默认方法)都是循环法,即主进程在端口上进行侦听,接受新连接并以循环方式在工作进程之间进行分配,其中一些已构建智能避免过载工作进程。第二种方法是主进程创建侦听套接字并将其发送给感兴趣的工作进程。工作进程然后直接接受传入连接。理论上,第二种方法应该是最好的。然而,在实践中,由于操作系统调度器的变幻莫测,分配趋于非常不平衡。已经观察到负载超过70%的连接只有两个过程,总共有八个。因为server.listen()将大部分工作交给了主进程,所以在正常的Node.js进程和集群工作进程之间的行为有三种情况:server.listen({fd:7})因为消息被传递给主进程,父进程中的文件描述符7将被监听,并且该句柄传递给工作进程,而不是监听工作进程的文件描述符7的引用。server.listen(句柄)明确地监听句柄会导致工作进程使用提供的句柄,而不是与主进程通信。server.listen(0)通常,这将导致服务器侦听随机端口。但是,在集群中,每个工作人员每次收听(0)时都会收到相同的“随机”端口。实质上,这个端口在第一时间是随机的,但此后可以预见。要监听唯一端口,请根据群集工作器ID生成端口号。Node.js不提供路由逻辑。因此,设计一个应用程序非常重要,因为它不会过度依赖内存数据对象,如会话和登录等。因为工作进程都是独立的进程,所以他们可以根据程序的需要被杀死或重新生成,而不会影响其他工作进程。只要有一些工作进程还活着,服务器将继续接受连接。如果没有工作进程活着,现有的连接将被丢弃,新的连接将被拒绝。但是,Node.js不会自动管理工作进程的数量。根据自己的需要管理工作进程池是应用程序的责任。虽然cluster模块的主要用例是联网,但它也可以用于需要工作进程的其他用例。类:Worker新增于v0.7.0Worker对象包含有关工作进程的所有公共信息和方法。在主进程中可以使用cluster.workers获得。在工作进程中,可以使用cluster.worker获得。Event:'disconnect'新增于v0.7.7类似于cluster.on('disconnect')事件,但是特定于此worker。cluster.fork().on('disconnect',()=>{//Workerhasdisconnected});Event:'error'新增于v0.7.3此事件与child_process.fork()提供的事件相同。在工作进程中,process.on('error')也可以被使用。Event:'exit'新增于v0.11.2code<number>退出代码,如果退出正常。signal<string>导致进程被终止的信号名称(例如'SIGHUP')。类似于cluster.on('exit'),但是只针对此workerconstworker=cluster.fork();worker.on('exit',(code,signal)=>{if(signal){console.log(`workerwaskilledbysignal:${signal}`);}elseif(code!==0){console.log(`workerexitedwitherrorcode:${code}`);}else{console.log('workersuccess!');}});Event:'listening'新增于v0.7.0address<object>类似于cluster.on('listening')事件,但是只针对此worker.cluster.fork().on('listening',(address)=>{//Workerislistening});它不会在工作进程之间分发。Event:'message'新增于v0.7.0message<Object>handle<undefined>|<Objec>类似于cluster的'message'事件,但是只针对此worker.在worker中,process.on('message')也可以使用。作为一个例子,下面是一个集群,它使用消息系统保持主进程中的请求数量:constcluster=require('cluster');consthttp=require('http');if(cluster.isMaster){//跟踪http请求letnumReqs=0;setInterval(()=>{console.log(`numReqs=${numReqs}`);},1000);//计数请求数目functionmessageHandler(msg){if(msg.cmd&&msg.cmd==='notifyRequest'){numReqs+=1;}}//启动工作进程并且监听通知的请求消息。constnumCPUs=require('os').cpus().length;for(leti=0;i<numCPUs;i++){cluster.fork();}for(constidincluster.workers){cluster.workers[id].on('message',messageHandler);}}else{//工作进程中创建httpServer服务http.Server((req,res)=>{res.writeHead(200);res.end('helloworld\n');//通知主进程关于这个请求process.send({cmd:'notifyRequest'});}).listen(8000);}Event:'online'新增于v0.7.0类似于cluster.on('online')事件,但是只针对此worker.cluster.fork().on('online',()=>{//Workerisonline});不会在其他工作进程中传递worker.disconnect()VersionChangesv7.3.0这个方法现在会返回一个工作进程的引用v0.7.7新增于:v0.7.7返回值:{cluster.Worker}引用于worker.在一个工作进程中,这个函数将关闭所有的服务,这些服务将等待'close'事件,随后断开IPC频道。在主进程中,一个内部消息被发送到这个工作进程中将引起这个工作进程自己调用.disconnect()。原因是.exitedAfterDisconnect将被设置.注意,一个服务关闭后它将不再接受新的连接,但是连接会被其他正在监听的工作进程接受。现有的连接照常允许关闭.当没有进程存在时,详见server.close(),通往该进程的IPC频道将被关闭。优雅的退出。以上情况仅适用服务端连接,客户端的连接不会被工作进程自动关闭。并且断开连接不会等待客户端退出后才关闭。注意这些工作进程中,还有一个process.disconnect方法存在,但是工作进程中的disconnect方法不是这个方法。因为上时间的服务端连接可能导致工作进程的disconnect方法阻塞,通过发送消息通知的方式也许可以,所以可能会采取特定于应用程序的操作关闭它们。通过设置超时来关闭工作进程,有时'disconnect'事件触发后的一段时间仍然不能关闭工作进程,通过超时关闭工作进程。if(cluster.isMaster){constworker=cluster.fork();lettimeout;worker.on('listening',(address)=>{worker.send('shutdown');worker.disconnect();timeout=setTimeout(()=>{worker.kill();},2000);});worker.on('disconnect',()=>{clearTimeout(timeout);});}elseif(cluster.isWorker){constnet=require('net');constserver=net.createServer((socket)=>{//连接没有结束});server.listen(8000);process.on('message',(msg)=>{if(msg==='shutdown'){//启动与服务器的任何连接的完美关闭}});}worker.exitedAfterDisconnect{boolean}通过调用.kill()或.disconnect()触发,在调用之前它一直是undefined.这个布尔值worker.exitedAfterDisconnect可以用于区分是自发退出还是被动退出。主进程可以根据这个值决定是否衍生新的工作进程。cluster.on('exit',(worker,code,signal)=>{if(worker.exitedAfterDisconnect===true){console.log('Oh,itwasjustvoluntary–noneedtoworry');}});//杀死工作进程worker.kill();worker.id{number}每个新的工作进程都会被分配一个唯一的id,并存储与这个id。当工作进程还在是,这是cluster.workers里面的一个索引。worker.isConnected()如果工作进程是通过IPC通道连接到主进程,则这个函数会返回true,否则返回false,一个工作进程建立连接后会自动连接到主进程。在触发'disconnect'事件后断开连接。worker.isDead()如果工作进程被终止这个函数会返回true(包括通过信号中止和退出)否则返回false。worker.kill([signal='SIGTERM'])signal{string}发送给工作进程的中止信号的名称。这个函数将关闭这个工作进程,在主进程中,他通过断开连接工作进程worker.process来完成,并且一旦断开连接,通过信号关闭工作进程,在工作进程中,他通过断开频道来完成,然后以代码0退出进程。设置.exitedAfterDisconnect属性。为了向后兼容,此方法被命名为worker.destroy()。注意,在工作进程中process.kill()是存在的,但是不同于这个函数。他是kill.worker.process{ChildProcess}所有工作进程都是使用child_process.fork()创建的,这个方法返回的对象被存储为.process。在一个工作进程中,process进程被储存了。看:ChildProcessmodule注意工作进程将调用process.exit(0)如果'disconnect'时间发生在process上并且.exitedAfterDisconnect不是true。这样可以防止连接意外断开worker.send(message[,sendHandle][,callback])message{Object}sendHandle{Handle}callback{Function}Returns:{boolean}发送消息给工作进程或主进程,可选句柄。在主进程中发送消息给指定的工作进程。等同于ChildProcess.send().工作进程发送消息给主进程等同于process.send()。这个例子将输出所有来自主进程的信息。if(cluster.isMaster){constworker=cluster.fork();worker.send('hithere');}elseif(cluster.isWorker){process.on('message',(msg)=>{process.send(msg);});}Event:'disconnect'worker{cluster.Worker}工作进程与IPC频道断开连接后触发。这可能发生在一个工作进程优雅的退出、关闭或手动断开连接,(例如调用worker.disconnect())disconnect和exit事件之间可能存在延迟。这些事件可以用来检测process是否停留在清理过程中,或者是否存在是长连接。cluster.on('disconnect',(worker)=>{console.log(`Theworker#${worker.id}hasdisconnected`);});Event:'exit'worker{cluster.Worker}code{number}退出的编码,如果是正常退出.signal{string}引起进程关闭的信号名称(e.g.'SIGHUP')当任何工作进程死亡时,cluster模块将发出exit事件。这可以通过再次调用.fork()来重新启动工作进程。cluster.on('exit',(worker,code,signal)=>{console.log('worker%ddied(%s).restarting...',worker.process.pid,signal||code);cluster.fork();});Seechild_processevent:'exit'.Event:'fork'worker{cluster.Worker}当一个新的工作进程通过clusterforked,cluster模块将触发'fork'事件这可以用来记录工作进程的活动,并且创建自定义超时。consttimeouts=[];functionerrorMsg(){console.error('Somethingmustbewrongwiththeconnection...');}cluster.on('fork',(worker)=>{timeouts[worker.id]=setTimeout(errorMsg,2000);});cluster.on('listening',(worker,address)=>{clearTimeout(timeouts[worker.id]);});cluster.on('exit',(worker,code,signal)=>{clearTimeout(timeouts[worker.id]);errorMsg();});Event:'listening'worker{cluster.Worker}address{Object}在工作进程调用listen()后,这个'listening'事件就会被触发。主线程的cluster的'listening'事件也会被触发。这个事件回调包含两个参数,worker是一个工作进程对象,address对象包含以下连接属性address、port和addressType,这对于监听超过一个ip地址是很有用的。cluster.on('listening',(worker,address)=>{console.log(`Aworkerisnowconnectedto${address.address}:${address.port}`);});addressType取值如下:4(TCPv4)6(TCPv6)-1(unixdomainsocket)"udp4"or"udp6"(UDPv4orv6)Event:'message'worker{cluster.Worker}message{Object}handle{undefined|Object}当主线程集群从任何一个工作进程中接收到信息时触发。详见child_processevent:'message'.在Node.jsv6.0之前,这个事件触发没有worker对象,只有message和handle,与文档中的说明不同。如果需要对旧版本的支持,但工作对象不是必须,可以通过检查参数个数。cluster.on('message',(worker,message,handle)=>{if(arguments.length===2){handle=message;message=worker;worker=undefined;}//...});Event:'online'worker{cluster.Worker}在分派新工作进程后,应通过online信息响应。当主进程接收到online消息时,它将发出此事件。'fork'和'online'事件的不同在于前者是在主进程新建工作进程后触发,而后者是在工作进程运行的时候触发。cluster.on('online',(worker)=>{console.log('Yay,theworkerrespondedafteritwasforked');});Event:'setup'settings{Object}Emittedeverytime.setupMaster()iscalled.每当.setupMaster()被调用的时候触发。settings对象是setupMaster()被调用时的cluster.settings对象,并且只能查询,因为在一个tick内.setupMaster()可以被调用多次。如果精确度十分重要,请使用cluster.settings。cluster.disconnect([callback])callback{Function}当所有工作进程都断开连接并且所有handle关闭的时候调用。在cluster.workers的每个工作进程中调用.disconnect()。当所有工作进程断开连接后,所有内部handle将会关闭,这个时候如果没有等待事件的话,运行主进程优雅地关闭。这个方法可以选择添加一个回调参数,当结束时会调用这个回调函数。这个方法只能由主进程调用。cluster.fork([env])env{Object}增加进程环境变量,以Key/value对的形式。Returns:{cluster.Worker}衍生出一个新的工作进程。只能通过主进程调用。cluster.isMaster{boolean}当该进程是主进程时,返回true。这是由process.env.NODE_UNIQUE_ID决定的,当process.env.NODE_UNIQUE_ID未定义时,isMaster为true。cluster.isWorker{boolean}当进程不是主进程时,返回true。(和cluster.isMaster刚好相反)cluster.schedulingPolicy调度策略,包括循环计数的cluster.SCHED_RR,以及由操作系统决定的cluster.SCHED_NONE。这是一个全局设置,当第一个工作进程被衍生或者调动cluster.setupMaster()时,都将第一时间生效。除Windows外的所有操作系统中,SCHED_RR都是默认设置。只要libuv可以有效地分发IOCPhandle,而不会导致严重的性能冲击的话,Windows系统也会更改为SCHED_RR。cluster.schedulingPolicy可以通过设置NODE_CLUSTER_SCHED_POLICY环境变量来实现。这个环境变量的有效值包括"rr"和"none"。cluster.settings{Object}execArgv{Array}传递给Node.js可执行文件的参数列表。默认:process.execArgvexec{string}worker文件的路径.默认:process.argv[1]args{Array}传递给worker的参数。默认:process.argv.slice(2)cwd{string}当前工作进程的工作目录默认:undefined(继承自父进程)silent{boolean}是否需要发送输出到父进程的stdio上。默认:falsestdio{Array}配置fork进程的stdio。由于cluster模块运行依赖于IPC,这个配置必须包含'ipc'。当提供了这个选项后,将撤销silent。uid设置进程的user标识符。(见setuid(2).)gid设置进程的group标识符。(见setgid(2).)inspectPort{number|Function}配置端口检查。这可以是一个数字或是不带参数的返回一个数字的函数。默认每个工作进程获取自己端口,从主线程的process.debugPort中递增。windowsHide{boolean}隐藏fork进程控制台窗口通常是在Windows系统上创建的。默认:false在调用.setupMaster()(或.fork())后,这个设置对象将包含这些设置。包含默认值。此对象不能手动更改或设置。cluster.setupMaster([settings])settings{Object}看cluster.settingssetupMaster常被用于改变默认的'fork'行为.一旦调用,这些设置将传递到cluster.settings中。注意这点:任何设置的改变仅仅影响将来调用.fork()并且对运行中工作进程没有影响。唯一无法通过.setupMaster()设置的属性是传递给.fork()的env属性上述的默认值只在第一次调用时有效,当后续调用时,将采用cluster.setupMaster()调用时的当前值。例如:constcluster=require('cluster');cluster.setupMaster({exec:'worker.js',args:['--use','https'],silent:true});cluster.fork();//https工作进程cluster.setupMaster({exec:'worker.js',args:['--use','http']});cluster.fork();//http工作进程这个只能从主进程中调用。cluster.worker{Object}对当前工作进程对象的引用。在主进程中不可用。constcluster=require('cluster');if(cluster.isMaster){console.log('Iammaster');cluster.fork();cluster.fork();}elseif(cluster.isWorker){console.log(`Iamworker#${cluster.worker.id}`);}cluster.workers{Object}通过哈希存储这个活动的工作进程对象,键名id字段。存储活动工作对象的哈希,由id字段标识。通过它很容易找到所有的工作进程。它只在主进程中可用。一个工作进程从cluster.workers中移除后,这个工作进程已经断开连接并退出,但是这个两个事件的先后顺序是不确定的。然而,可以确定的是工作进程从cluster.workers中移除是在'disconnect'和'exit'其中一个事件最后触发之前执行的。//GothroughallworkersfunctioneachWorker(callback){for(constidincluster.workers){callback(cluster.workers[id]);}}eachWorker((worker)=>{worker.send('bigannouncementtoallworkers');});使用工作进程的唯一id定位工作进程是最好的方法。socket.on('data',(id)=>{constworker=cluster.workers[id];});

Cluster 1871 5年前
npm包的制作与发布使用
大苹果

npm包的制作与发布使用

制作npm包的步骤注册npm账户npmadduser或者登陆npm官网注册登陆npm账户npmlogin编写npm包初始化package.json并配置npminit{"name":"包名","version":"版本号","description":"描述","main":"入口文件","scripts":{"test":"配置测试"},//配置git仓库"repository":{"type":"git","url":"git仓库地址"},"keywords":["关键字"],"author":"","license":"MIT","dependencies":{}}发布npmpublish#指定标签betanpmpublish--tagbeta删除npmunpublish--force包名命令行工具包的制作{"name":"工具名称","version":"版本号","description":"描述","bin":{"命令名称":"命令入口文件"},"author":"zengyuwen","license":"MIT"}代码编写#!/usr/bin/envnode//获取命令行参数constarguments=process.argv.splice(2);代码示例命令行工具package.json{"name":"cli","version":"1.0.0","description":"","main":"index.js","scripts":{"test":"echo\"Error:notestspecified\"&&exit1"},"bin":{"my-cli":"./index.js"},"author":"","license":"ISC"}index.js#!/usr/bin/envnode//获取命令行参数constarguments=process.argv.splice(2);//参数解析成数组functionparse(arguments){returnarguments.map(function(value){if(/^-+/.test(value)){returnvalue.replace(/^-+/,'');}returnvalue;});}//参数解析成键值对functionparseObj(arguments){letresult=[];for(leti=0;i<arguments.length;i+=2){tmp={};if((i+1)>=arguments.length){tmp[arguments[i].replace(/^-+/,'')]=null;}else{tmp[arguments[i].replace(/^-+/,'')]=arguments[i+1];}result.push(tmp);}returnresult;}//求和functionadd(arguments){varparams=parse(arguments);varresult=0;for(letiofparams){if(/^[0-9]{1,}$/.test(i)){result+=parseInt(i);}}returnresult;}console.log(parseObj(arguments));console.log(add(arguments));

npm 1832 5年前
nodejs之Buffer学习笔记(一)
大苹果

nodejs之Buffer学习笔记(一)

类方法:Buffer.alloc(size[,fill[,encoding]])History版本变化v8.9.3指定无效的字符串填充将会被替换为0来填充缓冲区。v5.10.0新增于:v5.10.0参数参数字段类型说明sizeinteger指定新创建的Buffer大小fillstring|Buffer|integer指定填充Buffer的值。Default:0encoding<string>如果填充的是字符串,这个参数指定的就是字符串的编码。默认:'utf8'分配一个指定字节大小的新Buffer。如果填充未定义,则Buffer将被零填充。constbuf=Buffer.alloc(5);//Prints:<Buffer0000000000>console.log(buf);buf.toString([encoding[,start[,end]]])新增于:v0.1.90encoding<string>要解码的字符编码。默认:'utf8'start<integer>开始解码的字节偏移量。默认:0end<integer>在(不包括)停止解码的字节偏移量.默认:buf.lengthReturns:<string>根据编码中指定的字符编码将BUF解码为字符串。可以通过开始和结束仅解码BUF的子集字符串实例的最大长度(在UTF-16编码单元中)可以作为Buffer-Struts.MaxString长度来使用。constbuf2=Buffer.alloc(11,"你好12345",'utf8');//以下输出:<Buffere4bda0e5a5bd3132333435>console.log(buf2);//以下输出:你好12345console.log(buf2.toString('utf8'));constbuf3=Buffer.alloc(11,"你好123456",'utf8');//以下输出:<Buffere4bda0e5a5bd3132333435>console.log(buf2);//以下输出:你好12345=>buf3长度11,utf-8编码中一个汉字占三个字节,你好123456共12个字节,所以丢失一个字节console.log(buf2.toString('utf8'));

Buffer,node 6321 5年前
1 2 3 4 >> 共 4 页