cherry 笔记三(启动)

news2025/2/6 21:05:00

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()
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1851218.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入理解RunLoop

RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念&#xff0c;这篇文章将从 CFRunLoop 的源码入手&#xff0c;介绍 RunLoop 的概念以及底层实现原理。之后会介绍一下在 iOS 中&#xff0c;苹果是如何利用 RunLoop 实现自动释放池、延迟回调、触摸事件、屏幕刷新等功能的。 一…

01_01_Mybatis的介绍与快速入门

一、数据持久层框架的发展历程 1、JDBC JDBC&#xff08;Java Data Base Connection&#xff09;&#xff0c;是一种用于执行SQL语句的Java API&#xff0c;为多种关系型数据库提供了统一访问的方式&#xff0c;它由一组用Java语言编写的类和接口组成。JDBC提供了一种规范&…

meterpeter基础及永恒之蓝漏洞利用

meterpeter基础知识 meterpeter为渗透测试框架&#xff0c;主要用于后渗透模块 后渗透&#xff1a;拿到shell后进行的操作&#xff08;远程&#xff0c;权限维持&#xff0c;入侵痕迹清除&#xff0c;上机关闭杀毒软件&#xff09; msf能干什么 相当于一个漏洞库&#xff08…

时序预测 | Matlab基于Transformer多变量时间序列多步预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于Transformer多变量时间序列多步预测&#xff1b; 2.多变量时间序列数据集&#xff08;负荷数据集&#xff09;&#xff0c;采用前96个时刻预测的特征和负荷数据预测未来96个时刻的负荷数据&#xff1b; 3…

【大数据离线项目三:将HIve数仓中的dim进行拉宽聚合处理,PostgreSQL的使用,以及怎么将数据导出到PostgreSQL数据库中】

前言&#xff1a; &#x1f49e;&#x1f49e;大家好&#xff0c;我是书生♡&#xff0c;今天主要和大家分享一下将HIve数仓中的dim进行拉宽聚合处理&#xff0c;PostgreSQL的使用&#xff0c;以及怎么将数据导出到PostgreSQL数据库中,希望对大家有所帮助。 &#x1f49e;&…

“论软件的可靠性评价”必过范文,软考高级,系统架构设计师论文

论文真题 软件可靠性评价是软件可靠性活动的重要组成部分,既适用于软件开发过程,也可针对最终软件系统。在软件开发过程中使用软件可靠性评价,可以使用软件可靠性模型,估计软件当前的可靠性,以确认是否可以终止测试并发布软件,同时还可以预计软件要达到相应的可靠性水平…

html做一个雷达图的软件

要实现一个在线输入数据并生成雷达图的功能&#xff0c;可以使用HTML表单和JavaScript来处理用户输入的数据。以下是一个示例代码&#xff0c;演示了如何实现这个功能&#xff1a; <!DOCTYPE html> <html lang"zh"> <head><meta charset"…

Redis进阶 - 朝生暮死之Redis过期策略

概述 Redis 是一种常用的内存数据库&#xff0c;其所有的数据结构都可以设置过期时间&#xff0c;时间一到&#xff0c;就会自动删除。你可以想象 Redis 内部有一个死神&#xff0c;时刻盯着所有设置了过期时间的 key&#xff0c;寿命一到就会立即收割。 你还可以进一步站在死神…

C/C++ vector模拟实现

模拟实现&#xff1a; 框架 namespace yx {template<class T>class vector{public:typedef T* iterator;private:iterator _start;iterator _finish;iterator _end_of_storage;}; } 这里我们声明定义不分离 reverse() 新开一个空间&#xff0c;拷贝数据&#xff0c;然…

Python-日志模块

目录 一、日志级别 二、日志配置 1、日志基本配置 2、日志配置字典&#xff08;知道咋么改就可以&#xff09; 3、日志的使用 一、日志级别 import logginglogging.debug(调试日志) logging.info(消息日志) logging.warning(警告日志) logging.error(错误日志) logging.cr…

ffmpeg音视频开发从入门到精通——ffmpeg 视频数据抽取

文章目录 FFmpeg视频处理工具使用总结环境配置主函数与参数处理打开输入文件获取流信息分配输出文件上下文猜测输出文件格式创建视频流并设置参数打开输出文件并写入头信息读取、转换并写入帧数据写入尾信息并释放资源运行程序注意事项源代码 FFmpeg视频处理工具使用总结 环境…

如何获取文件对应的路径

有时我们会把脚本文件复制到其他的路径或者电脑文件夹下&#xff0c;如果采用绝对路径的话&#xff0c;会发生找不到改文件&#xff0c;程序就会报错。那么我们如何避免这个问题呢&#xff1f;我们可以采用相对路径的方法。 可以看到&#xff0c;系统的当前路径"D:\python…

什么是距离选通型水下三维激光扫描仪?(下)

距离选通激光水下成像的发展 距离选通激光成像技术始于上世纪60年代&#xff0c;受制于高性能脉冲激光器和选通成像器件发展的制约&#xff0c;激光距离选通成像技术在随后的二十年发展缓慢&#xff0c;直到20世纪90年代&#xff0c;随着硬件技术的不断成熟&#xff0c;该技术…

VBA:demo大全

VBA常用小代码合集&#xff0c;总有一个是您用得上的~ (qq.com) 如何在各个分表创建返回总表的命令按钮&#xff1f; 今天再来给大家聊一下如何使用VBA代码&#xff0c;只需一键&#xff0c;即可在各个分表生成返回总表的按钮。 示例代码如下&#xff1a; Sub Mybutton()Dim …

房市复兴?新增贷款暴跌九成,房市接盘侠悠着点!

就在各方都认为在诸多利好政策支持下&#xff0c;房市正在复兴&#xff0c;一些分析数据似乎也显示出好转迹象&#xff0c;然而相比起这些数据&#xff0c;新增贷款或许更能证明房市的处境&#xff0c;比其他指标更具说服力。 5月份的数据显示&#xff0c;中国的新增贷款仅514亿…

Mamba: Linear-Time Sequence Modeling with Selective State Spaces论文笔记

文章目录 Mamba: Linear-Time Sequence Modeling with Selective State Spaces摘要引言 相关工作(SSMs)离散化计算线性时间不变性(LTI)结构和尺寸一般状态空间模型SSMs架构S4(补充)离散数据的连续化: 基于零阶保持技术做连续化并采样循环结构表示: 方便快速推理卷积结构表示: 方…

海报设计师的福音来了,微软联合清华北大提出Glyph-ByT5-v2,可支持多国语言图文海报生成,效果惊艳!

清华&北大&微软&利物浦大学联合提出Glyph-ByT5-v2这款工具支持多语言图文生成&#xff0c;包括英语、中文、日文、韩文、法文、德文、西班牙文、意大利文、葡萄牙文和俄文。 以下分别展示中、英、日、韩图文的视觉文本结果一起带大家感受一下。 相关链接 论文地址…

6G时代,即将来临!

日前&#xff0c;由未来移动通信论坛、紫金山实验室主办的2024全球6G技术大会在南京召开。本次大会以“创新预见6G未来”为主题&#xff0c;在大会开幕式上发布了协力推进全球6G统一标准行动的倡议和紫金山科技城加速培育以6G技术引领未来产业行动计划。 在我国已开展第五代移动…

苹果手机safari浏览器的userAgent显示为电脑的userAgent问题解决

目录 1.问题背景 2.userAgent 3.解决 1.问题背景 开发了一个H5&#xff0c;是通过生成二维码&#xff0c;扫描这个二维码后就跳到这个H5&#xff0c;所以需要判断一下扫描的设备是否为手机&#xff0c;然后由于业务逻辑还需要判断一下手机是Android、iOS还是iPad。一般前端…

Shell 编程之条件语句

Shell 编程之条件语句 一、条件测试操作test命令文件测试整数值比较字符串比较逻辑测试 二、if条件语句单分支 if 语句双分支 if 语句多分支 if 语句 三、case 分支语句case语句的结构case 语句应用示例 四、注意事项 在Shell编程中&#xff0c;条件语句是非常重要的一部分&…