最近使用erlang重构了游戏服务器的所有代码,之前看过C++/lua写的服务器引擎代码,引擎实现了玩家属性自动同步给前端和增量更新玩家数据到数据库的功能,这也是现在很多游戏服务器的优化方向,在引擎层面去解决数据同步和数据持久化,数据发生变化了业务层不需要关心怎么去同步给前端。由于游戏过程中玩家每个业务中玩家数据更改的量其实是很少的,增量的去持久化数据到数据库,可以大大减少数据库压力,基本可以做到实时回写数据(比如隔几s回写一次),就算服务器某个时候down了,对玩家的数据也不会造成很大影响。
本着程序员都要有偷懒的精神,所以我也希望在erlang这个架构下实现这样的功能。由于erlang没有类似lua有index, newindex这样的语义,在lua中数据改变时及时让引擎层知道,从而增量去记录数据的变动。一个方法就是自己重新封装所有修改数据的接口,但是这样效率会低很多。比如一次要修改record的某几个字段,就要循环遍历n次,每次修改一个字段,每次修改的时候会导致整个record浅拷贝一次。而且自己封装接口也显得非常不优雅。另一个方法就是改写erlang的vm代码,但是这样风险比较大,毕竟需要对vm很熟悉。怎么办??? 这时候parse_transform编译选项来搭救了。
编译erl文件,会先生成中间代码,然后在由中间代码进一步生成vm执行的opcode。erlang的parse_transform编译选项就是提供一个回调模块,让编程者在生成opcode之前对中间代码进行处理,以改变erlang的某些语义。
想了解erlang编译成vm最终执行的代码经过的步骤可参考:http://www.cnblogs.com/me-sa/p/know-a-little-erlang-opcode.html , 这里主要讲讲parse_transform这个编译选项,在github开源项目lager中就用到了。
在Emakefile的编译选项加上{parse_transform, test_transform}(test_transform可以是其他文件名),test_transform.erl需要export([parse_transform/2]). 在使用make编译erl文件生成abstract_code之后会调用test_transform:parse_transform/2对abstract_code进行处理,处理完返回新的abstract_code。在lager项目中就是通过在编译期间调用lager_transform:parse_transform/2对lager:debug/info/warn/error等函数进行展开实现代码的嵌入,因此在使用lager时,是找不到lager这个模块的,都是在编译期间展开。有兴趣的可以去阅读lager项目的代码。
我实现的方案就是在编译期间去记录数据的变更的,收集变更的数据,定时或者按照自己的方式把数据打包一次性发给前端,同时增量的去持久化数据。在写游戏逻辑的时候再也不需要关心数据同步和持久化了,框架层自动帮我做了,而且对性能几乎没什么损失。当然由于游戏数据是非常复杂的,很难做到所有数据的自动同步,而且由于某些业务逻辑的特殊性也不可能使用这种方式去同步数据。但是基本上单个数值这一类的属性就可以这样处理,已经可以省不少代码量了。具体最终怎么做到的就不细说了,这里只提供思路哈。
下面给个例子说明一下parse_transform编译选项的作用:
代码:
-module(test). -compile(export_all). main() -> A = 1, io:format("A: ~p~n", [A]).
-module(test_transform). -export([parse_transform/2]). parse_transform(AST, _Options) -> walk_ast([], AST). walk_ast(Acc, []) -> lists:reverse(Acc); walk_ast(Acc, [{function, Line, Name, Arity, Clauses}|T]) -> walk_ast([{function, Line, Name, Arity, walk_clauses([], Clauses)}|Acc], T); walk_ast(Acc, [H|L]) -> walk_ast([H|Acc], L). walk_clauses(Acc, []) -> lists:reverse(Acc); walk_clauses(Acc, [{clause, Line, Arguments, Guards, Body}|T]) -> walk_clauses([{clause, Line, Arguments, Guards, walk_body([], Body)}|Acc], T). walk_body(Acc, []) -> lists:reverse(Acc); walk_body(Acc, [H|T]) -> walk_body([transform_statement(H)|Acc], T). transform_statement({match, Line, {var, Line, 'A'}, {integer, Line, _V}}) -> {match, Line, {var, Line, 'A'}, {integer, Line, 2}}; transform_statement(Stmt) when is_tuple(Stmt) -> list_to_tuple(transform_statement(tuple_to_list(Stmt))); transform_statement(Stmt) when is_list(Stmt) -> [transform_statement(S) || S <- Stmt]; transform_statement(Stmt) -> Stmt.
test_transform的作用是把语句A = 1替换成A = 2.
测试结果:
(master@127.0.0.1)18> c(test).
{ok,test}
(master@127.0.0.1)19> test:main().
A: 1
ok
(master@127.0.0.1)20> c(test_transform).
{ok,test_transform}
(master@127.0.0.1)21> c(test, [{parse_transform,test_transform}]).
{ok,test}
(master@127.0.0.1)22> test:main().
A: 2
ok
我们看到A的值变成了2, 替换成功。
查看test.beam的abstract_code (一定要加上debug_info,才能查看abstract_code):
(master@127.0.0.1)25> c(test, [debug_info]).
{ok,test}
(master@127.0.0.1)26> {ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(filename:dirname(code:which(test)) ++ "/test.beam",[abstract_code]).
(master@127.0.0.1)27> AC.
[{attribute,1,file,{"test.erl",1}},
{attribute,1,module,test},
{attribute,2,compile,export_all},
{function,4,main,0,
[{clause,4,[],[],
[{match,5,{var,5,'A'},{integer,5,1}},
{call,6,
{remote,6,{atom,6,io},{atom,6,format}},
[{string,6,"A: ~p~n"},{cons,6,{var,6,'A'},{nil,6}}]}]}]},
{eof,7}]
(master@127.0.0.1)28>
由此可见erlang其实是很强大的,各种工具都齐全,只有想不到,没有做不到。。。。。
相关推荐
parse_trans应用程序 作者: Ulf Wiger( )。 Parse_transform实用程序 介绍 编写Parse_trans是为了捕获Erlang的语法转换和代码生成中的一些有用模式。 最值得注意的是,也许模块为记录生成了标准化的访问器函数...
erlang-sd_notify-1.0-2.el7.x86_64.rpm
esl-erlang_23.0和rabbitmq-3.8.4windows版本 直接下载安装就行,可以直接下载就可安装,非常的方便 ,欢迎大家下载 注意事项: 1. Erlang版本和RabbitMQ版本要配套 (Erlang23.0, RabbitMQ3.8.4) 2. amd芯片请乖乖...
是erlang_otp_20.3 的win64安装包,内为exe文件,一路next即可安装完成 是适用于多线程、分布式开发的语言,也是如rabbitmq等重要工具的必须品 使用前需要配置环境变量:1.变量名为ERLANG_HOME,变量值为安装Erlang...
这个erlang23.0版本,根据rabbitMQ官网的介绍,可以和下面这几个版本的rabbitMQ配合使用: 3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 其他版本的rabbit,请移步其他资源下载
**Erlang B模型**是通信工程领域中的一个重要概念,特别是在无线通信系统设计中用于评估系统容量...通过深入研究"Erlang_B_model.pdf",读者可以更全面地了解这一模型的原理和应用,从而提升在通信工程领域的专业素养。
在OTP中,Supervisor是核心组件之一,它负责管理和监控应用程序中的进程,当某个进程崩溃时,Supervisor可以自动重启该进程,确保系统的稳定性。此外,GenServer提供了一种状态管理机制,使得进程能够存储和操作状态...
在"erlang_otp_win64_25.0"这个标题中,我们可以提取出几个关键点: 1. **Erlang**:这是一种函数式编程语言,以其在处理并发性和容错性方面的强大能力而闻名。Erlang的设计理念是让程序员能够轻松地构建能够并行...
erlang_environment_win64_21.0.1 文件可能包含了Erlang SDK(软件开发工具包)的一部分,这通常包括头文件、库文件以及用于编译和调试Erlang应用程序的工具。这些工具可能包括rebar或rebar3这样的构建系统,它们...
erlang_otp_20.3_man开发手册,erlang_otp_20.3_man开发手册,erlang_otp_20.3_man开发手册
通常,这包括解压源码、配置编译选项、编译源代码以及安装到目标系统路径。在编译过程中,可能需要安装依赖库,如 GNU 编译工具链(GCC)、Make 等,并设置适当的环境变量。 Erlang 22.3 版本可能包含的更新和改进...
Erlang B 和 Erlang C 是在电信领域中广泛使用的两个数学公式,用于预测和管理电话交换系统的呼叫处理能力。这两个公式由丹麦工程师 Agner Krarup Erlang 在20世纪初开发,对于理解通信系统中的呼叫占用率、阻塞率和...
标题“esl-erlang_19.3_osx_10.10_amd64.dmg”指的是一个特定版本的Erlang编程环境的安装镜像文件,由Erlang Solutions Limited(ESL)发布,适用于Apple macOS 10.10 Yosemite操作系统,并且是为64位AMD处理器设计...
erlang-syntax_tools-19.3.6.4-1.el7.x86_64.rpm
这个过程会检查系统环境并确定适当的编译选项。 6. **编译和安装**: - 使用`make`命令进行编译,这可能需要一段时间,具体时间取决于你的系统性能。 - 编译成功后,使用`sudo make install`命令将Erlang OTP安装...
标题中的"erlang_otp_22.2_win64"和"rabbitmq-server-3.8.3"提到了两个关键的IT技术:Erlang OTP和RabbitMQ服务器,这两个都是在分布式系统和消息队列领域非常重要的组件。 **Erlang OTP** Erlang是一种函数式编程...
2. **编译源码**:进入解压后的目录,执行`./configure`来配置编译选项,确保与你的操作系统和硬件兼容。接着运行`make`进行编译,这可能需要一段时间。 3. **安装到系统**:编译成功后,使用`sudo make install`...
在给定的文件“erlang_otp_src_22.3.tar.gz”中,包含了Erlang OTP的源代码,版本为22.3。这个版本的发布通常会包含许多新特性、性能优化以及对已知问题的修复。源代码的获取对于开发者来说至关重要,因为这允许他们...
此版本为window64位下的,erlang23版本,erlang-otp_win64_23.0,官网原包,官网太难找了,好不容易找到,和大家分享下,需要的可以下载,也作为自己的一个记录吧,自己亲测很好用,erlang环境需要与对应的rabbitmq...
在这个案例中,我们讨论的是"erlang_win_64位安装包",这意味着它是专为64位Windows操作系统设计的。32位和64位系统之间的兼容性问题需要引起注意,因此选择正确的版本至关重要,以免出现安装失败或运行不正常的情况...