GoIM 源码分析(一):Comet

分析 GoIM 对外服务模块 Comet

Posted by pandaychen on August 1, 2020

0x00 前言

本篇文章,分析下 GoIM 的 Comet 模块。

  1. Comet 主方法
  2. Comet 主要数据结构
  3. Comet 管理服务(与 Job 模块通信)

Comet模块的位置: COMET

0x01 Comet 模块

Comet 模块为用户代理 Server(主要提供对外服务),用于客户端的连接,根据情况可部署多个 Comet-Server(扩展)。Comet 模块支持 Tcp, Http, WebSocket, TLS WebSocket 等多种服务。

0x02 主逻辑入口

Comet 的入口 main.go,主要完成初始化及服务器的启动工作及注册: 这里提几点可借鉴的经验:

  1. 使用随机数必须运行 rand.Seed(time.Now().UTC().UnixNano()),一般放在初始化时
  2. 使用 runtime.GOMAXPROCS(runtime.NumCPU()) 设置合适的 core 数是个好习惯,K8S 环境常用 automaxprocess 设置
  3. 启动了 N 个监听端口(配置文件)
    • 对外端口有:InitTCPInitWebsocket
    • 对内端口(管理):grpc.New(conf.Conf.RPCServer, srv)
  4. 服务的注册使用 register 完成,用于客户端服务发现
  5. Server 端优雅退出
  6. Comet 维护了一个 白名单 机制
func main() {
    ...
	rand.Seed(time.Now().UTC().UnixNano())
	runtime.GOMAXPROCS(runtime.NumCPU())
	println(conf.Conf.Debug)
	log.Infof("goim-comet [version: %s env: %+v] start", ver, conf.Conf.Env)
	// register discovery
	dis := naming.New(conf.Conf.Discovery)
	resolver.Register(dis)
	// new comet server
	srv := comet.NewServer(conf.Conf)
	if err := comet.InitWhitelist(conf.Conf.Whitelist); err != nil {
		panic(err)
	}
	if err := comet.InitTCP(srv, conf.Conf.TCP.Bind, runtime.NumCPU()); err != nil {
		panic(err)
	}
	if err := comet.InitWebsocket(srv, conf.Conf.Websocket.Bind, runtime.NumCPU()); err != nil {
		panic(err)
	}
	if conf.Conf.Websocket.TLSOpen {
		if err := comet.InitWebsocketWithTLS(srv, conf.Conf.Websocket.TLSBind, conf.Conf.Websocket.CertFile, conf.Conf.Websocket.PrivateFile, runtime.NumCPU()); err != nil {
			panic(err)
		}
	}
	// new grpc server
	rpcSrv := grpc.New(conf.Conf.RPCServer, srv)
	cancel := register(dis, srv)
	// signal
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
	for {
		s := <-c
		log.Infof("goim-comet get a signal %s", s.String())
		switch s {
		case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
			if cancel != nil {
				cancel()
			}
			rpcSrv.GracefulStop()
			srv.Close()
			log.Infof("goim-comet [version: %s] exit", ver)
			log.Flush()
			return
		case syscall.SIGHUP:
		default:
			return
		}
	}
}

0x03 Comet 结构

comet 结构定义,Comet 对应的结构是项目中最复杂的,下面先简单列举下:

Server

// Server is comet server.
type Server struct {
	c         *conf.Config
	round     *Round    // accept round store
	buckets   []*Bucket // subkey bucket
	bucketIdx uint32

	serverID  string
	rpcClient logic.LogicClient
}

Round

// Round userd for connection round-robin get a reader/writer/timer for split big lock.
type Round struct {
	readers []bytes.Pool
	writers []bytes.Pool
	timers  []time.Timer
	options RoundOptions
}

Bucket

// Bucket is a channel holder.
type Bucket struct {
	c     *conf.Bucket
	cLock sync.RWMutex        // protect the channels for chs
	chs   map[string]*Channel // map sub key to a channel
	// room
	rooms       map[string]*Room // bucket room channels
	routines    []chan *grpc.BroadcastRoomReq
	routinesNum uint64

	ipCnts map[string]int32
}

参考