作者:Code@Pig

hotfix, 热更新, 热部署, 动态代码更新


对运行中的系统,实时地替换某个模块(某个函数)的代码。


关于C/C++
对于 C/C++,函数入口在编译后,固定为一段内存地址。想要hotfix,则需要“每次调用此函数,先通过func_map做一个查表”这样来做到。
比如:
void foo();
func_map[quot;fooquot;] = foo;


void bar()
{
myfoo = func_map[quot;fooquot;];
myfoo();
}


将 foo() 函数放在独立DLL中,更新此DLL文件,然后通知主程序重新load此DLL,并更新func_map,则达到了动态修改代码的目的。
这是根据原理推导出来的方法,但我未在项目中实际用过。


关于脚本语言(lua/python)
来说说脚本语言的hotfix,这是我们每天都要用到的功能。
脚本中跨模块调用,都是“查表找到具体的函数,再调用之”的方式(所以才动态嘛),很适合来做 hotfix


关于python
编辑 foo.py 文件
class Foo(object):
def gogo(self):
print Foo.gogo 111


gt;gt; import foo
gt;gt; f = foo.Foo()
gt;gt; f.gogo()
Foo.gogo 111


修改 foo.py
class Foo(object):
def gogo(self):
print Foo.gogo 222


gt;gt; reload(foo)
gt;gt; f.__class__ = foo.Foo
gt;gt; f.gogo()
Foo.gogo 222


这里要注意,要hotfix生效,不要直接引用 instance 的 class method,比如:
gt;gt; callback = f.gogo
gt;gt; callback()
Foo.gogo 222


修改 foo.py
class Foo(object):
def gogo(self):
print Foo.gogo 333


gt;gt; reload(foo)
gt;gt; callback()
Foo.gogo 222


所以,要这样,才能保证hotfix。
gt;gt; callback = lambda: f.gogo()


同理,对于普通函数,也不要用局部变量引用起来
gt;gt; foo.somefunc()
gt;gt; callback = lambda: foo.somefunc()
gt;gt; callback()
不能
gt;gt; callback = foo.somefunc
gt;gt; callback()


关于lua
lua 没有类的概念,所有的替换,其实就是替换模块中的函数。


编辑 foo.lua
module(quot;fooquot;, package.seeall)


function hello()
print(quot;hello 111quot;)
end


gt; require(quot;fooquot;)
gt; foo.hello()
hello 111


修改 foo.lua
module(quot;fooquot;, package.seeall)


function hello()
print(quot;hello 222quot;)
end


gt; package.loaded[quot;fooquot;] = nil
gt; require(quot;fooquot;)
gt; foo.hello()
hello 222


一样的,不要直接引用模块中的函数。
如果通过 metatable 设计了一个类机制,自己考虑好,可以保证更新到位。


hotfix的终极解法(erlang)
以上脚本在设计之初,是没有考虑hotfix的,所以在实践hotfix时,需要写代码同学注意一些问题(不能直接引用模块中的函数)
erlang 这块是彻底解决了这个问题,包括变量都是不能二次赋值的,在并发和热更新方面都无比顺畅。


停止模块执行 =gt; 更换模块代码 =gt; 调用code_change转换模块老的数据 =gt; 恢复模块的执行锐亚教育

锐亚教育,游戏开发论坛|游戏制作人|游戏策划|游戏开发|独立游戏|游戏产业|游戏研发|游戏运营| unity|unity3d|unity3d官网|unity3d 教程|金融帝国3|8k8k8k|mcafee8.5i|游戏蛮牛|蛮牛 unity|蛮牛