Category: Golang

学习Golang之服务器框架编写 – 名字发现服务

名字发现服务(后文简称Naming)一般使用在分布式系统中,当新节点加入时,如何让其他节点知道?

原始的做法是使用配置文件,对每个节点可相连的节点进行配置。这种方法费时费力还容易出错。所以现代系统中一般使用配置中心或者Naming对新加入的节点进行管理,让其他节点感知到它的存在,从而进行通信。

剔除节点的情形也类似,使用Naming机制能更方便的屏蔽失效节点。所以Blue也内置了一套简单的Naming。

 

Blue中的Naming为了解决什么问题?

首先要说明是的,Blue不是针对分布式系统开发的,只是参考了分布式系统的思想,希望使用Blue搭建的服务能方便的平行扩缩容,让Blue内建的RPC更容易使用。所以在功能上来说Naming主要提供:

  • 存储所有节点的有效信息;
  • 感知节点的加入与失效;
  • 广播变化节点的信息至相关节点;

 

实现的方案选择

其实go语言对于Naming的实现有大名鼎鼎的etcd,一个分布式、高可用的k-v存储系统。它的一个特性是提供了gRPC naming and discovery,依托于gRPC提供Naming。其实我设计Blue Naming的思想有些受到它的影响 – RPC必须很好的和Naming配合起来。(自信点的说法是不谋而合,在最开始构思Blue的时候就选定了gRPC作为内置RPC实现方案,但是那个时候我并不知道有etcd这个大杀器)

最后之所以没有选择etcd的原因是:功能太大而全了!它的成名主要归功于k8s使用其做Naming,追根溯源的话,它并不是以Naming为目的开发。

要说有直接拿来用且功能符合预期的轮子还有一个consul,其生来就是为Naming。但是经过评估后,发现把它融合进Blue并且不破坏设计目标比较难,所以也放弃了。

所以最终还是手动写一个Blue专有的Naming,它不具有普适性,但是确是最适合Blue的定制版。

 

Blue Naming的实现

程序启动流程

在新的Goroutine中将节点内存数据落地到文件,这是一个定时的异步过程,对于一个节点存储的信息如下:

type ServerInfo struct {
    app           string
    set           string     
    name          string
    addr          string
    isActive      bool
    lastHeartBeat time.Time
    update        chan bool
}

需要注意的是update字段,这是一个更新通知器,当该节点需要更新数据时管道会被写入数据。(update管道缓存大小默认10)

节点上报更新

在gRPC使用的provider.proto文件里,一共有三个接口提供节点与Naming的交互:

  • Registry           –  节点注册,节点初始化时调用
  • HeartBeat        –  节点心跳,节点定期调用
  • UpdateInfo      –  节点更新,从Naming主动推送的stream

具体交互序列图:

一些设计细节思考

为什么不用HeartBeat带回UpdateInfo

最初的设计是想用HeartBeat带回UpdateInfo,一方面可以简化设计,一方面可以提高网络利用率,但是最终因为实时性的原因放弃了这个方案。还是增加了UpdateInfo推送流;

为什么Naming收到第一个HeartBeat才将节点置active

在Naming收到第一个HeartBeat才将节点置位active,这么做的原因是,Blue无法保证自定义的程序在Registry到程序正常运行这段时间是否会有异常。(比如这中间有一个自定义的预处理,它存在一个严重错误,需要整个程序退出,如果在Registry阶段将其设置成active广播到其他节点是不合理的,这样会导致在至少两个超时时间内节点处于假死状态)

所以在Registry10秒后才会发第一个HeartBeat告知Naming:我启动好了,可以接受服务。同时也要求使用Blue的自定义程序能在10秒内将初始化工作完成,让节点进入可服务状态。

这里比较完美做法是,留下一个通知接口让使用者调用,显式告诉Blue节点已经准备好了,可以进行服务了。但同时考虑到不应该对使用者的使用方式做过多的干预和要求,这里暂时还是使用约定时长的方式。

Naming的可用性问题

目前Blue的Naming是单点的,但即使Naming停止了服务也不影响现有节点之间的通讯与服务,可能的故障分三种情况:

  1. Naming进程跪了。不要紧,我们有落地的文件,Naming重启的时候会加载文件里内容,虽然文件可能会有脏数据,但是在HeartBeat时会把错误的状态纠正;
  2. Naming所在的机器宕机了。这个还好,只需要尽快回复机器,重启Naming进程;
  3. Naming所在的机器宕机无法回复并且硬盘挂了。这个有点麻烦,换机器会导致Naming服务地址变动,最终需要修改节点的Naming配置;

高可用的解决方案很多,从主备到主主,甚至分布式,我个人更倾向于主主,可用性理论最高可以达到99.9%,后面有必要的话会尝试扩展。

(全文结束)

 

学习Golang之服务器框架编写 – CS网络通信

概况

blue的整个CS网络通信设计参考了TarsGo(https://github.com/TarsCloud/TarsGo),在其基础上做了一些优化与微创新。

目前只提供了基于TCP通信协议的相关接口,后续会加入UDP或者RUDP通信。

代码包里提供了一个简单的TCPClient封装,里面有连接服务器、收发包基本功能。一般来说用不到,因为使用go做客户端的比较少。目前仅仅在blue的样例中使用。

 

设计细节

Server框架

利用go的语言特性,我们很方便的将Server需要的接口抽象出来,类似于C++的纯虚函数:

type ServerHandler interface {
    //设置监听地址端口
    Listen() error
    //接受连接的主循环,根据不同协议处理不同
    Run() error
}

如果你需要自己加入相关的协议监听,上述interface的内容是必须要实现的。

Read More →

学习Golang之服务器框架编写 – 开篇

写在前面的废话

最近几年一直关注Golang(后面简称Go)的发展,最开始翻看了很多关于Go独特优点以及如何解决传统C/C++程序员痛点的文章,这几年也断断续续的使用Go写一些工具来辅助开发或者服务器事务处理,一方面是在实践中学习Go,另一方面是体验一下Go的“妙处”,看看是不是真说的那么神。但从目前来看收效并不明显,或者说自己没有明显感觉到Go的魅力,归结原因还是对Go使用程度不够。

纸上得来终觉浅,绝知此事要躬行。所以我决定使用Go来造一个服务器框架的轮子。在此之前见过太多的服务器框架,凡是和“框架”两字沾边,必然有一些个人偏好或者选择倾向(从模块结构,代码组织,使用方式等几个角度),而且一般都是针对某种特定场景的框架,比如Http服务、游戏服务、微服务等等。

所以这个轮子也逃不出“凡是”:它会带有一些个人偏好,并且倾向于游戏服务,甚至因为个人经验水平有限会有一些不成熟的设计。不管怎么说,欢迎一起讨论学习。

目前来说初步的功能已经实现,包括一些基本的功能测试用例,后续除了未知BUG的修改,还会出一版完整的使用样例,扩容一下CS通信相关,以及接口易用性的调试修改。

Github地址:https://github.com/lanyutc/blue

Read More →

鄂公网安备42018502003990号