Ocelot 发现服务总是失败的解决办法
一、问题
今天用 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服务发现