elasticsearch 2种客户端的区别
Java连接ElasticSearch详解
Java连接ES有两种连接方式(即获得ES的Client)
1、创建一个node,加入集群中,通过这个node获得client。
2、通过TransportClient来连接集群。
区别
第一种方式,相当于额外启动了一个es的node节点,只是这个node是由我们编码控制的,可以设置成这个node是不存数据的节点(没有特殊原因你就应该这么设置),这个节点的配置和其他的节点没有什么区别,也是通过elasticsearch.yml文件来配置,配置文件放在classpath目录下面,当然你也可以通过编程的方式,在构建node的时候,设置settings参数。
Settings settings = ImmutableSettings.settingsBuilder().put("client.transport.ping_timeout", 1000) .put("discovery.zen.ping.multicast.enabled", "false").put("timeout", 1) .putArray("discovery.zen.ping.unicast.hosts", "l-flightdev18.f.dev.cn0.qunar.com:9300", "l-flightdev17.f.dev.cn0.qunar.com:9300") .build(); Node node = NodeBuilder.nodeBuilder().clusterName("flight_fuwu_order_index").client(true).settings(settings).node(); Client client = node.client();
第二种方法,相当于获取了所有node节点的client,发送请求的时候遍历当前可用的client(这里可用指的是可以连接,但是node本身不一定可用,比如node正在恢复中,还没有加入集群,这会产生问题,见下面)。跟方法一相比,方法二会产生双跳(double hop),即当前遍历到node1,但访问的数据在node2,请求先发到node1,又由node1转发到node2。
遍历方式:每次请求通过AtomicInteger进行原子加1(超出最大值后设置成0),按当前可用client数取模。
获取当前可用client的方法:使用线程池,遍历所有client,如果client未连接,尝试一次连接,连接成功加入可用client中,如果client已连接直接加入可用client。
可以通过设置参数client.transport.sniff是否为true,来设置是使用SimpleNodeSampler还是SniffNodesSampler,默认值为false,即使用SimpleNodeSampler。
具体实现在org.elasticsearch.client.transport.TransportClientNodesService类中。
简单的说,SimpleNodeSampler会限制当前可用client一定是在配置中设置的节点中的,而SniffNodesSampler会使用所有发现的client,即使这个client的node,不在配置中。
Settings settings = ImmutableSettings.settingsBuilder().put("cluster.name", clustersName).put( "client.transport.ping_timeout", 1000).put("discovery.zen.ping.multicast.enabled", "false").put( "timeout", 1)// .put("client.transport.sniff", true) .build(); TransportClient transportClient = new TransportClient(settings); for (String cluster : StringUtils.split(clusters,",")) { transportClient.addTransportAddress(new InetSocketTransportAddress("host1", 9300)) .addTransportAddress(new InetSocketTransportAddress("host2", 9300)); }
方法二有个问题,就是当集群中某个node挂掉之后,重新加入的时候,因为是通过判断client是否可以连接,而不是node是否可用,这会导致这个时候使用这个client发送请求的时候产生异常。而方法一不会,因为还没加入集群的node,对于自己创建的node来说是不可见的。
Elasticsearch本身是JAVA开发的,天生对JAVA的支持能力是最好的,所以用JAVA来开发Elasticsearch应该是一个不错的选择。可以把JAVA开发看出CS模式,客户端请求,服务端响应,所有的操作是完全异步的。此外在客户端上操作,很多的操作可以在客户端上来完成,增加了系统效率。JAVA客户端和服务端都是使用相同的程序。
配置
mvn仓库,在mvn项目中可以在pom.xml增加mvn仓库。
1
2
3
4
5
|
< dependency > < groupId >org.elasticsearch</ groupId > < artifactId >elasticsearch</ artifactId > < version >${es.version}</ version > </ dependency > |
打成独立的jar包。如果你想打成独立的jar包,并包含所有的依赖。不能使用mvn的maven-assembly-plugin插件,因为他不能很好的处理 META-INF/services结构。我们可以用 maven-shade-plugin插件来进行打包。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
< plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-shade-plugin</ artifactId > < version >2.4.1</ version > < executions > < execution > < phase >package</ phase > < goals >< goal >shade</ goal ></ goals > < configuration > < transformers > < transformer implementation = "org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> </ transformers > </ configuration > </ execution > </ executions ></ plugin > |
如果你有一个main函数,可以通过调用java -jar yourjar.jar运行时,只需要在插件配置中加入一句话:例如:
1
2
3
|
< transformer implementation = "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer" > < mainClass >com.secisland.es.demo.main</ mainClass > </ transformer > |
可以使用多种方式来调用JAVA客户端。
-
一种方式是把节点加入集群,不存储数据。
-
另一种是把节点作为客户端连接到集群。
注意:客户端版本要和集群版本一致,否则有可能出现不可预知的错误。
嵌入式节点客户端
实例化一个基于节点的客户,这是对Elasticsearch执行操作的最简单的方法。例如:
1
2
3
4
5
6
|
import static org.elasticsearch.node.NodeBuilder.*; // on startup Node node = nodeBuilder().node(); Client client = node.client(); // on shutdown node.close(); |
当你开始一个节点,它加入了一个Elasticsearch集群。你可以通过设置cluster.name来配置不同的集群,或显式使用clusterName方法来创建对象。在项目工程中可以定义cluster.name,配置在/src/main/resources/elasticsearch.yml文件中。只要在classpath中能找到elasticsearch.yml文件,节点启动的时候就会加载此文件。配置文件中定义:
1
|
cluster.name: secilog |
或者在java代码中指定:
1
2
|
Node node = nodeBuilder().clusterName( "secilog" ).node(); Client client = node.client(); |
使用客户端的好处是依赖Elasticsearch服务端的特性吧需要执行的操作自动路由到节点,当加入集群节点作为客户端时,最重要的是不能再此节点上增加数据保存,这可以通过配置来完成。把node.data设置为false或node.client设置true。这都可以通过NodeBuilder的方法来实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import static org.elasticsearch.node.NodeBuilder.*; // on startup // 嵌入式节点可以不用打开http端口 Node node = nodeBuilder() .settings(Settings.settingsBuilder().put( "http.enabled" , false )) .client( true ) .node(); Client client = node.client(); // on shutdown node.close(); |
另一个常见的客户端用处是用在单位/集成测试中。在这种情况下,只需要启动一个“本地(local)”节点。这里的local节点的意思是公用一个 Java虚拟机(JVM)。在这种情况下两个服务会互相发现自己并形成一个集群。例如:
1
2
3
4
5
6
7
8
|
import static org.elasticsearch.node.NodeBuilder.*; // on startup Node node = nodeBuilder().local( true ).node(); Client client = node.client(); // on shutdown node.close(); |
嵌入式节点的缺点:
-
频繁启动和停止一个或多个节点在群集上会创建不必要的噪声。
-
就像其他节点一样嵌入式节点客户端会响应外部请求。
注意:用eclipse开发的时候有可能会报错。
Exception in thread "main" java.lang.IllegalStateException: path.home is not configured
解决方法:在main方法的类中右键选择run configurations,在Arguments标签页下的vm arguments输入:-Des.path.home=-Des.path.home即可。比如:
1
|
-Des.path.home=D:\elasticsearch- 2.2 . 0 |
通过连接的客户端
客户端通过TransportClient对象可以使用远程连接的方式连接Elasticsearch集群。这种情况下不用加入集群,比较像传统的CS程序的架构,比如数据库连接。例如:
1
2
3
4
5
6
7
|
// on startup Client client = TransportClient.builder().build() .addTransportAddress( new InetSocketTransportAddress(InetAddress.getByName( "host1" ), 9300 )) .addTransportAddress( new InetSocketTransportAddress(InetAddress.getByName( "host2" ), 9300 )); // on shutdown client.close(); |
注意:如果群集名称不是“Elasticsearch”,你必须设置集群名称或者使用elasticsearch.yml配置文件在客户端工程中配置。可以连接一个集群中的多个节点。
1
2
3
|
Settings settings = Settings.settingsBuilder() .put( "cluster.name" , "secilog" ).build(); Client client = TransportClient.builder().settings(settings).build(); |
你可以设置client.transport.sniff为true来使客户端去嗅探整个集群的状态,把集群中其它机器的ip地址加到客户端中,这样做的好处是一般你不用手动设置集群里所有集群的ip到连接客户端,它会自动帮你添加,并且自动发现新加入集群的机器。
1
2
3
|
Settings settings = Settings.settingsBuilder() .put( "client.transport.sniff" , true ).build(); TransportClient client = TransportClient.builder().settings(settings).build(); |
其他参数说明
参数 | 描述 |
client.transport.ignore_cluster_name | 设置为true的时候忽略连接节点时的群集名称验证。 |
client.transport.ping_timeout | 等待一个节点的ping响应的时间,默认5秒。 |
client.transport.nodes_sampler_interval | 监听和连接节点的频率,默认5秒。 |