cherry启动很简单 app创建完 直接startup()就好了
func main() {app := cherry.Configure("./examples/config/profile-chat.json","chat-master",false,cherry.Cluster,)app.SetSerializer(cserializer.NewJSON())app.Startup()
}
Configure()--->NewApp()-->NewAppNode()
app := &Application{INode: node,serializer: cserializer.NewProtobuf(),isFrontend: isFrontend,nodeMode: mode,startTime: ctime.Now(),running: 0,dieChan: make(chan bool),actorSystem: cactor.New(),}
默认的 serializer 是Protobuf()的
在Startup()之前除了可以SetSerializer() 还可以干其他很多事情 比如以下。。。
httpServer := cherryGin.NewHttp("web_1", app.Address())
httpServer.Use(cherryGin.Cors(), cherryGin.MaxConnect(2))
httpServer.Register(new(Test1Controller))
app.Register(httpServer)func (a *Application) Register(components ...cfacade.IComponent) {if a.Running() {return}for _, c := range components {if c == nil || c.Name() == "" {clog.Errorf("[component = %T] name is nil", c)return}result := a.Find(c.Name())if result != nil {clog.Errorf("[component name = %s] is duplicate.", c.Name())return}a.components = append(a.components, c)}
}
注册httpServer 这个component,也是暂存在AppBuilder的components中
// 创建pomelo网络数据包解析器,它同时也是一个actoragentActor := pomelo.NewActor("user")// 添加websocket连接器, 根据业务需要可添加多类型的connectoragentActor.AddConnector(cconnector.NewWS(":34590"))// 创建Agent时,关联onClose函数agentActor.SetOnNewAgent(func(newAgent *pomelo.Agent) {newAgent.AddOnClose(func(agent *pomelo.Agent) {session := agent.Session()if !session.IsBind() {return}// 发送玩家断开连接的消息给room actorreq := &protocol.Int64{Value: session.Uid,}agentActor.Call(".room", "exit", req)clog.Debugf("[sid = %s,uid = %d] session disconnected.",session.Sid,session.Uid,)})})// 设置数据路由函数agentActor.SetOnDataRoute(onDataRoute)// 设置网络包解析器app.SetNetParser(agentActor)
设置NetParser
app.AddActors(&ActorLog{})func (p *AppBuilder) AddActors(actors ...cfacade.IActorHandler) {p.actorSystem.Add(actors...)
}
添加一些actors,这些actor都放在actorSystem中维护
设置一些东西之后,最后一步就是Startup()了
func (p *AppBuilder) Startup() {app := p.Applicationif app.NodeMode() == Cluster {cluster := ccluster.New()app.SetCluster(cluster)app.Register(cluster)discovery := cdiscovery.New()app.SetDiscovery(discovery)app.Register(discovery)}// Register custom componentsapp.Register(p.components...)// startupapp.Startup()
}
如果是Cluster模式,那么会自动注册 cluster、discovery这2个component
然后把 之前Register 暂存在AppBuilder的components里边的component(像httpServer)也注册到Application的components
由此可见,暂存到AppBuilder.components里边的component 最终都会汇总到Application.components 里边去。。。
最后调用app.Startup()
func (a *Application) Startup() {defer func() {if r := recover(); r != nil {clog.Error(r)}}()if a.Running() {clog.Error("Application has running.")return}defer func() {clog.Flush()}()// register actor systema.Register(a.actorSystem)// add connector componentif a.netParser != nil {for _, connector := range a.netParser.Connectors() {a.Register(connector)}}clog.Info("-------------------------------------------------")clog.Infof("[nodeId = %s] application is starting...", a.NodeId())clog.Infof("[nodeType = %s]", a.NodeType())clog.Infof("[pid = %d]", os.Getpid())clog.Infof("[startTime = %s]", a.StartTime())clog.Infof("[profilePath = %s]", cprofile.Path())clog.Infof("[profileName = %s]", cprofile.Name())clog.Infof("[env = %s]", cprofile.Env())clog.Infof("[debug = %v]", cprofile.Debug())clog.Infof("[printLevel = %s]", cprofile.PrintLevel())clog.Infof("[logLevel = %s]", clog.DefaultLogger.LogLevel)clog.Infof("[stackLevel = %s]", clog.DefaultLogger.StackLevel)clog.Infof("[writeFile = %v]", clog.DefaultLogger.EnableWriteFile)clog.Infof("[serializer = %s]", a.serializer.Name())clog.Info("-------------------------------------------------")// component listfor _, c := range a.components {c.Set(a)clog.Infof("[component = %s] is added.", c.Name())}clog.Info("-------------------------------------------------")// execute Init()for _, c := range a.components {clog.Infof("[component = %s] -> OnInit().", c.Name())c.Init()}clog.Info("-------------------------------------------------")// execute OnAfterInit()for _, c := range a.components {clog.Infof("[component = %s] -> OnAfterInit().", c.Name())c.OnAfterInit()}// load net packet parserif a.isFrontend {if a.netParser == nil {clog.Panic("net packet parser is nil.")}a.netParser.Load(a)}clog.Info("-------------------------------------------------")spendTime := a.startTime.DiffInMillisecond(ctime.Now())clog.Infof("[spend time = %dms] application is running.", spendTime)clog.Info("-------------------------------------------------")// set application is runningatomic.AddInt32(&a.running, 1)sg := make(chan os.Signal, 1)signal.Notify(sg, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)select {case <-a.dieChan:clog.Info("invoke shutdown().")case s := <-sg:clog.Infof("receive shutdown signal = %v.", s)}// stop statusatomic.StoreInt32(&a.running, 0)clog.Info("------- application will shutdown -------")if a.onShutdownFn != nil {for _, f := range a.onShutdownFn {cutils.Try(func() {f()}, func(errString string) {clog.Warnf("[onShutdownFn] error = %s", errString)})}}//all components in reverse orderfor i := len(a.components) - 1; i >= 0; i-- {cutils.Try(func() {clog.Infof("[component = %s] -> OnBeforeStop().", a.components[i].Name())a.components[i].OnBeforeStop()}, func(errString string) {clog.Warnf("[component = %s] -> OnBeforeStop(). error = %s", a.components[i].Name(), errString)})}for i := len(a.components) - 1; i >= 0; i-- {cutils.Try(func() {clog.Infof("[component = %s] -> OnStop().", a.components[i].Name())a.components[i].OnStop()}, func(errString string) {clog.Warnf("[component = %s] -> OnStop(). error = %s", a.components[i].Name(), errString)})}clog.Info("------- application has been shutdown... -------")
}
维护着所有actor的 actorSystem 也当做一个component Register到Application.components
netParser管理的所有Connector 也都被当做compenent Register到Application.components
func (a *Application) Register(components ...cfacade.IComponent) {if a.Running() {return}for _, c := range components {if c == nil || c.Name() == "" {clog.Errorf("[component = %T] name is nil", c)return}result := a.Find(c.Name())if result != nil {clog.Errorf("[component name = %s] is duplicate.", c.Name())return}a.components = append(a.components, c)}
}
所有Register的component 都是暂存在 Application.components
逐步遍历components 执行 c.Set(a) c.Init() c.OnAfterInit()
来看看component 接口和 基类 都长什么样?
type (IComponent interface {Name() stringApp() IApplicationIComponentLifecycle}IComponentLifecycle interface {Set(app IApplication)Init()OnAfterInit()OnBeforeStop()OnStop()}
)// Component base component
type Component struct {app IApplication
}func (*Component) Name() string {return ""
}func (p *Component) App() IApplication {return p.app
}func (p *Component) Set(app IApplication) {p.app = app
}func (*Component) Init() {
}func (*Component) OnAfterInit() {
}func (*Component) OnBeforeStop() {
}func (*Component) OnStop() {
}
紧接着 如果当前application isFrontend是true的话,就对netParser 执行 Load()操作
func (p *actor) Load(app cfacade.IApplication) {if len(p.connectors) < 1 {panic("connectors is nil. Please call the AddConnector(...) method add IConnector.")}cmd.init(app)// Create agent actorif _, err := app.ActorSystem().CreateActor(p.agentActorID, p); err != nil {clog.Panicf("Create agent actor fail. err = %+v", err)}for _, connector := range p.connectors {connector.OnConnect(p.defaultOnConnectFunc)go connector.Start() // start connector!}
}
init()这个里边会去初始化 一些常用的数据 比如说心跳、DataDic、序列化的名称、握手数据、心跳数据、握手回调、握手确认回调、心跳回调、收到数据包的回调。。。。这部分逻辑 跟pitaya是一样的
紧接着就是启动连接器,以便客户端来连接,类似pitaya的Acceptor 和Twisted的ClientFactory
OnConnect 是设置 当有客户端来连接时的回调函数。。。框架统一由defaultOnConnectFunc
func (p *actor) defaultOnConnectFunc(conn net.Conn) {session := &cproto.Session{Sid: nuid.Next(),AgentPath: p.Path().String(),Data: map[string]string{},}agent := NewAgent(p.App(), conn, session)if p.onNewAgentFunc != nil {p.onNewAgentFunc(&agent)}BindSID(&agent)agent.Run()
}
创建session,然后创建agent,onNewAgentFunc 是应用层 对于有连接过来了 设置的回调,比如聊天示例中,要设置这个连接关闭时 触发的api。。。
agentActor.SetOnNewAgent(func(newAgent *pomelo.Agent) {newAgent.AddOnClose(func(agent *pomelo.Agent) {session := agent.Session()if !session.IsBind() {return}// 发送玩家断开连接的消息给room actorreq := &protocol.Int64{Value: session.Uid,}agentActor.Call(".room", "exit", req)clog.Debugf("[sid = %s,uid = %d] session disconnected.",session.Sid,session.Uid,)})})
BindSID 主要是建立关联。。。
sidAgentMap = make(map[cfacade.SID]*Agent)
func BindSID(agent *Agent) {lock.Lock()defer lock.Unlock()sidAgentMap[agent.SID()] = agent
}
agent.Run()就是 启动2个协程 分别读写
func (a *Agent) Run() {go a.writeChan()go a.readChan()
}