環境: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 } } ... }