.Net Core Configuration Etcd数据源

前言

    .Net Core为我们提供了一套强大的Configuration配置系统,使用简单扩展性强。通过这套配置系统我们可以将Json、Xml、Ini等数据源加载到程序中,也可以自己扩展其他形式的存储源。今天我们要做的就是通过自定义的方式为其扩展Etcd数据源操作。

何为Etdc

    在使用etcd之前我们先介绍一下Etcd,我相信很多同学都早有耳闻。Etcd是一款高可用、强一致的分布式KV存储系统,它内部采用raft协议作为一致性算法,本身也是基于GO语言开发的,最新版本为v3.4.9,具体版本下载地址可参阅官方GitHub地址。相信了解过K8S的同学对这个肯定不陌生,它是K8S的数据管理系统。官方地址为https://etcd.io/。
    在此之前,我相信大家已经了解过很多存储系统了,Etcd到底能实现了什么功能呢?其一用于配置中心和服务发现,再者也可以实现分布式锁和消息系统。它本身就是基于目录型存储,并且内部有一套强大的Watch机制可以监听针对节点和数据的操作变化,每次对节点的事务操作都会有对于的版本信息。

Etcd VS Zookeeper

通过上面的介绍是不是感觉和Zookeeper有点类似呢????????????,网上有很多很多关于Etcd和Zookeeper的对比文章,大致如下可以得到以下结论

功能EtcdZookeeper
分布式锁有(采用节点版本号信息)有(采用临时节点和顺序临时节点)
watcher
一致性算法raftzab
选举
元数据(metadata)存储
应用场景EtcdZookeeper
发布与订阅(配置中心)有(不限次Watch)有(一次性触发的,需要重新注册Watch)
软负载均衡
命名服务(Naming Service)
服务发现有(基于租约节点)有(基于临时节点)
分布式通知/协调
集群管理与Master选举
分布式锁
分布式队列

说白了就是Zookeeper能干的活,Etcd也能干。那既然有了Zookeeper为啥还要选择Etcd,主要基于以下原因

  • 更轻量级(Etcd基于GO语言开发,Zookeeper基于Java开发)、更易用(开箱即用)

  • 高负载下的稳定读写

  • 数据模型的多版本并发控制

  • 稳定的watcher功能,通知订阅者监听值的变化(Zookeeper基于数据的监听是一次性的,每次监听完成还需重新注册)

  • 客户端协议使用GRPC协议,支持语言更广泛

一言以蔽之,就是不仅实现了Zookeeper的功能,还在很多方面吊打Zookeeper????????????,这么强大的东西忍不住都要试一试。

在.Net Core中使用Etcd

    在Nuget上可以搜索到很多.Net Core的Etcd客户端驱动程序,我使用了下载量最多的一个名字叫dotnet-etcd的驱动包,顺便找到了它在GayHub上,不好意思手滑打错了????????????GitHub上的项目地址,大概学习了一下基本的使用方式。其实我们结合Configuration配置这一块,只需要两个功能。一个是Get获取数据,另一个是Watch节点变化(更新数据会用到)。个人认为,前期有目有边界的学习还是非常重要的。

Configuration扩展Etcd

前面我们讲到过自定义扩展Configuration是非常方便的,相信了解过Configuration相关源码的小伙伴们已经非常熟悉了,大致总结一下分为三步:

  • 编写IConfigurationBuilder扩展方法,我们这里叫AddEtcd

  • 编写实现IConfigurationSource的配置源信息类,我们这里叫EtcdConfigurationSource

  • 编写继承自ConfigurationProvider的ConfigurationSource的配置数据提供类,我们这里叫EtcdConfigurationProvider

因为微软已经给我们提供了一部分便利,所以编写起来还是非常的简单的。好了,接下来我们开始编写具体的实现代码,重点的地方我会在代码中注释说明。
首先是定义扩展类EtcdConfigurationExtensions,这个类是针对IConfigurationBuilder的扩展方法,实现如下

public static class EtcdConfigurationExtensions
{/// <summary>/// AddEtcd扩展方法/// </summary>/// <param name="serverAddress">Etcd地址</param>/// <param name="path">读取路径</param>/// <returns></returns>public static IConfigurationBuilder AddEtcd(this IConfigurationBuilder builder, string serverAddress,string path){return AddEtcd(builder, serverAddress:serverAddress, path: path,reloadOnChange: false);}/// <summary>/// AddEtcd扩展方法/// </summary>/// <param name="serverAddress">Etcd地址</param>/// <param name="path">读取路径</param>/// <param name="reloadOnChange">如果数据发送改变是否刷新</param>/// <returns></returns>public static IConfigurationBuilder AddEtcd(this IConfigurationBuilder builder, string serverAddress, string path, bool reloadOnChange){return AddEtcd(builder,options => {options.Address = serverAddress;options.Path = path;options.ReloadOnChange = reloadOnChange;});}public static IConfigurationBuilder AddEtcd(this IConfigurationBuilder builder, Action<EtcdOptions> options){EtcdOptions etcdOptions = new EtcdOptions();options.Invoke(etcdOptions);return builder.Add(new EtcdConfigurationSource { EtcdOptions = etcdOptions });}
}

这里我还定义了一个EtcdOptions的POCO,用于承载读取Etcd的配置属性

public class EtcdOptions
{/// <summary>/// Etcd地址/// </summary>public string Address { get; set; }/// <summary>/// Etcd访问用户名/// </summary>public string UserName { get; set; }/// <summary>/// Etcd访问密码/// </summary>public string PassWord { get; set; }/// <summary>/// Etcd读取路径/// </summary>public string Path { get; set; }/// <summary>/// 数据变更是否刷新读取/// </summary>public bool ReloadOnChange { get; set; }
}

接下来我们定义EtcdConfigurationSource,这个类非常简单就是返回一个配置提供对象

public class EtcdConfigurationSource : IConfigurationSource
{public EtcdOptions EtcdOptions { get; set; }public IConfigurationProvider Build(IConfigurationBuilder builder){return new EtcdConfigurationProvider(EtcdOptions);}
}

真正的读取操作都在EtcdConfigurationProvider里

public class EtcdConfigurationProvider : ConfigurationProvider
{private readonly string _path;private readonly bool _reloadOnChange;private readonly EtcdClient _etcdClient;public EtcdConfigurationProvider(EtcdOptions options){//实例化EtcdClient_etcdClient = new EtcdClient(options.Address,username: options.UserName,password: options.PassWord);_path = options.Path;_reloadOnChange = options.ReloadOnChange;}/// <summary>/// 重写加载方法/// </summary>public override void Load(){//读取数据LoadData();//数据发生变化是否重新加载if (_reloadOnChange){ReloadData();}}private void LoadData(){//读取Etcd里的数据string result = _etcdClient.GetValAsync(_path).GetAwaiter().GetResult();if (string.IsNullOrEmpty(result)){return;}//转换一下数据结构,这里我使用的是json格式//读取的数据只要赋值到Data属性上即可,IConfiguration真正读取的数据就是存储到Data的字典数据Data = ConvertData(result);}private IDictionary<string,string> ConvertData(string result){byte[] array = Encoding.UTF8.GetBytes(result);MemoryStream stream = new MemoryStream(array);//JsonConfigurationFileParser是将json数据转换为Configuration可读取的结构(复制JsonConfiguration类库里的????????????)return JsonConfigurationFileParser.Parse(stream);}private void ReloadData(){WatchRequest request = new WatchRequest(){CreateRequest = new WatchCreateRequest(){//需要转换一个格式,因为etcd v3版本的接口都包含在grpc的定义中Key = ByteString.CopyFromUtf8(_path)}};//监听Etcd节点变化,获取变更数据,更新配置_etcdClient.Watch(request, rsp =>{if (rsp.Events.Any()){var @event = rsp.Events[0];//需要转换一个格式,因为etcd v3版本的接口都包含在grpc的定义中Data = ConvertData(@event.Kv.Value.ToStringUtf8());//需要调用ConfigurationProvider的OnReload方法触发ConfigurationReloadToken通知//这样才能对使用Configuration的类发送数据变更通知//比如IOptionsMonitor就是通过ConfigurationReloadToken通知变更数据的OnReload();}});}
}

使用方式如下

builder.AddEtcd("http://127.0.0.1:2379", "service/mydemo", true);

顺便给大家推荐一个Etcd可视化管理工具ETCD Manager,以便更好的学习Etcd。
到这里,基本上就结束了,是不是非常简单。主要还是Configuration本身的设计思路比较清晰,所以实现起来也不费劲。

总结

    以上代码都已经上传了我的GitHub,该仓库还扩展了其他数据源的读取比如Consul、Properties文件、Yaml文件的读取,实现思路也都大致相似,有兴趣的同学可以自行查阅。由于主要是讲解实现思路,可能许多细节并未做处理还望见谅。如果有疑问或者更好的建议,欢迎评论区交流指导。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309133.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法-二分查找

二分查找 特点&#xff1a; T(n) T(n/2 - 1) c 时间复杂度O&#xff08;lgn) 前提&#xff1a;需要已排序的集合 int binary_search(int *arr,int start,int end,int key) {if(start<end){int middle (startend)/2;if(arr[middle] key) return middle;if(key<arr[m…

Java IDEA断点调试

断点调试(debug) 断点调试应用案例 01&#xff1a; package Assign;public class Debug01 {public static void main(String[] args) {int sum 0;for (int i 0;i<5;i){sumi;System.out.println(i);System.out.println(sum);}System.out.println("continue");} …

.NET Core请求控制器Action方法正确匹配,但为何404?

【导读】提前预祝各位端午节快乐。有时候我们会发现方法名称都正确匹配&#xff0c;但就是找不到对应请求接口&#xff0c;所以本文我们来深入了解下何时会出现接口请求404的情况。匹配控制器Action方法&#xff08;404&#xff09;首先我们创建一个web api应用程序&#xff0c…

布斯乘法以及带符号数的运算

乘法理解 对于最熟悉的十进制乘法很最基本的运算原理就是一个乘数乘以另一个乘数的个位、十位、百位数字然后求和。比如 放到二进制来看其实它也是这样的&#xff0c;多位数的乘法就是一个乘数乘上另一个乘数的各位求和。那么&#xff1a; 布斯算法及原理 原理 已经知道两…

算法-排序-冒泡排序

冒泡排序 特点&#xff1a;原址排序&#xff0c;比较排序 时间复杂度O&#xff08;n^2) void bubble_sort(int *arr,int start,int end) {for (int i start; i < end; i) {for (int j end; j >i ; j--) {if(arr[j-1]>arr[j]){int temp arr[j];arr[j] arr[j-1];a…

C++重载输出操作符<<,为什么要返回引用

针对&#xff1a;ostream & operator <<(ostream & os, const ClassType &object) 说明几点&#xff1a; 1.第一个形参为对ostream对象的引用&#xff0c;在该对象上将产生输出&#xff0c;ostream为非const&#xff0c;因为写入到流会改变流的状态&#x…

angular 接入 IdentityServer4

angular 接入 IdentityServer4Intro最近把活动室预约的项目做了一个升级&#xff0c;预约活动室需要登录才能预约&#xff0c;并用 IdentityServer4 做了一个统一的登录注册中心&#xff0c;这样以后就可以把其他的需要用户操作的应用统一到 IdentityServer 这里&#xff0c;这…

算法-计算逆序对个数

求逆序对的个数 特点&#xff1a;利用归并排序中合并的步骤&#xff0c;计算逆序对 时间复杂度O&#xff08;nlgn&#xff09; int merge_inversion(int *arr,int start,int end,int middle); int count_inversion(int *arr,int start,int end) {if(start<end){int middle…

主机Redis服务迁移到现有Docker Overlay网络

“《麻雀虽小&#xff0c;五脏俱全》之主机现有Redis服务迁移到Docker Swarm Overlay网络&#xff0c;并搭建高可用容器集群。hello, 好久不见&#xff0c;之前文章记录了一个实战的2C分布式项目的改造过程&#xff0c;结果如下&#xff1a;其中Redis并未完成容器化改造&#x…

堆排序(包含最大堆和最小堆)

堆排序&#xff08;包含最大堆和最小堆&#xff09; 堆排序 时间复杂度nlgn&#xff0c;原址排序。 通用函数 int parent(int i) {return (i - 1) >> 1; } int left(int i) {return (i << 1) 1; } int right(int i) {return (i << 1) 2; }最大堆排序 v…

Java控制结构

控制结构 程序流程控制介绍 顺序控制 分支控制if-else 单分支 案例演示 01: import java.util.Scanner; public class IfWorkDemo {public static void main(String[] args){Scanner myScanner new Scanner(System.in);System.out.println("input your age");int…

.Net Core Configuration源码探究

前言上篇文章我们演示了为Configuration添加Etcd数据源&#xff0c;并且了解到为Configuration扩展自定义数据源还是非常简单的&#xff0c;核心就是把数据源的数据按照一定的规则读取到指定的字典里&#xff0c;这些都得益于微软设计的合理性和便捷性。本篇文章我们将一起探究…

最大堆实现优先队列

最大堆实现优先队列 头文件 #include "heap_sort.h" #include "stdexcept" #include <iostream> class MaxHeapPriorityQueue { public:int heap_size; private:int capacity;int *array;void increase_key(int key,int index); public:void inser…

面试官:你说你喜欢研究新技术,那么请说说你对 Blazor 的了解

阅读本文大概需要 1.5 分钟。最近在几个微信 .NET 交流群里大家讨论比较频繁的话题就是这几天自己的面试经历。面试官&#xff1a;“你刚说你喜欢研究新技术&#xff0c;那么你对 Blazor 了解多少&#xff1f;”作为一位专注于 .NET 开发的软件工程师&#xff0c;你好意思说你对…

Java变量

变量 ​ 变量是程序的基本组成单位 变量的介绍 概念 变量相当于内存中一个数据存储空间的表示&#xff0c;你可以把变量看做是一个房间的门牌号&#xff0c;通过门牌号我们可以找到房间&#xff0c;而通过变量名可以访问到变量(值)。 01&#xff1a; class Test {public s…

将k个有序链表合并成一个有序链表

将k个有序链表合并成一个有序链表 这里以从小到大排序为例&#xff0c; 时间复杂度为O&#xff08;nlgk&#xff09;。 特点&#xff1a;利用最小堆来完成k路归并 思路&#xff1a;取每个列表中的第一个元素&#xff0c;并将其放在最小堆中。与每个元素一起&#xff0c;必须跟…

[Student.Achieve] 学生教务管理系统开源

&#xff08;源自&#xff1a;https://neters.club&#xff09;一觉醒来Github改版了&#xff0c;我个人还是挺喜欢的????。还有两个月就是老张做开源两周年了&#xff0c;时间真快&#xff0c;也慢慢的贡献了很多的开源作品&#xff0c;上边的是主要的七个作品&#xff0c…

d叉堆实现优先队列

d叉堆实现优先队列 时间复杂度 dlog&#xff08;d,n) #include <stdexcept> #include <iostream> class DForkHeapPriorityQueue { private:int capacity;int *array;int d;int heap_size; public:DForkHeapPriorityQueue(int capacity,int d);DForkHeapPriority…

.NET Core HttpClient源码探究

前言在之前的文章我们介绍过HttpClient相关的服务发现&#xff0c;确实HttpClient是目前.NET Core进行Http网络编程的的主要手段。在之前的介绍中也看到了&#xff0c;我们使用了一个很重要的抽象HttpMessageHandler&#xff0c;接下来我们就探究一下HttpClient源码&#xff0c…

Young氏矩阵

Young氏矩阵 利用堆思想实现Young氏矩阵 #include <iostream> class YoungTableau {private:int *array;int row;int column;int heap_size; public:YoungTableau