MeteorCat / Erlang进阶(一)

Created Sun, 14 Jul 2024 17:17:36 +0800 Modified Wed, 29 Oct 2025 23:24:53 +0800
1166 Words

Erlang进阶(一)

之前演示过简单得学习基本类型/函数声明等, 后续需要构建项目建议先了解 Emakefile:

这里建议默认项目目录构建:

/(根目录)   -------   _build(编译打包出来数据)
              |         |
              |         |-------   release(正式包, 移除调试信息)
              |         |
              |         |-------   dev(开发包, 本地测试环境)                       
              |
              |
              |---   config(配置文件目录)
              |
              |
              |---   include(头文件目录)
              |
              |
              |---   src(源代码目录)
              |         |
              |         |
              |         |
              |         |-------  utils(工具目录, 所有常用工具采用 `类型_utils.erl` 编写)
              |         |
              |         |
              |         |-------  项目名.app.src( 工程信息文件 )
              |         |
              |         |
              |         |-------  项目名.erl( 工程入口文件 )
              |
              |
              |
              |---   Emakefile(主打包文件)
              |
              |
              |---   Emakefile.dev(开发调试)
              |
              |
              |---   Emakefile.release(正式编译)

Emakefileerl -make 必须的, 这里通过 Emakefile.dev|Emakefile.release 覆盖 Emakefile 再去执行命令

这里基本上就是最初的配置目录, Emakefile 配置文件基本如下:

%% 入口文件打包
{
  	["src/*"],
	[debug_info,{outdir, "./_build/dev"},{i, "include"},warn_keywords]
}.

%% 工具打包
{
    ["src/utils/*"],
    [debug_info,{outdir, "./_build/dev"},{i, "include"},warn_keywords]
}.

这里可以配置脚本来帮助你处理创建 ./_build/dev/ebin 目录, 也可以直接打包输出到 ./_build 输出都行,最后调用:

# 启动编译输出
erl -make

之后还有启动脚本和 sasl_log 日志等配置, 后续正式项目工程建议采用这种方式.

behaviour 模板

现在开始切入 behaviour 对应框架模板开始进行正式开发, OTP(Open Telecom Platform, 开放电信平台) 模板简单来说可以说是官方提供的应用框架, 框架总共有类型:

  • application: 命令行应用, 最常用的类型
  • supervisor: 进程管理器, 多进程管理器
  • gen_server: Erlang进程管理器, 用于管理 Erlang 进程
  • gen_fsm: 有限状态机, 定义数据状态机
  • gen_event: 通用事件配置, 用于实现自定义事件
  • …… 其他

其中正式项目用的比较多的是 application|supervisor|gen_server, 这里首先创建第一个游戏项目:

# 游戏项目工程目录构建
mkdir fight
cd fight

# 创建对应的目录
mkdir _build
mkdir _build/dev
mkdir _build/release
mkdir include
mkdir src
mkdir src/utils
mkdir config

# 创建打包文件
touch Emakefile

# 构建第一个项目入口文件
vim src/fight.erl # 项目入口文件
vim src/fight.app.src # 项目入口配置信息

注意: fight.app.src 是工程描述文件, 是必须创建的文件:

%%% 应用入口配置
{application, fight, % fight 为工程名称
  [{description, "Fight Game Service"}, %% 工程描述
    {vsn, "2024.07.14"}, % 工程版本
    {registered, []},
    {mod, {fight, []}}, % mod项目启动 module, 也就是 fight.erl 文件
    {applications,
      [ % 引入的系统应用库
        kernel,
        stdlib
      ]},
    {env, []},
    {modules, []},
    {licenses, ["MIT"]}, % 开源许可
    {links, []}
  ]}.

应用启动入口文件(fight.erl)内容如下:

%%%-------------------------------------------------------------------
%%% @author MeteorCat
%%% @copyright (C) 2024, <COMPANY>
%%% @doc
%%% fight游戏服务端启动入口 -> application:main
%%% @end
%%% Created : 14. 7月 2024 下午 07:31
%%%-------------------------------------------------------------------
-module(fight).
-author("MeteorCat").

-behaviour(application).

%% Application callbacks
-export([
  start/2,
  stop/1,
  main/0
]).

%%%===================================================================
%%% Application 回调
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% 项目主要回调入口, -spec 代表强类型声明, 后续讲解的方法
%% @end
%%--------------------------------------------------------------------
-spec(start(StartType :: normal | {takeover, node()} | {failover, node()},
    StartArgs :: term()) ->
  {ok, pid()} |
  {ok, pid(), State :: term()} |
  {error, Reason :: term()}).
start(_StartType, _StartArgs) ->
  io:fwrite("Fight Started~n"),
  application:start(crypto), % 启动加密服务

  {ok, erlang:self()}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% 程序退出回调
%% @end
%%--------------------------------------------------------------------
-spec(stop(State :: term()) -> term()).
stop(_State) ->
  io:fwrite("Fight Close~n"),
  application:stop(fight),
  application:stop(crypto).

%%%===================================================================
%%% Internal functions
%%%===================================================================


%%% 这里就是应用启动入口
main() ->
  io:fwrite("Main Started~n"),
  case application:start(fight) of
    {error, Reason} ->
      io:format("Application::start error -> ~p~n", [Reason]),
      {error, Reason};
    _ -> ok
  end.

以上就是最基础的命令行应用文件入口, 程序启动调用方式:

# 启动项目
erl -pa ./_build/dev -s fight main -noshell

这就是目前构建名为 fight 的命令行程序项目, 这时候暂停下 behaviour 深入, 转而以 behaviour(application) 进阶 Actor实现|并发进程|网络服务 的学习, 后续都会用到其他 behaviour 再去讲解.