通过memberlist库实现gossip管理集群以及集群数据交互
通过memberlist库实现gossip管理集群以及集群数据交互
概述
memberlist库的简单用法如下,注意下面使用for循环来执行list.Join,原因是一开始各节点都没有runing,直接执行Join会出现连接拒绝的错误。
package main
import (
"fmt"
"github.com/hashicorp/memberlist"
"time"
)
func main() {
/* Create the initial memberlist from a safe configuration.
Please reference the godoc for other default config types.
http://godoc.org/github.com/hashicorp/memberlist#Config
*/
list, err := memberlist.Create(memberlist.DefaultLocalConfig())
if err != nil {
panic("Failed to create memberlist: " + err.Error())
}
t := time.NewTicker(time.Second * 5)
for {
select {
case <-t.C:
// Join an existing cluster by specifying at least one known member.
n, err := list.Join([]string{"192.168.80.129"})
if err != nil {
fmt.Println("Failed to join cluster: " + err.Error())
continue
}
fmt.Println("member number is:", n)
goto END
}
}
END:
for {
select {
case <-t.C:
// Ask for members of the cluster
for _, member := range list.Members() {
fmt.Printf("Member: %s %s\n", member.Name, member.Addr)
}
}
}
// Continue doing whatever you need, memberlist will maintain membership
// information in the background. Delegates can be used for receiving
// events when members join or leave.
}
memberlist的两个主要接口如下:
Create:根据入参配置创建一个
Memberlist,初始化阶段Memberlist仅包含本节点状态。注意此时并不会连接到其他节点,执行成功之后就可以允许其他节点加入该memberlist。Join:使用已有的
Memberlist来尝试连接给定的主机,并与之同步状态,以此来加入某个cluster。执行该操作可以让其他节点了解到本节点的存在。最后返回成功建立连接的节点数以及错误信息,如果没有与任何节点建立连接,则返回错误。注意当join一个cluster时,至少需要指定集群中的一个已知成员,后续会通过gossip同步整个集群的成员信息。
memberlist提供的功能主要分为两块:维护成员状态(gossip)以及数据同步(boardcast、SendReliable)。下面看几个相关接口。
接口
memberlist.Create的入参要求给出相应的配置信息,DefaultLocalConfig()给出了通用的配置信息,但还需要实现相关接口来实现成员状态的同步以及用户数据的收发。注意下面有些接口是必选的,有些则可选:
type Config struct {
// ...
// Delegate and Events are delegates for receiving and providing
// data to memberlist via callback mechanisms. For Delegate, see
// the Delegate interface. For Events, see the EventDelegate interface.
//
// The DelegateProtocolMin/Max are used to guarantee protocol-compatibility
// for any custom messages that the delegate might do (broadcasts,
// local/remote state, etc.). If you don't set these, then the protocol
// versions will just be zero, and version compliance won't be done.
Delegate Delegate
Events EventDelegate
Conflict ConflictDelegate
Merge MergeDelegate
Ping PingDelegate
Alive AliveDelegate
//...
}
memberlist使用如下类型的消息来同步集群状态和处理用户消息:
const (
pingMsg messageType = iota
indirectPingMsg
ackRespMsg
suspectMsg
aliveMsg
deadMsg
pushPullMsg
compoundMsg
userMsg // User mesg, not handled by us
compressMsg
encryptMsg
nackRespMsg
hasCrcMsg
errMsg
)
Delegate
如果要使用memberlist的gossip协议,则必须实现该接口。所有这些方法都必须是线程安全的。
type Delegate interface {
// NodeMeta is used to retrieve meta-data about the current node
// when broadcasting an alive message. It's length is limited to
// the given byte size. This metadata is available in the Node structure.
NodeMeta(limit int) []byte
// NotifyMsg is called when a user-data message is received.
// Care should be taken that this method does not block, since doing
// so would block the entire UDP packet receive loop. Additionally, the byte
// slice may be modified after the call returns, so it should be copied if needed
NotifyMsg([]byte)
// GetBroadcasts is called when user data messages can be broadcast.
// It can return a list of buffers to send. Each buffer should assume an
// overhead as provided with a limit on the total byte size allowed.
// The total byte size of the resulting data to send must not exceed
// the limit. Care should be taken that this method does not block,
// since doing so would block the entire UDP packet receive loop.
GetBroadcasts(overhead, limit int) [][]byte
// LocalState is used for a TCP Push/Pull. This is sent to
// the remote side in addition to the membership information. Any
// data can be sent here. See MergeRemoteState as well. The `join`
// boolean indicates this is for a join instead of a push/pull.
LocalState(join bool) []byte
// MergeRemoteState is invoked after a TCP Push/Pull. This is the
// state received from the remote side and is the result of the
// remote side's LocalState call. The 'join'
// boolean indicates this is for a join instead of a push/pull.
MergeRemoteState(buf []byte, join bool)
}
主要方法如下:
NotifyMsg:用于接收用户消息(
userMsg)。注意不能阻塞该方法,否则会阻塞整个UDP/TCP报文接收循环。此外由于数据可能在方法调用时被修改,因此应该事先拷贝数据。该方法用于接收通过UDP/TCP方式发送的用户消息(
userMsg):注意UDP方式并不是立即发送的,它会随gossip周期性发送或在处理
pingMsg等消息时发送从GetBroadcasts获取到的用户消息。//使用UDP方式将用户消息传输到给定节点,消息大小受限于memberlist的UDPBufferSize配置。没有使用gossip机制
func (m *Memberlist) SendBestEffort(to *Node, msg []byte) error
//与SendBestEffort机制相同,只不过一个指定了Node,一个指定了Node地址
func (m *Memberlist) SendToAddress(a Address, msg []byte) error
//使用TCP方式将用户消息传输到给定节点,消息没有大小限制。没有使用gossip机制
func (m *Memberlist) SendReliable(to *Node, msg []byte) error
GetBroadcasts:用于在gossip周期性调度或处理处理
pingMsg等消息时携带用户消息,因此并不是即时的。通常会把需要发送的消息通过TransmitLimitedQueue.QueueBroadcast保存起来,然后在发送时通过TransmitLimitedQueue.GetBroadcasts获取需要发送的消息。见下面TransmitLimitedQueue的描述。LocalState:用于TCP Push/Pull,用于向远端发送除成员之外的信息(可以发送任意数据),用于定期同步成员状态。参数
join用于表示将该方法用于join阶段,而非push/pull。MergeRemoteState:TCP Push/Pull之后调用,接收到远端的状态(即远端调用LocalState的结果)。参数
join用于表示将该方法用于join阶段,而非push/pull。
定期(PushPullInterval)调用pushPull来随机执行一次完整的状态交互。但由于pushPull会与其他节点同步本节点的所有状态,因此代价也比较大。
EventDelegate
仅用于接收成员的joining 和leaving通知,可以用于更新本地的成员状态信息。
type EventDelegate interface {
// NotifyJoin is invoked when a node is detected to have joined.
// The Node argument must not be modified.
NotifyJoin(*Node)
// NotifyLeave is invoked when a node is detected to have left.
// The Node argument must not be modified.
NotifyLeave(*Node)
// NotifyUpdate is invoked when a node is detected to have
// updated, usually involving the meta data. The Node argument
// must not be modified.
NotifyUpdate(*Node)
}
ChannelEventDelegate实现了简单的EventDelegate接口:type ChannelEventDelegate struct {
Ch chan<- NodeEvent
}
ConflictDelegate
用于通知某个client在执行join时产生了命名冲突。通常是因为两个client配置了相同的名称,但使用了不同的地址。可以用于统计错误信息。
type ConflictDelegate interface {
// NotifyConflict is invoked when a name conflict is detected
NotifyConflict(existing, other *Node)
}
MergeDelegate
在集群执行merge操作时调用。NotifyMerge方法的参数peers提供了对端成员信息。可以不实现该接口。
type MergeDelegate interface {
// NotifyMerge is invoked when a merge could take place.
// Provides a list of the nodes known by the peer. If
// the return value is non-nil, the merge is canceled.
NotifyMerge(peers []*Node) error
}
PingDelegate
用于通知观察者完成一个ping消息(pingMsg)要花费多长时间。可以在NotifyPingComplete中(使用histogram)统计ping的执行时间。
type PingDelegate interface {
// AckPayload is invoked when an ack is being sent; the returned bytes will be appended to the ack
AckPayload() []byte
// NotifyPing is invoked when an ack for a ping is received
NotifyPingComplete(other *Node, rtt time.Duration, payload []byte)
}
AliveDelegate
当接收到aliveMsg消息时调用的接口,可以用于添加日志和指标等信息。
type AliveDelegate interface {
// NotifyAlive is invoked when a message about a live
// node is received from the network. Returning a non-nil
// error prevents the node from being considered a peer.
NotifyAlive(peer *Node) error
}
Broadcast
可以随gossip将数据广播到memberlist集群。
// Broadcast is something that can be broadcasted via gossip to
// the memberlist cluster.
type Broadcast interface {
// Invalidates checks if enqueuing the current broadcast
// invalidates a previous broadcast
Invalidates(b Broadcast) bool
// Returns a byte form of the message
Message() []byte
// Finished is invoked when the message will no longer
// be broadcast, either due to invalidation or to the
// transmit limit being reached
Finished()
}
Broadcast接口通常作为TransmitLimitedQueue.QueueBroadcast的入参:
func (q *TransmitLimitedQueue) QueueBroadcast(b Broadcast) {
q.queueBroadcast(b, 0)
}
alertmanager中的实现如下:
type simpleBroadcast []byte
func (b simpleBroadcast) Message() []byte { return []byte(b) }
func (b simpleBroadcast) Invalidates(memberlist.Broadcast) bool { return false }
func (b simpleBroadcast) Finished()
TransmitLimitedQueue
TransmitLimitedQueue主要用于处理广播消息。有两个主要的方法:QueueBroadcast和GetBroadcasts,前者用于保存广播消息,后者用于在发送的时候获取需要广播的消息。随gossip周期性调度或在处理pingMsg等消息时调用GetBroadcasts方法。
// TransmitLimitedQueue is used to queue messages to broadcast to
// the cluster (via gossip) but limits the number of transmits per
// message. It also prioritizes messages with lower transmit counts
// (hence newer messages).
type TransmitLimitedQueue struct {
// NumNodes returns the number of nodes in the cluster. This is
// used to determine the retransmit count, which is calculated
// based on the log of this.
NumNodes func() int
// RetransmitMult is the multiplier used to determine the maximum
// number of retransmissions attempted.
RetransmitMult int
mu sync.Mutex
tq *btree.BTree // stores *limitedBroadcast as btree.Item
tm map[string]*limitedBroadcast
idGen int64
}
小结
memberlist中的消息分为两种,一种是内部用于同步集群状态的消息,另一种是用户消息。
GossipInterval周期性调度的有两个方法:
// GossipInterval and GossipNodes are used to configure the gossip
// behavior of memberlist.
//
// GossipInterval is the interval between sending messages that need
// to be gossiped that haven't been able to piggyback on probing messages.
// If this is set to zero, non-piggyback gossip is disabled. By lowering
// this value (more frequent) gossip messages are propagated across
// the cluster more quickly at the expense of increased bandwidth.
//
// GossipNodes is the number of random nodes to send gossip messages to
// per GossipInterval. Increasing this number causes the gossip messages
// to propagate across the cluster more quickly at the expense of
// increased bandwidth.
//
// GossipToTheDeadTime is the interval after which a node has died that
// we will still try to gossip to it. This gives it a chance to refute.
GossipInterval time.Duration
GossipNodes int
GossipToTheDeadTime time.Duration
用户消息又分为两种:
- 周期性同步:
- 以
PushPullInterval为周期,使用Delegate.LocalState和Delegate.MergeRemoteState以TCP方式同步用户信息; - 使用
Delegate.GetBroadcasts随gossip发送用户信息。
- 以
- 主动发送:使用
SendReliable等方法实现主动发送用户消息。
alertmanager的处理
alertmanager通过两种方式发送用户消息,即UDP方式和TCP方式。在alertmanager中,当要发送的数据大于MaxGossipPacketSize/2将采用TCP方式(SendReliable方法),否则使用UDP方式(Broadcast接口)。
func (c *Channel) Broadcast(b []byte) {
b, err := proto.Marshal(&clusterpb.Part{Key: c.key, Data: b})
if err != nil {
return
}
if OversizedMessage(b) {
select {
case c.msgc <- b: //从c.msgc 接收数据,并使用SendReliable发送
default:
level.Debug(c.logger).Log("msg", "oversized gossip channel full")
c.oversizeGossipMessageDroppedTotal.Inc()
}
} else {
c.send(b)
}
}
func OversizedMessage(b []byte) bool {
return len(b) > MaxGossipPacketSize/2
}
demo
这里实现了一个简单的基于gossip管理集群信息,并通过TCP给集群成员发送信息的例子。
通过memberlist库实现gossip管理集群以及集群数据交互的更多相关文章
- 使用cloudrea manager管理已有的cdh集群(转)
转自:http://blog.51cto.com/teacheryan/1912116 本文介绍如何搭建cloudera manager去接入已有hadoop组件(cdh). 一.下载必备文件: 1. ...
- 利用 Puppet 实现自动化管理配置 Linux 计算机集群
随着虚拟化和云计算技术的兴起,计算机集群的自动化管理和配置成为了数据中心运维管理的热点.对于 IaaS.Paas.Saas 来说,随着业务需求的提升,后台计算机集群的数量也会线性增加.对于数据中心的运 ...
- 使用SolrJ客户端管理SolrCloud(Solr集群)
1.使用SolrJ客户端管理SolrCloud(Solr集群). package com.taotao.search.service; import java.io.IOException; impo ...
- 管理和维护RHCS集群
导读 管理和维护RHCS集群是一个非常复杂和繁琐的工作,要维护好一个RHCS集群,必须熟悉RHCS的基本运行原理,在集群管理方面,RHCS提供了两种方式:即Luci图形界面方式和命令行方式,这儿重点讲 ...
- 通过Heketi管理GlusterFS为K8S集群提供持久化存储
参考文档: Github project:https://github.com/heketi/heketi MANAGING VOLUMES USING HEKETI:https://access.r ...
- Hadoop集群-HDFS集群中大数据运维常用的命令总结
Hadoop集群-HDFS集群中大数据运维常用的命令总结 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本篇博客会简单涉及到滚动编辑,融合镜像文件,目录的空间配额等运维操作简介.话 ...
- Mongodb主从复制 及 副本集+分片集群梳理
转载努力哥原文,原文连接https://www.cnblogs.com/nulige/p/7613721.html 介绍了Mongodb的安装使用,在 MongoDB 中,有两种数据冗余方式,一种 是 ...
- redis 之redis集群与集群配置
一.为什么要用集群 redis3.0集群采用P2P模式,完全去中心化,将redis所有的key分成了16384个槽位,每个redis实例负责一部分slot,集群中的所有信息通过节点数据交换而更新. r ...
- 【MongoDB】windows平台搭建Mongo数据库复制集(类似集群)(转)
原文链接:[MongoDB]windows平台搭建Mongo数据库复制集(类似集群)(一) Replica Sets(复制集)是在mongodDB1.6版本开始新增的功能,它可以实现故障自动切换和自 ...
随机推荐
- java中的stream是啥?
函数式编程的执行是惰性的,按顺序真正执行的时候才会执行相应的代码.方法: 函数式编程是安全的,用的是monad架构 1 public class StreamTest { 2 3 public sta ...
- XCTF练习题---WEB---disabled_button
XCTF练习题---WEB---disabled_button flag:cyberpeace{74bcfce0746d18dd8d560e0f0529a8cf} 解题步骤: 1.观察题目,打开场景 ...
- java自学中出现的问题或者?
自学java之路,是如此的坎坷.经过一段时间的自学,我得出一些总结! 总结如下: 1. 在学习编程之路(Java)的,最基本的还是学习之路,对编程前程深感迷茫2. 网络中有许许多多的编程 ...
- 关于IPC和PTH用户权限问题,psexec拒绝访问(Access Denied)的原因
前瞻 关于net use和psexec无法使用本地管理员组用户建立连接的问题 测试环境: win7系统,存在域环境 域名:de1ay 普通域用户: de1ay\de1ay 域管理员用户:de1ay\A ...
- JUC自定义线程池练习
JUC自定义线程池练习 首先上面该线程池的大致流程 自定义阻塞队列 首先定义一个双向的队列和锁一定两个等待的condition 本类用lock来控制多线程下的流程执行 take和push方法就是死等, ...
- Vue的computed和watch的使用和区别
一.computed: 模板内表达式非常便利,可用于简单计算,当模板内放入太多的逻辑时,模板会过重且难以维护:可以使用computed替代 计算属性是基于它们的响应式依赖进行缓存的,当依赖的响应式数据 ...
- 论文阅读 DyREP:Learning Representations Over Dynamic Graphs
5 DyREP:Learning Representations Over Dynamic Graphs link:https://scholar.google.com/scholar_url?url ...
- 150_1秒获取Power BI Pro帐号
博客:www.jiaopengzi.com 请点击[阅读原文]获取帐号 一.背景 当你来到这篇文章的时候,我想你已经在网上搜索了一圈了.网上有一大把教你如何注册Power BI帐号的方法,我们这里就不 ...
- HCNP Routing&Switching之VRRP基础
前文我们了解了链路高可用技术链路聚合相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16279078.html:今天我们来聊一聊另一种高可用技术,网关高 ...
- 面试官:Netty心跳检测机制是什么,怎么自定义检测间隔时间?
哈喽!大家好,我是小奇,一位热爱分享的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 书接上回,昨天在地里干了一天的 ...