Chắc bạn đang nghĩ tới One time Password. Nhưng không phải, OTP là cụm từ viết tắt của Open Telecom Platform. Đây có thể xem như là 1 framework dùng để thiết kế ứng dụng mà cấu thành bới những phần nhỏ của ứng dụng có thể chạy trên 1 process riêng. Nó bao gồm:
- an Erlang interpreter (which is called BEAM)
- an Erlang compiler
- a protocol for communication between servers (nodes)
- a CORBA Object Request Broker
- a static analysis tool called Dialyzer
- a distributed database server (Mnesia)
- and many other libraries ...
�Đoạn này mình trích lại ở wiki mà không dịch để mọi người có thể search những từ khoá nếu muốn tìm hiểu thêm
Ref: https://en.wikipedia.org/wiki/Open_Telecom_Platform
Đây có thể xem như là 1 interface giúp ta hiện thực 1 server. Nó bao gồm các hàm
init(start_arguments)
: Hàm này sẽ được gọi khi start server.handle_call(request, from, state)
: Hàm này giúp ta handle những request từ client bên ngoài gọi đến server và reply về 1 giá trị gì đó cho clienthandle_cast(request, state)
: Tương tự handle_call nhứng không reply gì về cho client. Có thể xem như one way call từ client tới server.handle_info(info, state)
: Hàm này sẽ handle những message khác không gửi từ client. Ví dụ handle timeoutterminate(reason, state)
: Hàm này được gọi khi server tắtcode_change(from_version, state, extra)
: khi có 1 module được hot code swapping thì sẽ gọi hàm này. Bởi vì OPT cho phép chúng ta replace 1 server đang chạy mà không buộc dừng hệ thống lại.format_status(reason, [pdict, state])
: Giúp ta format lại cách hiển thị state hiện tại
Chúng ta chỉ cần quan tâm đến các hàm
init
,handle_call
,handle_cast
ở bài viết này
Có 3 kiểu return về cho client từ server là:
{ :stop, reason, new_state }
: Tín hiệu cho biết server bị stop{ :reply, response, new_state [ , :hibernate | timeout ] }
: Cách trả về phản hồi sau lời gọi từ client{ :noreply, new_state [ , :hibernate | timeout ] }
: Không trả về phản hồi nào
Chúng ta sẽ cài đặt 1 stack với 2 phương thức là push
và pop
bằng GenServer
File name: my_stack/server.ex
defmodule MyStack.Server do
use GenServer
def init(args) do
{:ok, args}
end
# Đây là hàm chạy process và link với process hiện tại
def start_link(stack) do
GenServer.start_link(__MODULE__, stack, name: __MODULE__)
end
## External API
@doc """
Put 1 giá trị vào trong stack
value: giá trị cần push
"""
def push(value) do
# Sử dụng cast nếu như không quan tâm kết quả trả về
GenServer.cast(__MODULE__, {:push, value})
end
@doc """
Lấy giá trị ở trên cùng của stack ra
"""
def pop() do
# Sử dụng call nếu như quan tâm kết quả trả về
GenServer.call(__MODULE__, :pop)
end
## Handle ở server
@doc """
:push là tên action
value: tham số được truyền từ bên ngoài vào
stack: là biến stack mà application đang giữ, được init ở hàm start_link
"""
def handle_cast({:push, value}, stack) do
{:noreply, [value | stack]}
end
@doc """
:pop là tên action
_form: là pid của process gọi đến
stack: là biến stack mà application đang giữ, được init ở hàm start_link
"""
def handle_call(:pop, _from, [h | t] = stack) do
{:reply, h, t}
end
end
Chạy thử chương trình
~/workspace/my_stack » iex -S mix
iex(1)> MyStack.Server.start_link
{:ok, #PID<0.120.0>}
iex(2)> MyStack.Server.push 1
:ok
iex(3)> MyStack.Server.push 2
:ok
iex(4)> MyStack.Server.pop
2
iex(5)> MyStack.Server.pop
1
Nếu như tiếp tục pop từ server ra thì chương trình sẽ bị dừng
iex(6)> MyStack.Server.pop
** (EXIT from #PID<0.118.0>) shell process exited with reason: an exception was raised:
** (FunctionClauseError) no function clause matching in MyStack.Server.handle_call/3
(my_stack) lib/my_stack/server.ex:45: MyStack.Server.handle_call(:pop, {#PID<0.118.0>, #Reference<0.4269747632.3638820865.190101>}, [])
(stdlib) gen_server.erl:636: :gen_server.try_handle_call/4
(stdlib) gen_server.erl:665: :gen_server.handle_msg/6
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
iex(1)> MyStack.Server.push 1
** (exit) exited in: GenServer.call(MyStack.Server, :pop, 5000)
** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
(elixir) lib/gen_server.ex:824: GenServer.call/3
Supervisor là 1 process có nhiệm vụ quản lý 1 hoặc nhiều worker con.
File name: my_stack/application.ex
defmodule MyStack.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
# List all child processes to be supervised
children = [
# Starts a worker by calling: MyStack.Worker.start_link(arg)
{MyStack.Server, []}
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: MyStack.Supervisor]
Supervisor.start_link(children, opts)
end
end
Chạy lại chương trình
~/workspace/my_stack » iex -S mix
iex(1)> MyStack.Server.push 1
:ok
iex(2)> MyStack.Server.push 2
:ok
iex(3)> MyStack.Server.pop
2
iex(4)> MyStack.Server.pop
1
iex(5)> MyStack.Server.pop
** (exit) exited in: GenServer.call(MyStack.Server, :pop, 5000)
** (EXIT) an exception was raised:
** (FunctionClauseError) no function clause matching in MyStack.Server.handle_call/3
(my_stack) lib/my_stack/server.ex:46: MyStack.Server.handle_call(:pop, {#PID<0.122.0>, #Reference<0.337936538.3931111425.61921>}, [])
(stdlib) gen_server.erl:636: :gen_server.try_handle_call/4
(stdlib) gen_server.erl:665: :gen_server.handle_msg/6
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
(elixir) lib/gen_server.ex:834: GenServer.call/3
iex(6)> MyStack.Server.push 1
:ok
Ta thấy ở lần gọi hàm push cuối cùng, chương trình tuy bị crash nhưng đã được restart lại như ban đầu
- OPT application
- Tasks và Agents
ToanHa 04-06-2017