热代码加载与升级
探索 Erlang 独特的热代码加载能力,并在不中断运行系统的情况下执行实时软件升级。
热代码加载与升级 是 CoddyKit 上的免费 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
What is Hot Code Loading?
Erlang's hot code loading is a standout feature! It lets you update a running application's code without stopping it. Imagine changing parts of a website's backend while it's actively serving users, without any downtime!
This capability is crucial for systems that need to run continuously, like telecommunications switches or large-scale distributed services. It ensures maximum uptime and service availability.
How Erlang Manages Code
The Erlang Virtual Machine (BEAM) manages code modules in a unique way. For each module, it can keep two versions loaded in memory: an 'old' version and a 'new' version.
- When a process starts, it runs the 'new' version of the code.
- If you reload a module, new calls to its functions will use the very latest 'new' version.
- However, existing processes continue to execute the code they were loaded with until they make a call to a function in the *reloaded* module or are explicitly told to change.
Simple Module Reloading
You can interactively reload a module in the Erlang shell. The l(Module) function (short for 'load') compiles and loads the latest version of a module from the code path.
Let's see a quick example. We'll define a simple math module, then change a function and reload it.
Module Reload Demo
First, create my_math.erl:
Then, in the Erlang shell, compile it with c(my_math). Call my_math:add(1, 2). Now, *change* the add/2 function in the file to X + Y + 10. Save. Run l(my_math) and call my_math:add(1, 2) again. Notice the new result!
-module(my_math).
-export([add/2]).
add(X, Y) -> X + Y.State Migration Challenge
Simple reloading works for purely functional changes (like our my_math example). But what if a running process, especially an OTP behavior like a GenServer, holds internal state that changes its structure?
If you just reload the code, the running GenServer still holds its old state format. The new code won't know how to interpret it, leading to crashes. We need a way to 'transform' the old state into the new state.
Introducing `code_change/3`
OTP behaviors provide a special callback function called code_change/3. This function is designed precisely for handling state migration during a hot code upgrade.
When you tell a running OTP process to upgrade its code, Erlang will call this function in the *new* version of the module. It's your chance to convert the process's old internal state to the new format.
The `code_change/3` Callback
The signature for code_change in a GenServer looks like this:
code_change(OldVsn, State, Extra) -> {ok, NewState}
OldVsn: The version of the code *being upgraded from*.State: The current internal state of the process (in the old format).Extra: Additional arguments, often unused.- You must return
{ok, NewState}, whereNewStateis the transformed state in the new format.
GenServer Upgrade: State Transformation
Let's imagine a GenServer that stores a simple counter as an integer. We want to upgrade it to store the counter as a map #{value => integer()}.
The code_change/3 function will receive the old integer state and return a new map state. This ensures the GenServer continues running smoothly with the updated code and state structure.
GenServer `code_change/3` Example
Here's a simplified example of how code_change/3 would look in my_counter_v2. If our old state was just an integer (e.g., 10), and our new state needs to be #{value => 10}, the conversion is straightforward:
This transformation is key to seamless upgrades.
-module(my_counter_v2).
-behaviour(gen_server).
-export([start_link/0, get_count/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
get_count() -> gen_server:call(?MODULE, get_count).
init([]) -> {ok, #{value => 0}}.
handle_call(get_count, _From, State) ->
{reply, maps:get(value, State), State};
handle_call(_Request, _From, State) ->
{reply, not_understood, State}.
handle_cast(_Msg, State) -> {noreply, State}.
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, OldState, _Extra) when is_integer(OldState) ->
io:format("~p: Upgrading state from ~p~n", [?MODULE, OldState]),
{ok, #{value => OldState}};
code_change(_OldVsn, State, _Extra) ->
io:format("~p: No upgrade needed for state ~p~n", [?MODULE, State]),
{ok, State}.Upgrade Best Practices
Hot code loading is powerful, but requires careful planning:
- Test Thoroughly: Always test your upgrade paths in a staging environment before deploying to production.
- Backward Compatibility: Design
code_change/3to handle multiple previous versions if necessary. - Small, Incremental Changes: Avoid massive changes in state structure in a single upgrade. Break them into smaller, manageable steps.
- Release Handling: In production, hot code upgrades are typically managed by 'release handlers' (like
release_handlerin OTP applications), which automate the process of loading new code and coordinating state changes across multiple processes and nodes.
Quick Check: Hot Code Loading
You've learned about Erlang's hot code loading and how it handles state changes. Which of the following statements about Erlang's code_change/3 callback are TRUE?
Recap: Live Upgrades
In this lesson, we explored Erlang's powerful hot code loading feature, which allows applications to be upgraded without downtime. We learned:
- Erlang can keep 'old' and 'new' versions of modules loaded.
- Simple code changes can be reloaded with
l(Module). - For stateful processes like GenServers, the
code_change/3callback is essential for transforming a process's internal state when the code structure changes. - Careful planning and testing are vital for successful hot code upgrades.
This unique capability is a cornerstone of Erlang's fault-tolerant and highly available systems!
常见问题解答
「热代码加载与升级」课时是免费的吗?
是的 — 「热代码加载与升级」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课程的其余内容,请升级到 CoddyKit PRO。 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课程共包含 4 节课。
「热代码加载与升级」这节课中我会学到什么?
探索 Erlang 独特的热代码加载能力,并在不中断运行系统的情况下执行实时软件升级。 你通过在浏览器中直接运行的动手代码来练习 Erlang OTP: Distributed & Fault-Tolerant Systems Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「热代码加载与升级」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课中编写并运行代码吗?
能。每节 Erlang OTP: Distributed & Fault-Tolerant Systems Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 创建 Erlang 发布包
- 热代码加载与升级
- 发布版本管理与部署
- 发布配置与启动脚本