cherry 笔记三(启动)

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python机器学习】NMF——将NMF应用于人脸图像

将NMF应用于之前用过的Wild数据集中的Labeled Faces。NMF的主要参数是我们想要提取的分量个数。通常来说&#xff0c;这个数字要小于输入特征的个数&#xff08;否则的话&#xff0c;将每个像素作为单独的分量就可以对数据进行解释&#xff09;。 首先&#xff0c;观察分类个数…

汽车销售系统

摘 要 在现代社会&#xff0c;电脑是企业运作和管理必不可少的工具。我们过去用手记下卖出的商品的年代已一去不复返了。在我国&#xff0c;汽车销售行业的竞争日趋激烈的情况下&#xff0c;如何提高企业的管理水平&#xff0c;提高企业的工作效率&#xff0c;提高企业的服务质…

ARC学习(3)基本编程模型认识(三)

笔者来介绍arc的编程模型的中断流程和异常流程 1、中断介绍 主要介绍一下中断进入的流程&#xff0c;包括需要配置的寄存器等信息。 中断号&#xff1a;16-255&#xff0c;总共240个中断。触发类型&#xff1a;脉冲或者电平触发中断优先级&#xff1a;16个&#xff0c;0最大&…

DAC测试实验——FPGA学习笔记7

一、DAC简介 DAC全称Digital to Analog Converter&#xff0c;即数模转换器。它用于将主控芯片产生的数字值(0和1)转换为模拟值(电压值)。 1、DAC参数指标 2、DAC类型 常用的DAC可大致分为权电阻网络DAC、T型电阻网络DAC、倒T型电阻网络DAC以及权电流型DAC。 3、AD9708/3PD9…

RSA 加密算法的基础数论、基本原理与 Python 实现 (包含扩展欧几里得算法)

Title: RSA 加密算法的基础数论、基本原理与 Python 实现&#xff08;包含扩展欧几里得算法&#xff09; 文章目录 前言I. 数学原理1. 整数环2. 单位元3. 欧拉定理 II. 算法原理1. 扩展欧几里得算法2. RSA 非对称加密算法 III. 算法实现1. 源代码2. 测试结果 总结参考文献 前言…

spdlog生产者消费者模式

spdlog生产者消费者模式 spdlog提供了异步模式&#xff0c;显示的创建async_logger, 配合环形队列实现的消息队列和线程池实现了异步模式。异步logger提交日志信息和自身指针&#xff0c; 任务线程从消息队列中取出消息后执行对应的sink和flush动作。 1. 环形队列 1.1 环形队…

智慧校园综合门户有哪些特点?

智慧校园的门户系统&#xff0c;作为整个智慧校园架构的门户窗口&#xff0c;扮演着至关重要的角色。它如同一座桥梁&#xff0c;将校园内的各种信息资源、应用服务以及管理功能紧密相连&#xff0c;为师生、家长及管理人员提供了一个集中访问的便捷通道。智慧校园门户的设计理…

RPC通信原理以及项目的技术选型

目录 1.引言 2、RPC通信原理 3.图示解析 4.再举个例子 1.引言 根据上一篇博客《单机&#xff0c;集群和分布式》的举的例子。 我们最终合理地通过对大型软件的合理划分&#xff0c;划分成不同模块&#xff0c;按需求&#xff08;硬件需求&#xff0c;高并发需求&#xff09…

Hadoop+Spark大数据技术(微课版)总复习

期末试卷组成 一、选择题(每小题2分&#xff0c;共20分) 二、判断题(每小题2分&#xff0c;共20分) 三、简答题(每小题5分&#xff0c;共20分) 四、程序分析题 (第1-5小题各6分&#xff0c;第6题10分&#xff0c;共40分) 图1 Hadoop开发环境 图2 HDFS 图3 MapReduce 图4 H…

IDEA插件推荐-CodeGeex

功能&#xff1a;这个插件可以实现快速翻译代码&#xff0c;json文件格式转换&#xff0c;代码语言类型转换。 安装方式&#xff1a;File->Settings->Plugins->MarketPlace->搜索“CodeGeex”即可 &#xff08;CodeGeex功能展示&#xff09; &#xff08;CodeGeex…

【科普】半导体制造过程的步骤、技术、流程

在这篇文章中&#xff0c;我们将学习基本的半导体制造过程。为了将晶圆转化为半导体芯片&#xff0c;它需要经历一系列复杂的制造过程&#xff0c;包括氧化、光刻、刻蚀、沉积、离子注入、金属布线、电气检测和封装等。 基本的半导体制造过程 1.晶圆&#xff08;Wafer&#xf…

算法篇-二叉树

二叉树的遍历 分为前序、中序和后续的遍历&#xff0c;思想就是利用递归。 前序遍历-中左右 代码&#xff1a; public void travelTree(TreeNode node, List<Integer> resulst) {if (node null){return;}// 中resulst.add(node.val);// 左travelTree(node.left, resul…

基于springboot websocket和okhttp实现消息中转

1、业务介绍 消息源服务的消息不能直接推给用户侧&#xff0c;用户与中间服务建立websocket连接&#xff0c;中间服务再与源服务建立websocket连接&#xff0c;源服务的消息推给中间服务&#xff0c;中间服务再将消息推送给用户。流程如下图&#xff1a; 此例中我们定义中间服…

Flink Sql Redis Connector

经常做开发的小伙伴肯定知道用flink连接redis的时候比较麻烦&#xff0c;更麻烦的是解析redis数据&#xff0c;如果rdis可以普通数据库那样用flink sql连接并且数据可以像表格那样展示出来就会非常方便。 历时多天&#xff0c;我终于把flink sql redis connector写出来了&…

关于Pytorch转换为MindSpore的一点建议

一、事先准备 必须要对Mindspore有一些了解&#xff0c;因为这个框架确实有些和其它流程不一样的地方&#xff0c;比如算子计算、训练过程中的自动微分&#xff0c;所以这两个课程要好好过一遍&#xff0c;官网介绍文档最好也要过一遍 1、零基础Mindspore&#xff1a;https://…

【MySQL统计函数count详解】

MySQL统计函数count详解 1. count()概述2. count(1)和count(*)和count(列名)的区别3. count(*)的实现方式 1. count()概述 count() 是一个聚合函数&#xff0c;返回指定匹配条件的行数。开发中常用来统计表中数据&#xff0c;全部数据&#xff0c;不为null数据&#xff0c;或…

手持弹幕LED滚动字幕屏夜店表白手灯接机微信抖音小程序开源版开发

手持弹幕LED滚动字幕屏夜店表白手灯接机微信抖音小程序开源版开发 专业版 插件版 手持弹幕小程序通常提供多种功能&#xff0c;以便用户在不同的场合如夜店、表白、接机等使用。以下是一些常见的功能列表&#xff1a; 文本输入&#xff1a; 输入要显示的文字内容&#xff0c;…

独角兽品牌獭崎酱酒:高性价比的酱香之选

在酱香型白酒领域中&#xff0c;獭崎酱酒以其独特的品牌定位和高性价比迅速崛起&#xff0c;成为市场上备受关注的独角兽品牌。作为贵州茅台镇的一款新秀酱香酒&#xff0c;獭崎酱酒不仅传承了百年酿造工艺&#xff0c;还以创新的商业模式和亲民的价格赢得了广大消费者的青睐。…

【C++算法】——高精度(加,减,乘,除)

前言 高精度算法就是为了去解决一些比较大的数&#xff0c;这些数大到long long都存不下。&#xff0c;这里的主要思想就是用字符串来存。 下面的内容有很多用到c的容器&#xff0c;不明白的可以先去学习stl。 一 高精度加法 首先第一步就是去模拟我们自己写的加法&#xff…

活用变量,让Postman的使用飞起来

在 Postman 中使用变量是一种非常强大的功能&#xff0c;它可以极大地增强 API 测试和开发的灵活性和效率。 Postman变量的类型 变量在 Postman 中可以在多个层次设置和使用&#xff0c;包括 全局变量环境变量集合变量局部变量&#xff08;如在脚本中暂时创建的变量&#xf…