开源服务器框架NoahFrame分享:第四章 数据驱动模型

发表于2017-10-23
评论3 7k浏览
NF(https://github.com/ketoo/NoahGameFrame)全称为 NoahFrame/NoahGameFrame。
交流QQ群:341159815


NF最早为客户端设计,后来随着时代的变化,而为自己又转为服务器开发,故在吸收了众多引擎的优点后(包含Ogre的插件模式&模块化管理机制,Bigworld的数据管理&配置机制,类似MYGUI的接口层次设计),经过多年演化和实践,变成了一套游戏开发J解决方案。方案中包含开源的服务器架构,网络库(站在libevent的肩膀上),和unity3d的demo源码。现在NF已经在多个公司的多个项目中使用,其中包含知名产品 《全民无双》。


关键词


NoahGameFrame/NoahFrame/NF
集群/负载均衡/分布式
网关服务器 GateServer 心跳 多线程/线程池 开源网络框架/模型
一致性hash算法/ConsistentHash
游戏开发中的设计模式/数据结构
Socket Nagle/粘包/开源游戏服务器/ Game Server



数据驱动,在NF的观念里面,意思就是数据的变更来驱动业务逻辑的运算,而非通常编程中的while循环来不断的检测内容而驱动业务逻辑的运算。真正的意思就是设计模式中的观察者模式,但是怎么用好观察者模式是一门很大的学问,君不见多少公司多少高手,就折在这里了。

数据驱动有2个必要条件,数据和驱动模式,驱动模式已经说了是观察者模式,那么怎么结合呢?我们回忆上一章 属性管理思想介绍,看如何结合俩者。
首先有一点很关键,数据驱动,何时驱动这个时机问题,在我的想象中,就是当有效数据变化的时候才会驱动运行新的逻辑。比如HP=10 ---> HP =20此时会驱动逻辑运行,而我们的HP是一个这样的类(Property)

  1. class NFCProperty
  2. {
  3.     public bool SetInt(const NFINT64 value);
  4.     public bool SetFloat(const double value);
  5.     public bool SetString(const std::string& value);
  6.     public bool SetObject(const NFGUID& value);
  7.     public bool SetVector2(const NFVector2& value);
  8.     public bool SetVector3(const NFVector3& value);

  9.     public NFINT64 GetInt() const;
  10.     public double GetFloat() const;
  11.     public const std::string& GetString() const;
  12.     public const NFGUID& GetObject() const;
  13.     public const NFVector2& GetVector2() const;
  14.     public const NFVector3& GetVector3() const;

  15.     public void RegisterCallback(PROPERTY_EVENT_FUNCTOR_PTR cb);

  16.     private int OnEventHandler(const NFIDataList::TData& oldValue, const NFIDataList::TData& newValue);
  17.     private vector<PROPERTY_EVENT_FUNCTOR_PTR> mtPropertyCallback;
  18. }
复制代码



接下来实现观察着模式来驱动逻辑,观察着模式少不了注册和事件触发,分别为RegisterCallback 和 OnEventHandler 函数,而PROPERTY_EVENT_FUNCTOR_PTR则为回调函数(委托),他们的实现分别是:


  1. void NFCProperty::RegisterCallback(const PROPERTY_EVENT_FUNCTOR_PTR& cb)
  2. {
  3.      mtPropertyCallback.push_back(cb);
  4. }

  5. int NFCProperty::OnEventHandler(const NFIDataList::TData& oldVar, const NFIDataList::TData& newVar)
  6. {
  7.     TPROPERTYCALLBACKEX::iterator it = mtPropertyCallback.begin();
  8.     TPROPERTYCALLBACKEX::iterator end = mtPropertyCallback.end();
  9.     for (it; it != end; it)
  10.     {
  11.         
  12.         PROPERTY_EVENT_FUNCTOR_PTR& pFunPtr = *it;
  13.         PROPERTY_EVENT_FUNCTOR* pFunc = pFunPtr.get();
  14.         //对象ID,属性名,OLD属性值,NEW属性值
  15.         pFunc->operator()(mSelf, msPropertyName, oldVar, newVar);
  16.     }

  17.     return 0;
  18. }
复制代码


其中RegisterCallback函数,当某个模块对这个属性有兴趣的时候(或者说业务需求需要的时候),才会调用此函数进行回调函数注册,而注册过的函数会被在Property的内存中,等待事件触发被调用。存储下面我们来看,当数值改变的时候(SetInt, SetString等函数),触发回调的过程:

  1. bool NFCProperty::SetInt(const NFINT64 value)
  2. {
  3.     if (eType != TDATA_INT)
  4.     {
  5.         return false;
  6.     }

  7.     if (value == GetInt())
  8.     {
  9.         return false;
  10.     }

  11.     NFCDataList::TData oldValue;
  12.     oldValue = GetInt();

  13.     mxData->SetInt(value);

  14.     OnEventHandler(oldValue, value);

  15.     return true;
  16. }
复制代码



代码中的NFCDataList::TData,可以理解为c#/java语言中的object,c 则理解为联合体就行(Property的简单体),总之它能兼容我们的基础数据,这就够了。
因此,当有效数据变化的时候就会调用回调函数(委托),从而达到数据驱动逻辑的目的。看起来很简单,但是实际大范围应用起来后,可以得到意向不到的效果,不仅仅是效率高,结合NFClass,开发效率直逼火箭速度。同时,复杂一点的record(二维记录表)的执行原理,和property的原理几乎一模一样,不过是在回调函数接口中,增加了row和col的参数,却几乎可以应用在所有的逻辑系统中。

NF引擎中大量使用了这种基础技术,来达到逻辑解耦的目的,比如是否广播,是否存档,广播范围,这些属性均是属性对象的属性,配合数据驱动(甚至可以加上脚本),可以很简单的做出很多很复杂的自动化功能,同时几何级降低了维护成本。


NF项目为开源的分布式服务器解决方案,其中包含了网络库,actor库,以及数据驱动等新技术,能大幅提升开发效率节省开发周期以及提高程序的稳定性。
如感觉对您有帮助,请给与star,同时也邀请广大同行参与开发和维护,作者QQ 342006,交流QQ群 341159815。
欢迎转载,转载请注明来源,本文版权归作者所有!

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引