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网络数据包解析器,它同时也是一个actor
agentActor := pomelo.NewActor("user")
// 添加websocket连接器, 根据业务需要可添加多类型的connector
agentActor.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 actor
req := &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.Application
if app.NodeMode() == Cluster {
cluster := ccluster.New()
app.SetCluster(cluster)
app.Register(cluster)
discovery := cdiscovery.New()
app.SetDiscovery(discovery)
app.Register(discovery)
}
// Register custom components
app.Register(p.components...)
// startup
app.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 system
a.Register(a.actorSystem)
// add connector component
if 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 list
for _, 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 parser
if 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 running
atomic.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 status
atomic.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 order
for 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() string
App() IApplication
IComponentLifecycle
}
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 actor
if _, 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 actor
req := &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()
}