클러스터에 ETS 데이터베이스 배포
분산 데이터가 필요한 이유는 무엇입니까? "확장된"웹 앱과 상호 작용할 때 도달할 노드를 알 수 없으므로 모든 노드/포드에 동일한 데이터가 있어야 합니다. 일부 데이터는 클라이언트 측(예: 장바구니)에 보관할 수 있지만 클라이언트 자격 증명이나 캐시와 같은 일부 데이터는 서버 측에도 보관할 수 있습니다.
빠른 읽기 데이터를 관리하는 몇 가지 방법:
Redis 와 같은 외부 데이터베이스를 사용하여 앱을 상태 비저장으로 만듭니다. 그러나 Redis를 보호하고 클러스터를 만들려는 경우 특히 눈에 띄는 오버헤드가 발생합니다. 고정 세션 사용의 단점은 새 사용자만 새 노드에 도달하기 때문에 노드 간에 로드를 분산할 수 없다는 것입니다.
BEAM은 기본적으로 클러스터링(Elixir/Phoenix를 실행하는 Erlang의 가상 머신)을 지원하기 때문에 이 마지막 경로를 사용합니다. 클러스터 모드에서는 분산형
PG이 있는 PubSub 모듈이 무료입니다. 이것은 Erlang 생태계의 두 번째 주요 지점입니다. 덕분에 단일 실패 지점이 없고 외부 종속성이 없습니다.Why would you choose ETS and not MNESIA which is a build-in distributed solution? The main reason was the setup of MNESIA in a dynamic environment such as Kubernetes. Bad synchronisation and proper startup on node discovery can be difficult.
MNESIA에는 메모리 또는 디스크의 두 가지 모드가 있고 ETS에는 디스크 복사 분신인 DETS가 있습니다. 여전히 ETS 테이블의 디스크 복사본을 만들 수 있습니다. 테이블 이름이
:users 이면 :ets.tab2file(:users, 'data.txt') 명령이 디스크에 저장됩니다(! 작은따옴표 사용 !).사용 사례의 경우
libcluster와 함께 노드 검색을 사용하고 노드 시작 시 PubSub를 통해 ETS를 동기화하는 것이 쉽고 안정적이었습니다. 1,000명의 회원을 쉽게 팔로우할 수 있습니다.설명할 코드가 있습니다. 사용자를 생성하고 ETS에 저장합니다. 우리는
GenServer 동작을 사용합니다. 우리는 GenServer 메모리에 상태를 유지하지 않고(실제로 상태는 단지 nil) 오히려 GenServer에서 제공하는 프로세스 간의 메시징 기능을 사용합니다.Since the ETS database is configured as a
set, they will be no duplicate or data lost.
앱에서 ETS 작업에 저장하고(예: 사용자가 생성될 때) 키
:new를 통해 주제 "new"에 이 데이터를 브로드캐스트하는 작업을 수행하는 "Repo"모듈이 있습니다.defmodule MyApp.Repo do
require Logger
alias :ets, as: Ets
def all,
do: Ets.tab2list(:users)
def save(message),
do: true = Ets.insert(:users, message)
def save_and_emit(email, context) do
user = build_from{email, token)
true = save(user)
:ok = Phoenix.PubSub.broadcast_from!(PwdlessGs.PubSub, self(), "new", {:new, user})
user
end
end
노드(Erlang
EPMD 통합 서버)를 모니터링하는 GenServer "NodeListener"가 있습니다. 그의 임무는 :nodeup 이벤트를 수신하고 :sync 키를 통해 "node_up"주제의 다른 노드에 ETS 테이블을 브로드캐스트하는 것입니다.defmodule MyApp.NodeListener do
use GenServer
alias MyApp.Repo
# delay between the "up" event and the node read
@sync_init 3_000
def start_link(_opts),
do: GenServer.start_link(__MODULE__,[], name: __MODULE__)
@impl true
def init([]) do
:ok = :net_kernel.monitor_nodes(true)
{:ok, nil}
end
@impl true
def handle_info({:nodeup,_}, _) do
Process.send_after(self(), {:perform_sync}, @sync_init)
{:noreply, nil}
end
@impl true
def handle_info({:perform_sync}, _state) do
:ok = Phoenix.PubSub.broadcast_from!(MyApp.PubSub, self(), "node_up", {:sync, Repo.all()}, node())
{:noreply, nil}
end
def handle_info({:nodedown, _},_), do: {:noreply, nil}
end
"node_up"과 "new"라는 두 개의 토픽을 구독하는 GenServer "Syncer"가 있습니다. 그에게는 세 가지 임무가 있습니다.
:sync 처리기를 듣습니다. 그런 다음 노드는 n-1개의 복사본(n개 노드 중)을 수신하고 데이터를 자신의 테이블과 병합합니다. :new 처리기를 듣습니다. 각 노드(예: 새 사용자)의 작업 변경 사항을 전달합니다.defmodule MyApp.Syncer do
use GenServer
require Logger
alias :ets, as: Ets
alias MyApp.Repo
def load_data(loading) do
if loading do
{:ok, _} = Task.start(fn ->
fetch_data() |> Repo.save()
:ok = Phoenix.PubSub.broadcast_from!(
MyApp.PubSub,
self(),
"node_up",
{:sync, Repo.all(), node()}
)
end)
end
:ok
end
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
@impl true
def init(opts) do
:users = Ets.new(:users, [:set, :public, :named_table, keypos: 1])
Logger.info("ETS table started...")
:ok = Phoenix.PubSub.subscribe(MyApp.PubSub, "new")
:ok = Phoenix.PubSub.subscribe(MyApp.PubSub, "node_up")
:ok = if (opts[:preload]),
do: load_data(true)
{:ok, nil}
end
@impl true
def handle_info({:new, user}, _state) do
Repo.save(user)
{:noreply, nil}
end
@impl true
def handle_info({:sync, table, node, message}, _state) do
if node != node(), do: Repo.save(table)
{:noreply, nil}
end
end
구성에 대한 설명
(이 순서대로) 다음과 같이 감독됩니다.
{Phoenix.PubSub, name: MyApp.PubSub, adapter: Phoenix.PubSub.PG2},
MyApp.NodeListener,
{MyApp.Syncer, [preload: true]},
구성은 다음과 같습니다.
# config.exs
config :my_app, MyAppWeb.Endpoint,
[...],
pubsub_server: MyApp.PubSub,
Reference
이 문제에 관하여(클러스터에 ETS 데이터베이스 배포), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/ndrean/distribute-an-ets-database-in-a-cluster-aop텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)