GVKun编程网logo

如何使用IDE在Storm生产集群中提交拓扑

10

本文将分享如何使用IDE在Storm生产集群中提交拓扑的详细内容,此外,我们还将为大家带来关于c–Xcode在函数中使用时如何使用IBOutlet进行标记、Elasticsearch系列---生产集群

本文将分享如何使用IDE在Storm生产集群中提交拓扑的详细内容,此外,我们还将为大家带来关于c – Xcode在函数中使用时如何使用IBOutlet进行标记、Elasticsearch系列---生产集群的索引管理、Elasticsearch系列---生产集群部署(上)、Elasticsearch系列---生产集群部署(下)的相关知识,希望对你有所帮助。

本文目录一览:

如何使用IDE在Storm生产集群中提交拓扑

如何使用IDE在Storm生产集群中提交拓扑

我在Must submit topologies using the ''storm'' client script so thatStormSubmitter knows which jar toupload使用IDE向生产集群提交拓扑时遇到了一个问题,而如果我在命令行中使用storm jarcommand
执行同样的事情,它的运行就像天堂一样。我从githublink看到了同样的例子。

为了提交拓扑,我正在使用这些行集

conf.put(Config.NIMBUS_HOST, NIMBUS_NODE);conf.put(Config.NIMBUS_THRIFT_PORT,6627);conf.put(Config.STORM_ZOOKEEPER_PORT,2181);conf.put(Config.STORM_ZOOKEEPER_SERVERS,ZOOKEEPER_ID);conf.setNumWorkers(20);conf.setMaxSpoutPending(5000);StormSubmitter submitter = new StormSubmitter();submitter.submitTopology("test", conf, builder.createTopology());

请建议我这是否是运行的正确方法?

答案1

小编典典

很好找到解决方案。当我们运行“ storm
jar”时,它将在提交的jar中触发storm.jar的属性标志。因此,如果我们要以编程方式提交jar,则只需以这种方式设置标志

System.setProperty("storm.jar", <path-to-jar>);

例如:

System.setProperty("storm.jar", "/Users/programming/apache-storm-1.0.1/lib/storm-core-1.0.1.jar");StormSubmitter.submitTopology("myTopology", config, builder.createTopology());

c – Xcode在函数中使用时如何使用IBOutlet进行标记

c – Xcode在函数中使用时如何使用IBOutlet进行标记

我与我的标签有关:

@property (weak) IBOutlet NSTextField *scoreBox;

正确的,我正试图像这样访问它:

void namedfunction(button) {

    if (button == button) {
        score = score + 100;
        [scoreBox setIntValue:score];
      // ^ error
    }
}

我收到这个错误:

AppDelegate.m:52:10: Use of undeclared identifier ‘scoreBox’

我究竟做错了什么?

解决方法

使用

[_scoreBox setIntValue:score];

要么

[self.scoreBox setIntValue:score];

*同时检查您最终是否比较相同的按钮,如:button ==按钮.

编辑2:

因为你的代码是:

int perus(int nappi){

}

将其更改为:

- (NSInteger *)perus:(NSInteger *)nappi{
    //all should do inside,rest are OK.
}

编辑:

我不确定这个here发现了以下内容

*我建议你切换到obj-c方法,而不是使用C函数来做这种事情.

C函数只是一个代码块,没有附加到任何东西
其他.您的实例变量附加到每个Controller对象.
所以当你调用printChatter()时,无法知道哪一个
您要使用的Controller实例.你可以添加一个对象
变量到你的功能:

void namedfunction(const void *button,const void *appDele){
    NSTextField *myButton=[appDele scoreBox];
    ....
}

Elasticsearch系列---生产集群的索引管理

Elasticsearch系列---生产集群的索引管理

概要

索引是我们使用Elasticsearch里最频繁的部分日常的操作都与索引有关,本篇从运维人员的视角,来玩一玩Elasticsearch的索引操作。

基本操作

在运维童鞋的视角里,索引的日常操作除了CRUD,还是打开关闭、压缩、alias重置,我们来了解一下。

创建索引

[esuser@elasticsearch02 ~]$curl -XPUT ''http://elasticsearch02:9200/music?pretty'' -H ''Content-Type: application/json'' -d ''
{
    "settings" : {
        "index" : {
            "number_of_shards" : 3, 
            "number_of_replicas" : 2 
        }
    },
    "mappings" : {
        "type1" : {
            "properties" : {
                "name" : { "type" : "text" }
            }
        }
    }
}''

{
    "acknowledged": true,
    "shards_acknowledged": true
}

默认情况下,索引创建命令会在每个primary shard的replica shard 开始进行复制后,或者是请求超时之后,返回响应消息,如上。

acknowledged表示这个索引是否创建成功,shards_acknowledged表明了每个primary shard有没有足够数量的replica开始进行复制。

可能这两个参数会为false,但是索引依然可以创建成功。因为这些参数仅仅是表明在请求超时之前,这两个操作有没有成功,也有可能请求超时了,在超时前都没成功,但是实际上Elasticsearch Server端接收到了消息,并且都执行了,只是响应前还没来得及执行,所以响应的是false。

删除索引

curl -XDELETE ''http://elasticsearch02:9200/music?pretty''

查询索引设置信息

curl -XGET ''http://elasticsearch02:9200/music?pretty''

打开/关闭索引

curl -XPOST ''http://elasticsearch02:9200/music/_close?pretty''
curl -XPOST ''http://elasticsearch02:9200/music/_open?pretty''

如果一个索引关闭了,那么这个索引就没有任何的性能开销了,只要保留这个索引的元数据即可,然后对这个索引的读写操作都不会成功。一个关闭的索引可以接着再打开,打开以后会进行shard recovery过程。

如果集群数据定时有备份,在执行恢复的操作之前,必须将待恢复的索引关闭,否则恢复会报失败。

压缩索引

我们知道索引的primary shard数量在创建时一旦指定,后期就不能修改了,但是有一个这样的情况:预估的shard数量在实际生产之后,发现估算得有点高,比如原来设置number_of_shards为8,结果生产上线后发现数据量没那么大,我想把这个索引的primary shard压缩一下,该如何操作呢?

shrink命令的作用就是对索引进行压缩的,不过有个限制:压缩后的shard数量必须可以被原来的shard数量整除。如我们的8个primary shard的index可以只能被压缩成4个,2个,或者1个primary shard的index。

shrink命令的工作流程:
  1. 创建一个跟source index的定义一样的target index,但是唯一的变化就是primary shard变成了指定的数量。
  2. 将source index的segment file直接用hard-link的方式连接到target index的segment file,如果操作系统不支持hard-link,那么就会将source index的segment file都拷贝到target index的data dir中,会很耗时。如果用hard-link会很快。
  3. target index进行shard recovery恢复。
案例演示
  1. 我们创建一个number_of_shards为8的索引,名称为music8
curl -XPUT ''http://elasticsearch02:9200/music8?pretty'' -H ''Content-Type: application/json'' -d ''
{
    "settings" : {
        "index" : {
            "number_of_shards" : 8, 
            "number_of_replicas" : 2 
        }
    },
    "mappings" : {
        "children" : {
            "properties" : {
                "name" : { "type" : "text" }
            }
        }
    }
}''
  1. 在索引内灌点数据进去
  2. 将索引的shard都移到一个node上去,如node1
curl -XPUT ''http://elasticsearch02:9200/music8/_settings?pretty'' -H ''Content-Type: application/json'' -d ''
{
  "settings": {
    "index.routing.allocation.require._name": "node-1", 
    "index.blocks.write": true 
  }
}''

这个过程叫shard copy relocate,使用

`curl -XGET ''http://elasticsearch02:9200/_cat/recovery?v''

可以查看该过程的进度。

  1. 执行shrink命令,新的索引名称为music9
curl -XPOST ''http://elasticsearch02:9200/music8/_shrink/music9?pretty'' -H ''Content-Type: application/json'' -d ''
{
  "settings": {
    "index.number_of_shards": 2, 
    "index.number_of_replicas": 1,
    "index.codec": "best_compression" 
  }
}''

执行完成后,可以看到music9的shard数据变化了,并且拥有music8所有的数据。

  1. 将别名指向新的music9索引,客户端访问无感知。

rollover索引

我们最常见的日志索引,需要每天创建一个新的带日期的索引,但客户端又使用同一个alias进行写入,此时可以用rollover命令将alias重置到这个新的索引上。

假设log_write别名已经存在,示例命令:

curl -XPOST ''http://elasticsearch02:9200/log_write/_rollover/log-20120122
-H ''Content-Type: application/json'' -d ''
{
  "conditions": {
    "max_age":   "1d"
  }
}''

用crontab定时每天执行一次,并且将日期部分用shell脚本进行参数化,这样每天都创建一个带日期的索引名字,而客户端那边一直使用log_write别名作写入操作,对日志系统非常实用。

索引mapping管理

索引的mapping管理是非常基础的操作,我们可以在创建索引时定义mapping信息,也可以在索引创建成功后执行增加字段操作。

列举以下几个常用示例:

查看索引的mapping信息

curl -XGET ''http://elasticsearch02:9200/music/_mapping/children?pretty''

查看索引指定field的mapping信息

curl -XGET ''http://elasticsearch02:9200/music/_mapping/children/field/content?pretty''

创建索引时带上mapping信息

# 节省篇幅,省略大部分字段
curl -XPUT ''http://elasticsearch02:9200/music?pretty'' -H ''Content-Type: application/json'' -d '' 
{
  "mappings": {
    "children": {
      "properties": {
        "content": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        }
      }
    }
  }
}''

为索引增加一个字段name,类型为text

curl -XPUT ''http://elasticsearch02:9200/music/_mapping/children?pretty'' -H ''Content-Type: application/json'' -d '' 
{
  "properties": {
    "name": {
      "type": "text"
    }
  }
}''

索引别名

客户端访问Elasticsearch的索引时,规范化操作都不会直接使用索引名称,而是使用索引别名,索引别名能够起到封装Elasticsearch真实索引的作用,像上面的rollover操作,索引重建操作,别名起到了非常关键的作用。

我们来简单看一下索引的基本操作:

# 创建索引别名
curl -XPOST ''http://elasticsearch02:9200/_aliases?pretty'' -H ''Content-Type: application/json'' -d ''
{
    "actions" : [
        { "add" : { "index" : "music", "alias" : "music_prd" } }
    ]
}''
# 删除索引别名
curl -XPOST ''http://elasticsearch02:9200/_aliases?pretty'' -H ''Content-Type: application/json'' -d ''
{
    "actions" : [
        { "remove" : { "index" : "music", "alias" : "music_prd" } }
    ]
}''
# 重命名别名:先删掉后添加
curl -XPOST ''http://elasticsearch02:9200/_aliases?pretty'' -H ''Content-Type: application/json'' -d ''
{
    "actions" : [
        { "remove" : { "index" : "music", "alias" : "music_prd" } },
        { "add" : { "index" : "music2", "alias" : "music_prd" } }
    ]
}''
# 多个索引绑定一个别名
curl -XPOST ''http://elasticsearch02:9200/_aliases?pretty'' -H ''Content-Type: application/json'' -d ''
{
    "actions" : [
        { "add" : { "indices" : ["music1", "music2"], "alias" : "music_prd" } }
    ]
}''

索引setting修改

查看索引setting信息:

curl -XGET ''http://elasticsearch02:9200/music/_settings?pretty''

修改setting信息:

curl -XPUT ''http://elasticsearch02:9200/music/_settings?pretty'' -H ''Content-Type: application/json'' -d ''
{
    "index" : {
        "number_of_replicas" : 1
    }
}''

setting最常见的修改项就是replicas的数量,其他的参数修改的场景不是特别多。

索引template

假设我们正在设计日志系统的索引结构,日志数据量较大,可能每天创建一个新的索引,索引名称按日期标记,但别名是同一个,这种场景就比较适合使用index template。

我们举个示例,先创建一个索引模板:

curl -XPUT ''http://elasticsearch02:9200/_template/template_access_log?pretty'' -H ''Content-Type: application/json'' -d ''
{
  "template": "access-log-*",
  "settings": {
    "number_of_shards": 2
  },
  "mappings": {
    "log": {
      "_source": {
        "enabled": false
      },
      "properties": {
        "host_name": {
          "type": "keyword"
        },
        "thread_name": {
          "type": "keyword"
        },
        "created_at": {
          "type": "date",
          "format": "YYYY-MM-dd HH:mm:ss"
        }
      }
    }
  },
  "aliases" : {
      "access-log" : {}
  }
}''

索引名称符合"access-log-*"将使用该模板,我们创建一个索引:

curl -XPUT ''http://elasticsearch02:9200/access-log-01?pretty''

查看该索引:

curl -XGET ''http://elasticsearch02:9200/access-log-01?pretty''

可以看到如下结构:

[esuser@elasticsearch02 bin]$ curl -XGET ''http://elasticsearch02:9200/access-log-01?pretty''
{
  "access-log-01" : {
    "aliases" : {
      "access-log" : { }
    },
    "mappings" : {
      "log" : {
        "_source" : {
          "enabled" : false
        },
        "properties" : {
          "created_at" : {
            "type" : "date",
            "format" : "YYYY-MM-dd HH:mm:ss"
          },
          "host_name" : {
            "type" : "keyword"
          },
          "thread_name" : {
            "type" : "keyword"
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1581373546223",
        "number_of_shards" : "2",
        "number_of_replicas" : "1",
        "uuid" : "N8AHh3wITg-Zh4T6umCS2Q",
        "version" : {
          "created" : "6030199"
        },
        "provided_name" : "access-log-01"
      }
    }
  }
}

说明使用了模板的内容。

当然也有命令可以查看和删除template:

curl -XGET ''http://elasticsearch02:9200/_template/template_access_log?pretty''

curl -XDELETE ''http://elasticsearch02:9200/_template/template_access_log?pretty''

索引常用查询

索引操作统计查询

发生在索引上的所有CRUD操作,Elasticsearch都是会做统计的,而且统计的内容非常翔实,我们可以使用这条命令:

curl -XGET ''http://elasticsearch02:9200/music/_stats?pretty''

内容非常详细,有好几百行,从doc的数据和占用的磁盘字节数,到get、search、merge、translog等底层数据应有尽有。

segment信息查询

索引下的segment信息,可以使用这条命令进行查询:

curl -XGET ''http://elasticsearch02:9200/music/_segments?pretty''

内容也同样挺多,我们摘抄出关键的部分做个示例:

"segments" : {
  "_1" : {
    "generation" : 1,
    "num_docs" : 1,
    "deleted_docs" : 0,
    "size_in_bytes" : 7013,
    "memory_in_bytes" : 3823,
    "committed" : true,
    "search" : true,
    "version" : "7.3.1",
    "compound" : true,
    "attributes" : {
      "Lucene50StoredFieldsFormat.mode" : "BEST_SPEED"
    }
  }
}

这个片段表示名称为_1的segment的信息。详细如下:

  • _1:segment的名称
  • generation:segment的自增长ID
  • num_docs:segment中没有被删除的document的数量
  • deleted_docs:segment中被删除的document数量
  • size_in_bytes:segment占用的磁盘空间
  • memory_in_bytes:segment会将一些数据缓存在内存中,这个数值就是segment占用的内存的空间大小
  • committed:segment是否被sync到磁盘上去了
  • search:segment是否可被搜索,如果这个segment已经被sync到磁盘上,但是还没有进行refresh,值为false
  • version:lucene的版本号
  • compound:true表示lucene已将这个segment所有的文件都merge成了一个文件
shard存储信息

查看索引下shard的存储情况,分布在哪个node上,这条命令还是挺有用处的:

curl -XGET ''http://elasticsearch02:9200/music/_shard_stores?status=green&pretty''

摘抄了一个片段,3表示shard的id:

"3" : {
  "stores" : [
    {
      "A1s1uus7TpuDSiT4xFLOoQ" : {
        "name" : "node-2",
        "ephemeral_id" : "Q3uoxLeJRnWQrw3E2nOq-Q",
        "transport_address" : "192.168.17.137:9300",
        "attributes" : {
          "ml.machine_memory" : "3954196480",
          "rack" : "r1",
          "xpack.installed" : "true",
          "ml.max_open_jobs" : "20",
          "ml.enabled" : "true"
        }
      },
      "allocation_id" : "o-t-AwGZRrWTflYLP030jA",
      "allocation" : "primary"
    },
    {
      "RGw1IXzZR4CeZh9FUrGHDw" : {
        "name" : "node-1",
        "ephemeral_id" : "B1pv6c4TRuu1vQNvL40iPg",
        "transport_address" : "192.168.17.138:9300",
        "attributes" : {
          "ml.machine_memory" : "3954184192",
          "rack" : "r1",
          "ml.max_open_jobs" : "20",
          "xpack.installed" : "true",
          "ml.enabled" : "true"
        }
      },
      "allocation_id" : "SaXqL8igRUmLAoBBQyQNqw",
      "allocation" : "replica"
    }
  ]
},
补充几个操作
  1. 清空索引缓存

curl -XPOST ''http://elasticsearch02:9200/music/_cache/clear?pretty''

  1. 强制flush

强行将os cache里的数据强制fsync到磁盘上去,同时还会清理掉translog中的日志

curl -XPOST ''http://elasticsearch02:9200/music/_flush?pretty''

  1. refresh操作

显式地刷新索引,让在自动refresh前的所有操作变成可见

curl -XPOST ''http://elasticsearch02:9200/music/_flush?pretty''

  1. force merge

强制合并segment file,可以减小segment的数量
curl -XPOST ''http://elasticsearch02:9200/music/_forcemerge?pretty''

以上4个操作,一般是由Elasticsearch自动去执行,非特殊情况下不需要人工干预。

小结

本篇从运维角度简单介绍了一下索引的一些日常操作与管理,能够熟练应用的话,可以提升操纵索引的效率。

专注Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区
可以扫左边二维码添加好友,邀请你加入Java架构社区微信群共同探讨技术
Java架构社区.jpg

Elasticsearch系列---生产集群部署(上)

Elasticsearch系列---生产集群部署(上)

概要

本篇开始介绍Elasticsearch生产集群的搭建及相关参数的配置。

ES集群的硬件特性

我们从开始编程就接触过各种各样的组件,而每种功能的组件,对硬件要求的特性都不太相同,有的需要很强的CPU计算能力,有的对内存需求量大,有的对网卡要求高等待,下面我们讨论一下ES集群对几种硬件的特性需求。

CPU

ES集群对CPU的要求相对低一些,毕竟纯计算的比重要小一些,选用主流的CPU,2核到8核的都可以。

如果有两种CPU可以挑选,一种是主频高但核数少的CPU,另一种是主频一般核数多的CPU,肯定选后一种,因为多核的CPU可以提供更多的并发处理能力,远比单核高性能带来的效益要高。

内存

ES集群对内存的要求很高,部署ES集群时,要把大部分资源投入到内存当中。内存分配主要有两部分,JVM heap内存(堆内存)和OS Cache内存。

JVM heap内存用得不多,主要是OS Cache,我们知道,ES建立的倒排索引,正排索引,过滤器缓存,都是优先放在内存当中的,OS Cache的大小直接决定搜索的性能,如果OS Cache不够,ES搜索等操作只有被迫读硬盘,延时就会从毫秒级升到秒级。

OS Cache具体在多大才算够,取决于数据量,如果是百万级别的数据,16GB左右应该可以接受,如果是亿级,一般单节点都是64GB内存。生产环境最低要求内存应不低于8GB。

硬盘

硬盘成本本身比较便宜,能用SSD就用SSD,访问速度肯定比机械硬盘快,预估好数据量后就尽可能多规划一些容量。

另外尽量使用本地存储,网络存储还依赖于网络传输,这个容易造成一些延迟。

网络

对ES集群这种分布式系统来说,快速并且可靠的网络还是比较重要的,shard的分配和rebalance都需要占用大量的带宽,集群最好部署在同一个局域网内,异地容灾等跨数据中心的部署方案,要考虑到网络故障带来的影响。

JVM选择

使用ES官网推荐的JDK版本,服务端和客户端尽量使用同一个版本的JDK。

涉及到ES服务端的JVM调优设置,保持原样不要轻易改动,毕竟ES已经花了大量人力物力验证过的,随意调整jvm参数可能适得其反。

容量规划

规划集群里,要规划好投入几台服务器,数据量上限是多少,业务模型数据读写的比例是多少,历史数据的迁移方案等,一般来说,百万到10亿内的数据量,使用ES集群还是能够支撑下来的,ES节点数建议不要超过100个。

举个例子:数据量10亿以内,部署5台服务器,8核64GB内存,是能够支撑的。

生产案例模拟

Linux操作系统搭建

我们使用Linux虚拟机来演示一个生产ES集群的搭建。我们创建4台虚拟机,每台2核CPU,4GB内存,操作系统为CentOS 7 64bit。

虚拟机我用的是VMware workstation,有用virtual box也行,CentOS 7、JDK的安装不赘述。记得把CentOS的防火墙关了。

修改每台机器的hostname信息,命令
vi /etc/hostname,修改文件,保存即可,建议修改成elasticsearch01,elasticsearch02,elasticsearch03,elasticsearch04。

假定我们4台虚拟机的域名和IP是这样分配的:

192.168.17.138 elasticsearch01
192.168.17.137 elasticsearch02
192.168.17.132 elasticsearch03
192.168.17.139 elasticsearch04

把这段配置放在 /etc/hosts文件末尾,4台机器做相同的配置。

这4台机器之间,可以配置免密登录,如在elasticsearch01机器上,我们执行以下操作:

  1. 生成公钥文件,命令:
ssh-keygen -t rsa

一直输入回车,不要设置密码默认会将公钥放在/root/.ssh目录下生成id_rsa.pub和id_rsa两个文件

  1. 拷贝公钥文件
cp id_rsa.pub authorized_keys
  1. 将公钥文件拷贝到另外三台机器
ssh-copy-id -i elasticsearch02
ssh-copy-id -i elasticsearch03
ssh-copy-id -i elasticsearch03

拷贝完成后,可以在目标机器上/root/.ssh/目录下看到多了一个authorized_keys文件。

  1. 尝试免密登录,在elasticsearch01机器上输入ssh elasticsearch02,如果不需要输入密码就能登录到elasticsearch02,说明配置成功,其他机器类似。

这4台机器也可以相互做ssh免密设置。

这里补充一点免密登录的方向性问题,上面的案例是在elasticsearch01机器生成的公钥,并且发送给了elasticsearch02等三台机器,那么我从elasticsearch01跳到elasticsearch02是不需要密码的,反过来从elasticsearch02登录到elasticsearch01,还是需要密码的。

最后补充几个常用检查命令:

  • 检查NetManager的状态:systemctl status NetworkManager.service
  • 检查NetManager管理的网络接口:nmcli dev status
  • 检查NetManager管理的网络连接:nmcli connection show

Elasticsearch服务端

这里选用的JDK版本为1.8.0_211,Elasticsearch版本为6.3.1,自行安装不赘述。

ES解压后的目录结构:

# 用 "tree -L 1" 命令得到的树状结构
.
├── bin
├── config
├── lib
├── LICENSE.txt
├── logs
├── modules
├── NOTICE.txt
├── plugins
└── README.textile
  • bin:存放es的一些可执行脚本,比如用于启动进程的elasticsearch命令,以及用于安装插件的elasticsearch-plugin插件
  • config:用于存放es的配置文件,比如elasticsearch.yml
  • logs:用于存放es的日志文件
  • plugins:用于存放es的插件
  • data:用于存放es的数据文件的默认目录,就是每个索引的shard的数据文件,一般会另外指定一个目录。

Elasticsearch参数设置

在config目录下的文件,包含了ES的基本配置信息:

.
├── elasticsearch.yml
├── jvm.options
├── log4j2.properties
├── role_mapping.yml
├── roles.yml
├── users
└── users_roles

默认参数

Elasticsearch的配置项比较丰富并且默认配置已经非常优秀了,基本上我们需要改动的是跟服务器环境相关的配置,如IP地址,集群名称,数据存储位置,日志存储位置等外围参数,涉及到内部机制及JVM参数的,一般不干预,不恰当的JVM参数调整反而会导致集群出现性能故障,如果没有充足的理由或数据验证结果,不要轻易尝试修改。

集群和节点名称

在elasticsearch.yml文件里这项配置表示集群名称,配置项默认是注释掉的,集群名称默认为elasticsearch。

#cluster.name: my-application

这个配置项强烈建议打开,用项目约定的命名规范进行重命名,并且将研发环境、测试环境、STG准生产环境、生产环境分别命名,如elasticsearch_music_app_dev表示研发环境,elasticsearch_music_app_sit表示测试环境,elasticsearch_music_app_pro表示生产环境等。避免开发测试环境连错环境,无意中加入集群导致数据问题。

cluster.name: elasticsearch_music_app_pro

节点名称的配置项

#node.name: node-1

默认也是注释掉的,ES启动时会分配一个随机的名称,建议还是自行分配一个名称,这样容易记住是哪台机器,如

node.name: es_node_001_data

文件路径

涉及到文件路径的几个参数,主要有数据、日志、插件等,默认这几个地址都是在Elasticsearch安装的根目录下,但Elasticsearch升级时,有些目录可能会有影响,安全起见,可以单独设置目录。

#
# ----------------------------------- Paths ------------------------------------
#
# Path to directory where to store the data (separate multiple locations by comma):
#
#path.data: /path/to/data
#
# Path to log files:
#
#path.logs: /path/to/logs
#

例如我们可以在/var目录下创建相应的文件夹,并且赋予相应的读写权限,如:

path.data: /var/es/data
path.logs: /var/es/logs

日志文件配置

log4j2.properties文件,ES日志框架选用的是log4j2,也就是log4j的进化版本,对Java技术栈熟悉的童鞋,看到这个配置文件会非常熟悉,默认的日志输入配置、格式均能满足日常的故障定位和分析,也不需要什么改动。

默认是一天生成一个日期文件,如果ES承载的数据量特别大,可以调整日志文件产生频率和每个日志文件的大小,以及ES最多存储日志的大小、数量。

Elasticsearch集群发现机制

配置参数

Zen Discovery是Elasticsearch集群发现机制的默认实现,底层通信依赖transport组件,我们完成Elasticsearch集群的配置主要有下面几个参数:

  • cluster.name 指定集群的名称。
  • node.name 节点名称。
  • network.host 节点绑定的IP。
  • node.master 可选值为true/false,决定该节点类型为master eligible或data node。
  • discovery.zen.ping.unicast.hosts gossip路由服务的IP地址,即集群发现协议通信的公共节点,可以写多个,有节点启动时会向里面的IP发送消息,获取集群其他节点的信息,最后加入集群。

Elasticsearch集群是点对点(P2P)的分布式系统架构,数据索引、搜索操作是node之间直接通信的,没有中心式的master节点,但Elasticsearch集群内的节点也分成master node和data node两种角色。

正常情况下,Elasticsearch集群只有一个master节点,它负责维护整个集群的状态信息,集群的元数据信息,有新的node加入或集群内node宕机下线时,重新分配shard,并同步node的状态信息给所有的node节点,这样所有的node节点都有一份完整的cluster state信息。

集群发现的一般步骤如下:

  1. 节点配置network.host绑定内网地址,配置各自的node.name信息,cluster.name设置为相同的值。
  2. discovery.zen.ping.unicast.hosts配置了几个gossip路由的node。
  3. 所有node都可以发送ping消息到路由node,再从路由node获取cluster state回来。
  4. 所有node执行master选举。
  5. 所有node都会跟master进行通信,然后加入master的集群。

master选举

node.master设置为true的,将成为master eligible node,也叫master候选节点,只有master eligible node才能被选举成master node。如果是个小集群,那么所有节点都可以是master eligible node,10个节点以上的集群,可以考虑拆分master node和data node,一般建议master eligible node给3个即可。

master选举过程是自动完成的,有几个参数可以影响选举的过程:

  • discovery.zen.ping_timeout: 选举超时时间,默认3秒,网络状况不好时可以增加超时时间。
  • discovery.zen.join_timeout: 有新的node加入集群时,会发送一个join request到master node,同样因为网络原因可以调大,如果一次超时,默认最多重试20次。
  • discovery.zen.master_election.ignore_non_master_pings:如果master node意外宕机了,集群进行重新选举,如果此值为true,那么只有master eligible node才有资格被选为master。
  • discovery.zen.minimum_master_nodes: 新选举master时,要求必须有多少个 master eligible node去连接那个新选举的master。而且还用于设置一个集群中必须拥有的master eligible node。如果这些要求没有被满足,那么master node就会被停止,然后会重新选举一个新的master。这个参数必须设置为我们的master eligible node的quorum数量。一般避免说只有两个master eligible node,因为2的quorum还是2。如果在那个情况下,任何一个master候选节点宕机了,集群就无法正常运作了。

集群故障探查

有两种集群故障探查机制

  1. master主动对集群中所有的其他node发起ping命令,判断它们是否是存活着的。
  2. 每个node向master node发送ping请求,判断master node是否存活,否则就会发起一个选举过程。

有下面三个参数用来配置集群故障的探查过程:

  • ping_interval:ping一次node的间隔时间,默认是1s
  • ping_timeout:每次ping的timeout等待时长,默认是30s
  • ping_retries:对node的ping请求失败了,重试次数,默认3次。

集群状态更新

master node是集群中唯一可以对cluster state进行更新的node。更新的步骤如下:

  1. master node收到更新事件,如shard移动,可能会有多条事件,但master node一次只处理一个集群状态的更新事件。
  2. master node将事件更新到本地,并发布publish message到集群所有的node上。
  3. node接收publish message后,对这个message返回ack响应,但是不会立即更新。
  4. 如果master没有在指定的时间内(discovery.zen.commit_timeout配置项,默认是30s),从至少N个节点(discovery.zen.minimum_master_nodes配置项)获取ack响应,那么这次cluster state change事件就会被reject,最终不会被提交。
  5. 如果在指定时间内,指定数量的node都返回了ack消息,那么cluster state就会被commit,然后master node把 commit message发送给所有的node。所有的node接收到那个commit message之后,接着才会将之前接收到的集群状态应用到自己本地的状态副本中去。
  6. master会等待所有node的commit message 的ack消息,在一个等待超时时长内,如果接收到了响应,表示状态更新成功,master node继续处理内存queue中保存的下一个更新事件。

discovery.zen.publish_timeout默认是30s,这个超时等待时长是从plublish cluster state开始计算的。

我们可以参照此图:

master node宕机问题

Elasticsearch集群中,master node扮演着非常重要的角色,如果master node宕机了,那岂不是群龙无首了?虽然有master选举,但这个也是要时间的,没有master node那段空档期集群该怎么办?

说了一半,基本上是完了,但我们也可以设置,群龙无首时哪些操作可以做,哪些操作不能做。

discovery.zen.no_master_block配置项可以控制在群龙无首时的策略:

  • all: 一旦master宕机,那么所有的操作都会被拒绝。
  • write:默认的选项,所有写操作都会被拒绝,但是读操作是被允许的。

split-brain(脑分裂问题)

在Elasticsearch集群中,master node非常重要,并且只有一个,相当于整个集群的大脑,控制将整个集群状态的更新,如果Elasticsearch集群节点之间出现区域性的网络中断,比如10个节点的Elasticsearch集群,4台node部署在机房A区,6台node部署在机房B区,如果A区与B区的交换机故障,导致两个区隔离开来了,那么没有master node的那个区,会触发master选举,如果选举了新的master,那么整个集群就会出现两个master node,这种现象叫做脑分裂。

这样现象很严重,会破坏集群的数据,该如何避免呢?

回到我们前面提到的discovery.zen.minimum_master_nodes参数,这个值的正确设置,可以避免上述的脑分裂问题。

discovery.zen.minimum_master_nodes参数表示至少需要多少个master eligible node,才可以成功地选举出master,否则不进行选举。

足够的master eligible node计算公式:

quorum = master_eligible_nodes / 2 + 1

如上图我们10个node的集群,如果全部是master eligible node,那么quorum = 10/2 + 1 = 6。

如果我们有3个master eligible node,7个data node,那么quorum = 3/2 + 1 = 2。

如果集群只有2个节点,并且全是master eligible node,那么quorum = 2/2 + 1 = 2,问题就来了,如果随便一个node宕机,在只剩下一个node情况下,无法满足quorum的值,master永远选举不成功,集群就彻底无法写入了,所以只能设置成1,后果是只要这两个node之间网络断了,就会发生脑分裂的现象。

所以一个Elasticsearch集群至少得有3个node,全部为master eligible node的话,quorum = 3/2 + 1 = 2。如果我们设置minimum_master_nodes=2,分析一下会不会出现脑分裂的问题。

场景一:A区一个node,为master,B区两个node,为master eligible node

A区因为只剩下一个node,无法满足quorum的条件,此时master取消当前的master角色,且无法选举成功。

B区两个master eligible node,满足quorum条件,成功选举出master。

此时集群还是只有一个master,待网络故障恢复后,集群数据正常。

场景二:A区一个node,为master eligible node,B区2个node,其中一个是master

A区只有一个master eligible node,不满足quorum的条件,无法进行选举。

B区原本的master存在,不需要进行选举,并且满quorum的条件,master角色可以保留。

此时集群还是一个master,正常。

综上所述:3个节点的集群,全部为master eligible node,配置discovery.zen.minimum_master_nodes: 2,就可以避免脑裂问题的产生。

minimum_master_nodes动态修改

因为集群是可以动态增加和下线节点的,quorum的值也会跟着改变。minimum_master_nodes参数值需要通过api随时修改的,特别是在节点上线和下线的时候,都需要作出对应的修改。而且一旦修改过后,这个配置就会持久化保存下来。

修改api请求如下:

PUT /_cluster/settings
{
    "persistent" : {
        "discovery.zen.minimum_master_nodes" : 2
    }
}

响应报文:

{
  "acknowledged": true,
  "persistent": {
    "discovery": {
      "zen": {
        "minimum_master_nodes": "2"
      }
    }
  },
  "transient": {}
}

也可以通过命令查询当前的配置:

GET /_cluster/settings

响应结果如下:

{
  "persistent": {
    "discovery": {
      "zen": {
        "minimum_master_nodes": "1"
      }
    }
  },
  "transient": {}
}
留一个问题

上图10个节点的集群,假设全是master eligible node,按照上述的网络故障,会不会出现脑分裂现象 ?配置项minimum_master_nodes最低要配置成多少,才不会出现脑分裂的问题?

小结

本篇主要介绍了Elasticsearch集群的部署和参数设置等知识,大部分都不需要人工干预,默认值已经是最优选,集群发现机制和master选举机制了解一下就OK。

专注Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区
可以扫左边二维码添加好友,邀请你加入Java架构社区微信群共同探讨技术
Java架构社区.jpg

Elasticsearch系列---生产集群部署(下)

Elasticsearch系列---生产集群部署(下)

概要

本篇继续讲解Elasticsearch集群部署的细节问题

集群重启问题

如果我们的Elasticsearch集群做了一些离线的维护操作时,如扩容磁盘,升级版本等,需要对集群进行启动,节点数较多时,从第一个节点开始启动,到最后一个节点启动完成,耗时可能较长,有时候还可能出现某几个节点因故障无法启动,排查问题、修复故障后才能加入到集群中,此时集群会干什么呢?

假设10个节点的集群,每个节点有1个shard,升级后重启节点,结果有3台节点因故障未能启动,需要耗费时间排查故障,如下图所示:

整个过程步骤如下:

  1. 集群已完成master选举(node6),master发现未加入集群的node1、node2、node3包含的shard丢失,便立即发出shard恢复的指令。
  2. 在线的7台node,将其中一个replica shard升级为primary shard,并且进行为这些primary shard复制足够的replica shard。
  3. 执行shard rebalance操作。
  4. 故障的3台节点已排除,启动成功后加入集群。
  5. 这3台节点发现自己的shard已经在集群中的其他节点上了,便删除本地的shard数据。
  6. master发现新的3台node没有shard数据,重新执行一次shard rebalance操作。

这个过程可以发现,多做了四次IO操作,shard复制,shard首次移动,shard本地删除,shard再次移动,这样凭空造成大量的IO压力,如果数据量是TB级别的,那费时费力不讨好。

出现此类问题的原因是节点启动的间隔时间不能确定,并且节点越多,这个问题越容易出现,如果可以设置集群等待多少个节点启动后,再决定是否对shard进行移动,这样IO压力就能小很多。

针对这个问题,我们有下面几个参数:

  • gateway.recover_after_nodes:集群必须要有多少个节点时,才开始做shard恢复操作。
  • gateway.expected_nodes: 集群应该有多少个节点
  • gateway.recover_after_time: 集群启动后等待的shard恢复时间

如上面的案例,我们可以这样设置:

gateway.recover_after_nodes: 8
gateway.expected_nodes: 10
gateway.recover_after_time: 5m

这三个参数的含义:集群总共有10个节点,必须要有8个节点加入集群时,才允许执行shard恢复操作,如果10个节点未全部启动成功,最长的等待时间为5分钟。

这几个参数的值可以根据实际的集群规模来设置,并且只能在elasticsearch.yml文件里设置,没有动态修改的入口。

上面的参数设置合理的情况,集群启动是没有shard移动的现象,这样集群启动的时候就可以由之前的几小时,变成几秒钟。

JVM和Thread Pool设置

一提到JVM的调优,大家都有手痒的感觉,好几百个JVM参数,说不定开启了正确的按钮,从此ES踏上高性能、高吞吐量的道路。现实情况可能是我们想多了,ES的大部分参数是经过反复论证的,基本上不用咱们太操心。

JVM GC

Elasticsearch默认使用的垃圾回收器是CMS。

## GC configuration
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly

CMS回收器是并发式的回收器,能够跟应用程序工作线程并发工作,最大程度减少垃圾回收时的服务停顿时间。

CMS还是会有两个停顿阶段,同时在回收特别大的heap时也会有一些问题。尽管有一些缺点,但是CMS对于要求低延时请求响应的软件来说,还是最佳的垃圾回收器,因此官方的推荐就是使用CMS垃圾回收器。

有一种最新的垃圾回收器叫做G1。G1回收器可以比CMS提供更少的回收停顿时间,而且能够这对大heap有更好的回收表现。它会将heap划分为多个region,然后自动预测哪个region会有最多可以回收的空间。通过回收那些region,就可以最小化停顿时长,而且可以针对大heap进行回收。

听起来还挺好的,只是G1还是比较年轻的一种垃圾回收器,而且经常会发现一些新的bug,这些bug可能会导致jvm挂掉。稳定起见,暂时不用G1,等G1成熟后ES官方推荐后再用不迟。

线程池

我们开发Java应用系统时,对系统调优的一个常见手段就是调整线程池,但在ES中,默认的threadpool设置是非常合理的,对于所有的threadpool来说,除了搜索的线程池,都是线程数量设置的跟cpu core一样多的。如果我们有8个cpu core,那么就可以并行运行8个线程。那么对于大部分的线程池来说,分配8个线程就是最合理的数量。

搜索会有一个更加大的threadpool,线程数量一般被配置为:cpu core * 3 / 2 + 1。

Elasticsearch的线程池分成两种:接受请求的线程和处理磁盘IO操作的线程,前面那种由ES管理,后一种由Lucene管理,它们之间会进行协作,ES的线程不会因为IO操作而block住,所以ES的线程设置跟CPU核数一样或略大于CPU核数即可。

服务器的计算能力是非常有限的,线程池的数量过大会导致上下文频繁切换,更费资源,如果threadpool大小设置为50,100,甚至500,会导致CPU资源利用率很低,性能反而下降。

只需要记住:用默认的线程池,如果真要修改,以CPU核数为准。

heap内存设置最佳实践

Elasticsearch默认的jvm heap内存大小是2G,如果是研发环境,我会改成512MB,但在生产环境2GB有点少。

在config/jvm.options文件下,可以看到heap的设置:

# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space

-Xms2g
-Xmx2g

分配规则

Elasticsearch使用内存主要有两个大户:jvm heap和lucene,前者ES用来存放很多数据结构来提供更快的操作性能,后者使用os cache缓存索引文件,包括倒排索引、正排索引,os cache内存是否充足,直接影响查询检索的性能。

一般的分配规则是:jvm heap占用小于一半的内存,剩下的全归lucene使用。

如果单台机器总内存64GB,那么heap顶格内存分配为32GB,因为32GB内存以下,jvm会使用compressed oops来解决object pointer耗费过大空间的问题,超过32GB后,jvm的compressed oops功能关闭,这样就只能使用64位的object pointer,会耗费更多的空间,过大的object pointer还会在cpu,main memory和LLC、L1等多级缓存间移动数据的时候,吃掉更多的带宽。最终的结果可能是50GB内存的效果和32GB一样,白白浪费了十几GB内存。

这里涉及到jvm的object pointer指针压缩技术,有兴趣可以单独了解一下。

如果单台机器总内存小于64GB,一般heap分配为总内存的一半即可,具体要看预估的数据量是多少。

如果使用超级机器,1TB内存的那种,官网不建议上那么牛逼的机器,建议分配4-32GB内存给heap,其他的全部用来做os cache,这样数据量全部缓存在内存中,不落盘查询,性能杠杠滴。

最佳实践建议

  1. 将heap的最小值和最大值设置为一样大。
  2. elasticsearch jvm heap设置得越大,就有越多的内存用来进行缓存,但是过大的jvm heap可能会导致长时间的gc停顿。
  3. jvm heap size的最大值不要超过物理内存的50%,才能给lucene的file system cache留下足够的内存。
  4. jvm heap size设置不要超过32GB,否则jvm无法启用compressed oops,将对象指针进行压缩,确认日志里有[node-1] heap size [1007.3mb], compressed ordinary object pointers [true] 字样出现。
  5. 最佳实践数据:heap size设置的小于zero-based compressed ooops,也就是26GB,但是有时也可以是30GB。通过-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode开启对应,确认有heap address: 0x00000000e0000000, size: 27648 MB, Compressed Oops mode: 32-bit字样,而不是heap address: 0x00000000f4000000, size: 28672 MB, Compressed Oops with base: 0x00000000f3ff0000字样。

swapping问题

部署Elasticsearch的服务尽可能关闭到swap,如果内存缓存到磁盘上,那查询效率会由微秒级降到毫秒级,会造成性能急剧下降的隐患。

关闭办法:

  1. Linux系统执行 swapoff -a 关闭swap,或在/etc/fstab文件中配置。
  2. elasticsearch.yml中可以设置:bootstrap.mlockall: true 锁住自己的内存不被swap到磁盘上。

使用命令 GET _nodes?filter_path=**.mlockall 可以查看是否开启mlockall
响应信息:

{
  "nodes": {
    "A1s1uus7TpuDSiT4xFLOoQ": {
      "process": {
        "mlockall": true
      }
    }
  }
}

Elasticsearch启动的几个问题

  1. root用户启动实例的问题

如果你用root用户启动Elasticsearch的实例,将得到如下的错误提示:

org.elasticsearch.bootstrap.StartupException: java.lang.RuntimeException: can not run elasticsearch as root
    at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:140) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:127) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124) ~[elasticsearch-cli-6.3.1.jar:6.3.1]
    at org.elasticsearch.cli.Command.main(Command.java:90) ~[elasticsearch-cli-6.3.1.jar:6.3.1]
    at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:93) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:86) ~[elasticsearch-6.3.1.jar:6.3.1]
Caused by: java.lang.RuntimeException: can not run elasticsearch as root
    at org.elasticsearch.bootstrap.Bootstrap.initializeNatives(Bootstrap.java:104) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:171) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:326) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:136) ~[elasticsearch-6.3.1.jar:6.3.1]
    ... 6 more

无它,建立一个用户,专门用来启动Elasticsearch的,如esuser,相应的系统目录和数据存储目录都赋予esuser账户为归属者。

  1. 启动时提示elasticsearch process is too low,并且无法启动成功

完整的提示信息:

max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]
memory locking requested for elasticsearch process but memory is not locked

解决办法:设置系统参数,命令行中的esuser为建立的Linux用户。

[root@elasticsearch01 bin]# vi /etc/security/limits.conf

# 在文件最后添加
esuser hard nofile 65536
esuser soft nofile 65536
esuser soft memlock unlimited
esuser hard memlock unlimited

设置完成后,可以通过命令查看结果:

# 请求命令
GET _nodes/stats/process?filter_path=**.max_file_descriptors

# 响应结果
{
  "nodes": {
    "A1s1uus7TpuDSiT4xFLOoQ": {
      "process": {
        "max_file_descriptors": 65536
      }
    }
  }
}
  1. 提示vm.max_map_count [65530] is too low错误,无法启动实例

完整的提示信息:

max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

解决办法:添加vm.max_map_count配置项

临时设置:sysctl -w vm.max_map_count=262144

永久修改:修改vim /etc/sysctl.conf文件,添加vm.max_map_count设置

[root@elasticsearch01 bin]# vim /etc/sysctl.conf

# 在文件最后添加
vm.max_map_count=262144

# 执行命令
[root@elasticsearch01 bin]# sysctl -p

Elasticsearch实例启停

实例一般使用后台启动的方式,在ES的bin目录下执行命令:

[esuser@elasticsearch01 bin]$ nohup ./elasticsearch &
[1] 15544
[esuser@elasticsearch01 bin]$ nohup: 忽略输入并把输出追加到"nohup.out"

这个elasticsearch没有stop参数,停止时使用kill pid命令。

[esuser@elasticsearch01 bin]$ jps | grep Elasticsearch
15544 Elasticsearch
[esuser@elasticsearch01 bin]$ kill -SIGTERM 15544

发送一个SIGTERM信号给elasticsearch进程,可以优雅的关闭实例。

小结

本篇接着上篇的内容,讲解了集群重启时要注意的问题,JVM Heap设置的最佳实践,以及Elasticsearch实例启动时常见的问题解决办法,最后是Elasticsearch优雅关闭的命令。

专注Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区
可以扫左边二维码添加好友,邀请你加入Java架构社区微信群共同探讨技术
Java架构社区.jpg

今天关于如何使用IDE在Storm生产集群中提交拓扑的讲解已经结束,谢谢您的阅读,如果想了解更多关于c – Xcode在函数中使用时如何使用IBOutlet进行标记、Elasticsearch系列---生产集群的索引管理、Elasticsearch系列---生产集群部署(上)、Elasticsearch系列---生产集群部署(下)的相关知识,请在本站搜索。

本文标签: