winserver的consul部署实践与.net core客户端使用(附demo源码)

 

前言

随着微服务兴起,服务的管理显得极其重要。都知道微服务就是”拆“,把臃肿的单块应用,拆分成多个轻量级的服务,每个服务可以在短周期内重构、迭代、交付。随着微服务的数量增多,因量变引起了质量,带来新的问题其中一个是服务的管理问题。随着业务发展微服务增多,可能技术负责人也无法清楚记着服务的部署情况,服务的健康也不能时刻关注着,因此服务治理系统的作用必不可少。

本篇文章的源码:

demo:https://github.com/SkyChenSky/Consul.Demo

封装:https://github.com/SkyChenSky/Sikiro.Tookits.Consul

Consul

Consul是一款简单、易用、可伸缩性强的服务治理系统。

主要核心功能有:

  1. 服务发现
  2. 健康检查
  3. 键值存储
  4. 多数据中心

consul代理会每个一段时间对注册中心的服务节点进行访问,如果响应码为“20X"认为是健康。

键值存储可以认为是一个简易的k/v数据库,因此可以用此来存放配置信息。

服务发现

服务发现分服务注册和服务查找。

服务注册

将服务节点信息(地址+端口)添加(删除)到注册表,注册表会记录着服务的节点信息和状态

服务查找

由其他的服务或者系统通过注册表查询到指定可用服务的节点信息。

服务发现的方式

服务发现的方式又分自主式和代理式。

自主式

由各个服务主动的将自己节点信息添加(删除)到注册中心。实现是通过统一封装或者程序库,由服务各个节点承担服务发现的功能,与代理式相比由各自节点分担的访问压力。

代理式

由一个系统(负载均衡系统)或者服务(API网关)来完成服务发现。因为由一个系统或者服务完成,随着注册服务的增加会带来性能瓶颈,因此需要对此做集群。

Consul模式

Consul有两种模式,Client和Server,无论各种模式都有一个consul agent。

Client模式

Client模式是一个轻量级的consul agent,只拥有注册服务、健康检查、转发查询等功能。

Server模式

Server模式与Client模式相比,除了拥有Client模式的功能还多出了数据存储,leader选举等。

官方建议Server模式应保证3-5个,而且应该是奇数,为什么呢,因为少于3个无法保证高可用,多于5个又会给数据库同步的一致性带来压力,而Client数量控制则没有讲究。

集群部署

下载consul https://www.consul.io/downloads.html

在服务器A,打开cmd,

consul agent -server -bootstrap-expect=1 -bind=192.168.20.80 -client=192.168.20.80 -join=192.168.20.80 -datacenter=dc1 -data-dir=data -ui -node=consul-80

在服务器B,打开cmd,

consul agent -server -bind=192.168.20.81 -client=192.168.20.81 -join=192.168.20.80 -data-dir=data -node=consul-81 

打开浏览器输入http://192.168.20.80:8500

指令简析

  • -server

    • consul以server模式启动,不填则默认以client模式
  • -bootstrap-expect=1
    • 集群节点数,当集群节点数达到声明数量才会进行数据同步
  • -bind=192.168.20.80
    • 当前consul服务绑定地址
  • -client=192.168.20.80
    • http接口绑定地址,客户端调用需要
  • -join=192.168.20.80
    • 启动服务时加入目标集群
  • -node=consul-81
    • 服务节点名称
  • -ui
    • 启动web管理后台

客户端实践

安装Consul

封装扩展

只贴部分核心代码,具体可以查看demo源码。

注入ConsulClient

public static IServiceCollection AddConsul(this IServiceCollection serviceCollection, Action<ConsulConfiguration> optionAction)
{
_consulConfiguration = new ConsulConfiguration();
optionAction(_consulConfiguration); var consulClient = new ConsulClient(x =>
x.Address = new Uri(_consulConfiguration.Host)); serviceCollection.AddSingleton(consulClient); return serviceCollection;
}

把当前服务注册到Consul

private static ConsulConfiguration _consulConfiguration;

        public static IApplicationBuilder UseConsul(this IApplicationBuilder app, IApplicationLifetime lifetime, Action<ServerConfiguration> optionAction)
{
var consulClient = app.ApplicationServices.GetService<ConsulClient>();
if (consulClient == null)
throw new Exception("please AddConsul first"); var serverConfiguration = new ServerConfiguration();
optionAction(serverConfiguration); var serviceRegistration = GetServiceRegistration(serverConfiguration); //添加注册
consulClient.Agent.ServiceRegister(serviceRegistration).Wait(); //取消注册
lifetime.ApplicationStopping.Register(() =>
{
consulClient.Agent.ServiceDeregister(serviceRegistration.ID).Wait();
});
return app;
}
private static Uri GetSelfUri(string uristring)
{
return new Uri(uristring);
} private static AgentServiceRegistration GetServiceRegistration(ServerConfiguration serverConfiguration)
{
var localIp = GetSelfUri(serverConfiguration.SelfHost); var serviceRegistration = new AgentServiceRegistration
{
Check = new AgentServiceCheck//健康检查
{
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(60),
Interval = TimeSpan.FromSeconds(30),
HTTP = $"http://{localIp.Host}:{localIp.Port}/api/health",
Timeout = TimeSpan.FromSeconds(3)
},
ID = Guid.NewGuid().ToString("N"),
Name = serverConfiguration.ServerName,
Address = localIp.Host,
Port = localIp.Port,
Tags =
new[]
{
serverConfiguration.ServerName
}
}; return serviceRegistration;
}

添加健康检查接口

与上述封装可以在同一个库,避免每个web服务都要写一个

[Route("api/[Controller]")]
public class HealthController : Controller
{
[HttpGet]
public OkResult Get()
{
return Ok();
}
}

在Startup.cs对Consul封装进行调用

ConfigureServices

 public void ConfigureServices(IServiceCollection services)
{
services.AddOptions().AddConsul(option =>
{
option.WithHost(Configuration["ConsulConfiguration:Host"]);
}).AddMvc();
}

Configure

app.UseConsul(lifetime, option =>
{
option.WithSelfHost(Configuration["SelfHost"]);
option.WithServerName(Configuration["ConsulConfiguration:ServerName"]);
});

K/V扩展

只实现了put、get、delete,剩下可以自行按需添加

public static class ConsulKyExtensions
{
public static async Task<bool> KvPutAsync(this ConsulClient consulClient, string key, string value)
{
var kvPair = new KVPair(key)
{
Value = Encoding.UTF8.GetBytes(value)
};
var result = await consulClient.KV.Put(kvPair); if (result.StatusCode == HttpStatusCode.OK)
return result.Response; return false;
} public static bool KvPut(this ConsulClient consulClient, string key, string value)
{
var kvPair = new KVPair(key)
{
Value = Encoding.UTF8.GetBytes(value)
};
var result = consulClient.KV.Put(kvPair).ConfigureAwait(false).GetAwaiter().GetResult(); if (result.StatusCode == HttpStatusCode.OK)
return result.Response; return false;
} public static async Task<string> KvGetAsync(this ConsulClient consulClient, string key)
{
var result = await consulClient.KV.Get(key); return Encoding.UTF8.GetString(result.Response.Value);
} public static string KvGet(this ConsulClient consulClient, string key)
{
var result = consulClient.KV.Get(key).ConfigureAwait(false).GetAwaiter().GetResult(); return Encoding.UTF8.GetString(result.Response.Value);
} public static async Task<bool> KvDeleteAsync(this ConsulClient consulClient, string key)
{
var result = await consulClient.KV.Delete(key); if (result.StatusCode == HttpStatusCode.OK)
return result.Response; return false;
} public static bool KvDelete(this ConsulClient consulClient, string key)
{
var result = consulClient.KV.Delete(key).ConfigureAwait(false).GetAwaiter().GetResult(); if (result.StatusCode == HttpStatusCode.OK)
return result.Response; return false;
}
}

部署启动

修改appsettings.json,填写目标consul地址和本服务地址

{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"ConsulConfiguration": {
"Host": "http://192.168.20.80:8500",
"ServerName": "ConsulWebDemo",
"Id": "20E2CFBB-95C0-496A-B70F-11111111"
},
"SelfHost": "http://localhost:1495/"
}

启动后,如果服务正常则可以显示下图效果。

作  者: 陈珙
出  处:http://www.cnblogs.com/skychen1218/ 
关于作者:专注于微软平台的项目开发。如有问题或建议,请多多赐教! 
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。 
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力!

winserver的consul部署实践与.net core客户端使用(附demo源码)的更多相关文章

  1. .NET Core 3.0之深入源码理解Startup的注册及运行

    原文:.NET Core 3.0之深入源码理解Startup的注册及运行   写在前面 开发.NET Core应用,直接映入眼帘的就是Startup类和Program类,它们是.NET Core应用程 ...

  2. 可在广域网部署运行的QQ高仿版 -- GG叽叽(源码)

    前段时间看到园子里有朋友开发了QQ高仿版的程序,我也非常有兴趣,以前一直有个做即时聊天程序的梦,趁这段时间工作不是很忙,就开始动手来做这个事情.根据我以往积累下来的项目经验,实现QQ的基本功能,问题应 ...

  3. ASP.NET CORE小试牛刀:干货(完整源码)

    扯淡 .NET Core 的推出让开发者欣喜万分,从封闭到拥抱开源十分振奋人心.对跨平台的支持,也让咱.NET开发者体验了一把 Write once,run any where 的感觉!近期离职后,时 ...

  4. .NET Core 3.0之深入源码理解Configuration(三)

      写在前面 上一篇文章讨论了文件型配置的基本内容,本篇内容讨论JSON型配置的实现方式,理解了这一种配置类型的实现方式,那么其他类型的配置实现方式基本可以触类旁通.看过了上一篇文章的朋友,应该看得出 ...

  5. .NET Core 3.0之深入源码理解Configuration(二)

      文件型配置基本内容 上一篇文章讨论了Configuration的几个核心对象,本文继续讨论Configuration中关于文件型配置的相关内容.相比较而言,文件型配置的使用场景更加广泛,用户自定义 ...

  6. .NET Core 3.0之深入源码理解Configuration(一)

    Configuration总体介绍 微软在.NET Core里设计出了全新的配置体系,并以非常灵活.可扩展的方式实现.从其源码来看,其运行机制大致是,根据其Source,创建一个Builder实例,并 ...

  7. .NET Core 3.0之深入源码理解Host(二)

      写在前面 停了近一个月的技术博客,随着正式脱离996的魔窟,接下来也正式恢复了.本文从源码角度进一步讨论.NET Core 3.0 中关于Host扩展的一些技术点,主要讨论Long Run Pro ...

  8. .NET Core 3.0之深入源码理解HealthCheck(一)

    写在前面 我们的系统可能因为正在部署.服务异常终止或者其他问题导致系统处于非健康状态,这个时候我们需要知道系统的健康状况,而健康检查可以帮助我们快速确定系统是否处于正常状态.一般情况下,我们会提供公开 ...

  9. org.springframework.core.io包内的源码分析

    前些日子看<深入理解javaweb开发>时,看到第一章java的io流,发觉自己对io流真的不是很熟悉.然后看了下JDK1.7中io包的一点点代码,又看了org.springframewo ...

随机推荐

  1. Linux服务器时间相关命令记录

    前言 以往安装服务器时间都是正常,但是最近服务器的时间经常出现问题,所以在安装配置完成服务器之后需要对服务器的时间进行测试,如果服务器时间异常,那么当程序去取系统时间的时候就会出现问题. 时间相关命令 ...

  2. dbcontext实例创建问题

    dbcontext初始化 Private DemoContext db=new DemoContext (): 问题:什么时候释放db对象? 使用Using()方法中创建,每次调用会造成频繁的连接关闭 ...

  3. 通过钩子程序跨程序关闭Window

    需求: 在实际场景中会有自身程序在调用第三方的动态库过程中,因为第三方的动态库弹框导致线程阻塞,必须手动将弹窗关闭后才能回到自身程序的主线程中. 最简单的场景就是很多自助设备,本身是没有固定操作员的, ...

  4. 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU启动那些事(3)- Serial Downloader模式(sdphost/MfgTool)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RT系列MCU的Serial Downloader模式. 在上一篇文章 Boot配置(BOOT Pin, eFUSE) ...

  5. Gartner 2018 数据库系列报告发布 巨杉数据库连续两年入选

    近期,Gartner陆续发布了2018年的数据库系列报告,包括<数据库魔力象限><数据库核心能力>以及<数据库推荐报告>.其中,SequoiaDB巨杉数据库作为业界 ...

  6. xshell连接虚拟机详解--技术流ken

    xshell连接虚拟机 第一步:网络模式更改为桥接模式 第二步:重启网络 [root@ken1 ~]# systemctl restart network 第三步:获取IP地址 输入命令ip a 第四 ...

  7. web类协议脚本-飞机订票系统示例

    以下是LR自带的飞机订票系统的Demo,希望能帮助大家. Action() { int iRand; int iTmp; char *strTmpA; char *strTmpB; char *str ...

  8. Unlink——2016 ZCTF note2解析

    简介 Unlink是经典的堆漏洞,刚看到这个漏洞不知道如何实现任意代码执行,所以找了一个CTF题,发现还有一些细节的地方没有讲的很清楚,题目在这里.自己也动手写一遍,体验一下 题目描述 首先,我们先分 ...

  9. jquery 实现省市二级联动,附带完整的省市json数据 (粘贴即用)

    1.可以单独定义一个js,保存省市json数据. citydata = { "安徽": [ "合肥", "芜湖", "蚌埠&quo ...

  10. MySQL查询执行的基础——查询优化处理

    查询的生命周期的下一步是将一个SQL转换成一个可执行计划,MySQL再按照这个计划和存储引擎进行交互 语法解析器和预处理 首先,MySQL通过关键词将SQL语句进行解析,并生成一颗对应的"解 ...