fix: 将全局的连接管理器与nnet路由修改为局部,以达到多实例的目的。

main v0.5.0
NoahLan 2 years ago
parent 87b0ae6cec
commit 7de6cee180

@ -1,6 +1,9 @@
package config package config
import "git.noahlan.cn/noahlan/ntools-go/core/pool" import (
"git.noahlan.cn/noahlan/ntools-go/core/pool"
"time"
)
const ( const (
// DevMode means development mode. // DevMode means development mode.
@ -28,6 +31,9 @@ type (
Addr string `json:",default=0.0.0.0"` Addr string `json:",default=0.0.0.0"`
// Name 服务端名称默认为n-net // Name 服务端名称默认为n-net
Name string `json:",default=n-net"` Name string `json:",default=n-net"`
// TaskTimerPrecision // 全局任务的timer间隔
TaskTimerPrecision time.Duration `json:",default=1s"`
// Mode 运行模式
Mode string `json:",default=dev,options=[dev,test,prod]"` Mode string `json:",default=dev,options=[dev,test,prod]"`
} }
) )

@ -5,8 +5,6 @@ import (
"sync" "sync"
) )
var ConnManager *Manager
type Manager struct { type Manager struct {
sync.RWMutex sync.RWMutex
@ -16,8 +14,8 @@ type Manager struct {
conns map[int64]entity.NetworkEntity conns map[int64]entity.NetworkEntity
} }
func init() { func NewManager() *Manager {
ConnManager = &Manager{ return &Manager{
RWMutex: sync.RWMutex{}, RWMutex: sync.RWMutex{},
groups: make(map[string]*Group), groups: make(map[string]*Group),
conns: make(map[int64]entity.NetworkEntity), conns: make(map[int64]entity.NetworkEntity),
@ -25,7 +23,7 @@ func init() {
} }
// Store 保存连接,同时加入到指定分组,若给定分组名为空,则不进行分组操作 // Store 保存连接,同时加入到指定分组,若给定分组名为空,则不进行分组操作
func (m *Manager) Store(groupName string, s entity.NetworkEntity) { func (m *Manager) Store(groupName string, s entity.NetworkEntity) error {
m.Lock() m.Lock()
m.conns[s.Session().ID()] = s m.conns[s.Session().ID()] = s
m.Unlock() m.Unlock()
@ -34,7 +32,7 @@ func (m *Manager) Store(groupName string, s entity.NetworkEntity) {
if !ok { if !ok {
group = m.NewGroup(groupName) group = m.NewGroup(groupName)
} }
_ = group.Add(s) return group.Add(s)
} }
// NewGroup 新增分组,若分组已存在,则返回现有分组 // NewGroup 新增分组,若分组已存在,则返回现有分组

@ -141,7 +141,7 @@ func (r *connection) write() {
close(r.chWrite) close(r.chWrite)
_ = r.Close() _ = r.Close()
nlog.Debugf("connection write goroutine exit, ConnID=%d, SessionUID=%s", r.ID(), r.session.UID()) nlog.Debugf("[writeLoop] connection write goroutine exit, ConnID=%d, SessionUID=%s", r.ID(), r.session.UID())
}() }()
for { for {
@ -202,12 +202,12 @@ func (r *connection) read() {
n, err := r.conn.Read(buf) n, err := r.conn.Read(buf)
//nlog.Debugf("receive data %v", buf[:n]) //nlog.Debugf("receive data %v", buf[:n])
if err != nil { if err != nil {
nlog.Errorf("Read message error: %s, session will be closed immediately", err.Error()) nlog.Errorf("[readLoop] Read message error: %s, session will be closed immediately", err.Error())
return return
} }
if r.packer == nil { if r.packer == nil {
nlog.Errorf("unexpected error: packer is nil") nlog.Errorf("[readLoop] unexpected error: packer is nil")
return return
} }

@ -47,8 +47,10 @@ type (
packerFn packet.NewPackerFunc // 封包、拆包器 packerFn packet.NewPackerFunc // 封包、拆包器
serializer serialize.Serializer // 消息 序列化/反序列化 serializer serialize.Serializer // 消息 序列化/反序列化
retryInterval time.Duration // 消息重试间隔时长
wsOpt wsOptions // websocket wsOpt wsOptions // websocket
taskTimerPrecision time.Duration
connManager *conn2.Manager
} }
wsOptions struct { wsOptions struct {
@ -67,6 +69,8 @@ func newEngine(conf config.EngineConf) *engine {
pipeline: pipeline.New(), pipeline: pipeline.New(),
middlewares: make([]Middleware, 0), middlewares: make([]Middleware, 0),
routes: make([]Route, 0), routes: make([]Route, 0),
taskTimerPrecision: conf.TaskTimerPrecision,
connManager: conn2.NewManager(),
} }
pool.InitPool(conf.Pool) pool.InitPool(conf.Pool)
@ -113,7 +117,7 @@ func (ng *engine) dial(addr string, router Router) (entity.NetworkEntity, error)
if err := ng.bindRoutes(router); err != nil { if err := ng.bindRoutes(router); err != nil {
return nil, err return nil, err
} }
go scheduler.Schedule() go scheduler.Schedule(ng.taskTimerPrecision)
// connection // connection
conn, err := net.Dial("tcp", addr) conn, err := net.Dial("tcp", addr)
@ -123,6 +127,9 @@ func (ng *engine) dial(addr string, router Router) (entity.NetworkEntity, error)
c.serve() c.serve()
// hook // hook
Lifetime.Open(c) Lifetime.Open(c)
// connection manager
err = ng.connManager.Store(conn2.DefaultGroupName, c)
nlog.Must(err)
// 连接成功,客户端已启动 // 连接成功,客户端已启动
nlog.Infof("now connect to %s.", addr) nlog.Infof("now connect to %s.", addr)
@ -136,7 +143,7 @@ func (ng *engine) serve(router Router) error {
if err := ng.bindRoutes(router); err != nil { if err := ng.bindRoutes(router); err != nil {
return err return err
} }
go scheduler.Schedule() go scheduler.Schedule(ng.taskTimerPrecision)
defer func() { defer func() {
nlog.Info("NNet is stopping...") nlog.Info("NNet is stopping...")
@ -248,7 +255,8 @@ func (ng *engine) handleWS(conn *websocket.Conn) {
func (ng *engine) handle(conn net.Conn) { func (ng *engine) handle(conn net.Conn) {
c := newConnection(ng, conn) c := newConnection(ng, conn)
conn2.ConnManager.Store(conn2.DefaultGroupName, c) err := ng.connManager.Store(conn2.DefaultGroupName, c)
nlog.Must(err)
c.serve() c.serve()
// hook // hook

@ -2,8 +2,8 @@ package core
import ( import (
"git.noahlan.cn/noahlan/nnet/config" "git.noahlan.cn/noahlan/nnet/config"
"git.noahlan.cn/noahlan/nnet/conn"
"git.noahlan.cn/noahlan/nnet/entity" "git.noahlan.cn/noahlan/nnet/entity"
"git.noahlan.cn/noahlan/nnet/internal/env"
"git.noahlan.cn/noahlan/nnet/packet" "git.noahlan.cn/noahlan/nnet/packet"
"git.noahlan.cn/noahlan/nnet/pipeline" "git.noahlan.cn/noahlan/nnet/pipeline"
"git.noahlan.cn/noahlan/nnet/serialize" "git.noahlan.cn/noahlan/nnet/serialize"
@ -110,6 +110,11 @@ func (s *NNet) Pipeline() pipeline.Pipeline {
return s.ngin.pipeline return s.ngin.pipeline
} }
// ConnManager returns connection manager
func (s *NNet) ConnManager() *conn.Manager {
return s.ngin.connManager
}
// ToMiddleware converts the given handler to a Middleware. // ToMiddleware converts the given handler to a Middleware.
func ToMiddleware(handler func(next Handler) Handler) Middleware { func ToMiddleware(handler func(next Handler) Handler) Middleware {
return func(next HandlerFunc) HandlerFunc { return func(next HandlerFunc) HandlerFunc {
@ -174,15 +179,15 @@ func WithSerializer(s serialize.Serializer) RunOption {
} }
} }
// WithTimerPrecision 设置Timer精度 // WithTimerPrecision 设置Timer精度,需在 Start 或 Dial 之前执行
// 注精度需大于1ms, 并且不能在运行时更改 // 注精度需大于1ms, 并且不能在运行时更改
// 默认精度是 time.Second // 默认精度是 time.Second
func WithTimerPrecision(precision time.Duration) RunOption { func WithTimerPrecision(precision time.Duration) RunOption {
if precision < time.Millisecond { if precision < time.Millisecond {
panic("time precision can not less than a Millisecond") panic("time precision can not less than a Millisecond")
} }
return func(_ *NNet) { return func(s *NNet) {
env.TimerPrecision = precision s.ngin.taskTimerPrecision = precision
} }
} }

@ -1,8 +0,0 @@
package env
import "time"
var (
// TimerPrecision indicates the precision of timer, default is time.Second
TimerPrecision = time.Second
)

@ -6,13 +6,13 @@ import (
"fmt" "fmt"
"git.noahlan.cn/noahlan/nnet/core" "git.noahlan.cn/noahlan/nnet/core"
"git.noahlan.cn/noahlan/nnet/entity" "git.noahlan.cn/noahlan/nnet/entity"
"git.noahlan.cn/noahlan/nnet/packet"
"git.noahlan.cn/noahlan/ntools-go/core/nlog" "git.noahlan.cn/noahlan/ntools-go/core/nlog"
) )
type OnReadyFunc func() type OnReadyFunc func()
func WithNNetClientPipeline(onReady OnReadyFunc) core.RunOption { func WithNNetClientPipeline(onReady OnReadyFunc, packer packet.Packer) core.RunOption {
packer := NewNNetPacker()
return func(server *core.NNet) { return func(server *core.NNet) {
server.Pipeline().Inbound().PushFront(func(entity entity.NetworkEntity, v interface{}) error { server.Pipeline().Inbound().PushFront(func(entity entity.NetworkEntity, v interface{}) error {
pkg, ok := v.(*NNetPacket) pkg, ok := v.(*NNetPacket)

@ -36,20 +36,13 @@ type (
} }
) )
var routeMap *RouteMap
func init() {
routeMap = &RouteMap{
Routes: make(map[string]uint16),
Codes: make(map[uint16]string),
}
}
func WithNNetClientProtocol(onReady OnReadyFunc) []core.RunOption { func WithNNetClientProtocol(onReady OnReadyFunc) []core.RunOption {
router := NewNNetRouter().(*nNetRouter)
packer := NewNNetPacker(router.routeMap)
opts := []core.RunOption{ opts := []core.RunOption{
WithNNetClientPipeline(onReady), WithNNetClientPipeline(onReady, packer),
core.WithRouter(NewNNetRouter()), core.WithRouter(router),
core.WithPacker(func() packet.Packer { return NewNNetPacker() }), core.WithPacker(func() packet.Packer { return NewNNetPacker(router.routeMap) }),
} }
return opts return opts
@ -61,19 +54,22 @@ func WithNNetProtocol(config NNetConfig) []core.RunOption {
return nil return nil
} }
} }
router := NewNNetRouter().(*nNetRouter)
handshakeAckData := &HandshakeResp{ handshakeAckData := &HandshakeResp{
Heartbeat: int64(config.HeartbeatInterval.Seconds()), Heartbeat: int64(config.HeartbeatInterval.Seconds()),
RouteMap: routeMap, RouteMap: router.routeMap,
} }
router.routeMap.Routes["hahah"] = 222
packer := NewNNetPacker(router.routeMap)
opts := []core.RunOption{ opts := []core.RunOption{
WithNNetPipeline(handshakeAckData, config.HandshakeValidator), WithNNetPipeline(handshakeAckData, config.HandshakeValidator, packer),
core.WithRouter(NewNNetRouter()), core.WithRouter(router),
core.WithPacker(func() packet.Packer { return NewNNetPacker() }), core.WithPacker(func() packet.Packer { return NewNNetPacker(router.routeMap) }),
} }
if config.HeartbeatInterval.Seconds() > 0 { if config.HeartbeatInterval.Seconds() > 0 {
packer := NewNNetPacker()
hbd, err := packer.Pack(Heartbeat, nil) hbd, err := packer.Pack(Heartbeat, nil)
nlog.Must(err) nlog.Must(err)

@ -12,6 +12,7 @@ type NNetPacker struct {
size int // 最近一次 length size int // 最近一次 length
typ byte // 最近一次 packet type typ byte // 最近一次 packet type
flag byte // 最近一次 flag flag byte // 最近一次 flag
routeMap *RouteMap
} }
// packer constants. // packer constants.
@ -35,9 +36,10 @@ var (
ErrWrongPacketType = errors.New("wrong packet type") ErrWrongPacketType = errors.New("wrong packet type")
) )
func NewNNetPacker() *NNetPacker { func NewNNetPacker(routeMap *RouteMap) *NNetPacker {
p := &NNetPacker{ p := &NNetPacker{
buf: bytes.NewBuffer(nil), buf: bytes.NewBuffer(nil),
routeMap: routeMap,
} }
p.resetFlags() p.resetFlags()
return p return p
@ -82,7 +84,7 @@ func (d *NNetPacker) Pack(header interface{}, data []byte) ([]byte, error) {
// flag // flag
flag := byte(h.MsgType << 1) // 编译器提示,此处 byte 转换不能删 flag := byte(h.MsgType << 1) // 编译器提示,此处 byte 转换不能删
code, compressed := routeMap.Routes[h.Route] code, compressed := d.routeMap.Routes[h.Route]
if compressed { if compressed {
flag |= msgRouteCompressMask flag |= msgRouteCompressMask
} }
@ -180,7 +182,7 @@ func (d *NNetPacker) Unpack(data []byte) ([]packet.IPacket, error) {
if d.flag&msgRouteCompressMask == 1 { if d.flag&msgRouteCompressMask == 1 {
p.compressed = true p.compressed = true
code := binary.BigEndian.Uint16(d.buf.Next(2)) code := binary.BigEndian.Uint16(d.buf.Next(2))
route, ok := routeMap.Codes[code] route, ok := d.routeMap.Codes[code]
if !ok { if !ok {
return nil, ErrRouteInfoNotFound return nil, ErrRouteInfoNotFound
} }

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"git.noahlan.cn/noahlan/nnet/core" "git.noahlan.cn/noahlan/nnet/core"
"git.noahlan.cn/noahlan/nnet/entity" "git.noahlan.cn/noahlan/nnet/entity"
"git.noahlan.cn/noahlan/nnet/packet"
"git.noahlan.cn/noahlan/ntools-go/core/nlog" "git.noahlan.cn/noahlan/ntools-go/core/nlog"
) )
@ -17,8 +18,8 @@ type (
func WithNNetPipeline( func WithNNetPipeline(
handshakeResp *HandshakeResp, handshakeResp *HandshakeResp,
validator HandshakeValidatorFunc, validator HandshakeValidatorFunc,
packer packet.Packer,
) core.RunOption { ) core.RunOption {
packer := NewNNetPacker()
return func(server *core.NNet) { return func(server *core.NNet) {
server.Pipeline().Inbound().PushFront(func(entity entity.NetworkEntity, v interface{}) error { server.Pipeline().Inbound().PushFront(func(entity entity.NetworkEntity, v interface{}) error {
pkg, ok := v.(*NNetPacket) pkg, ok := v.(*NNetPacket)

@ -27,13 +27,22 @@ type (
} }
nNetRouter struct { nNetRouter struct {
routeMap *RouteMap
handlers map[string]core.Handler handlers map[string]core.Handler
notFound core.Handler notFound core.Handler
} }
) )
func NewRouteMap() *RouteMap {
return &RouteMap{
Routes: make(map[string]uint16),
Codes: make(map[uint16]string),
}
}
func NewNNetRouter() core.Router { func NewNNetRouter() core.Router {
return &nNetRouter{ return &nNetRouter{
routeMap: NewRouteMap(),
handlers: make(map[string]core.Handler), handlers: make(map[string]core.Handler),
} }
} }
@ -63,8 +72,8 @@ func (r *nNetRouter) Register(matches interface{}, handler core.Handler) error {
} }
r.handlers[match.Route] = handler r.handlers[match.Route] = handler
// routeMap // routeMap
routeMap.Routes[match.Route] = match.Code r.routeMap.Routes[match.Route] = match.Code
routeMap.Codes[match.Code] = match.Route r.routeMap.Codes[match.Code] = match.Route
return nil return nil
} }

@ -1,7 +1,6 @@
package scheduler package scheduler
import ( import (
"git.noahlan.cn/noahlan/nnet/internal/env"
"git.noahlan.cn/noahlan/ntools-go/core/nlog" "git.noahlan.cn/noahlan/ntools-go/core/nlog"
"runtime/debug" "runtime/debug"
"sync/atomic" "sync/atomic"
@ -39,12 +38,16 @@ func try(f func()) {
f() f()
} }
func Schedule() { func Schedule(timerPrecision time.Duration) {
if atomic.AddInt32(&started, 1) != 1 { if atomic.AddInt32(&started, 1) != 1 {
return return
} }
ticker := time.NewTicker(env.TimerPrecision) if timerPrecision.Seconds() == 0 {
timerPrecision = time.Second
}
ticker := time.NewTicker(timerPrecision)
defer func() { defer func() {
ticker.Stop() ticker.Stop()
close(chExit) close(chExit)

@ -98,7 +98,7 @@ func runClient(addr string) (client *core.Client, et entity.NetworkEntity) {
}) })
nlog.Must(err) nlog.Must(err)
packer := protocol.NewNNetPacker() packer := protocol.NewNNetPacker(protocol.NewRouteMap())
hsd, err := packer.Pack(protocol.Header{ hsd, err := packer.Pack(protocol.Header{
PacketType: protocol.Handshake, PacketType: protocol.Handshake,
MessageHeader: protocol.MessageHeader{ MessageHeader: protocol.MessageHeader{

Loading…
Cancel
Save