- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
1.为什么需要服务注册与发现 微服务架构中,服务于服务之间内部通信必不可少,比如A服务调用B服务,起初我们的做法是,A服务从配置文件中拿到B服务的IP、端口地址,进行访问,本身是没什么问题的,但是随着业务的复杂性越来越高,会遇到一个最蛋疼的问题,服务A可能依赖很多其他服务,这样就要维护好多个服务的地址, 如果某个服务的负载地址换了,服务A就要去跟着更改对应的地址配置,这一看可能也没什么,当服务依赖之间的复杂度在非常高的时候,成批的服务由于某种原因更换地址时,噩梦就来了。 解决上述问题的办法很简单,服务名肯定是不会变化的,当我们服务之间调用时候,知道对方服务名称就能对此通信就解决了,所以就需要一个中间件来实现如下功能:
2.Consul Consul是一款基于Go语言开发的支持多数据中心,分布式高可用的服务注册发现的中间件,支持注册服务的健康检查,并且自带管理后台便于查看维护Consul集群。 官方网站 。
上图是官方的架构图,可见Consul几大核心概念如下:
网上讨论很多的问题是Consul官方给出的集群架构很奇怪,有Server集群了,完全可以满足业务了,为什么还要来一层Client呢?我个人的理解如下,但不一定保证对或者完全,大家可以自己去深入思考下:
3.基于docker的consul集群搭建 本文采用3Server+2Client的方式搭建 。
1)启动server1 。
docker run -d -p 8501:8500 -v /e/docker/consul/data/server1:/consul/data -v /e/docker/consul/conf/server1:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --privileged=true --name=consul_server_1 consul agent -server -bootstrap-expect=3 -ui -node=node_consul_server_1 -client='0.0.0.0' -data-dir /consul/data -config-dir /consul/config -datacenter=dc1 。
2)查询一下server1的容器ip docker exec consinspect -f '{{.NetworkSettings.IPAddress}}' consul_server_1 3)启动server2 docker run -d -p 8502:8500 -v /e/docker/consul/data/server2:/consul/data -v /e/docker/consul/conf/server2:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --privileged=true --name=consul_server_2 consul agent -server -ui -node=consul_server_2 -client='0.0.0.0' -data-dir /consul/data -config-dir /consul/config -datacenter=dc1 -join 172.17.0.5 4)启动server4 docker run -d -p 8503:8500 --restart=always -v /e/docker/consul/data/server3:/consul/data -v /e/docker/consul/conf/server3:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --privileged=true --name=consul_server_3 consul agent -server -ui -node=consul_server_3 -client='0.0.0.0' -data-dir /consul/data -config-dir /consul/config -datacenter=dc1 -join 172.17.0.5 5)列出server集群 docker exec consexec consul_server_1 consul operator raft list-peers 6)启动client1 docker run -d -p 8504:8500 -v /e/docker/consul/conf/client1:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --name=consul_client_1 consul agent -node=consul_client_1 -client='0.0.0.0' -config-dir /consul/config -datacenter=dc1 -join 172.17.0.5 7)启动client2 docker run -d -p 8505:8500 -v /e/docker/consul/conf/client1:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --name=consul_client_2 consul agent -node=consul_client_2 -client='0.0.0.0' -config-dir /consul/config -datacenter=dc1 -join 172.17.0.5 。
都运行起来后,运行 docker conscontainer ls -a 全部运行成功 8)运行管理后台,输入http://xxxxxx:8501/ui/dc1/nodes 。
标识所有节点正常.
4. .net core使用consul 代码里面用到了apollo配置中心 。
/// <summary>
/// 注册Consul,使用在startup中Configure最后面添加app.RegisterConsul(lifetime,configuration),只能使用内网IP注册,如果需要联调请使用ConsulRegister注册
/// </summary>
/// <param name="app"></param>
/// <param name="lifetime"></param>
/// <param name="configuration">配置文件</param>
/// <param name="serviceName">服务名称</param>
/// <param name="servicePort">服务端口</param>
/// <param name="serviceHealthPath">服务健康检查地址 默认/api/healthcheck</param>
/// <param name="runtimeEnv">服务执行环境,默认apollo["Meta:Env"]</param>
/// <param name="tagList">标签列表</param>
/// <returns></returns>
public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, IHostApplicationLifetime lifetime, IConfiguration configuration, string serviceName = "", int servicePort = 0, string serviceHealthPath = "", string runtimeEnv = "", List<string> tagList = null)
{
Random rnd = new Random();
string[] clientIps = configuration["Consul:ClientRegisterAddress"].Split(",");
var serviceAddress = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList.FirstOrDefault(p => p.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)?.ToString();
foreach (var clientIp in clientIps)
{
//创建Consul客户端
ConsulClient consulClient = new ConsulClient(c =>
{
//请求注册的 Consul 地址(你要注册到哪个consul服务就填写哪个)
c.Address = new Uri($"http://{clientIp}/");
});
//服务名称
serviceName = string.IsNullOrEmpty(serviceName.Trim()) ? configuration["Consul:ServiceName"] : serviceName.Trim();
if (string.IsNullOrEmpty(serviceName))
{
serviceName = $"core-service-{Guid.NewGuid()}";
}
if (servicePort == 0)
{
//服务接口
var servicePortStr = configuration["Consul:ServicePort"];
if (string.IsNullOrEmpty(servicePortStr))
{
servicePortStr = "5000";
}
servicePort = Convert.ToInt32(servicePortStr);
}
//健康检查地址
serviceHealthPath = string.IsNullOrEmpty(serviceHealthPath.Trim()) ? configuration["Consul:ServiceHealthPath"] : serviceHealthPath.Trim();
if (string.IsNullOrEmpty(serviceHealthPath))
{
serviceHealthPath = "/api/healthcheck";
}
var httpCheck = new AgentServiceCheck()
{
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册,在遇到异常后关闭自身服务通道,失败后多久移除
Interval = TimeSpan.FromSeconds(15),//健康检查时间间隔,或者称为心跳间隔 间隔15s一次
HTTP = $"http://{serviceAddress}:{servicePort}{serviceHealthPath}",
Timeout = TimeSpan.FromSeconds(3) //多少秒为超时
};
List<string> tags = new List<string>();
runtimeEnv = string.IsNullOrEmpty(runtimeEnv.Trim()) ? configuration["Meta:Env"] : runtimeEnv.Trim();
if (!string.IsNullOrEmpty(runtimeEnv))
{
tags.Add(runtimeEnv);
tags.Add($"group={runtimeEnv}");
}
if (tagList != null && tagList.Count > 0)
{
foreach (var item in tagList)
{
tags.Add(item);
}
}
var registration = new AgentServiceRegistration()
{
//ID = Guid.NewGuid().ToString(),
ID = $"{serviceName}-{serviceAddress.Replace(".", "-")}-{servicePort}",
Name = serviceName, // 服务名
Address = serviceAddress, // 服务绑定IP
Port = servicePort, // 服务绑定端口
Tags = tags.ToArray(),//添加 urlprefix-/servicename 格式的 tag 标签,以便 Fabio 识别
Checks = new[] { httpCheck }
};
//服务启动时注册,内部实现其实就是使用 Consul API 进行注册(HttpClient发起)
consulClient.Agent.ServiceRegister(registration).Wait();
//服务停止时取消注册
lifetime.ApplicationStopping.Register(() =>
{
consulClient.Agent.ServiceDeregister(registration.ID).Wait();
});
}
return app;
}
}
app.RegisterConsul(lifetime, Configuration, Configuration["Consul:ServiceName:MyDockerApi"]);
服务启动后,注册成功后如下几张图 。
2.服务发现,核心代码如下 。
/// <summary>
/// 获取服务其中一个
/// </summary>
/// <returns></returns>
private async Task<Tuple<bool, string>> GetConsulServiceUrl(string serviceName, string tagSign)
{
Random rnd = new Random();
string[] ips = _configuration["Consul:ClientRegisterAddress"].Split(",");
var consulAddress = ips[rnd.Next(ips.Length)];
_logger.LogInformation($"Consul服务->{serviceName},地址->{consulAddress}");
var consuleClient = new ConsulClient(consulConfig =>
{
consulConfig.Address = new Uri($"http://{consulAddress}/");
});
var result = new List<string>();
//获取健康的服务
var queryResult = await consuleClient.Health.Service(serviceName, string.Empty, true);
var healthList = queryResult.Response.ToList();
_logger.LogInformation($"Consul服务->{serviceName},地址->{consulAddress},查询到可用ip");
if (!string.IsNullOrEmpty(tagSign))
{
healthList = queryResult.Response.Where(p => p.Service.Tags.Contains(tagSign)).ToList();
}
foreach (var serviceEntry in healthList)
{
result.Add($"http://{serviceEntry.Service.Address}:{serviceEntry.Service.Port}");
}
if (result.Count > 0)
{
string consulCacheKey = $"cache_{_consulCacheKey}{serviceName}";
CacheHelper.CacheInsertAddMinutes(consulCacheKey, result, 600);
return await ServiceRequestHost(result, serviceName);
}
else
{
return new Tuple<bool, string>(false, "未找到服务");
}
}
/// <summary>
/// consul服务Get请求
/// </summary>
/// <param name="httpClient">http请求</param>
/// <param name="requestPath">请求的地址</param>
/// <param name="serviceName">服务名称</param>
/// <param name="serviceTag">服务标志</param>
/// <param name="dicParam">url参数</param>
/// <param name="isNeedLocalDebug">是否需要本机调试,如果需要,请在consul_service_ip配置服务IP,命名是服务名称-host 例:accountbook-service-host</param>
/// <returns></returns>
private async Task<HttpResponseMessage> ConsulHttpClientGetByHttpResponseMessage(HttpClient httpClient, string serviceName, string serviceTag, string requestPath, Dictionary<string, string> dicParam, bool isNeedLocalDebug = false)
{
_logger.LogInformation($"Consul服务get请求参数{serviceName},{requestPath},结果为HttpResponseMessage");
if (!requestPath.StartsWith("/"))
{
requestPath = "/" + requestPath;
}
int n = 0;
again:
var serviceHost = await GetHost(serviceName, serviceTag, isNeedLocalDebug);
if (serviceHost.Item1)
{
try
{
if (dicParam != null && dicParam.Count > 0)
{
if (requestPath.Contains("?"))
{
requestPath = requestPath + "&" + string.Join("&", dicParam.Select(x => $"{x.Key}={x.Value}"));
}
else
{
requestPath = requestPath + "?" + string.Join("&", dicParam.Select(x => $"{x.Key}={x.Value}"));
}
}
string httpGetUrl = $"{serviceHost.Item2}{requestPath}";
var response = await httpClient.GetAsync(httpGetUrl);
if (!response.IsSuccessStatusCode)
{
n = n + 1;
if (n < 2)
{
await ConsulCacheRemove(serviceName);
goto again;
}
}
return response;
}
catch (Exception ex)
{
_logger.LogError($"{JsonConvert.SerializeObject(serviceHost)}-{serviceName}-异常{JsonConvert.SerializeObject(ex)}");
n = n + 1;
if (n < 2)
{
await ConsulCacheRemove(serviceName);
goto again;
}
return null;
}
}
else
{
_logger.LogInformation($"{serviceName}-{JsonConvert.SerializeObject(serviceHost)}");
return null;
}
}
/// <summary>
/// 通过consul调用服务
/// </summary>
/// <returns></returns>
[HttpGet("getby/consul")]
public async Task<IActionResult> GetByConsul()
{
var serverHost = configuration["Consul:ServiceName:MyDockerApi"];
var response = await consulServiceProxy.ConsulGetByHttpResponseMessage(serverHost, "", "api/demo/get/configs", null);
var res = "";
if (response.IsSuccessStatusCode)
res = await response.Content.ReadAsStringAsync();
return Ok($"通过consul请求结果:{res}");
}
[HttpGet("get/configs")]
public async Task<IActionResult> GetConfigs()
{
var res = "";
//获取公共命名空间下的配置
var publicConfig = configuration["env"];
//获取该私有项目下的配置
var privateConfig = configuration["myConfig1"];
res = $"公共:{publicConfig},私有:{privateConfig}";
return Ok(res);
}
我这边懒了,就用了1个服务,自己注册,自己发现自己。 .net core集成consul大概就是这个样子了 。
5.问题 。
调试问题,我这个使用的腾讯云,并且基于docker部署的,由于注册时候是注册的docker ip,我本机的网络并没有与腾讯云打通,所以我只能部署到云上面请求,正常情况下,公司里边运维会打通腾讯云你们的测试环境集群网络与你们公司本地环境的网络,这样就可以调试了,但是也可能人家会隔离你们的本地网络与docker的直接通信,这时候运维会提供该服务的docker单点对外IP或者集群的对外负载IP,用于你们本机调试。我们公司就是这样目前,当然代码里需要一些判断.
注册docker ip还是该服务的负载ip,都可以,我们就是注册的docker ip所以在服务发现的时候,会拉取一个服务列表,通过轮询算法,来取其中一个.
最后此篇关于微服务系列之服务注册发现Consul的文章就讲到这里了,如果你想了解更多关于微服务系列之服务注册发现Consul的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
有谁知道蓝牙设备如何获取范围内可发现设备的设备 ID? 理想情况下,我正在寻找涉及蓝牙协议(protocol)最小实现的最简单解决方案。 一个起点会很好,我只是想创建一个设备,它可以以最小的功耗存储附
我有用于搜索Kibana中特定事件的查询,该查询与其他搜索一起保存,是否可以通过REST调用以编程方式更改它? 最佳答案 正如@Mohammad所说,所有与Kibana相关的元数据都存储在elasti
我正在使用带有这些注释的基本集成测试: @ExtendWith(SpringExtension::class) @SpringBootTest(classes = [SomeApplication::
以下是我的代码 HTML: Hello !! Javascript: $(function() { $('#content .child').click(function() {
我试图避免在每个 GDB session 中输入相同的命令。为此,我遵循了 rust discovery book 中的说明。但是当我通过 cargo run 运行程序时,程序没有像书中提到的那样工作
好的,我记得有一些命令可以放在 settings.py 文件中,这样基本上当您将 django 项目移动到另一个目录时,它就不会启动 foo-bar . 我知道我可以通过在它提到主目录的任何地方设置一
假设我正在制作一份注册表单。现在我希望它突出显示四个字段中的空白字段。现在我可以只执行一堆 if-else 语句,但这将花费很长时间。 假设我有以下代码: Javascript: if($firstn
我试图理解 C++ 中 regex 的逻辑 std::string s ("Ni Ni Ni NI"); std::regex e ("(Ni)"); std::smatch sm; std::re
运行时: vim /tmp/blah :q echo $? 我的退出状态为 1 .这破坏了包括 Git 在内的各种东西。如果我在没有 vimrc 的情况下运行 vim: vim -u NONE /tm
我无法通过“查找”来查找旧文件。我将我的发现链接到一个声明中,所有其他部分都运行良好。这是我所拥有的精简版。它搜索 $path 的目录树,并为找到的每个项目创建仅包含换行符的单独临时文件:所有文件、超
我已经多次看到这个问题,但没有一个答案对我有用。 我的 DotNet Core 应用程序失败 编码: public static void Main(string[] args) {
已解决见编辑 2 你好, 我一直在编写一个 Perl 程序来处理本地(专有)程序的自动升级(对于我工作的公司)。 基本上,它通过 cron 运行,不幸的是有内存泄漏(或类似的东西)。问题是泄漏只发生在
在 icCube 中创建到 Oracle 数据库的连接时,“选择现有数据库表”返回一个空的表列表。 连接很好,我可以查询模式创建 SQL 查询。 最佳答案 用户用作模式名称,但 Oracle 使用大写
我正在使用 VBA 循环遍历两个工作表上的行,如果它们匹配,则将工作表 2 中的行复制到工作表 1 中。 我的代码应该: 打开第二个工作簿 将所有信息复制到新工作表上的原始工作簿中 然后循环遍历原始工
当我尝试同步我的数据库时出现这个奇怪的错误: Unhandled rejection Error: Cyclic dependency found. roles is dependent of its
以编程方式发现 perl 模块具有的所有子例程的最佳方法是什么?这可以是一个模块、一个类(没有@EXPORT)或任何介于两者之间的东西。 编辑:下面的所有方法看起来都可以工作。我可能会在生产中使用 C
如何在“讨论”按钮左侧添加“共享”按钮。我希望该按钮与当前的“讨论”按钮具有相同的样式/颜色。 我从https://atmospherejs.com/joshowens/shareit添加了包 我将
我最近从 Netbeans 切换到 Eclipse,Eclipse 在我的项目中发现了许多 Netbeans 没有的语法错误,我不知道为什么。据可以看出,两个 IDE 都设置为使用 java 1.6。
我必须为我的项目设置一些不接受错误网址的规则。我为此使用正则表达式。 我的网址是“http://some/resource/location”。 此网址的开头、中间或结尾不应留有空格。 例如,这些空格
问题:鉴于作为 VMSS 的一部分启动的 N 个实例,我希望每个 Azure 实例上的应用程序代码能够发现其他对等实例的 IP 地址。我该怎么做? 总体目的是对实例进行集群,以提供主动被动 HA 或保
我是一名优秀的程序员,十分优秀!