一、問題
今天用 Ocelot + Consul 項目,進行微服務實踐,可是 Ocelot 的發現服務總是失敗。
二、分析問題
2.1、分析方法:
不得不下載了 Ocelot 源碼進行追蹤排查。
2.2、源碼分析
源碼對應文件為 Ocelot-develop\src\Ocelot.Provider.Consul\Consul.cs
public async Task<List<Service>> Get() { var queryResult = await _consul.Health.Service(_config.KeyOfServiceInConsul, string.Empty, true); var services = new List<Service>(); foreach (var serviceEntry in queryResult.Response) { if (IsValid(serviceEntry)) { var nodes = await _consul.Catalog.Nodes(); if (nodes.Response == null) { services.Add(BuildService(serviceEntry, null)); } else { var serviceNode = nodes.Response.FirstOrDefault(n => n.Address == serviceEntry.Service.Address); services.Add(BuildService(serviceEntry, serviceNode)); } } else { _logger.LogWarning($"Unable to use service Address: {serviceEntry.Service.Address} and Port: {serviceEntry.Service.Port} as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"); } } return services.ToList(); } private Service BuildService(ServiceEntry serviceEntry, Node serviceNode) { return new Service( serviceEntry.Service.Service, new ServiceHostAndPort(serviceNode == null ? serviceEntry.Service.Address : serviceNode.Name, serviceEntry.Service.Port), serviceEntry.Service.ID, GetVersionFromStrings(serviceEntry.Service.Tags), serviceEntry.Service.Tags ?? Enumerable.Empty<string>()); }
第3行,獲得已注測的健康的服務;第11行,獲得已注冊 Consul 節點的電腦。
第18行,如果當前服務與Consul節點是在同一台電腦上,則返回電腦節點 ServiceNode
第31行中的 BuildSevrice 函數完成最后的 service信息創建,
第35行 如果找到了 ServiceNode,則返回對應的電腦名字 hostname, 如果沒找到則返回對應的 ipAddress.
就是說:服務和Consul 在同一台電腦上則返回hostname , 在不同電腦上則返回服務所在電腦的 ipAddress.
2.3 問題根源:
是 Consul 注冊時,hostname 參數由 -node參數指定。
當服務和 Consul 在同一台電腦上時,Ocelot 最終變換成 http://hostname:port/url 的形式進行訪問。
三、解決問題
3.1、Consul 的配置參數注意事項
注意而 hostname 是由Consul 的 -node 參數指定的!
所以,
consul agent -server -datacenter=dc1 -bootstrap -data-dir ./data -ui -node=n1 -bind 192.168.11.211 -client=0.0.0.0
其中 -node=n1 是一個大坑。應該略去,系統會自己設置為自己的主機名字 hostname。
所以我實際上用了配置文件 node1.json,也是去掉了該項。
{ "datacenter": "dc1", "data_dir": "c:/data/app/consul/node1", "log_level": "INFO", "server": true, "ui": true, "bind_addr": "192.168.11.211", "client_addr": "127.0.0.1", "advertise_addr": "192.168.11.211", "bootstrap_expect": 1, "ports":{ "http": 8500, "dns": 8600, "server": 8300, "serf_lan": 8301, "serf_wan": 8302 } }
然后調用方式:
consul agent -config-dir=e:/consul/node1.json
然后用另一台服務器加入
consul agent -data-dir /tmp/consul -bind=192.168.11.246 -join 192.168.11.248
這時 Consul 的 web 管理界面為:
它會自動帶上主機名: HNSever 和 LGB-PC
在這兩台電腦上注冊服務后,最終會變成 http://hostname:port/ + url 模板 的形式訪問。
3.2、hostname 不能訪問問題修改
如果 http://hostname:port/url,還是返回 錯誤代碼 HTTP ERROR 500 訪問失敗。就是 hostname 不能轉換為 ipaddress.
所以需要修改 windows 的 hosts 文件:
打開系統目錄:c:/windows/system32/drivers/etc找到hosts文件,打開hosts文件並在最后面添加一條記錄
例如:
192.168.11.248 HNServer
192.168.11.211 LGB-PC
然后就能正常的發現服務了!
3.3 附上 Ocelot 的配置文件 ocelot.json
{ "ReRoutes": [ { "DownstreamPathTemplate": "/api/{url}", "DownstreamScheme": "http", "ServiceName": "ProductService", "UseServiceDiscovery": true, "LoadBalancerOptions": { "Type": "LeastConnection" }, "UpstreamPathTemplate": "/test/{url}", "UpstreamHttpMethod": [ "Post", "Get" ] } ], "GlobalConfiguration": { "ServiceDiscoveryProvider": { "Host": "localhost", "Port": 8500 } } }
四、參考
.net core Ocelot Consul 實現API網關 服務注冊 服務發現 負載均衡
netcore ocelot api網關結合consul服務發現