You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nnet/engine.go

137 lines
3.4 KiB
Go

package nnet
import (
"git.noahlan.cn/noahlan/nnet/config"
"git.noahlan.cn/noahlan/nnet/connection"
"git.noahlan.cn/noahlan/nnet/lifetime"
"git.noahlan.cn/noahlan/nnet/packet"
rt "git.noahlan.cn/noahlan/nnet/router"
"git.noahlan.cn/noahlan/nnet/scheduler"
"git.noahlan.cn/noahlan/nnet/serialize"
"git.noahlan.cn/noahlan/nnet/session"
"git.noahlan.cn/noahlan/ntool/nlog"
"github.com/panjf2000/ants/v2"
"math"
"net"
)
// Engine 引擎
type Engine struct {
config.EngineConf // 引擎配置
middlewares []rt.Middleware // 中间件
routes []rt.Route // 路由
router rt.Router // 消息处理器
dieChan chan struct{} // 应用程序退出信号
pipeline connection.Pipeline // 消息管道
packerBuilder packet.PackerBuilder // 封包、拆包器
serializer serialize.Serializer // 消息 序列化/反序列化
goPool *ants.Pool // goroutine池
connManager *connection.Manager // 连接管理器
lifetime *lifetime.Mgr // 生命周期
sessIdMgr *session.IDMgr // SessionId管理器
}
func NewEngine(conf config.EngineConf, opts ...RunOption) *Engine {
ngin := &Engine{
EngineConf: conf,
middlewares: make([]rt.Middleware, 0),
routes: make([]rt.Route, 0),
router: rt.NewDefaultRouter(),
packerBuilder: nil,
serializer: nil,
dieChan: make(chan struct{}),
pipeline: connection.NewPipeline(),
connManager: connection.NewManager(),
lifetime: lifetime.NewLifetime(),
sessIdMgr: session.NewSessionIDMgr(),
goPool: nil,
}
for _, opt := range opts {
opt(ngin)
}
if ngin.goPool == nil {
ngin.goPool, _ = ants.NewPool(math.MaxInt32)
}
return ngin
}
func (ngin *Engine) Use(middleware ...rt.Middleware) {
ngin.middlewares = append(ngin.middlewares, middleware...)
}
func (ngin *Engine) AddRoutes(rs ...rt.Route) {
ngin.routes = append(ngin.routes, rs...)
err := ngin.bindRoutes()
nlog.Must(err)
}
func (ngin *Engine) bindRoutes() error {
for _, fr := range ngin.routes {
if err := ngin.bindRoute(fr); err != nil {
return err
}
}
return nil
}
func (ngin *Engine) bindRoute(route rt.Route) error {
// TODO default middleware
chain := rt.NewChain()
// build chain
for _, middleware := range ngin.middlewares {
chain.Append(rt.ConvertMiddleware(middleware))
}
return ngin.router.Register(route.Matches, route.Handler)
}
func (ngin *Engine) setup() error {
if err := ngin.bindRoutes(); err != nil {
return err
}
if err := ngin.goPool.Submit(func() {
scheduler.Schedule(ngin.TaskTimerPrecision)
}); err != nil {
return err
}
return nil
}
func (ngin *Engine) Stop() {
nlog.Infof("%s is stopping...", ngin.LogPrefix())
close(ngin.dieChan)
scheduler.Close()
}
func (ngin *Engine) handle(conn net.Conn) *connection.Connection {
nc := connection.NewConnection(
ngin.sessIdMgr.SessionID(),
conn,
ngin.goPool,
connection.Config{LogDebug: ngin.ShallLogDebug(), LogPrefix: ngin.LogPrefix()},
ngin.packerBuilder, ngin.serializer, ngin.pipeline,
ngin.router.Handle,
)
nc.Serve()
err := ngin.connManager.Store(connection.DefaultGroupName, nc)
nlog.Must(err)
// dieChan
go func() {
// lifetime
ngin.lifetime.Open(nc)
select {
case <-nc.DieChan():
scheduler.PushTask(func() { ngin.lifetime.Close(nc) })
_ = ngin.connManager.Remove(nc)
}
}()
return nc
}