在上我们讲解了es的单节点模式,今天我们说一说ES的集群。
首先我们先知道什么叫元数据:
元数据就是描述数据的信息。其中ES集群的元数据比如有,分片个数,分片的储存位置,document文档中的index,type,version,这些都可以叫做元数据。
角色1:master主节点
首先有主节点就应该联想到高可用,在一个ES集群中,会指定多个master,但是只有一个leader,其他的master同步他的元数据,leader宕机时就会有其他master来顶替。
角色2:data数据节点
数据节点就是用来存放master的5个分片和5个备用分片的。从下图也可以看出,随着master的增多,数据节点也会增多,每个数据节点保存的分片数量就越少。
角色3:负载均衡节点
同步元数据,给外界链接使用,也可以说是对外的一个统一接口。
角色4:协调器
协调器可以通过链接一个集群的所有节点,将集群链接成一整个服务。就是各个模块的相互调用。
当es集群中,出现了两个leader master就称为脑裂。
为何会有两个leader?
如果有网络波动产生,其他的master以为leader master宕机了,于是投票产生了新的leader,等网络恢复正常,就产生了两个leader。
如何避免脑裂?
在配置文件中,可以配置最小有效集群数(集群中所有master节点个数过半数)。
比如有3个master,因为网络波动,leader master和其他两个master失去了联系,这个时候从分区的角度我们看出,leader master是一个节点小于最小有效集群数,所以他的leader失效;而另外一个分区是两个节点,满足最小有效集群数,所以可以新选出leader。
1.导入依赖:
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>5.5.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>5.5.2</version>
</dependency>
2.客户端——索引管理
Transportclient对象,底层需要java的socket支持,创建连接时需要通过socket代码获取ip地址和端口。
public class ESTest {
//通过私有属性 通过@Before构建一个连接对象
private TransportClient client;
//给私有属性赋值 提供ip port
//Before的作用就是测试@Test 每次执行之前都会执行
//Before
@Before
public void init() throws UnknownHostException {
//给我的client赋值 10.9.151.60:9300
//传递一个settings值 定义连接的集群名称 elasticsearch
Settings setting= Settings.builder()
.put("cluster.name","elasticsearch").build();
client=new PreBuiltTransportClient(Settings);
//收集ip和端口的对象address
InetSocketTransportAddress address= new InetSocketTransportAddress(InetAddress.getByName("10.9.151.60"),9300);
//添加到client对象
client.addTransportAddress(address);
}
}
接上个类里:
//集群的索引管理
//创建索引,删除索引,获取查看个索引
//transportclient封装了2中通的操作逻辑
//对索引对集群的操作 需要先获取admin管理对象
@Test
public void indexManage() throws IOException {
//创建索引 http://10.9.151.60:9200/index01 put
//获取admin 可以管理集群cluster也可以管理indices
AdminClient admin = client.admin();
//操作index
IndicesAdminClient indices = admin.indices();
ClusterAdminClient cluster = admin.cluster();
//建一个索引 index07
//indices.create();//方法调用,命令就发送
//CreateIndexRequestBuilder createIndexRequest =
//indices.prepareCreate("index07");//没有发送命令
//prepare**方法调用没法送命令,但是获取了返回值request对象
//想把代码命令发送出去
//CreateIndexResponse createIndexResponse = createIndexRequest.get();
//response解析就是创建索引的返回值 {"acknowlaged","shards_acknowlaged"}
//createIndexResponse.isShardsAcked();
//createIndexResponse.isAcknowledged();
GetAliasesRequestBuilder GetAliasesRequest = indices.prepareGetAliases("index07");
GetMappingsRequestBuilder getMappingsRequest = indices.prepareGetMappings("index02");
IndicesExistsRequestBuilder indicesExistesRequest = indices.prepareExists("index07");
GetMappingsResponse getMappingsResponse = getMappingsRequest.get();
System.out.println("index07存在吗:"+indicesExistesRequest.get().isExists());
/*ImmutableOpenMap> mappings = getMappingsResponse.getMappings();
ImmutableOpenMap index02 = mappings.get("index02");
MappingMetaData article = index02.get("article");
Map stringObjectMap = article.sourceAsMap();
String content =(String)stringObjectMap.get("content");
System.out.println(content);*/
/*System.out.println("content:type值:"+);//text
ObjectLookupContainer keys = article.keys();
System.out.println("article下的所有域属性:"+keys.toString());
*/
//常用的方法,创建索引,不存在时创建索引
//prepareCreate prepareExists prepareDelete;
}
3.客户端——文档管理
还是接上个类里
//文档管理 增删查改
@Test
public void docManage() throws JsonProcessingException {
//新增文档,读取数据源封装文档数据写入es
Product product=new Product();
product.setProductCategory("手机");
product.setProductId("好用");
product.setProductId("123456");
product.setProductImgurl("http://image.jt.com");
product.setProductName("华为手机");
product.setProductNum(50);
product.setProductPrice(50.0);
//http://10.9.151.60:9200/index/article/1 -d
//将其转化为json
ObjectMapper mapper=new ObjectMapper();
String json = mapper.writeValueAsString(product);
//通过client的prepare方法获取request request.get
IndexRequestBuilder request = client.prepareIndex("index07", "product", product.getProductId());
//request添加请求数据 json字符串
request.setSource(json);
IndexResponse indexResponse = request.get();
//新建document返回字符串json _index _type _id _version
long version = indexResponse.getVersion();
System.out.println("创建version:"+version);
//删除document
client.prepareDelete("index07","product","123456");
//获取
client.prepareGet("index07","product","123456");
}
4.客户端——搜索
//搜索
@Test
public void search(){
//通过query对象的封装 实现不同搜索逻辑
//搜索的过程的分页时浅查询
TermQueryBuilder query = QueryBuilders.termQuery("title", "介");
//搜索
SearchRequestBuilder request = client.prepareSearch("index02");
//搜索条件
request.setQuery(query);
//搜索分页数据,其实位置,条数
request.setFrom(0);//limit start
request.setSize(5);
SearchResponse searchResponse = request.get();
//从searchResponse解析查询结果
SearchHits topDocs = searchResponse.getHits();
System.out.println("查询总数:"+topDocs.totalHits);
//遍历数组
SearchHit[] scoreDocs = topDocs.getHits();
for (SearchHit hit:scoreDocs){
//获取一个doument返回结果
//{"id":"","title"}
System.out.println("source的json:"+hit.getSourceAsString());
}
//easymall将productJson转化为product对象去使用.
}
这里的思路是,把初始化创建TransportClient对象的过程,做成一个配置类,把初始化方法添加上@bean,这样spring初始化的时候,这个方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义。
@Configuration
//配合@Configuration实现配置类中属性初始化赋值
//@Value作用一样可以读取properties
//自定义前缀,多级赋值
@ConfigurationProperties("easymall.es")
public class ESConfig {
/* private String name;//只要properties中
//有一个属性easymall.es.name
//在properties文件中
//easymall.es.nodes=10.9.9.9:9300,10.9.9.9:9500
private List nodes;//["10.9.9.9:9300","10.9.9.9:9500"]
private String nodes;//nodes=10.9.9.9:9300,10.9.9.9:9500
*/
/*public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}*/
//配置类的作用就是生成一个容器管理的对象Transport
//创建对象时,需要ip:port的es节点集群
//easymall.es.nodes=es01,es02,es03
private List<String> nodes;
public List<String> getNodes() {
return nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
@Bean
public TransportClient initClient(){
System.out.println("开始创建一个es连接客户端");
System.out.println("nodes的个数"+nodes.size());
//new个对象
TransportClient client=new
PreBuiltTransportClient(Settings.EMPTY);
//利用nodes获取连接对象ip:port
for(String node:nodes){
//node=""10.9.48.69""9300";
String host=node.split(":")[0];
int port=Integer.parseInt(node.
split(":")[1]);
//做一个address
InetSocketTransportAddress address;
try{
address=
new InetSocketTransportAddress(
InetAddress.getByName(host),port);
}catch (Exception e){
e.printStackTrace();
continue;
}
client.addTransportAddress(address);
}
return client;
// return new PreBuiltTransportClient(Settings.EMPTY);
}
}
在application文件中,需要说明ES集群的IP地址和端口号,这样方法中的nodes就知道值是多少了:
easymall.es.nodes=10.9.48.69:9300,10.9.151.60:9300
之后在需要使用transportclient对象的地方,直接使用@Autowired注入,就可以使用transportclient对象的索引操作方法,文档操作方法,搜索方法了。