教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 Go 编程:tcpserver & graceful shutdown

Go 编程:tcpserver & graceful shutdown

发布时间:2021-05-27   编辑:jiaochengji.com
教程集为您提供Go 编程:tcpserver & graceful shutdown等资源,欢迎您收藏本站,我们将为您提供最新的Go 编程:tcpserver & graceful shutdown资源

转载请使用原文链接: www.gitdig.com/go-tcpserve…

工作需要快速写了个tcpserver的框架,有效代码差不多 100 行左右,写篇文章分享下实现思路, 顺便解释一下如何实现类似网络服务的Graceful Shutdown功能。

首先,什么是框架?简单总结一下就是将变化交给用户,将不变交给自己,即面向接口编程。所以要实现一个简单的框架,第一步就是明确接口,对所有可能的变化进行接口级别的抽象。画一张简图说明下:

tcpserver 框架的实现

tcpserver框架程序而言,变化的部分非常有限:

  • 监听类型
  • 监听地址
  • 客户端连接处理

那么,就可以将这三部分作为tcpserver的可选参数传递进去。所以,在尚未实现之前, README 文档中的 Quick Start 部分可以写起来了。

import "github.com/x-mod/tcpserver"

srv := tcpserver.NewServer(
    tcpserver.Network("tcp"),
    tcpserver.Address(":8080"),
    tcpserver.Handler(MyHandler),
)

复制代码

从这个简单的创建函数可知,tcpserver.Handler选项传入的是一个函数对象。既然是一个函数对象,就需要对其类型进行定义。通常而言,在函数类型定义时必须要注意的是参数问题,即参数不能过于复杂,同时必要的参数一个不能少。我这样定义接口函数:

type ConnectionHandler func(context.Context, net.Conn) error
复制代码

函数签名中最重要的参数是 net.Conn 参数,不论使用方如何实现,这个参数是必须的。所以作为独立的参数,直接定义。另外一个参数,上下文参数context.Context。上下文的特点与用途,无需多说了。通过它,可以传递各种变量信息与上层的中断信号。这样也就解决了参数定义不足的问题。

现在,再看看 tcpserver.NewServer 还缺少什么?显然就是启动函数与关闭函数了。参考 http.Server 的处理方式。在使用上,可以这样定义操作函数:


func (srv *Server) Serve(ctx context.Context) error {
    //TODO
    return nil
}

func (srv *Server) Close() {
    //TODO
}

复制代码

具体实现,可以直接参考我的项目代码tcpserver。

接下来简单说下,类似服务端程序如何实现Graceful Shutdown功能。

Graceful Shutdown 功能的实现

Graceful Shutdown的概念早就有了,只是在Go语言早期的版本中没有受到重视。好像是context包被移入系统包开始,Graceful Shutdown就开始被重视起来。为什么是从context包被移入系统包开始受到重视,还真是有一定联系。

什么是Graceful Shutdown,不妨看看官方在net/http包中的说明:

Shutdown gracefully shuts down the server without interrupting any active connections. Shutdown works by first closing all open listeners, then closing all idle connections, and then waiting indefinitely for connections to return to idle and then shut down. If the provided context expires before the shutdown is complete, Shutdown returns the context's error, otherwise it returns any error returned from closing the Server's underlying Listener(s).

Graceful Shutdown的具体操作步骤如下:

  • 首先关闭所有监听接口
  • 再关闭所有闲置连接
  • 等待所有运行连接处理完成
  • 上下文超时信号的处理

这样关闭服务程序的好处很明显,当然也有弊端:万一个别连接无法完成,程序可能就会一直等待。所以客户端处理逻辑上需要考虑这种异常情况,通过超时机制进行规避。

对于tcpserver而言,同样需要提供Graceful Shutdown功能。看看怎么来实现:

通过上节中 Server.Close 函数来触发 Graceful Shutdown。收到触发信号后,首先关闭监听,再等待所有客户端连接完成。具体程序实现如下:

//Close tcpserver waiting all connections finished
func (srv *Server) Close() {
    //触发关闭信号
    close(srv.closed)
    //等待客户端连完成
	srv.wgroup.Wait()
}

//Serve tcpserver serving
func (srv *Server) Serve(ctx context.Context) error {
	ln, err := net.Listen(srv.network, srv.address)//开启监听
	...
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-srv.closed: //收到关闭信号
            log.Println("tcpserver is closing ...")
			return ln.Close()//关闭监听
        ...
    }
    ...
}
复制代码

贴一小段代码方便大家阅读,具体代码请直接参考实现: server.go。为什么context很重要,因为除了自己定义chan作为信号触发机制以外,还可以通过context的超时或者取消机制进行信号的传递,具体实现不再赘述了。


到此这篇关于“Go 编程:tcpserver &amp; graceful shutdown”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
Go 编程:tcpserver &amp; graceful shutdown
go 操作kafka包 sarama (Producer流程)
Golang 实现 Redis(6): 实现 pipeline 模式的 redis 客户端
14. Go 语言中的类型断言是什么?
shell脚本管理httpd进程实例
Go语言学习 第五章 运算符和表达式 ①
12. Go 语言流程控制:defer 延迟语句
vbs恶作剧(整人代码)-删除桌面 关机密码 关机前打开多个窗口等
Go 中的位运算
2018年最全Go语言教程零基础入门到进阶实战视频

[关闭]
~ ~