东坡区建设局网站,网站模板 古典,网站菜单导航,wordpress自定义短码分布式(一致性协议)之领导人选举( DotNext.Net.Cluster 实现Raft 选举 )继分布式锁之后的又一高可用技术爽文之分布式领导选举 或者说 分布式一致性协议的实现分布式选举是实现高可用的必备技术#xff0c;想实现主从#xff0c;就必须得有选举的策略#xff0c;有主从才会有… 分布式(一致性协议)之领导人选举( DotNext.Net.Cluster 实现Raft 选举 )继分布式锁之后的又一高可用技术爽文之分布式领导选举 或者说 分布式一致性协议的实现分布式选举是实现高可用的必备技术想实现主从就必须得有选举的策略有主从才会有一个真正的管理端进行资源的协调分配。首先需要明确的是一致性算法的目标是什么主要面对的问题是在只使用单个服务器时由于发生错误导致数据丢失等事情发生。解决这个问题的思路也很简单就是备份集群多个服务器将操作重复到多个机器上就不怕单个机器出错了。但随之而来的就是数据不一致、乱序等问题一致性算法想要做到的是即使有结点出错对外仍是一个完整的可以正常工作的整体。选举算法实现一致性协议选举的主要算法有两种1. Raft2. Paxos相当于 Paxos 来讲 Raft协议相对来讲简单一点。但是Raft 实现起来也不是很容易如果有朋友试图想去实现可以参考这个地方地址 https://zinglix.xyz/2020/06/25/raft/我个人也是 简单的理解了一下。Raft 是一个非拜占庭的一致性算法即所有通信是正确的而非伪造的。N 个结点的情况下N为奇数可以最多容忍 (N−1)/2个结点故障为啥需要单独的选举算法我曾经试图实现一个WEB服务功能我不能保证这个服务的高可用我又不想用其他的现有服务我就想让服务自己本身能支持高可用。当时因为自己认知的问题并没有短时间找到一个可用的方案不过现在有了。要是当时有可能就是别样风景了不好说。分布式选举的大致算法简单的来讲就是找到一个领头的假设有一个leader keyredis里谁抢到了谁就是leader也是可以实现的。这种分布式锁实现的领导选举也可以适用于简单的项目中并且支持单个服务主机。想用分布式选举算法机器最少得2台以上或者是概念上的两台。Raft中主要有三个角色 Leader(领导人)、Follower 跟随者和 Candidate(候选人)当某台机器成为了领导人就会成为主要对外对接人然后把对接的事情同步给下边的跟随者或者候选人同步信息。因为对外只能有一个主服务起到协调管理的作用。这样就会把相应的指令日志分配各个客户端起到数据一致性的作用 这样当领导人废了下个人接任还能继续起到作用。Raft 选举的实例我找了很多.Net 实现的Raft很多只能说是个玩具不能用于生产。不过幸好还真有生产级别的。那就是 DotNext.Net.Cluster 和 DotNext.AspNetCore.Cluster (支持http ) 。主要是基于 DotNext 中的组件。DotNext.Net.Cluster 和 DotNext.AspNetCore.Cluster1. DotNext.Net.Cluster包含集群编程模型、Raft 算法的传输无关实现、Raft 的 TCP 和 UDP 传输绑定、HyParView membersip 协议的传输无关实现用于基于 Gossip 的消息传递2. DotNext.AspNetCore.Cluster是基于DotNext.Net.Cluster库的 Raft 和 HyParView 算法的具体实现用于构建 ASP.NET Core 应用程序支持的功能列表支持的功能列表1. 网络传输TCP、UDP、HTTP 1.1、HTTP/2、HTTP/32. TLS 支持TCP、HTTP 1.1、HTTP/2、HTTP/33. 支持日志压缩的高性能、通用Persistent Write-Ahead Log4. 跨集群节点复制日志条目5. 与 ASP.NET Core 框架紧密集成6. 对 Docker/LXC/Windows 容器友好7. 一切都是可扩展的• 7.1 自定义预写日志• 7.2 自定义网络传输• 7.3 集群成员发现基于 DotNext.Net.Cluster 的TCP 选举实例其实他也是支持http的当然更多姿势得大佬自己去挖掘了。项目大致结构细心的小伙伴就会发现这个是个.Net 6的项目因为它的nuget包只支持.Net 6的。有需要的可以自己改改。项目是参考源示例改了一下有需要的朋友直接去看官方案例项目重点Install-Package DotNext.Net.Cluster -Version 4.6.0DataModifier.csinternal sealed class DataModifier : BackgroundService
{private readonly IRaftCluster cluster;private readonly ISupplierlong valueProvider;public DataModifier(IRaftCluster cluster, ISupplierlong provider){this.cluster cluster;valueProvider provider;}protected override async Task ExecuteAsync(CancellationToken stoppingToken){while (!stoppingToken.IsCancellationRequested){await Task.Delay(1000, stoppingToken).ConfigureAwait(false);var leadershipToken cluster.LeadershipToken;TitleInfo.Show(!leadershipToken.IsCancellationRequested);if (!leadershipToken.IsCancellationRequested){var newValue valueProvider.Invoke() 500L;Console.WriteLine(保存领导节点生成的值 {0}, newValue);var source CancellationTokenSource.CreateLinkedTokenSource(stoppingToken, leadershipToken);try{var entry new Int64LogEntry { Content newValue, Term cluster.Term };await cluster.ReplicateAsync(entry, source.Token);}catch (Exception e){Console.WriteLine(未知异常 {0}, e);}finally{source?.Dispose();}}}}
}这个应该是核心服务会与其他客户端进行通信和具体的选举以及日志的传输Program.csclass Program
{static async Task Main(string[] args){await UseTcpTransport(Path.Combine(AppContext.BaseDirectory, raftConfig));}static Task UseTcpTransport(string path){//获取所有配置var jsonConfiguration new ConfigurationBuilder().SetBasePath(Environment.CurrentDirectory).AddJsonFile(appsettings.json, optional: true, reloadOnChange: true).Build();var NodeInfo new NodeInfo();jsonConfiguration.Bind(NodeInfo, NodeInfo);Console.WriteLine($MainNode:{NodeInfo.MainNode});TitleInfo.Node NodeInfo.MainNode;var configuration new RaftCluster.TcpConfiguration(IPEndPoint.Parse(NodeInfo.MainNode)){RequestTimeout TimeSpan.FromMilliseconds(140),LowerElectionTimeout 150,UpperElectionTimeout 300,TransmissionBlockSize 4096,ColdStart false,};//加载全部地址//线上环境自己重写服务var builder configuration.UseInMemoryConfigurationStorage().CreateActiveConfigurationBuilder();foreach (var item in NodeInfo.Nodes){var address IPEndPoint.Parse(item);builder.Add(ClusterMemberId.FromEndPoint(address), address);}builder.Build();TitleInfo.Show();return UseConfiguration(configuration, path);}static async Task UseConfiguration(RaftCluster.NodeConfiguration config, string? persistentStorage){var loggerFactory new LoggerFactory();var loggerOptions new ConsoleLoggerOptions{LogToStandardErrorThreshold LogLevel.Warning};loggerFactory.AddProvider(new ConsoleLoggerProvider(new FakeOptionsMonitorConsoleLoggerOptions(loggerOptions)));config.LoggerFactory loggerFactory;using var cluster new RaftCluster(config);cluster.LeaderChanged ClusterConfigurator.LeaderChanged;var modifier default(DataModifier?);if (!string.IsNullOrEmpty(persistentStorage)){var state new SimplePersistentState(persistentStorage, new AppEventSource());cluster.AuditTrail state;modifier new DataModifier(cluster, state);}await cluster.StartAsync(CancellationToken.None);await (modifier?.StartAsync(CancellationToken.None) ?? Task.CompletedTask);//控制台等待取消using var handler new CancelKeyPressHandler();Console.CancelKeyPress handler.Handler;await handler.WaitAsync();Console.CancelKeyPress - handler.Handler;//停止服务await (modifier?.StopAsync(CancellationToken.None) ?? Task.CompletedTask);await cluster.StopAsync(CancellationToken.None);}
}总体来说项目还是很简单的。我把客户端的地址给配置了appsettings.json这个结构应该很容易理解一个是当前端的地址一个是所有节点的地址当然也包含当前地址。{NodeInfo: {MainNode: 127.0.0.1:6001 ,Nodes: [127.0.0.1:6001,127.0.0.1:6002,127.0.0.1:6003]}
}运行方式我自己是把Bin目录复制三份每份的 appsettings.json 修改下然后双击 RaftDemo.exe 就运行起来了。注意如果起用一个节点没个卵用最少得两个节点。运行效果总结这个库是可以用在生产环境的所以还是值得研究一下下的。代码地址https://github.com/kesshei/RaftDemo.githttps://gitee.com/kesshei/RaftDemo.git参考文档https://zinglix.xyz/2020/06/25/raft/https://github.com/dotnet/dotNext/tree/master/src/cluster阅一键三连呦感谢大佬的支持您的支持就是我的动力!另坚持更了大概一个月也有几个铁粉了会持续更但是连续更太累了按天更吃不消哦。感谢大佬的支持。