diff --git a/CODE_LOGIC_OVERVIEW.md b/CODE_LOGIC_OVERVIEW.md index c19c0a9..f76d186 100644 --- a/CODE_LOGIC_OVERVIEW.md +++ b/CODE_LOGIC_OVERVIEW.md @@ -34,14 +34,14 @@ WebSocket server: - 从 `config/sys.config` 读取 `efka.websocket_server`。 - 监听 `/ws`。 -- 请求处理模块是 `service_channel`。 +- 请求处理模块是 `efka_service_channel`。 `efka_sup` 启动的子进程: - `efka_logger`:部署日志落盘。 - `efka_service_sup`:动态管理每个已注册微服务对应的 `efka_service` 进程。 - `cache_model`:DETS 离线缓存。 -- `service_model`:DETS 服务状态表。 +- `efka_service_model`:DETS 服务状态表。 - `efka_subscription`:本地 topic 订阅中心。 - `efka_client`:连接上游 TLS server 的状态机。 @@ -60,12 +60,12 @@ WebSocket server: 本机微服务连接 `ws://:18080/ws`。 -协议处理在 `service_channel`: +协议处理在 `efka_service_channel`: - WebSocket 收到 `ping` 直接回复 `pong`。 - 收到 binary 帧时,第一个字节是帧类型。 -- `FRAME_REQUEST = 0x01`,用 `service_pb` 解码为 `ServiceRequest`。 -- `FRAME_CAST = 0x03`,用 `service_pb` 解码为 `ServiceCast`。 +- `FRAME_REQUEST = 0x01`,用 `efka_service_pb` 解码为 `ServiceRequest`。 +- `FRAME_CAST = 0x03`,用 `efka_service_pb` 解码为 `ServiceCast`。 - 回复时使用 `FRAME_REPLY = 0x02`。 支持的微服务请求: @@ -80,17 +80,17 @@ WebSocket server: 注册流程: 1. 微服务发送 `ServiceRequest.Register`,包含 `service_id`。 -2. `service_channel` 调用 `efka_service_sup:start_service(ServiceId)`。 +2. `efka_service_channel` 调用 `efka_service_sup:start_service(ServiceId)`。 3. 如果该服务进程不存在,则动态启动一个 `efka_service`;如果已存在,复用已有 pid。 -4. `service_channel` 调用 `efka_service:attach_channel(ServicePid, self())` 绑定当前 WebSocket channel。 +4. `efka_service_channel` 调用 `efka_service:attach_channel(ServicePid, self())` 绑定当前 WebSocket channel。 5. `efka_service` 只允许绑定一个存活 channel;已有 channel 存活时返回 `channel exists`。 -6. 注册成功后写入 `service_model`,服务状态设为 running。 +6. 注册成功后写入 `efka_service_model`,服务状态设为 running。 7. channel 退出时,取消全部订阅,并把服务状态改为 stopped。 指标上报流程: 1. 微服务发送 `ServiceCast.MetricData`。 -2. `service_channel` 调用 `efka_service:metric_data(ServicePid, RouteKey, Metric)`。 +2. `efka_service_channel` 调用 `efka_service:metric_data(ServicePid, RouteKey, Metric)`。 3. `efka_service` 转发给 `efka_client:metric_data(RouteKey, Metric)`。 4. `efka_client` 如果处于 activated 状态,直接发给上游;否则写入 `cache_model` 离线缓存。 @@ -120,7 +120,7 @@ WebSocket server: - `FRAME_REPLY = 0x02` - `FRAME_CAST = 0x03` -但 protobuf 使用的是 `message_pb`,不是微服务侧的 `service_pb`。 +但 protobuf 使用的是 `message_pb`,不是微服务侧的 `efka_service_pb`。 `efka_client` 上报的内容: @@ -140,7 +140,7 @@ WebSocket server: 订阅: -- `service_channel` 收到微服务 `subscribe` 后调用 `efka_subscription:subscribe(Topic, self())`。 +- `efka_service_channel` 收到微服务 `subscribe` 后调用 `efka_subscription:subscribe(Topic, self())`。 - 同一个 pid 对同一个 topic 重复订阅会被忽略。 - 首次看到某个 subscriber pid 时,会 monitor 该 pid;pid 退出后自动移除订阅。 @@ -154,8 +154,8 @@ topic 匹配规则: 发布: - 上游发来的 `CastFrame.Pub` 会调用 `efka_subscription:publish(Topic, Qos, Content)`。 -- 如果匹配到订阅者,向对应 `service_channel` 发送 `{topic_broadcast, Topic, Content}`。 -- `service_channel` 再编码为 `ServiceCast.TopicEvent` 推送给微服务。 +- 如果匹配到订阅者,向对应 `efka_service_channel` 发送 `{topic_broadcast, Topic, Content}`。 +- `efka_service_channel` 再编码为 `ServiceCast.TopicEvent` 推送给微服务。 - 如果没有订阅者且 `Qos != 0`,消息会暂存在 `remaining_messages`。 - 新订阅建立时,会尝试把匹配的 remaining messages 补发给该订阅者。 @@ -215,7 +215,7 @@ topic 匹配规则: ## 6. 本地持久化 -### 6.1 `service_model` +### 6.1 `efka_service_model` 使用 DETS 表 `service`。 @@ -272,13 +272,13 @@ topic 匹配规则: 注意: -- `service_model` 和 `cache_model` 打开 DETS 前假设 `dets_dir` 已存在,代码里没有显式创建目录。 +- `efka_service_model` 和 `cache_model` 打开 DETS 前假设 `dets_dir` 已存在,代码里没有显式创建目录。 - `docker_client` 固定使用 `/var/run/docker.sock`,每次请求内部由短生命周期普通进程执行 open/request/close。 ## 9. 当前看到的几个注意点 - `README.md` 描述的是 JSON-RPC 风格 WebSocket,但当前代码实际处理的是 binary protobuf 帧;README 可能已经过期。 -- `service_channel` 里 `register` 只使用 `service_id`,没有使用 README 中提到的 `meta_data/container_name`。 +- `efka_service_channel` 里 `register` 只使用 `service_id`,没有使用 README 中提到的 `meta_data/container_name`。 - `efka_client:send_result_reply/3` 和 `send_error_reply/3` 编码 `ReplyFrame` 后没有加 `FRAME_REPLY` 前缀;接收侧是否期望裸 protobuf 需要确认。 - `docker_deployer:ensure_container_absent/2` 当前没有真正确保旧容器不存在,只是上报日志。 - `efka_subscription` 计算了 topic `order`,但匹配广播时没有使用优先级排序。 diff --git a/apps/efka/include/service_pb.hrl b/apps/efka/include/efka_service_pb.hrl similarity index 79% rename from apps/efka/include/service_pb.hrl rename to apps/efka/include/efka_service_pb.hrl index ab20c18..af290d0 100644 --- a/apps/efka/include/service_pb.hrl +++ b/apps/efka/include/efka_service_pb.hrl @@ -2,10 +2,10 @@ %% Automatically generated, do not edit %% Generated by gpb_compile version 4.21.7 --ifndef(service_pb). --define(service_pb, true). +-ifndef(efka_service_pb). +-define(efka_service_pb, true). --define(service_pb_gpb_version, "4.21.7"). +-define(efka_service_pb_gpb_version, "4.21.7"). -ifndef('SERVICEREQUEST.REGISTER_PB_H'). @@ -26,7 +26,7 @@ -define('SERVICEREQUEST_PB_H', true). -record('ServiceRequest', {packet_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits - request :: {register, service_pb:'ServiceRequest.Register'()} | {subscribe, service_pb:'ServiceRequest.Subscribe'()} | undefined % oneof + request :: {register, efka_service_pb:'ServiceRequest.Register'()} | {subscribe, efka_service_pb:'ServiceRequest.Subscribe'()} | undefined % oneof }). -endif. @@ -42,7 +42,7 @@ -define('SERVICEREPLY_PB_H', true). -record('ServiceReply', {packet_id = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits - reply :: {result, iodata()} | {error, service_pb:'ServiceReply.Error'()} | undefined % oneof + reply :: {result, iodata()} | {error, efka_service_pb:'ServiceReply.Error'()} | undefined % oneof }). -endif. @@ -65,7 +65,7 @@ -ifndef('SERVICECAST_PB_H'). -define('SERVICECAST_PB_H', true). -record('ServiceCast', - {body :: {topic_event, service_pb:'ServiceCast.TopicEvent'()} | {metric_data, service_pb:'ServiceCast.MetricData'()} | undefined % oneof + {body :: {topic_event, efka_service_pb:'ServiceCast.TopicEvent'()} | {metric_data, efka_service_pb:'ServiceCast.MetricData'()} | undefined % oneof }). -endif. diff --git a/apps/efka/rebar.config b/apps/efka/rebar.config index 12ede5a..599df9c 100644 --- a/apps/efka/rebar.config +++ b/apps/efka/rebar.config @@ -4,6 +4,7 @@ {i, "proto"}, {f, ["service.proto"]}, recursive, + {module_name_prefix, "efka_"}, {module_name_suffix, "_pb"}, {o_erl, "src/protobuf"}, {o_hrl, "include"}, diff --git a/apps/efka/src/efka_app.erl b/apps/efka/src/efka_app.erl index 41d51b2..cc6f27d 100644 --- a/apps/efka/src/efka_app.erl +++ b/apps/efka/src/efka_app.erl @@ -32,7 +32,7 @@ start_websocket_server() -> Port = proplists:get_value(port, Props), Dispatcher = cowboy_router:compile([ - {'_', [{"/ws", service_channel, []}]} + {'_', [{"/ws", efka_service_channel, []}]} ]), TransOpts = [ diff --git a/apps/efka/src/efka_sup.erl b/apps/efka/src/efka_sup.erl index 76186ea..fa59295 100644 --- a/apps/efka/src/efka_sup.erl +++ b/apps/efka/src/efka_sup.erl @@ -49,12 +49,12 @@ init([]) -> }, #{ - id => service_model, - start => {service_model, start_link, []}, + id => efka_service_model, + start => {efka_service_model, start_link, []}, restart => permanent, shutdown => 5000, type => worker, - modules => ['service_model'] + modules => ['efka_service_model'] }, #{ diff --git a/apps/efka/src/protobuf/service_pb.erl b/apps/efka/src/protobuf/efka_service_pb.erl similarity index 99% rename from apps/efka/src/protobuf/service_pb.erl rename to apps/efka/src/protobuf/efka_service_pb.erl index 9d4b763..e08b796 100644 --- a/apps/efka/src/protobuf/service_pb.erl +++ b/apps/efka/src/protobuf/efka_service_pb.erl @@ -3,7 +3,7 @@ %% Automatically @generated, do not edit %% Generated by gpb_compile version 4.21.7 %% Version source: file --module(service_pb). +-module(efka_service_pb). -export([encode_msg/1, encode_msg/2, encode_msg/3]). -export([decode_msg/2, decode_msg/3]). @@ -46,7 +46,7 @@ -export([gpb_version_as_string/0, gpb_version_as_list/0]). -export([gpb_version_source/0]). --include("service_pb.hrl"). +-include("efka_service_pb.hrl"). -include_lib("gpb/include/gpb.hrl"). %% enumerated types diff --git a/apps/efka/src/service/service_channel.erl b/apps/efka/src/service/efka_service_channel.erl similarity index 78% rename from apps/efka/src/service/service_channel.erl rename to apps/efka/src/service/efka_service_channel.erl index cb18f8e..e5e118f 100644 --- a/apps/efka/src/service/service_channel.erl +++ b/apps/efka/src/service/efka_service_channel.erl @@ -6,10 +6,10 @@ %%% @end %%% Created : 11. 1月 2021 上午12:17 %%%------------------------------------------------------------------- --module(service_channel). +-module(efka_service_channel). -author("licheng5"). -include("efka_tables.hrl"). --include("service_pb.hrl"). +-include("efka_service_pb.hrl"). %% 一级帧类型 %% REQUEST: 需要响应的请求帧 @@ -40,7 +40,7 @@ init(Req, Opts) -> -spec websocket_init(term()) -> {ok, #state{}}. websocket_init(_State) -> - logger:debug("[service_channel] get a new connection"), + logger:debug("[efka_service_channel] get a new connection"), %% 初始状态为true {ok, #state{}}. @@ -50,41 +50,41 @@ websocket_handle(ping, State) -> {reply, pong, State}; websocket_handle({binary, <>}, State) -> - Request = service_pb:decode_msg(PacketBin, 'ServiceRequest'), - logger:debug("[service_channel] get request: ~p", [Request]), + Request = efka_service_pb:decode_msg(PacketBin, 'ServiceRequest'), + logger:debug("[efka_service_channel] get request: ~p", [Request]), handle_request(Request, State); websocket_handle({binary, <>}, State) -> - Cast = service_pb:decode_msg(PacketBin, 'ServiceCast'), - logger:debug("[service_channel] get cast: ~p", [Cast]), + Cast = efka_service_pb:decode_msg(PacketBin, 'ServiceCast'), + logger:debug("[efka_service_channel] get cast: ~p", [Cast]), handle_cast(Cast, State); websocket_handle(Info, State) -> - logger:error("[service_channel] get a unknown message: ~p, channel will closed", [Info]), + logger:error("[efka_service_channel] get a unknown message: ~p, channel will closed", [Info]), {ok, State}. %% 订阅的消息 -spec websocket_info(term(), #state{}) -> {reply, term(), #state{}} | {stop, #state{}} | {ok, #state{}}. websocket_info({topic_broadcast, Topic, Content}, State = #state{}) -> - Packet = service_pb:encode_msg(#'ServiceCast'{ + Packet = efka_service_pb:encode_msg(#'ServiceCast'{ body = {topic_event, #'ServiceCast.TopicEvent'{topic = Topic, content = Content}} }), - logger:debug("[service_channel] will publish topic: ~p", [Topic]), + logger:debug("[efka_service_channel] will publish topic: ~p", [Topic]), {reply, {binary, <>}, State}; %% service进程关闭 websocket_info({'DOWN', _Ref, process, ServicePid, Reason}, State = #state{service_pid = ServicePid}) -> - logger:debug("[service_channel] container_pid: ~p, exited: ~p", [ServicePid, Reason]), + logger:debug("[efka_service_channel] container_pid: ~p, exited: ~p", [ServicePid, Reason]), {stop, State#state{service_pid = undefined}}; %% 处理关闭信号 websocket_info({stop, Reason}, State) -> - logger:debug("[service_channel] the channel will be closed with reason: ~p", [Reason]), + logger:debug("[efka_service_channel] the channel will be closed with reason: ~p", [Reason]), {stop, State}; %% 处理其他未知消息 websocket_info(Info, State) -> - logger:debug("[service_channel] channel get unknown info: ~p", [Info]), + logger:debug("[efka_service_channel] channel get unknown info: ~p", [Info]), {ok, State}. %% 进程关闭事件 @@ -93,11 +93,11 @@ terminate(Reason, _Req, State = #state{service_id = ServiceId, is_registered = I ok = efka_subscription:unsubscribe_all(self()), case IsRegistered of true -> - ok = service_model:change_status(ServiceId, 0); + ok = efka_service_model:change_status(ServiceId, 0); false -> ok end, - logger:debug("[service_channel] channel close with reason: ~p, state is: ~p", [Reason, State]), + logger:debug("[efka_service_channel] channel close with reason: ~p, state is: ~p", [Reason, State]), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -105,7 +105,7 @@ terminate(Reason, _Req, State = #state{service_id = ServiceId, is_registered = I %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% 注册, 要建立程序和容器之间的关系 --spec handle_request(service_pb:'ServiceRequest'(), #state{}) -> {reply, {binary, binary()}, #state{}}. +-spec handle_request(efka_service_pb:'ServiceRequest'(), #state{}) -> {reply, {binary, binary()}, #state{}}. handle_request(#'ServiceRequest'{packet_id = PacketId, request = {register, #'ServiceRequest.Register'{service_id = ServiceId}}}, State) -> {ok, ServicePid} = efka_service_sup:start_service(ServiceId), case efka_service:attach_channel(ServicePid, self()) of @@ -113,7 +113,7 @@ handle_request(#'ServiceRequest'{packet_id = PacketId, request = {register, #'Se erlang:monitor(process, ServicePid), %% 更新微服务的状态 - ok = service_model:insert(#service{ + ok = efka_service_model:insert(#service{ service_id = ServiceId, container_name = <<>>, status = ?SERVICE_RUNNING, @@ -125,7 +125,7 @@ handle_request(#'ServiceRequest'{packet_id = PacketId, request = {register, #'Se {reply, {binary, result_reply_packet(PacketId, <<"ok">>)}, State#state{service_id = ServiceId, service_pid = ServicePid, is_registered = true}}; {error, Error} -> - logger:warning("[service_channel] service_id: ~p, attach_channel get error: ~p", [ServiceId, Error]), + logger:warning("[efka_service_channel] service_id: ~p, attach_channel get error: ~p", [ServiceId, Error]), {reply, {binary, error_reply_packet(PacketId, -1, <<"attach channel failed">>)}, State} end; @@ -144,7 +144,7 @@ handle_request(#'ServiceRequest'{packet_id = PacketId, request = {subscribe, #'S handle_request(#'ServiceRequest'{packet_id = PacketId}, State) -> {reply, {binary, error_reply_packet(PacketId, -1, <<"invalid request">>)}, State}. --spec handle_cast(service_pb:'ServiceCast'(), #state{}) -> {ok, #state{}}. +-spec handle_cast(efka_service_pb:'ServiceCast'(), #state{}) -> {ok, #state{}}. handle_cast(#'ServiceCast'{body = {metric_data, #'ServiceCast.MetricData'{route_key = RouteKey, metric = Metric}}}, State = #state{service_pid = ServicePid, is_registered = true}) -> efka_service:metric_data(ServicePid, RouteKey, Metric), @@ -154,7 +154,7 @@ handle_cast(#'ServiceCast'{body = _Body}, State) -> -spec result_reply_packet(integer(), binary()) -> binary(). result_reply_packet(PacketId, Result) when is_integer(PacketId), is_binary(Result) -> - Reply = service_pb:encode_msg(#'ServiceReply'{ + Reply = efka_service_pb:encode_msg(#'ServiceReply'{ packet_id = PacketId, reply = {result, Result} }), @@ -162,7 +162,7 @@ result_reply_packet(PacketId, Result) when is_integer(PacketId), is_binary(Resul -spec error_reply_packet(integer(), integer(), binary()) -> binary(). error_reply_packet(PacketId, Code, Message) when is_integer(PacketId), is_integer(Code), is_binary(Message) -> - Reply = service_pb:encode_msg(#'ServiceReply'{ + Reply = efka_service_pb:encode_msg(#'ServiceReply'{ packet_id = PacketId, reply = {error, #'ServiceReply.Error'{code = Code, message = Message}} }), diff --git a/apps/efka/src/service/service_model.erl b/apps/efka/src/service/efka_service_model.erl similarity index 99% rename from apps/efka/src/service/service_model.erl rename to apps/efka/src/service/efka_service_model.erl index 3c17347..bc67db0 100644 --- a/apps/efka/src/service/service_model.erl +++ b/apps/efka/src/service/efka_service_model.erl @@ -6,7 +6,7 @@ %%% @end %%% Created : 13. 8月 2025 16:41 %%%------------------------------------------------------------------- --module(service_model). +-module(efka_service_model). -author("anlicheng"). -include("efka_tables.hrl"). diff --git a/docs/service_websocket_protocol.md b/docs/service_websocket_protocol.md index fde9952..5e8459b 100644 --- a/docs/service_websocket_protocol.md +++ b/docs/service_websocket_protocol.md @@ -2,7 +2,7 @@ 本文档描述部署在 `efka` 主机上的 service 与 `efka` 之间的 WebSocket 通讯逻辑。当前接入实现位于: -- [service_channel.erl](/usr/local/code/cloudkit/efka/apps/efka/src/service/service_channel.erl) +- [efka_service_channel.erl](/usr/local/code/cloudkit/efka/apps/efka/src/service/efka_service_channel.erl) - [service.proto](/usr/local/code/cloudkit/efka/apps/efka/proto/service.proto) - [efka_subscription.erl](/usr/local/code/cloudkit/efka/apps/efka/src/service/efka_subscription.erl) - [efka_service.erl](/usr/local/code/cloudkit/efka/apps/efka/src/service/efka_service.erl) @@ -26,11 +26,11 @@ WebSocket 路由固定为: GET /ws ``` -连接建立后,每个 WebSocket 连接对应一个 `service_channel` 进程。 +连接建立后,每个 WebSocket 连接对应一个 `efka_service_channel` 进程。 当前协议没有独立的认证字段。service 必须先发送 `register` request 完成注册;未注册前只能处理 `register`,其它 request 会返回 `invalid request`,cast 会被忽略。 -Cowboy WebSocket ping 帧由 `service_channel` 直接回复 pong: +Cowboy WebSocket ping 帧由 `efka_service_channel` 直接回复 pong: ```erlang websocket_handle(ping, State) -> {reply, pong, State} @@ -52,7 +52,7 @@ service 与 `efka` 之间使用 WebSocket binary frame。每个业务 frame 的 | `REPLY` | `0x02` | efka -> service | request 的响应 | `ServiceReply` | | `CAST` | `0x03` | 双向 | 单向消息,不要求回复 | `ServiceCast` | -`service_channel` 当前只处理 service 发来的 `REQUEST` 和 `CAST`。收到未知格式时只记录日志,不主动发送错误响应。 +`efka_service_channel` 当前只处理 service 发来的 `REQUEST` 和 `CAST`。收到未知格式时只记录日志,不主动发送错误响应。 ## 3. Protobuf 定义 @@ -142,10 +142,10 @@ WebSocket binary frame: `efka` 处理逻辑: -1. `service_channel` 解码 `ServiceRequest`。 +1. `efka_service_channel` 解码 `ServiceRequest`。 2. 调用 `efka_service_sup:start_service(ServiceId)` 启动或复用 service 进程。 3. 调用 `efka_service:attach_channel(ServicePid, ChannelPid)` 绑定当前 WebSocket channel。 -4. 通过 `service_model:insert/1` 写入 service 记录: +4. 通过 `efka_service_model:insert/1` 写入 service 记录: ```erlang #service{ @@ -158,9 +158,9 @@ WebSocket binary frame: } ``` -如果该 `service_id` 是新记录,`status` 会写入 `?SERVICE_RUNNING`。如果该 `service_id` 已存在,当前 `service_model:insert/1` 只更新 `meta_data`、`container_name` 和 `update_ts`,不会覆盖已有 `status`。 +如果该 `service_id` 是新记录,`status` 会写入 `?SERVICE_RUNNING`。如果该 `service_id` 已存在,当前 `efka_service_model:insert/1` 只更新 `meta_data`、`container_name` 和 `update_ts`,不会覆盖已有 `status`。 -5. `service_channel` 状态更新为: +5. `efka_service_channel` 状态更新为: ```erlang #state{ @@ -219,7 +219,7 @@ ServiceRequest { 1. 要求当前 channel 已注册,即 `is_registered = true`。 2. 调用 `efka_subscription:subscribe(Topic, ChannelPid)`。 -3. 订阅成功后,把 topic 加入 `service_channel` 的 `subscribed_topics` 集合。 +3. 订阅成功后,把 topic 加入 `efka_service_channel` 的 `subscribed_topics` 集合。 成功响应: @@ -277,7 +277,7 @@ WebSocket binary frame: `efka` 处理逻辑: -1. `service_channel` 解码 `ServiceCast`。 +1. `efka_service_channel` 解码 `ServiceCast`。 2. 要求当前 channel 已注册。 3. 调用: @@ -310,13 +310,13 @@ efka_client:metric_data(RouteKey, Metric) efka_subscription:publish(Topic, Qos, Content) ``` -`efka_subscription` 根据 topic 匹配已订阅的 `service_channel`。匹配成功后向 channel 进程发送: +`efka_subscription` 根据 topic 匹配已订阅的 `efka_service_channel`。匹配成功后向 channel 进程发送: ```erlang {topic_broadcast, Topic, Content} ``` -`service_channel` 再推送 WebSocket `CAST` 给 service: +`efka_service_channel` 再推送 WebSocket `CAST` 给 service: ```protobuf ServiceCast { @@ -361,7 +361,7 @@ binary:split(Topic, <<$/>>, [global]) | `device/+` | `device/a/temp` | 是 | | `device/+` | `device` | 否 | -同一个 `service_channel` 订阅多个 topic 时,同一次 publish 最多投递一次。 +同一个 `efka_service_channel` 订阅多个 topic 时,同一次 publish 最多投递一次。 ## 7. QoS 与遗留消息 @@ -378,20 +378,20 @@ binary:split(Topic, <<$/>>, [global]) ### WebSocket channel 关闭 -`service_channel:terminate/3` 会执行: +`efka_service_channel:terminate/3` 会执行: 1. 调用 `efka_subscription:unsubscribe_all(ChannelPid)` 清理该 channel 的所有订阅。 2. 如果 channel 已完成 register,则调用: ```erlang -service_model:change_status(ServiceId, ?SERVICE_STOPPED) +efka_service_model:change_status(ServiceId, ?SERVICE_STOPPED) ``` 也就是把 service 状态更新为停止。 ### service 进程退出 -`service_channel` monitor 绑定的 `efka_service` 进程。如果 service 进程退出,channel 会停止: +`efka_service_channel` monitor 绑定的 `efka_service` 进程。如果 service 进程退出,channel 会停止: ```erlang websocket_info({'DOWN', _Ref, process, ServicePid, Reason}, State) -> @@ -405,7 +405,7 @@ websocket_info({'DOWN', _Ref, process, ServicePid, Reason}, State) -> 典型流程: ```text -service efka/service_channel efka_service efka_subscription +service efka/efka_service_channel efka_service efka_subscription | | | | |-- WebSocket connect /ws ------------>| | | | | | | @@ -435,5 +435,5 @@ service efka/service_channel efka_service - `register` 可重复连接同一个 `service_id`,但同一时间只允许一个活跃 channel 绑定到 `efka_service`。 - request 只支持 `register` 和 `subscribe`。 - cast 只支持 `metric_data` 和 `topic_event`。 -- `subscribed_topics` 目前只在 `service_channel` 内记录,关闭时实际清理由 `efka_subscription:unsubscribe_all/1` 完成。 +- `subscribed_topics` 目前只在 `efka_service_channel` 内记录,关闭时实际清理由 `efka_subscription:unsubscribe_all/1` 完成。 - `efka_subscription` 当前基于列表扫描匹配,订阅规模很大时需要后续优化为索引结构。 diff --git a/docs/todo.md b/docs/todo.md index 27144f5..ea31c7a 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -36,7 +36,7 @@ ### 中优先级 - 继续收敛 `efka_client` 的职责,将传输层状态管理与 request/cast 协议处理进一步拆开。 -- 评估并落实 `service_channel` 中 `subscribed_topics` 集合的用途;如果只是被动保存状态,则应简化。 +- 评估并落实 `efka_service_channel` 中 `subscribed_topics` 集合的用途;如果只是被动保存状态,则应简化。 ### 低优先级