简介
dubbo-getty 是一个用 go 编写的异步网络 I/O 库,提供了三种通信模式,tcp、udp 以及 websocket,并为其设计了绝对对立的流程和 API,目前 dubbogo 等出名我的项目都应用 getty 作为底层的网络通信库,其特点是封装性好,应用简略。下图是 getty 外围构造的类图,根本囊括了整个 getty 框架的设计。
灰色局部为 go 内置库
上面以 TCP 为例介绍下 getty 如何应用以及该类图里各个接口或对象的作用。
TCP Server 端
server
其中 server/client 是提供给用户应用的封装好的构造,client 的逻辑与 server 很多水平上统一,因而这里只讲 server。
在 getty 中,server 服务的启动流程只须要两行代码
server := getty.NewTCPServer(options...)
server.RunEventLoop(NewHelloServerSession)
第一行非常明显是一个创立 server 的过程,options 是一个个 func(*ServerOptions) 函数,用于给 server 增加一些额定性能设置,如启用 ssl,应用工作队列提交工作的模式执行工作等。
第二行的 server.RunEventLoop(NewHelloServerSession) 则是启动 server,同时也是整个 server 服务的入口,它的作用是监听某个端口(具体监听哪个端口能够通过 options 指定),并解决 client 发来的数据。RunEventLoop 办法须要提供一个参数 NewSessionCallback,该参数的类型定义如下:
type NewSessionCallback func(Session) error
这是一个回调函数,将在胜利建设和 client 的连贯后被调用,个别提供给用户用于设置网络参数,如设置连贯的 keepAlive 参数、缓冲区大小、最大音讯长度、read/write 超时工夫等,但最重要的是, 用户须要通过该函数,为 session 设置好要用的 Reader、Writer 以及 EventListener。
Session
Session 能够说是 getty 中最外围的接口了,每个 Session 代表着一次会话连贯,向下,Session 对 go 内置的网络库做了欠缺的封装,包含对 net.Conn 的数据流读写、超时机制等,向上,Session 提供了业务可切入的接口,用户只需实现 EventListener 就能够将 getty 接入到本人的业务逻辑中。目前 Session 接口的实现只有 session 构造体,Session 作为接口仅仅是提供了对外可见性以及遵循面向编程接口的机制,之后咱们谈到 Session,其实都是在讲 session 构造体。
Connection
Connection 则是刚提到的,Session 向下对于 go 内置网络库的形象封装,依据不同的通信模式,Connection 别离有三种实现:
- gettyTCPConn:底层是 *net.TCPConn
- gettyUDPConn:底层是 *net.UDPConn
- gettyWSConn:底层应用第三方库实现
EventListener
EventListener 则对应 Session 向上凋谢给用户的接口,用户须要在该接口的实现中封装好业务逻辑,由 session 在运行的过程中调用。EventListener 接口的定义如下
// EventListener is used to process pkg that received from remote session
typeEventListenerinterface{
// invoked when session opened
// If the return error is not nil, @Session will be closed.
OnOpen(Session) error
// invoked when session closed.
OnClose(Session)
// invoked when got error.
OnError(Session, error)
// invoked periodically, its period can be set by (Session)SetCronPeriod
OnCron(Session)
// invoked when getty received a package. Pls attention that do not handle long time
// logic processing in this func. You'd better set the package's maximum length.
// If the message's length is greater than it, u should should return err in
// Reader{Read} and getty will close this connection soon.
//
// If ur logic processing in this func will take a long time, u should start a goroutine
// pool(like working thread pool in cpp) to handle the processing asynchronously. Or u
// can do the logic processing in other asynchronous way.
// !!!In short, urOnMessage callback func should return asap.
//
// If this is a udp event listener, the second parameter type isUDPContext.
OnMessage(Session, interface{})
}
这五个接口中最外围的是 OnMessage 办法,该办法有一个 interface{} 类型的参数,用于接管 client 端发来的数据。可能大家有个纳闷,网络连接最底层传输的是二进制,到咱们应用的协定层个别以字节流的形式对连贯进行读写,那这里为什么要应用 interface{} 呢?这是 getty 为了让咱们可能专一编写业务逻辑,将序列化和反序列化的逻辑抽取到了 EventListener 里面,也就是后面提到的 Reader/Writer 接口,session 在运行过程中,会先从 net.Conn 中读取字节流,并通过 Reader 接口进行反序列化,再将反序列化的后果传递给 OnMessage 办法。
Writer/Reader
Writer 和 Reader 别离是序列化 / 反序列化接口,两者的定义非常简单,都只有一个办法。
type Reader interface{Read(Session, []byte) (interface{}, int, error)
}
type Writer interface{Write(Session,interface{}) ([]byte, error)
}
具体的序列化 / 反序列化逻辑则交给了用户手动实现,其中 Reader 接口之前提过,当 server 端读取了 client 发送的字节流后,会调用它的 Read 办法进行反序列化。而 Writer 接口则是在 client 端被应用,当 client 发送数据时,须要调用 Write 办法将发送的数据序列化为字节流,再写入到 net.Conn 中。
至此,getty 中 server 的解决流程大体如下图