环境:ES-5.4.0版本,部署方式:3master node+2client node+3data node
说明:data node和client node都配置了http.enabled: false,程序在写数据时报错:No data nodes with HTTP-enabled available
源码分析:
1 public static void filterNonDataNodesIfNeeded(Settings settings, Log log) { 2 if (!settings.getNodesDataOnly()) { 3 return; 4 } 5 6 RestClient bootstrap = new RestClient(settings); 7 try { 8 String message = "No data nodes with HTTP-enabled available"; 9 List<NodeInfo> dataNodes = bootstrap.getHttpDataNodes(); 10 // 找不到dataNodes就会报错 11 if (dataNodes.isEmpty()) { 12 throw new EsHadoopIllegalArgumentException(message); 13 } 14 ... 15 } finally { 16 bootstrap.close(); 17 } 18 }
接下来看看RestClient.getHttpDataNodes()方法的取值逻辑
1 public List<NodeInfo> getHttpDataNodes() { 2 List<NodeInfo> nodes = getHttpNodes(false); 3 // 遍历上面获取到的节点 4 Iterator<NodeInfo> it = nodes.iterator(); 5 while (it.hasNext()) { 6 NodeInfo node = it.next(); 7 // 如果不是数据节点,则移除 8 if (!node.isData()) { 9 it.remove(); 10 } 11 } 12 return nodes; 13 } 14 15 // 获取http节点_nodes/http 16 public List<NodeInfo> getHttpNodes(boolean clientNodeOnly) { 17 // 通过es接口“_nodes/http”来获取nodes的信息 18 Map<String, Map<String, Object>> nodesData = get("_nodes/http", "nodes"); 19 List<NodeInfo> nodes = new ArrayList<NodeInfo>(); 20 21 for (Entry<String, Map<String, Object>> entry : nodesData.entrySet()) { 22 NodeInfo node = new NodeInfo(entry.getKey(), entry.getValue()); 23 // 如果不是查找client节点,则只要节点运行网络访问就可以add了;如果查找client节点,则还要通过isClient验证才能add 24 if (node.hasHttp() && (!clientNodeOnly || node.isClient())) { 25 nodes.add(node); 26 } 27 } 28 return nodes; 29 }
最后再来看看node.hasHttp(),isClient(),isData()的方法
1 private final String id; 2 private final String name; 3 private final String host; 4 private final String ip; 5 private final String publishAddress; 6 private final boolean hasHttp; 7 private final boolean isClient; 8 private final boolean isData; 9 private final boolean isIngest; 10 11 public NodeInfo(String id, Map<String, Object> map) { 12 this.id = id; 13 EsMajorVersion version = EsMajorVersion.parse((String) map.get("version")); 14 this.name = (String) map.get("name"); 15 this.host = (String) map.get("host"); 16 this.ip = (String) map.get("ip"); 17 // 5.0以下版本的分支 18 if (version.before(EsMajorVersion.V_5_X)) { 19 Map<String, Object> attributes = (Map<String, Object>) map.get("attributes"); 20 if (attributes == null) { 21 this.isClient = false; 22 this.isData = true; 23 } else { 24 String data = (String) attributes.get("data"); 25 this.isClient = data == null ? true : !Boolean.parseBoolean(data); 26 this.isData = data == null ? true : Boolean.parseBoolean(data); 27 } 28 this.isIngest = false; 29 // 5.0版本以上的分支 30 } else { 31 List<String> roles = (List<String>) map.get("roles"); 32 // 如果roles列表中不包含"data",则此节点是client 33 this.isClient = roles.contains("data") == false; 34 // 如果roles列表中包含"data",则此节点是data 35 this.isData = roles.contains("data"); 36 // 如果roles列表中包含"ingest",则此节点是ingest 37 this.isIngest = roles.contains("ingest"); 38 } 39 Map<String, Object> httpMap = (Map<String, Object>) map.get("http"); 40 // 如果节点数据中包含key:http 41 if (httpMap != null) { 42 String addr = (String) httpMap.get("publish_address"); 43 // 如果http数据中包含key:publish_address 44 if (addr != null) { 45 StringUtils.IpAndPort ipAndPort = StringUtils.parseIpAddress(addr); 46 this.publishAddress = ipAndPort.ip + ":" + ipAndPort.port; 47 // 则此节点可以提供http服务,即:http.enabled: true 48 this.hasHttp = true; 49 } else { 50 this.publishAddress = null; 51 this.hasHttp = false; 52 } 53 } else { 54 this.publishAddress = null; 55 this.hasHttp = false; 56 } 57 }
从上面的源码分析可以得出:如果一个data节点不配置http.enabled:true,则此节点不会被getHttpDataNodes()方法搜索到,那么就会直接抛出异常:No data nodes with HTTP-enabled available
解决的方法无非两种:
第一:数据节点配置 http.enabled:true
第二:绕过filterNonDataNodesIfNeeded()校验,需要settings.getNodesDataOnly()返回false;看下面源码可知,默认es.nodes.data.only是true,在客户端中将其设置为false即可。
1 /** Clients only */ 2 String ES_NODES_CLIENT_ONLY = "es.nodes.client.only"; 3 String ES_NODES_CLIENT_ONLY_DEFAULT = "false"; 4 5 /** Data only */ 6 String ES_NODES_DATA_ONLY = "es.nodes.data.only"; 7 String ES_NODES_DATA_ONLY_DEFAULT = "true"; 8 9 /** Ingest only */ 10 String ES_NODES_INGEST_ONLY = "es.nodes.ingest.only"; 11 String ES_NODES_INGEST_ONLY_DEFAULT = "false"; 12 13 /** WAN only */ 14 String ES_NODES_WAN_ONLY = "es.nodes.wan.only"; 15 String ES_NODES_WAN_ONLY_DEFAULT = "false"; 16 17 ... 18 19 public boolean getNodesDataOnly() { 20 // by default, if not set, return a value compatible with the other settings 21 // 默认es.nodes.data.only是true,在客户端中将其设置为false即可 22 return Booleans.parseBoolean(getProperty(ES_NODES_DATA_ONLY), !getNodesWANOnly() && !getNodesClientOnly() && !getNodesIngestOnly()); 23 } 24 25 public boolean getNodesIngestOnly() { 26 return Booleans.parseBoolean(getProperty(ES_NODES_INGEST_ONLY, ES_NODES_INGEST_ONLY_DEFAULT)); 27 } 28 29 public boolean getNodesClientOnly() { 30 return Booleans.parseBoolean(getProperty(ES_NODES_CLIENT_ONLY, ES_NODES_CLIENT_ONLY_DEFAULT)); 31 } 32 33 public boolean getNodesWANOnly() { 34 return Booleans.parseBoolean(getProperty(ES_NODES_WAN_ONLY, ES_NODES_WAN_ONLY_DEFAULT)); 35 }
最后附上一段"_nodes/http"接口的返回值:
"nodes": { "YgwRm4j1RwiK3jjDHY8Hzw": { "name": "node-02", "transport_address": "192.168.100.10:9300", "host": "192.168.100.10", "ip": "192.168.100.10", "version": "5.4.0", "build_hash": "780f8c4", "roles": [ "master", "ingest" ], "attributes": { "ml.enabled": "true" }, "http": { "bound_address": [ "192.168.100.10:9200" ], "publish_address": "192.168.100.10:9200", "max_content_length_in_bytes": 104857600 } } ... }