diff --git a/.dockerignore b/.dockerignore
index 698df1e447..7a2789c185 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,9 +1,15 @@
+# Build outputs — regenerated inside the image, never shipped from context.
dist/
+dist-connector/
build/
+**/build/
-# VCS
+# VCS and gradle caches (the wrapper is not used; system gradle lives in the
+# builder image).
.git/
.gitignore
+.gradle/
+**/.gradle/
# Non-Java SDKs (not included in Gradle build)
eventmesh-sdks/eventmesh-sdk-rust/
@@ -14,10 +20,12 @@ eventmesh-sdks/eventmesh-sdk-c/
**/target/
**/node_modules/
-# IDE
+# IDE / editor cruft.
.idea/
.vscode/
*.iml
+*.ipr
+*.iws
# Docs & misc
docs/
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d961cb20ae..2f0837464c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -32,7 +32,7 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest, macOS-latest ]
- java: [ 8, 11 ]
+ java: [ 21 ]
language: [ 'java' ]
runs-on: ${{ matrix.os }}
@@ -49,11 +49,11 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e #v6.1.0
- - name: Set up JDK 11
+ - name: Set up JDK 21
uses: actions/setup-java@v5
with:
distribution: 'zulu'
- java-version: 11
+ java-version: 21
- name: GenerateGrammarSource
run: ./gradlew clean generateGrammarSource --parallel --daemon --scan
diff --git a/.github/workflows/code-scanning.yml b/.github/workflows/code-scanning.yml
index efc28fd485..e8dbd9d931 100644
--- a/.github/workflows/code-scanning.yml
+++ b/.github/workflows/code-scanning.yml
@@ -51,12 +51,12 @@ jobs:
# queries: ./path/to/local/query, your-org/your-repo/queries@main
languages: ${{ matrix.language }}
- - name: Set up JDK 11
+ - name: Set up JDK 21
if: matrix.language == 'java'
uses: actions/setup-java@v5
with:
distribution: 'zulu'
- java-version: 11
+ java-version: 21
- name: Setup Gradle
if: matrix.language == 'java'
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index ff8179b011..f388f1d6e8 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -23,6 +23,16 @@ on:
jobs:
docker:
runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ # EventMesh unified runtime (traffic HTTP + admin).
+ - image: apache/eventmesh
+ dockerfile: ./docker/Dockerfile
+ # Connector-runtime (separate process, talks HTTP+CloudEvents to the runtime).
+ - image: apache/eventmesh-connector
+ dockerfile: ./docker/Dockerfile.connector
steps:
- name: Checkout repository
uses: actions/checkout@v6
@@ -37,8 +47,7 @@ jobs:
id: meta
uses: docker/metadata-action@v5
with:
- images: |
- apache/eventmesh
+ images: ${{ matrix.image }}
- name: Build and push
uses: docker/build-push-action@v6
@@ -46,5 +55,5 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- file: ./docker/Dockerfile_jdk8
- context: ./
\ No newline at end of file
+ file: ${{ matrix.dockerfile }}
+ context: ./
diff --git a/.github/workflows/license.yml b/.github/workflows/license.yml
index f9c1507d96..a00755db5a 100644
--- a/.github/workflows/license.yml
+++ b/.github/workflows/license.yml
@@ -31,11 +31,11 @@ jobs:
- name: Check license header
uses: apache/skywalking-eyes@main
- - name: Set up JDK 11
+ - name: Set up JDK 21
uses: actions/setup-java@v5
with:
distribution: 'zulu'
- java-version: 11
+ java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e #v6.1.0
diff --git a/.gitignore b/.gitignore
index 2379b9f175..a3aa6cbf22 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,6 +44,9 @@ x86/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
+# … but keep source service scripts shipped from module bin/ dirs (dist copies them to dist/bin).
+!eventmesh-runtime/bin/
+!eventmesh-connector-runtime/bin/
[Oo]bj/
[Ll]og/
[Ll]ogs/
@@ -51,3 +54,5 @@ bld/
#rust
Cargo.lock
+rocketmq-pull-offsets.properties
+kafka-pull-offsets.properties
diff --git a/README.md b/README.md
index a82a7479f5..45c3256980 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,12 @@
-
+
[](https://github.com/apache/eventmesh/actions/workflows/ci.yml)
[](https://codecov.io/gh/apache/eventmesh)
-[](https://lgtm.com/projects/g/apache/eventmesh/context:java)
-[](https://lgtm.com/projects/g/apache/eventmesh/alerts/)
+[](https://github.com/apache/eventmesh/actions/workflows/code-scanning.yml)
[](https://www.apache.org/licenses/LICENSE-2.0.html)
[](https://github.com/apache/eventmesh/releases)
@@ -27,27 +26,33 @@
### EventMesh Architecture
-
+
+
+EventMesh adopts a unified **CloudEvents-over-MQ** architecture. The message queue (MQ) acts as a pure write-ahead log (WAL) for durable storage only — there are no consumer groups, no tags, and no broker-side subscription semantics. Instead, the stateless EventMesh Runtime owns all delivery logic: its **SubscriptionManager** maintains the subscription registry and offset tracking, and dispatches events with load-balance, broadcast, and multicast semantics. Applications interact through a lightweight **HTTP + CloudEvents 1.0** SDK (`publish` / `subscribe` / `unsubscribe`), while integration with external systems runs in a standalone Connector Runtime via the connector SPI.
## Features
-Apache EventMesh has a vast amount of features to help users achieve their goals. Let us share with you some of the key features EventMesh has to offer:
+Apache EventMesh is packed with features that help users build event-driven applications with ease. Here are the highlights that set EventMesh apart:
+
+**Core architecture**
-- Built around the [CloudEvents](https://cloudevents.io) specification.
-- Rapidty extendsible interconnector layer [connectors](https://github.com/apache/eventmesh/tree/master/eventmesh-connectors) using [openConnect](https://github.com/apache/eventmesh/tree/master/eventmesh-openconnect) such as the source or sink of Saas, CloudService, and Database etc.
-- Rapidty extendsible storage layer such as [Apache RocketMQ](https://rocketmq.apache.org), [Apache Kafka](https://kafka.apache.org), [Apache Pulsar](https://pulsar.apache.org), [RabbitMQ](https://rabbitmq.com), [Redis](https://redis.io).
-- Rapidty extendsible meta such as [Consul](https://consulproject.org/en/), [Nacos](https://nacos.io), [ETCD](https://etcd.io) and [Zookeeper](https://zookeeper.apache.org/).
-- Guaranteed at-least-once delivery.
-- Deliver events between multiple EventMesh deployments.
-- Event schema management by catalog service.
-- Powerful event orchestration by [Serverless workflow](https://serverlessworkflow.io/) engine.
-- Powerful event filtering and transformation.
-- Rapid, seamless scalability.
-- Easy Function develop and framework integration.
+- **CloudEvents-native, end to end** — built entirely around the [CloudEvents](https://cloudevents.io) 1.0 specification, so events stay vendor-neutral and portable.
+- **Lightweight, language-agnostic SDK** — just three operations over plain HTTP (`publish`, `subscribe`, `unsubscribe`); no heavyweight client, no vendor lock-in.
+- **Runtime-owned subscription & dispatch** — subscription state and delivery semantics (load-balance / broadcast / multicast) are managed by EventMesh itself, not the underlying MQ, giving you consistent behavior across any storage backend.
+- **MQ as a pure write-ahead log (WAL)** — append-only, no consumer groups, no tags; the broker is reduced to durable storage, dramatically simplifying operations.
+- **Guaranteed at-least-once delivery** — EventMesh owns reliability through self-managed offsets and explicit ACK.
+- **Multiple delivery transports** — subscribers choose HTTP long-polling, Server-Sent Events (SSE), or WebSocket push, with request-reply support.
+- **Effortless horizontal scaling** — stateless Runtime instances scale out seamlessly with no rebalancing cost.
-## Roadmap
+**Extensibility & ecosystem**
-Please go to the [roadmap](https://eventmesh.apache.org/docs/roadmap) to get the release history and new features of Apache EventMesh.
+- **Agent-to-Agent (A2A) collaboration** — a built-in [A2A protocol](docs/a2a-protocol/README_EN.md) turns EventMesh into an agent collaboration bus, bridging synchronous MCP / JSON-RPC 2.0 tool calls and asynchronous event-driven pub/sub for LLM and multi-agent systems.
+- **Pluggable storage layer** — [Apache RocketMQ](https://rocketmq.apache.org), [Apache Kafka](https://kafka.apache.org), [Apache Pulsar](https://pulsar.apache.org), [RabbitMQ](https://rabbitmq.com), [Redis](https://redis.io), and more.
+- **Pluggable interconnector layer** — [connectors](https://github.com/apache/eventmesh/tree/master/eventmesh-connectors) run as standalone processes acting as the source or sink of SaaS, CloudService, Database, etc.
+- **Pluggable meta service** — [Consul](https://consulproject.org/en/), [Nacos](https://nacos.io), [ETCD](https://etcd.io), and [Zookeeper](https://zookeeper.apache.org/).
+- **Event schema management** via catalog service.
+- **Powerful event orchestration** through the [Serverless workflow](https://serverlessworkflow.io/) engine.
+- **Powerful event filtering and transformation.**
## Subprojects
@@ -76,60 +81,106 @@ sudo docker pull apache/eventmesh:latest
Use the following command to start the EventMesh container:
```shell
-sudo docker run -d --name eventmesh -p 10000:10000 -p 10105:10105 -p 10205:10205 -p 10106:10106 -t apache/eventmesh:latest
+sudo docker run -d --name eventmesh -p 8080:8080 -p 8081:8081 -p 8082:8082 -t apache/eventmesh:latest
```
-#### 3. Creating Topics
-Creating a topic is the first step in using EventMesh. You need to send an HTTP POST request to create a topic.
-Example Request
+
+> Ports: `8080` = traffic HTTP (`/events/*` CloudEvents API), `8081` = admin HTTP (`/admin/*`), `8082` = WebSocket push (opt-in). The connector runtime image exposes `8083` for its optional admin port.
+#### 3. Publishing a CloudEvent
+
+Applications talk to EventMesh over the `/events/*` HTTP endpoints using standard [CloudEvents](https://cloudevents.io) 1.0. Publish an event with an HTTP POST (`202 Accepted` means the event was written to the WAL):
+
```shell
-POST /eventmesh/topic/create HTTP/1.1
-Host: localhost:10105
-Content-Type: application/json
-
-{
- "topic": "example-topic"
+POST /events/publish HTTP/1.1
+Host: localhost:8080
+Content-Type: application/cloudevents+json
+
+{
+ "specversion": "1.0",
+ "id": "89010a5a-3c6f-4a1e-9b2d-0f7c1f2e3a4b",
+ "source": "/example/producer",
+ "type": "com.example.order.created",
+ "subject": "orders",
+ "datacontenttype": "application/json",
+ "data": {
+ "content": "Hello, EventMesh!"
+ }
}
```
-#### 4. Subscribing to Topics
-After creating a topic, you can subscribe to it to receive messages. EventMesh provides two subscription methods: local subscription and remote subscription.
+
+#### 4. Subscribing to a Topic
+
+Subscriptions are registered with EventMesh (there are no consumer groups or tags). Provide a `clientId`, the topic, and a distribution `mode` (`LOAD_BALANCE`, `BROADCAST`, `MULTICAST`, or `LOAD_BALANCE_STICKY`). The response returns a `subscriptionId`:
```shell
-POST /eventmesh/subscribe/local HTTP/1.1
-Host: localhost:10105
-Content-Type: application/json
-{
- "url": "http://localhost:8080/callback",
- "consumerGroup": "example-consumer-group",
- "topic": [
- {
- "topic": "example-topic",
- "mode": "CLUSTERING",
- "type": "SYNC"
- }
- ]
+POST /events/subscribe HTTP/1.1
+Host: localhost:8080
+Content-Type: application/json
+
+{
+ "clientId": "order-svc",
+ "topic": "orders",
+ "mode": "LOAD_BALANCE"
}
```
-#### 5. Sending Messages
-EventMesh provides multiple message sending methods, including asynchronous sending, synchronous sending, and batch sending.
+
+#### 5. Receiving Events
+
+Subscribers can receive dispatched events through three transports. Pick whichever fits your workload — all of them deliver the same CloudEvents and honor EventMesh's ACK / at-least-once semantics.
+
+**a) HTTP Long-Polling** — pull events; the request blocks until events arrive or the timeout elapses:
+
```shell
-POST /eventmesh/publish HTTP/1.1
-Host: localhost:10105
-Content-Type: application/json
-eventmesh-message-topic: example-topic
-{
- "content": "Hello, EventMesh!"
+GET /events/poll?clientId=order-svc&topics=orders&timeout=30000 HTTP/1.1
+Host: localhost:8080
+```
+
+After processing the delivered events, acknowledge them so the offset advances (at-least-once delivery):
+
+```shell
+POST /events/ack HTTP/1.1
+Host: localhost:8080
+Content-Type: application/json
+
+{
+ "subId": "sub-123",
+ "clientId": "order-svc",
+ "topic": "orders",
+ "partition": 0,
+ "offset": 42
}
```
+
+**b) Server-Sent Events (SSE)** — server push over a long-lived HTTP connection; the client does not poll:
+
+```shell
+GET /events/stream?clientId=order-svc&topics=orders HTTP/1.1
+Host: localhost:8080
+Accept: text/event-stream
+```
+
+**c) WebSocket** — full-duplex server push over a dedicated WebSocket port:
+
+```shell
+GET /events/stream HTTP/1.1
+Host: localhost:8082
+Upgrade: websocket
+Connection: Upgrade
+```
+
+> Long-polling, SSE, and WebSocket are interchangeable delivery transports — a subscriber chooses one. SSE and WebSocket are pushed by the server (no polling loop), while long-polling is client-driven. Request-reply (`POST /events/request` + `POST /events/reply`) is also supported. The `CloudEventsClient` Java SDK wraps all of these (`subscribe` / `subscribeSse` / `subscribeWs`); see the [CloudEvents client guide](docs/eventmesh-cloudevents-client-guide.md).
+
#### 6. Unsubscribing
-When you no longer need to receive messages for a topic, you can unsubscribe.
+
+When you no longer need to receive events for a topic, unsubscribe by `clientId` (optionally with the `subscriptionId`):
+
```shell
-POST /eventmesh/unsubscribe/local HTTP/1.1
-Host: localhost:10105
-Content-Type: application/json
-{
- "url": "http://localhost:8080/callback",
- "consumerGroup": "example-consumer-group",
- "topics": ["example-topic"]
+POST /events/unsubscribe HTTP/1.1
+Host: localhost:8080
+Content-Type: application/json
+
+{
+ "clientId": "order-svc",
+ "topic": "orders"
}
```
## Contributing
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 9aebab3bf7..cdb7a0b46d 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -2,13 +2,12 @@
-
+
[](https://github.com/apache/eventmesh/actions/workflows/ci.yml)
[](https://codecov.io/gh/apache/eventmesh)
-[](https://lgtm.com/projects/g/apache/eventmesh/context:java)
-[](https://lgtm.com/projects/g/apache/eventmesh/alerts/)
+[](https://github.com/apache/eventmesh/actions/workflows/code-scanning.yml)
[](https://www.apache.org/licenses/LICENSE-2.0.html)
[](https://github.com/apache/eventmesh/releases)
@@ -27,28 +26,34 @@
### EventMesh 架构
-
+
+
+EventMesh 采用统一的 **CloudEvents-over-MQ** 架构。消息队列(MQ)仅作为持久化的预写日志(WAL)存储层——没有消费者组(consumer group)、没有 Tag,也不承担 Broker 端的订阅语义。所有投递逻辑由无状态的 EventMesh Runtime 掌控:其中的 **SubscriptionManager** 负责维护订阅注册表与位点(offset)跟踪,并以负载均衡(load-balance)、广播(broadcast)、多播(multicast)等语义分发事件。应用通过轻量的 **HTTP + CloudEvents 1.0** SDK(`publish` / `subscribe` / `unsubscribe`)接入,与外部系统的集成则通过 connector SPI 在独立进程的 Connector Runtime 中运行。
## 特性
-Apache EventMesh提供了许多功能来帮助用户实现他们的目标,以下是一些EventMesh的关键特点:
+Apache EventMesh 提供了丰富的能力,帮助用户轻松构建事件驱动应用。以下是让 EventMesh 与众不同的核心亮点:
+
+**核心架构**
-- 基于 [CloudEvents](https://cloudevents.io) 规范构建。
-- 快速可扩展的Connector,[connectors](https://github.com/apache/eventmesh/tree/master/eventmesh-connectors),例如作为Saas、CloudService和数据库等的source 或sink。.
-- 快速可扩展的存储层,使用 [JDBC](https://en.wikipedia.org/wiki/Java_Database_Connectivity)和[Apache RocketMQ](https://rocketmq.apache.org), [Apache Kafka](https://kafka.apache.org), [Apache Pulsar](https://pulsar.apache.org), [RabbitMQ](https://rabbitmq.com), [Redis](https://redis.io), [Pravega](https://cncf.pravega.io), 和 [RDMS](https://en.wikipedia.org/wiki/Relational_database)(正在进行中)集成。
-- 快速可扩展的控制器,例如 [Consul](https://consulproject.org/en/), [Nacos](https://nacos.io), [ETCD](https://etcd.io) 和 [Zookeeper](https://zookeeper.apache.org/)。
-- 至少一次的可靠性投递。
-- 在多个EventMesh部署之间传递事件。
-- 通过目录服务进行事件模式管理。
-- 通过 [Serverless workflow](https://serverlessworkflow.io/) 引擎实现强大的事件编排。
-- 强大的事件过滤和转换功能。
-- 快速、无缝的可扩展性。
-- 易于函数开发和框架集成。
+- **全链路 CloudEvents 原生** —— 完全基于 [CloudEvents](https://cloudevents.io) 1.0 规范构建,事件天然厂商中立、可移植。
+- **轻量、语言无关的 SDK** —— 纯 HTTP 上仅需 `publish`、`subscribe`、`unsubscribe` 三个操作,无重客户端、无厂商锁定。
+- **订阅与分发由 Runtime 掌控** —— 订阅状态与投递语义(负载均衡 / 广播 / 多播)由 EventMesh 自主管理,而非底层 MQ,无论后端是哪种存储,行为都保持一致。
+- **MQ 仅作纯预写日志(WAL)** —— 仅追加写入,无消费者组、无 Tag;Broker 退化为纯持久化存储,大幅简化运维。
+- **保证至少一次(at-least-once)投递** —— 可靠性由 EventMesh 通过自管 offset + 显式 ACK 掌控。
+- **多种下发通道** —— 订阅者可自由选择 HTTP 长轮询、Server-Sent Events(SSE)或 WebSocket 推送,并支持请求-应答(request-reply)。
+- **轻松水平扩展** —— 无状态 Runtime 实例可无缝扩容,无重平衡成本。
-## 路线图
+**扩展性与生态**
-请前往[路线图](https://eventmesh.apache.org/docs/roadmap)查看Apache EventMesh的版本历史和新功能。.
+- **智能体协作(A2A)** —— 内置的 [A2A 协议](docs/a2a-protocol/README.md) 将 EventMesh 打造成智能体协作总线,打通同步的 MCP / JSON-RPC 2.0 工具调用与异步的事件驱动发布订阅,原生支撑大模型(LLM)与多智能体(Multi-Agent)场景。
+- **可插拔存储层** —— [Apache RocketMQ](https://rocketmq.apache.org)、[Apache Kafka](https://kafka.apache.org)、[Apache Pulsar](https://pulsar.apache.org)、[RabbitMQ](https://rabbitmq.com)、[Redis](https://redis.io) 等。
+- **可插拔互联层(Connector)** —— [connectors](https://github.com/apache/eventmesh/tree/master/eventmesh-connectors) 作为独立进程运行,可充当 SaaS、CloudService、数据库等的 source 或 sink。
+- **可插拔元数据服务** —— [Consul](https://consulproject.org/en/)、[Nacos](https://nacos.io)、[ETCD](https://etcd.io) 和 [Zookeeper](https://zookeeper.apache.org/)。
+- **事件模式管理** —— 通过目录(catalog)服务实现。
+- **强大的事件编排** —— 基于 [Serverless workflow](https://serverlessworkflow.io/) 引擎。
+- **强大的事件过滤与转换能力。**
## 子项目
@@ -119,9 +124,11 @@ sudo docker pull apache/eventmesh:latest
使用以下命令启动 EventMesh 容器:
```shell
-sudo docker run -d --name eventmesh -p 10000:10000 -p 10105:10105 -p 10205:10205 -p 10106:10106 -t apache/eventmesh:latest
+sudo docker run -d --name eventmesh -p 8080:8080 -p 8081:8081 -p 8082:8082 -t apache/eventmesh:latest
```
+> 端口说明:`8080` = 流量 HTTP(`/events/*` CloudEvents API),`8081` = 管理 HTTP(`/admin/*`),`8082` = WebSocket 推送(按需开启)。Connector 运行时镜像另暴露 `8083` 作为其可选的管理端口。
+
进入容器:
```shell
@@ -174,6 +181,109 @@ eventmesh-operator-59c59f4f7b-nmmlm 1/1 Running 0 3m12s
eventmesh-runtime-0-a-0 1/1 Running 0 15s
```
+### 使用 EventMesh API
+
+应用通过 `/events/*` HTTP 端点以标准 [CloudEvents](https://cloudevents.io) 1.0 与 EventMesh 交互,仅需 `publish` / `subscribe` / `unsubscribe` 等操作。订阅与分发由 EventMesh 自主管理,无需消费者组或 Tag。
+
+#### 1. 发布 CloudEvent
+
+以 CloudEvents 1.0 格式通过 HTTP POST 发布事件(返回 `202 Accepted` 表示事件已写入 WAL):
+
+```shell
+POST /events/publish HTTP/1.1
+Host: localhost:8080
+Content-Type: application/cloudevents+json
+
+{
+ "specversion": "1.0",
+ "id": "89010a5a-3c6f-4a1e-9b2d-0f7c1f2e3a4b",
+ "source": "/example/producer",
+ "type": "com.example.order.created",
+ "subject": "orders",
+ "datacontenttype": "application/json",
+ "data": {
+ "content": "Hello, EventMesh!"
+ }
+}
+```
+
+#### 2. 订阅 Topic
+
+向 EventMesh 注册订阅(无消费者组、无 Tag)。提供 `clientId`、`topic` 以及分发模式 `mode`(`LOAD_BALANCE`、`BROADCAST`、`MULTICAST` 或 `LOAD_BALANCE_STICKY`),响应返回 `subscriptionId`:
+
+```shell
+POST /events/subscribe HTTP/1.1
+Host: localhost:8080
+Content-Type: application/json
+
+{
+ "clientId": "order-svc",
+ "topic": "orders",
+ "mode": "LOAD_BALANCE"
+}
+```
+
+#### 3. 接收事件
+
+订阅者可通过三种传输方式接收下发的事件,按业务场景任选其一——它们下发相同的 CloudEvents,并遵循 EventMesh 的 ACK / 至少一次语义。
+
+**a) HTTP 长轮询** —— 主动拉取,请求会阻塞直到有事件或超时:
+
+```shell
+GET /events/poll?clientId=order-svc&topics=orders&timeout=30000 HTTP/1.1
+Host: localhost:8080
+```
+
+处理完事件后,进行 ACK 以推进 offset(至少一次投递):
+
+```shell
+POST /events/ack HTTP/1.1
+Host: localhost:8080
+Content-Type: application/json
+
+{
+ "subId": "sub-123",
+ "clientId": "order-svc",
+ "topic": "orders",
+ "partition": 0,
+ "offset": 42
+}
+```
+
+**b) Server-Sent Events(SSE)** —— 服务端经长连接主动推送,客户端无需轮询:
+
+```shell
+GET /events/stream?clientId=order-svc&topics=orders HTTP/1.1
+Host: localhost:8080
+Accept: text/event-stream
+```
+
+**c) WebSocket** —— 经独立 WebSocket 端口的全双工服务端推送:
+
+```shell
+GET /events/stream HTTP/1.1
+Host: localhost:8082
+Upgrade: websocket
+Connection: Upgrade
+```
+
+> 长轮询、SSE、WebSocket 是可互换的下发传输——订阅者任选其一。SSE 与 WebSocket 由服务端推送(无需轮询循环),长轮询则由客户端驱动。此外还支持请求-应答(`POST /events/request` + `POST /events/reply`)。`CloudEventsClient` Java SDK 封装了以上全部方式(`subscribe` / `subscribeSse` / `subscribeWs`),用法见[CloudEvents 客户端使用指引](docs/eventmesh-cloudevents-client-guide.md)。
+
+#### 4. 取消订阅
+
+当不再需要接收某 Topic 的事件时,按 `clientId` 取消订阅(可选带 `subscriptionId`):
+
+```shell
+POST /events/unsubscribe HTTP/1.1
+Host: localhost:8080
+Content-Type: application/json
+
+{
+ "clientId": "order-svc",
+ "topic": "orders"
+}
+```
+
## 贡献
每个贡献者在推动 Apache EventMesh 的健康发展中都发挥了重要作用。我们真诚感谢所有为代码和文档作出贡献的贡献者。
diff --git a/build.gradle b/build.gradle
index 663fbaf7e6..569838202e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -32,9 +32,6 @@ import java.util.concurrent.TimeUnit
buildscript {
repositories {
mavenCentral()
- maven {
- url "https://maven.aliyun.com/repository/public"
- }
maven {
url "https://plugins.gradle.org/m2/"
}
@@ -74,8 +71,8 @@ allprojects {
compileJava.options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
java {
- sourceCompatibility = "1.8"
- targetCompatibility = "1.8"
+ sourceCompatibility = JavaVersion.VERSION_21
+ targetCompatibility = JavaVersion.VERSION_21
}
clean.doFirst {
@@ -105,9 +102,6 @@ allprojects {
dependencies {
repositories {
mavenCentral()
- maven {
- url "https://maven.aliyun.com/repository/public"
- }
}
testImplementation "org.junit.jupiter:junit-jupiter:5.11.0"
}
@@ -159,22 +153,18 @@ tasks.register('dist') {
dependsOn("${subProject.path}:jar")
}
dependsOn('generateDistLicense', 'generateDistNotice')
+ // Only modules that ship on the runtime classpath of the unified runtime
+ // (EventMeshApplication). Legacy api/registry/retry/security/trace/meta/
+ // metrics/starter/runtime-v2/openconnect modules were deleted in the uni
+ // rewrite; listing them here made `findProject` return null and NPE.
def includedProjects =
["eventmesh-common",
- "eventmesh-meta:eventmesh-meta-api",
- "eventmesh-metrics-plugin:eventmesh-metrics-api",
- "eventmesh-openconnect:eventmesh-openconnect-java",
- "eventmesh-openconnect:eventmesh-openconnect-offsetmgmt-plugin:eventmesh-openconnect-offsetmgmt-api",
- "eventmesh-protocol-plugin:eventmesh-protocol-api",
- "eventmesh-registry:eventmesh-registry-api",
- "eventmesh-retry:eventmesh-retry-api",
"eventmesh-runtime",
- "eventmesh-runtime-v2",
- "eventmesh-security-plugin:eventmesh-security-api",
"eventmesh-spi",
- "eventmesh-starter",
"eventmesh-storage-plugin:eventmesh-storage-api",
- "eventmesh-trace-plugin:eventmesh-trace-api"]
+ "eventmesh-protocol-plugin:eventmesh-protocol-api",
+ "eventmesh-protocol-plugin:eventmesh-protocol-meshmessage",
+ "eventmesh-protocol-plugin:eventmesh-protocol-a2a"]
doLast {
includedProjects.each {
def subProject = findProject(it)
@@ -207,53 +197,13 @@ tasks.register('dist') {
}
}
-tasks.register('dist-admin') {
- subprojects.forEach { subProject ->
- dependsOn("${subProject.path}:jar")
- }
- def includedProjects =
- [
- "eventmesh-admin-server",
- "eventmesh-common",
- "eventmesh-spi",
- "eventmesh-registry:eventmesh-registry-api",
- "eventmesh-registry:eventmesh-registry-nacos",
- "eventmesh-openconnect:eventmesh-openconnect-offsetmgmt-plugin:eventmesh-openconnect-offsetmgmt-api"
- ]
- doLast {
- includedProjects.each {
- def subProject = findProject(it)
- copy {
- from subProject.jar.archivePath
- into rootProject.file('dist/apps')
- }
- copy {
- from subProject.configurations.runtimeClasspath
- into rootProject.file('dist/lib')
- exclude 'eventmesh-*'
- }
- copy {
- from subProject.file('bin')
- into rootProject.file('dist/bin')
- }
- copy {
- from subProject.file('conf')
- from subProject.sourceSets.main.resources.srcDirs
- into rootProject.file('dist/conf')
- duplicatesStrategy = DuplicatesStrategy.EXCLUDE
- exclude 'META-INF'
- }
-
- }
- }
-
-}
-
tasks.register('installPlugin') {
var pluginProjects = subprojects.findAll {
it.file('gradle.properties').exists()
&& it.properties.containsKey('pluginType')
&& it.properties.containsKey('pluginName')
+ // connectors ship in the connector-runtime image (dist-connector), not the runtime image
+ && it.properties.get('pluginType') != 'connector'
}
doLast {
String[] libJars = java.util.Optional.ofNullable(file('dist/lib').list()).orElse(new String[0])
@@ -281,6 +231,64 @@ tasks.register('installPlugin') {
}
}
+tasks.register('dist-connector') {
+ subprojects.forEach { subProject ->
+ dependsOn("${subProject.path}:jar")
+ }
+ dependsOn('generateDistLicense', 'generateDistNotice')
+ doLast {
+ // Core: the connector-runtime process jar + its third-party deps (cloudevents, jackson,
+ // rocksdb, log4j binding, …). ConnectorApplication loads connectors via Class.forName, so
+ // connector jars must be on the classpath — start-connector.sh builds -cp from plugin/**/*.jar.
+ def core = findProject('eventmesh-connector-runtime')
+ logger.lifecycle('Install module: module: {}', core.name)
+ copy {
+ from core.jar.archivePath
+ into rootProject.file('dist-connector/apps')
+ }
+ copy {
+ from core.configurations.runtimeClasspath
+ into rootProject.file('dist-connector/lib')
+ exclude 'eventmesh-*'
+ }
+ copy {
+ from core.file('bin')
+ into rootProject.file('dist-connector/bin')
+ }
+ copy {
+ from core.file('conf')
+ from core.sourceSets.main.resources.srcDirs
+ into rootProject.file('dist-connector/conf')
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+ exclude 'META-INF'
+ }
+ // Connector plugins (pluginType == connector) into dist-connector/plugin/connector/
/
+ String[] libJars = java.util.Optional.ofNullable(file('dist-connector/lib').list()).orElse(new String[0])
+ def connectorPlugins = subprojects.findAll {
+ it.file('gradle.properties').exists()
+ && it.properties.containsKey('pluginType')
+ && it.properties.get('pluginType') == 'connector'
+ }
+ connectorPlugins.forEach(subProject -> {
+ var pluginName = subProject.properties.get('pluginName')
+ logger.lifecycle('Install connector plugin: {}, module: {}', pluginName, subProject.name)
+ copy {
+ from subProject.jar.archivePath
+ into rootProject.file("dist-connector/plugin/connector/${pluginName}")
+ }
+ copy {
+ from subProject.configurations.runtimeClasspath
+ into rootProject.file("dist-connector/plugin/connector/${pluginName}")
+ exclude(libJars)
+ }
+ })
+ copy {
+ from 'tools/dist-license'
+ into rootProject.file('dist-connector')
+ }
+ }
+}
+
tasks.register('tar', Tar) {
archiveBaseName.set(project.name)
archiveVersion.set(project.version.toString())
@@ -630,7 +638,6 @@ subprojects {
repositories {
mavenCentral()
- maven { url "https://maven.aliyun.com/repository/public" }
}
configurations.configureEach {
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000000..c6b106fa59
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,64 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Builder: Gradle 8.5 + JDK 21, matching the fork's local build setup. The
+# gradle wrapper (8.7) is intentionally not used: its distribution is fetched
+# from services.gradle.org, which is unreachable on this fork's network.
+# Dependencies resolve against the Maven repositories configured in settings.gradle / build.gradle.
+FROM gradle:8.5.0-jdk21 AS builder
+USER root
+WORKDIR /build
+COPY . .
+# `dist` builds every subproject jar and assembles dist/{apps,lib,conf,bin};
+# `installPlugin` lays out dist/plugin/// (storage + protocol only;
+# connectors are filtered out — they ship in the connector image). Neither runs
+# tests nor style gates (jar does not depend on check/test).
+RUN gradle clean dist --no-daemon \
+ && gradle installPlugin --no-daemon
+
+# Runtime: EventMeshApplication boots traffic HTTP + admin HTTP (+ opt-in
+# WebSocket) as one JVM. Storage/protocol plugins are discovered from ./plugin/
+# by the SPI JarExtensionClassLoader, so only conf/apps/lib need the classpath.
+# bin/start.sh assembles the classpath and maps ENV vars to -D flags.
+FROM eclipse-temurin:21-jre
+RUN apt-get update && apt-get install -y --no-install-recommends locales procps \
+ && rm -rf /var/lib/apt/lists/*
+RUN localedef -i en_US -f UTF-8 en_US.UTF-8 --quiet
+WORKDIR /data/app/eventmesh
+COPY --from=builder /build/dist ./
+RUN chmod +x bin/*.sh
+
+# 8080 = traffic HTTP (/events/* CloudEvents + legacy /eventmesh/*).
+# 8081 = independent admin port (UniAdminServer /admin/*).
+# 8082 = WebSocket push transport (opt-in via -Deventmesh.ws.port in JAVA_OPTS).
+EXPOSE 8080 8081 8082
+
+ENV DOCKER=true
+ENV EVENTMESH_HOME=/data/app/eventmesh
+ENV EVENTMESH_LOG_HOME=/data/app/eventmesh/logs
+ENV CONFPATH=/data/app/eventmesh/conf
+# Only kafka/rocketmq storage plugins ship (standalone was removed). Override
+# with -e EVENTMESH_STORAGE_TYPE=rocketmq. Storage bootstrap (namesrv) is read
+# from conf/eventmesh.properties — mount a volume over conf/ to override.
+ENV EVENTMESH_STORAGE_TYPE=kafka
+ENV EVENTMESH_HTTP_PORT=8080
+ENV EVENTMESH_ADMIN_PORT=8081
+ENV EVENTMESH_OFFSET_PATH=/data/app/eventmesh/data/offset
+# Extra -D flags: TLS keystore, ws.port, meta.type/meta.addr for clustering, …
+ENV JAVA_OPTS=""
+
+CMD ["bash", "bin/start.sh"]
diff --git a/docker/Dockerfile.connector b/docker/Dockerfile.connector
new file mode 100644
index 0000000000..6bb6f9fea3
--- /dev/null
+++ b/docker/Dockerfile.connector
@@ -0,0 +1,55 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Connector-runtime image — a separate process that talks HTTP+CloudEvents to the
+# EventMesh runtime and runs source/sink connectors. Built from `dist-connector`
+# (connector-runtime jar + deps + every connector plugin under plugin/connector/).
+# Same builder constraints as docker/Dockerfile (system gradle 8.5 + JDK 21).
+FROM gradle:8.5.0-jdk21 AS builder
+USER root
+WORKDIR /build
+COPY . .
+RUN gradle clean dist-connector --no-daemon
+
+# Runtime: ConnectorApplication loads connectors via Class.forName, so bin/start-connector.sh
+# flattens plugin/connector/**/*.jar onto the classpath (find is required by that script).
+FROM eclipse-temurin:21-jre
+RUN apt-get update && apt-get install -y --no-install-recommends locales procps findutils \
+ && rm -rf /var/lib/apt/lists/*
+RUN localedef -i en_US -f UTF-8 en_US.UTF-8 --quiet
+WORKDIR /data/app/connector
+COPY --from=builder /build/dist-connector ./
+RUN chmod +x bin/*.sh
+
+# Connector admin port is opt-in (CONNECTOR_ADMIN_PORT=0 = off by default). Expose 8083 as the
+# conventional port for operators who enable it; the connector otherwise makes only outbound calls.
+EXPOSE 8083
+
+ENV DOCKER=true
+ENV EVENTMESH_HOME=/data/app/connector
+ENV EVENTMESH_LOG_HOME=/data/app/connector/logs
+ENV CONFPATH=/data/app/connector/conf
+ENV EVENTMESH_RUNTIME_URL=http://localhost:8080
+ENV CONNECTOR_ADMIN_PORT=0
+ENV CONNECTOR_OFFSET_MODE=remote
+ENV CONNECTOR_OFFSET_PATH=/data/app/connector/data/connector-offset
+# Connector -D flags, e.g. -Dconnector.1.class=... -Dconnector.1.mode=source -Dconnector.1.topic=t
+ENV CONNECTOR_OPTS=""
+# Extra -D flags (TLS, …)
+ENV JAVA_OPTS=""
+
+CMD ["bash", "bin/start-connector.sh"]
diff --git a/docker/Dockerfile_jdk11 b/docker/Dockerfile_jdk11
deleted file mode 100644
index 18eb54fb1d..0000000000
--- a/docker/Dockerfile_jdk11
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-FROM eclipse-temurin:11-jdk AS builder
-WORKDIR /build
-COPY . .
-RUN ./gradlew clean build dist --parallel --daemon
-RUN ./gradlew installPlugin
-
-FROM eclipse-temurin:11-jdk
-RUN apt-get update && apt-get install -y locales procps
-RUN localedef -i en_US -f UTF-8 en_US.UTF-8 --quiet
-WORKDIR /data/app/eventmesh
-COPY --from=builder /build/dist ./
-
-EXPOSE 10000 10105 10106 10205
-
-ENV DOCKER=true
-ENV EVENTMESH_HOME=/data/app/eventmesh
-ENV EVENTMESH_LOG_HOME=/data/app/eventmesh/logs
-ENV CONFPATH=/data/app/eventmesh/conf
-
-CMD ["bash", "bin/start.sh"]
diff --git a/docker/Dockerfile_jdk8 b/docker/Dockerfile_jdk8
deleted file mode 100644
index 65e8fdc8ee..0000000000
--- a/docker/Dockerfile_jdk8
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-FROM eclipse-temurin:11-jdk AS builder_11
-WORKDIR /build
-COPY . .
-RUN ./gradlew clean generateGrammarSource --parallel --daemon
-
-FROM eclipse-temurin:8-jdk AS builder_8
-WORKDIR /build
-COPY --from=builder_11 /build ./
-RUN ./gradlew clean build dist -x spotlessJava -x generateGrammarSource --parallel --daemon
-RUN ./gradlew installPlugin
-
-FROM eclipse-temurin:8-jdk
-RUN apt-get update && apt-get install -y locales procps
-RUN localedef -i en_US -f UTF-8 en_US.UTF-8 --quiet
-WORKDIR /data/app/eventmesh
-COPY --from=builder_8 /build/dist ./
-
-EXPOSE 10000 10105 10106 10205
-
-ENV DOCKER=true
-ENV EVENTMESH_HOME=/data/app/eventmesh
-ENV EVENTMESH_LOG_HOME=/data/app/eventmesh/logs
-ENV CONFPATH=/data/app/eventmesh/conf
-
-CMD ["bash", "bin/start.sh"]
diff --git a/docs/eventmesh-cloudevents-client-guide.md b/docs/eventmesh-cloudevents-client-guide.md
new file mode 100644
index 0000000000..689c33e4fe
--- /dev/null
+++ b/docs/eventmesh-cloudevents-client-guide.md
@@ -0,0 +1,274 @@
+# EventMesh CloudEvents 客户端使用指引(RocketMQ 4.x / 5.x / Kafka 后端通用)
+
+## 1. 这是什么
+
+新架构下 EventMesh 对外**只提供一套客户端 SDK**:`eventmesh-sdk-java` 的 `CloudEventsClient`。
+它是 **HTTP + CloudEvents** 的极简客户端(无 TCP/gRPC、无 MQ 组语义),通过 EventMesh Runtime 的
+`/events/*` HTTP 端点收发标准 [CloudEvents](https://cloudevents.io) 事件。
+
+**关键点:客户端与 MQ 后端完全解耦。** 客户端只跟 EventMesh Runtime(HTTP)打交道,Runtime 后面接
+RocketMQ 4.x、RocketMQ 5.x 还是 Kafka,对客户端代码**完全透明**——同一份客户端代码可以原样跑在任一
+后端上。三种后端的区别只在 **Runtime(服务端)的部署配置**,不在客户端。
+
+> 旧的 `EventMeshHttpClient` / `EventMeshTCPClient`(legacy)仍保留作老协议兼容;新接入请用 `CloudEventsClient`。
+
+---
+
+## 2. 客户端 API 总览
+
+```java
+org.apache.eventmesh.client.cloudevents.CloudEventsClient
+```
+
+| 方法 | 说明 |
+|---|---|
+| `builder()` | 构造器入口(见下) |
+| `publish(topic, CloudEvent)` → `boolean` | 发布单条(202=成功) |
+| `publish(topic, List)` → `boolean` | 批量发布 |
+| `request(topic, CloudEvent, timeoutMs)` → `CloudEvent` | **阻塞式请求-应答**(等回复) |
+| `reply(correlationId, CloudEvent)` → `boolean` | 应答方回送一个 reply |
+| `subscribe(topic, mode, Consumer)` | 长轮询订阅,**handler 返回后自动 ACK** |
+| `subscribeWithAck(topic, mode, Predicate)` | 长轮询订阅,**手动 ACK**:predicate 返回 `true`=已处理(ACK,offset 推进);`false`=不 ACK(dispatcher 超时后重投,at-least-once) |
+| `createLiteTopic(parent, lite)` → `boolean` | **(仅 5.x)** 建/声明 lite topic(确保 parent 为 LITE 类型) |
+| `publishLite(parent, lite, CloudEvent)` → `boolean` | **(仅 5.x)** 发布到 lite topic(路由进 LMQ) |
+| `subscribeLite(parent, lite, Consumer)` | **(仅 5.x)** 订阅 lite topic(后台循环拉 LMQ + handler 回调,push 风格,与 `subscribe` 一致;无 ACK,offset 自管) |
+| `subscribeSse(topic, mode, Consumer)` | SSE 推送订阅(`/events/stream`) |
+| `subscribeWs(topic, mode, Consumer)` | WebSocket 推送订阅 |
+| `unsubscribe(topic)` | 退订单个 topic(服务端移除该 `{clientId, topic}` 订阅,其他保留;若无普通 topic 剩余则停长轮询循环) |
+| `unsubscribeLite(parent, lite)` | **(仅 5.x)** 停某个 lite 订阅的后台拉取循环(lite 无服务端注册,纯客户端停循环) |
+| `unsubscribe()` | 退订全部(服务端按 clientId 移除所有订阅 + 停所有循环/推送) |
+| `shutdown()` | 关闭客户端(停轮询/SSE/WS) |
+| `static event(id, source, type, byte[] data)` | 便捷构造一个 CloudEvent |
+
+**Builder:**
+```java
+CloudEventsClient.builder()
+ .runtimeUrl("http://localhost:8080") // EventMesh Runtime 地址(必填)
+ .clientId("my-service") // 客户端标识(必填)
+ .pollIntervalMs(500L) // 长轮询间隔(默认由 builder 决定)
+ .build();
+```
+
+**订阅模式 `mode`**(`org.apache.eventmesh.runtime.subscription.DistributionMode`,传字符串):
+- `BROADCAST` — 广播:每个订阅者都收到全量消息。
+- `LOAD_BALANCE` — 负载均衡:同一消息只投递给组内一个订阅者。
+- `MULTICAST` — 多播。
+- `LOAD_BALANCE_STICKY` — 按 partition key 稳定哈希到同一个订阅者(保序)。
+
+---
+
+## 3. 快速上手
+
+### 依赖
+客户端 jar:`eventmesh-sdk-java`(Maven 坐标 `org.apache.eventmesh:eventmesh-sdk-java`,或直接用本仓库
+`eventmesh-sdks/eventmesh-sdk-java` 模块)。**客户端不依赖任何 MQ jar**(不引 RocketMQ / kafka-clients)——它只发 HTTP。
+
+### 发布 + 订阅(长轮询,自动 ACK)
+```java
+CloudEventsClient client = CloudEventsClient.builder()
+ .runtimeUrl("http://localhost:8080")
+ .clientId("order-svc")
+ .pollIntervalMs(500L)
+ .build();
+
+// 订阅(后台长轮询,每条事件 handler 返回后自动 ACK)
+client.subscribe("orders", "BROADCAST", event -> {
+ System.out.println("收到: " + event.getId());
+});
+
+// 发布
+CloudEvent e = CloudEventsClient.event("evt-1", "order-svc", "order.created",
+ "{\"amt\":99}".getBytes(StandardCharsets.UTF_8));
+boolean ok = client.publish("orders", e);
+```
+
+### 手动 ACK(at-least-once,业务幂等窗口由客户端控制)
+```java
+client.subscribeWithAck("orders", "LOAD_BALANCE", event -> {
+ try {
+ process(event); // 业务处理
+ return true; // true → ACK,offset 推进
+ } catch (Exception ex) {
+ return false; // false → 不 ACK,ACK 超时后重投
+ }
+});
+```
+
+### 请求-应答(阻塞)
+```java
+// 请求方:发一个请求,阻塞等回复(带 emcorrelationid)
+CloudEvent req = CloudEventsClient.event("req-1", "caller", "query.price", payload);
+CloudEvent reply = client.request("price-req", req, 10_000L); // 最多等 10s
+if (reply != null) { /* 用 reply */ }
+
+// 应答方:订阅请求 topic,看到带 correlation 的请求就 reply
+responder.subscribe("price-req", "LOAD_BALANCE", event -> {
+ Object corr = event.getExtension("emcorrelationid");
+ if (corr != null) {
+ CloudEvent r = CloudEventsClient.event("reply-1", "price-svc", "query.price.reply",
+ priceJson(event).getBytes(StandardCharsets.UTF_8));
+ responder.reply(corr.toString(), r);
+ }
+});
+```
+
+> 注意:correlation 用 CloudEvents 扩展名 **`emcorrelationid`**(全小写无连字符,CloudEvents 规范不允许扩展名含连字符)。
+
+### Lite Topic(仅 5.x 后端)
+
+RocketMQ 5.5 Lite Topic(RIP-83):topic 内的二级消息容器。客户端三步走(仅对 5.x 后端有效;4.x 后端返回 `false`、服务端 501):
+
+```java
+// 1. 建/声明 lite topic(确保 parent 为 LITE 类型)—— 幂等,首次调用一次即可
+client.createLiteTopic("orders", "user-42");
+
+// 2. 订阅 lite topic(后台循环拉 LMQ + handler 回调,push 风格,和 subscribe 一致)
+client.subscribeLite("orders", "user-42", event -> { /* 处理 lite 事件 */ });
+
+// 3. 发布到 lite topic(带 __LITE_TOPIC,broker 路由进 LMQ)
+client.publishLite("orders", "user-42",
+ CloudEventsClient.event("lt-1", "order-svc", "order.lite", payload));
+```
+
+> Lite topic 语义与普通 topic 不同:`(parent, lite)` 唯一标识一个 LMQ 容器;`subscribeLite` 走**后台拉取**(不走
+> EventMesh 的 ACK/重投/DLQ 可靠层,offset 在存储插件内自管)。适合海量轻量会话/子分类场景。停止用 `unsubscribe()` / `shutdown()`。
+
+### SSE / WebSocket 推送
+```java
+// SSE:复用 HTTP 端口的 /events/stream(text/event-stream)
+client.subscribeSse("orders", "BROADCAST", event -> { /* 服务端长连接推送 */ });
+
+// WebSocket:runtime 的 WS 服务跑在独立端口(启动参数 -Deventmesh.ws.port=,0=自动),
+// 客户端需显式指定 wsUrl(否则会连到 HTTP 端口的 SSE 端点,握手失败)
+CloudEventsClient wsClient = CloudEventsClient.builder()
+ .runtimeUrl("http://localhost:8080") // HTTP 端口(publish / subscribe / SSE)
+ .wsUrl("http://localhost:8082") // WS 推送端口
+ .clientId("ws-sub").build();
+wsClient.subscribeWs("orders", "BROADCAST", event -> { /* WS 推送 */ });
+```
+> SSE 与 WS 都是服务端推送(客户端不用轮询);WS 需独立端口 + `wsUrl`,SSE 走 HTTP 端口。两者都自动 ACK。
+
+### 关闭
+```java
+client.unsubscribe();
+client.shutdown();
+```
+
+---
+
+## 4. 后端选择:RocketMQ 4.x / RocketMQ 5.x / Kafka
+
+**结论:客户端代码完全一致。** 三种后端的差异 100% 在 Runtime(服务端)侧——你只是把 Runtime 指向不同的
+MQ 集群,客户端那份 `CloudEventsClient` 代码一行都不用改。
+
+### 4.1 唯一要改的:Runtime(服务端)配置
+
+| | RocketMQ 4.x | RocketMQ 5.x | Kafka |
+|---|---|---|---|
+| 选插件 | `-Deventmesh.storage.type=rocketmq` | `-Deventmesh.storage.type=rocketmq5` | `-Deventmesh.storage.type=kafka` |
+| 接入地址 | `eventMesh.server.rocketmq.namesrvAddr=<4.x:9876>` | `eventMesh.server.rocketmq5.namesrvAddr=<5.x:9876>` | `eventMesh.server.kafka.namesrvAddr=` |
+| 鉴权 | 无 / ACL | 无 / ACL | **SASL**:`security.protocol` / `sasl.mechanism` / `sasl.jaas.config`(透传给 kafka-clients) |
+| 插件实现 | `RocketMQRemotingStoragePlugin`(4.9 remoting) | `RocketMQ5RemotingStoragePlugin`(5.5 remoting) | `KafkaMeshStoragePlugin`(kafka-clients) |
+| 连接方式 | `NettyRemotingClient` 直连(remoting,**不引 rocketmq-client**) | 同左(**不引 rocketmq-client/gRPC**) | `KafkaProducer`/`Consumer`/`AdminClient`(assign+seek+poll,**无 consumer group**,EventMesh 自管 offset) |
+
+部署时在 `eventmesh.properties`(或 `-D` 参数)里设这些值,Runtime 启动时按 `eventmesh.storage.type`
+SPI 加载对应插件。客户端只要把 `runtimeUrl` 指向该 Runtime 的 HTTP 端口即可。
+
+**Kafka + SASL(如 wemq-kafka)配置示例**(`eventmesh.properties`):
+```properties
+eventMesh.server.kafka.namesrvAddr=127.0.0.1:9094
+security.protocol=SASL_PLAINTEXT
+sasl.mechanism=PLAIN
+sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="" password="";
+```
+> Kafka 的 `security.*` / `sasl.*` / `ssl.*` 配置由 `KafkaMeshStoragePlugin` 透传给 producer / consumer / AdminClient(`kafka.` 前缀也接受)。非 SASL 的明文 Kafka 不设这些即可。
+
+### 4.2 对客户端透明的行为差异(了解即可,不影响 API)
+
+| 维度 | RocketMQ 4.x | RocketMQ 5.x | Kafka |
+|---|---|---|---|
+| 消费模型 | 经典 PULL(EventMesh 自管 offset + 分区所有权) | **POP**(broker 端分配队列,poll-all) | **assign+seek+poll**(无 consumer group,EventMesh 自管 offset) |
+| 多实例去重 | EventMesh `PartitionOwnership` + 分区分配 | broker POP + lease gate | EventMesh `PartitionOwnership`(Kafka assign 分区) |
+| ACK 语义 | offset 仅 ACK 后推进 | 相同 | 相同(Kafka offset 不 commit,EventMesh 自管) |
+| 普通 publish/subscribe/request-reply | 一致 | 一致 | 一致 |
+
+客户端调用的 `publish` / `subscribe` / `subscribeWithAck` / `request` / `reply` / `ack`,行为契约在三种后端上
+完全一致——这就是抽象的价值。
+
+### 4.3 Lite Topic(仅 5.x)
+
+RocketMQ 5.5 的 **Lite Topic(RIP-83)** 是 5.x 独有能力(topic 内的二级消息容器,面向海量会话/子分类)。
+**已端到端接入 HTTP 客户端**(仅对 5.x 后端可用;4.x / kafka / standalone 后端调 lite 方法返回 `false`,服务端 501):
+
+- 客户端:`CloudEventsClient.createLiteTopic(parent, lite)` / `publishLite(parent, lite, event)` / `subscribeLite(parent, lite, handler)`。
+- Runtime 端点:`POST /events/lite/create`、`POST /events/lite/publish`、`GET /events/lite/poll`(→ `UniIngressService` → `LiteTopicCapable` 存储插件;`subscribeLite` 在客户端后台循环调 `/events/lite/poll`)。
+- 插件:`RocketMQ5RemotingStoragePlugin` 实现 `LiteTopicCapable`(`sendLite` 带 `__LITE_TOPIC` 消息属性路由进 LMQ;`pullLite` 经典 PULL + liteTopic 从 LMQ 拉,offset 自管)。
+
+Lite 用法见 §3.5。**注意**:4.x 后端没有 lite 能力,调 `publishLite` / `subscribeLite` 会得到 `false`/无推送(服务端返回 501)。
+
+---
+
+## 5. 一份端到端示例(4.x / 5.x 通用客户端)
+
+```java
+// === 这份代码同时适用于 4.x 后端和 5.x 后端,无需改动 ===
+public class Demo {
+ public static void main(String[] args) {
+ CloudEventsClient client = CloudEventsClient.builder()
+ .runtimeUrl(System.getProperty("eventmesh.runtime.url", "http://localhost:8080"))
+ .clientId("demo-" + System.currentTimeMillis())
+ .pollIntervalMs(500L)
+ .build();
+
+ client.subscribeWithAck("demo-topic", "LOAD_BALANCE", event -> {
+ System.out.println("处理: " + event.getId() + " type=" + event.getType());
+ return true; // ACK
+ });
+
+ for (int i = 0; i < 10; i++) {
+ CloudEvent e = CloudEventsClient.event("e" + i, "demo", "demo.tick",
+ ("tick-" + i).getBytes(StandardCharsets.UTF_8));
+ client.publish("demo-topic", e);
+ }
+
+ // Runtime.sleep / 处理 …
+ client.shutdown();
+ }
+}
+```
+
+切后端时**只改服务端**:
+```bash
+# 4.x 后端
+gradle :eventmesh-runtime:dist
+EVENTMESH_STORAGE_TYPE=rocketmq EVENTMESH_ROCKETMQ_NAMESRV=127.0.0.1:9876 bin/start.sh
+
+# 5.x 后端(同一个 dist 镜像,三个 storage 插件都在)
+EVENTMESH_STORAGE_TYPE=rocketmq5 EVENTMESH_ROCKETMQ5_NAMESRV=127.0.0.1:9876 bin/start.sh
+
+# Kafka 后端(bootstrap + SASL 在 eventmesh.properties 里配,见 §4.1)
+EVENTMESH_STORAGE_TYPE=kafka bin/start.sh
+```
+(具体环境变量名以 `bin/start.sh` 的 `-D` 映射为准;`eventmesh.storage.type` / `eventMesh.server..namesrvAddr` 是关键属性。)
+
+---
+
+## 6. 注意事项 / 限制
+
+- **ACK 语义**:`subscribe`(自动 ACK)在 handler 抛异常时**不会**重投(已 ACK);要 at-least-once,用
+ `subscribeWithAck` 并在失败时返回 `false`。
+- **CloudEvents 扩展名**不能含连字符:`emcorrelationid`、`emsignature`、`emdlqreason`、`emtenantid` 等(全小写)。
+- **请求-应答超时**:`request(topic, event, timeoutMs)` 是阻塞调用,超时返回 `null`;迟到回复会被丢弃。
+- **Lite Topic**:仅 5.x 后端(已端到端接入 HTTP 客户端,见 §4.3);4.x / Kafka 后端调 lite 方法返回 `false`(服务端 501)。
+- **后端切换**:RocketMQ 4.x / 5.x / Kafka 之间切换是服务端配置切换,客户端代码无需改动、无需重新打包。
+- **Kafka SASL**:对带鉴权的 Kafka(如 wemq-kafka),在 `eventmesh.properties` 配 `security.protocol` / `sasl.mechanism` / `sasl.jaas.config`,插件透传给 kafka-clients(见 §4.1)。
+
+---
+
+## 附:相关代码位置
+- 客户端:`eventmesh-sdks/eventmesh-sdk-java/.../client/cloudevents/CloudEventsClient.java`(+ `CloudEventsClientBuilder`)
+- Runtime HTTP 端点:`eventmesh-runtime/.../http/UniHttpServer.java`(`/events/*`)
+- 4.x 存储插件:`eventmesh-storage-plugin/eventmesh-storage-rocketmq`(SPI key `rocketmq`)
+- 5.x 存储插件:`eventmesh-storage-plugin/eventmesh-storage-rocketmq5`(SPI key `rocketmq5`,含 `LiteTopicCapable`)
+- Kafka 存储插件:`eventmesh-storage-plugin/eventmesh-storage-kafka`(SPI key `kafka`,assign+seek+poll + SASL 透传)
+- 设计文档:`docs/eventmesh-uni-architecture-redesign.md`
diff --git a/docs/eventmesh-uni-architecture-redesign.md b/docs/eventmesh-uni-architecture-redesign.md
new file mode 100644
index 0000000000..62c5eb42f7
--- /dev/null
+++ b/docs/eventmesh-uni-architecture-redesign.md
@@ -0,0 +1,4578 @@
+# EventMesh 架构简化重构方案
+
+> 分支:`refactor/unified-runtime-pipeline` → 基于本次讨论的全新方向
+>
+> 核心变化:**丢弃 MQ 的 Producer Group / Consumer Group 语义,MQ 仅作存储层,EventMesh 自主维护订阅分发语义(负载均衡 / 多播 / 广播)。SDK 简化为 HTTP-only,全部基于 CloudEvents。**
+
+---
+
+## 结论先行
+
+当前 EventMesh 架构继承了 Kafka/RocketMQ 的 Producer Group / Consumer Group 概念,导致:
+
+- **SDK 复杂**:必须理解 Group、Topic、Tag 等 MQ 语义,学习成本高
+- **语义混乱**:EventMesh 自己的订阅模型(负载均衡 / 广播 / 多播)与 MQ Group 概念纠缠不清
+- **多协议负担**:TCP + HTTP + gRPC 三个 SDK 并行维护,协议适配代码膨胀
+
+**重构方向:EventMesh 变成一个纯粹的"CloudEvents over MQ"的消息总线。MQ 是 EventMesh 的存储后端,EventMesh 管理自己的订阅分发逻辑,客户端只需要知道 HTTP + CloudEvents。**
+
+---
+
+## 一、核心设计原则
+
+### 1.1 五条铁律
+
+| 铁律 | 说明 |
+|------|------|
+| **MQ 无语义** | Kafka/RocketMQ 只当"持久化的 FIFO 队列"用,不暴露任何 Producer Group / Consumer Group / Tag 概念给客户端 |
+| **EventMesh 自主订阅** | 订阅分发逻辑(谁收哪条消息、按什么策略分发)全部由 EventMesh 自己维护,不委托给 MQ |
+| **EventMesh 自主 Offset** | EventMesh 自主管理每个订阅关系的消费位点(topic#clientId#partition → offset),参考 RocketMQ Client OffsetStore 实现,使用 RocksDB 持久化,不依赖 MQ Consumer Group offset |
+| **SDK 极简** | 只有一个 SDK(HTTP),只暴露两个对象(CloudEvent + Subscription),只有三个 API(publish / subscribe / unsubscribe) |
+| **全链路 CloudEvents** | 从 SDK → EventMesh Runtime → MQ 存储 → 下发客户端,全部使用 CloudEvents 1.0 规范,不再有自定义的 EventMeshMessage / Package 等中间格式 |
+| **Connector 独立部署** | Connector Runtime 是独立进程,与 EventMesh Runtime 通过 HTTP + CloudEvents 接口通信,不共享内部组件,各自有独立的生命周期和 OffsetStore |
+
+> **🔎 实现状态速览(v1.11 / 2026-07-06 盘点)**:五条铁律中,"全链路 CloudEvents / Connector 独立部署 / SDK 极简(HTTP-only)"已落实;"EventMesh 自主订阅 / 自主 Offset"单实例成立。**但"MQ 无语义"在 Kafka 实现侧有矛盾**——`KafkaMeshStoragePlugin` 仍设 `group.id`(附录 F G7),多实例下会触发 Kafka rebalance,须修复。各铁律的逐 Phase 落实状态见 §11 各 Phase 标题处标注与附录 F。
+
+### 1.2 与当前设计的本质区别
+
+```
+ 当前设计 重构后设计
+ ───────── ──────────
+
+ EventMesh SDK:
+ ├─ TCP SDK (Package 协议) → 全部删除
+ ├─ HTTP SDK (EventMeshMessage) → 简化为 CloudEvents-only
+ └─ gRPC SDK (proto CloudEvent) → 全部删除
+
+ MQ 角色:
+ ├─ 暴露 Group 语义给客户端 ────────────── → 纯存储,不暴露任何语义
+ ├─ 多个 Consumer Group 竞争消费 ├─ EventMesh 1 个 Consumer 拉取
+ └─ Producer Group (RocketMQ) └─ EventMesh 1 个 Producer 写入
+
+ 订阅分发:
+ ├─ Consumer Group 竞争/广播继承 MQ 语义 ── → EventMesh 自主实现
+ │ ├─ 负载均衡: RoundRobin / 粘性会话
+ └─ 分发策略散落在各 Processor 中 └─ 广播: 所有订阅者全量收到
+ 多播: 按 tag 匹配订阅者
+
+ 协议适配:
+ ├─ 24 HTTP Processor → 1 个统一的 IngressHandler
+ ├─ 11 gRPC Processor → 删除(无 gRPC SDK)
+ ├─ TCP Dispatcher + Session → 删除(无 TCP SDK)
+ └─ 102 个 Processor 类 → ~10 个以内
+```
+
+---
+
+## 二、目标架构总览
+
+### 2.1 宏观架构
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ EventMesh Client │
+│ (HTTP SDK, CloudEvents-only) │
+│ │
+│ cloudEventsClient.publish(CloudEvent) │
+│ cloudEventsClient.subscribe(topic, handler) │
+│ cloudEventsClient.unsubscribe(topic) │
+└────────────────────────────┬────────────────────────────────────┘
+ │ HTTP POST / GET
+ │ (application/cloudevents+json)
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ EventMesh Runtime │
+│ (单进程, 统一 Runtime) │
+│ │
+│ ┌────────────────────────────────────────────────────────────┐ │
+│ │ HTTP Endpoint (唯一传输层) │ │
+│ │ POST /events/publish → Ingress Pipeline │ │
+│ │ POST /events/subscribe → SubscriptionManager │ │
+│ │ POST /events/unsubscribe │ │
+│ │ GET /events/poll → PushService 下发 │ │
+│ └──────────────────────────────┬───────────────────────────┘ │
+│ │ │
+│ ┌──────────────────────────────▼───────────────────────────┐ │
+│ │ Ingress Pipeline │ │
+│ │ AuthFilter → RateLimitFilter → AclFilter │ │
+│ │ → ProtocolFilter → TransformerEngine │ │
+│ │ → RouterEngine → │ │
+│ └──────────────────────────────┬───────────────────────────┘ │
+│ │ CloudEvent │
+│ ▼ │
+│ ┌────────────────────────────────────────────────────────────┐ │
+│ │ Storage Plugin (MQ 存储层) │ │
+│ │ ┌──────────────────────────────────────────────────────┐ │ │
+│ │ │ 单 Producer (EventMesh 自主管理) │ │ │
+│ │ │ · 按 topic 分区写入 │ │ │
+│ │ │ · 不暴露 Producer Group 概念 │ │ │
+│ │ │ · 不需要 clientId / producerGroup 配置 │ │ │
+│ │ │ · 由 EventMesh SubscriptionManager 统一路由 │ │ │
+│ │ └──────────────────────────────────────────────────────┘ │ │
+│ └──────────────────────────────┬────────────────────────────┘ │
+│ │ Consumer.poll() │
+│ ┌──────────────────────────────▼────────────────────────────┐ │
+│ │ ┌──────────────────────────────────────────────────────┐ │ │
+│ │ │ 单 Consumer (EventMesh 自主管理) │ │ │
+│ │ │ · 全量拉取所有 topic 的消息 │ │ │
+│ │ │ · 按 EventMesh 订阅规则分发,不走 Consumer Group │ │ │
+│ │ │ · 不暴露 Consumer Group / 负载均衡算法到客户端 │ │ │
+│ │ └──────────────────────────────────────────────────────┘ │ │
+│ └──────────────────────────────┬────────────────────────────┘ │
+│ │ CloudEvent (按订阅过滤后) │
+│ ┌──────────────────────────────▼───────────────────────────┐ │
+│ │ Egress Pipeline │ │
+│ │ EnrichmentTransformer → FilterEngine (消费者过滤) │ │
+│ └──────────────────────────────┬───────────────────────────┘ │
+│ │ │
+│ ┌──────────────────────────────▼───────────────────────────┐ │
+│ │ SubscriptionManager (核心) │ │
+│ │ 管理所有客户端订阅关系,按分发策略下发消息 │ │
+│ │ · 负载均衡分发 (RoundRobin / 最小连接数) │ │
+│ │ · 广播分发 (所有订阅者都收到) │ │
+│ │ · 多播分发 (按 subject/type/header 匹配) │ │
+│ │ · 自主 OffsetStore (RocksDB, 参考 RocketMQ Client 实现) │ │
+│ └──────────────────────────────┬───────────────────────────┘ │
+│ │ HTTP Long-Polling │
+│ ┌──────────────────────────────▼───────────────────────────┐ │
+│ │ PushService (HTTP Long-Polling) │ │
+│ │ 客户端 GET /events/poll?clientId=xxx&topics=topic1,topic2 │ │
+│ └────────────────────────────────────────────────────────────┘ │
+│ │
+│ HTTP Server: GET /connector/sink/{id}/poll (供 Connector Sink 拉取) │
+└────────────────────────────┬────────────────────────────────────┘
+ │
+ │ Kafka / RocketMQ
+ │ (纯存储,不理解业务语义)
+ ▼
+ ┌──────────────────────┐
+ │ Kafka / RocketMQ │
+ │ (视为分布式 WAL) │
+ │ │
+ │ · 无 Producer Group │
+ │ · 无 Consumer Group │
+ │ · 无 Tag 过滤 │
+ │ · EventMesh 是唯一 │
+ │ Producer + Consumer│
+ └──────────────────────┘
+
+┌─────────────────────────────────────────────────────────────────┐
+│ Connector Runtime (独立进程) │
+│ (Source/Sink Connector 管理器) │
+│ │
+│ SourceConnector.poll() → CloudEvent │
+│ → HTTP POST /events/publish → EventMesh Runtime │
+│ → Storage (MQ) │
+│ │
+│ SinkConnector: HTTP Long-Polling GET /connector/sink/{id}/poll │
+│ ← EventMesh 下发 CloudEvent │
+│ ← Storage.poll() → EgressPipeline │
+│ → 写入外部系统 (MySQL/Redis/HTTP API/...) │
+│ │
+│ 各自维护独立的 OffsetStore (RocksDB) │
+└─────────────────────────────────────────────────────────────────┘
+
+```
+
+### 2.2 协议栈简化对比
+
+```
+当前架构(5 层 + 遗留层) 重构后架构(3 层)
+
+Layer 5 · 编程模型
+├─ OpenMessaging API Layer 3 · 编程模型
+│ ├─ Producer / Consumer CloudEvents HTTP SDK
+├─ EventMeshMessage (SDK POJO) ├─ cloudEventsClient.publish()
+├─ A2A Protocol ├─ cloudEventsClient.subscribe()
+│ ├─ cloudEventsClient.unsubscribe()
+Layer 4 · 数据格式 Layer 2 · 数据格式
+├─ CloudEvents (统一内部) CloudEvents 1.0 (唯一格式)
+├─ EventMeshMessage (TCP SDK) (从客户端到 MQ 全链路)
+├─ Package (TCP 帧)
+├─ HttpEventWrapper (HTTP 旧) Layer 1 · 传输
+├─ proto CloudEvent (gRPC) HTTP Server (唯一传输层)
+│ (REST + Long-Polling)
+Layer 3 · 传输协议
+├─ TCP Server ─── MQ 存储 ───
+├─ HTTP Server
+├─ gRPC Server
+ Kafka / RocketMQ
+Layer 2 · 处理引擎 (纯 WAL,EventMesh 单 Producer
+├─ Ingress/Egress Pipeline 单 Consumer 模式)
+Layer 1 · 存储
+├─ Kafka / RocketMQ / Pulsar
+
+遗留问题:
+├─ TCP/gRPC SDK 必须维护
+├─ EventMeshMessage/Package 协议适配代码
+└─ OpenMessaging API 与 MQ Group 绑定
+```
+
+---
+
+## 三、Storage Plugin 重构:MQ 无状态存储化
+
+### 3.1 当前问题
+
+当前 Storage Plugin 的设计暴露了 MQ 的 Producer Group / Consumer Group 语义:
+
+```java
+// eventmesh-storage-rocketmq
+MeshMQProducer.java:
+ producerGroup: String // RocketMQ ProducerGroup,客户端需要理解
+ createTransactionProducer() // 事务消息,EventMesh 需要继承这个复杂度
+
+MeshMQConsumer.java:
+ consumerGroup: String // RocketMQ ConsumerGroup,核心问题
+ subscribe(topic, subExpression) // subExpression = RocketMQ Tag 过滤
+ push() / pull() // 两种消费模式混用
+```
+
+**问题:客户端通过 EventMesh SDK 配置 `consumerGroup`,本质上还是在用 RocketMQ 的 Consumer Group 语义。EventMesh 并没有真正提供自己的订阅分发模型。**
+
+### 3.2 目标接口
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:✅ 接口 `MeshStoragePlugin` 已按此定义落地(send/poll-by-offset/assignPartitions/commitOffset/start/shutdown),无 producerGroup/consumerGroup/tag 参数。**但实现侧矛盾**:Kafka 实现内部仍设 `group.id`(G7),assignPartitions/commitOffset 在 Kafka 已实装、RocketMQ 仍部分为 stub。
+
+重构后的 Storage Plugin 只提供两个核心方法:
+
+```java
+public interface MeshStoragePlugin {
+
+ /**
+ * 写入消息(EventMesh 管理 producer,不暴露 producerGroup)
+ * @param topic EventMesh topic(映射到 MQ topic)
+ * @param event CloudEvent
+ */
+ void send(String topic, CloudEvent event, SendCallback callback);
+
+ /**
+ * 订阅消息(全量拉取,不走 MQ 消费组)
+ * @param topic EventMesh topic
+ * @param offset 消费位点(earliest / latest / 指定 offset)
+ * @param handler 消息处理器
+ */
+ void subscribe(String topic, String offset, ConsumerHandler handler);
+
+ /**
+ * 批量拉取(供 SubscriptionManager 消费)
+ */
+ List poll(String topic, int maxEvents, long timeoutMs);
+
+ /**
+ * 提交消费位点(EventMesh 自己维护 offset)
+ */
+ void commitOffset(String topic, String offset);
+
+ void start();
+ void shutdown();
+}
+```
+
+**关键变化:**
+
+1. **删除 `producerGroup` / `consumerGroup` 全部配置项**
+2. **Storage Plugin 内部只有一个 Producer 实例**(由 EventMesh Runtime 持有)
+3. **Storage Plugin 内部只有一个 Consumer 实例**(由 EventMesh Runtime 持有,按需分区拉取)
+4. **`subscribe()` 方法是内部订阅(EventMesh 用它拉取消息),不是客户端订阅**
+
+### 3.3 Kafka 实现重构
+
+```java
+public class KafkaStoragePlugin implements MeshStoragePlugin {
+
+ // EventMesh 管理一个 Producer,不暴露 producerGroup
+ private org.apache.kafka.clients.producer.KafkaProducer producer;
+
+ // EventMesh 管理一个 Consumer,不暴露 consumerGroup
+ private org.apache.kafka.clients.consumer.KafkaConsumer consumer;
+
+ // 分区分配由 EventMesh SubscriptionManager 控制
+ private Map> topicPartitions = new ConcurrentHashMap<>();
+
+ @Override
+ public void send(String topic, CloudEvent event, SendCallback callback) {
+ // CloudEvent → 二进制
+ byte[] bytes = CloudEventMapper.toBytes(event);
+ ProducerRecord record = new ProducerRecord<>(topic, bytes);
+
+ // 不设置 producerGroup / transactionalId
+ // Kafka producer 由 EventMesh Runtime 全局单例
+ producer.send(record, (metadata, exception) -> {
+ if (exception != null) callback.onError(exception);
+ else callback.onSuccess(metadata.offset());
+ });
+ }
+
+ @Override
+ public List poll(String topic, int maxEvents, long timeoutMs) {
+ // 手动指定消费分区(由 SubscriptionManager 路由)
+ List partitions = topicPartitions.get(topic);
+ if (partitions == null || partitions.isEmpty()) return Collections.emptyList();
+
+ consumer.assign(partitions);
+ ConsumerRecords records = consumer.poll(Duration.ofMillis(timeoutMs));
+
+ List events = new ArrayList<>();
+ for (ConsumerRecord record : records) {
+ events.add(CloudEventMapper.fromBytes(record.value()));
+ }
+ return events;
+ }
+}
+```
+
+### 3.4 RocketMQ 实现重构
+
+```java
+public class RocketMQStoragePlugin implements MeshStoragePlugin {
+
+ // EventMesh 管理一个 DefaultMQProducer,不暴露 producerGroup
+ private DefaultMQProducer producer;
+
+ // EventMesh 管理一个 DefaultMQPushConsumer,不暴露 consumerGroup
+ private DefaultMQPushConsumer consumer;
+
+ @Override
+ public void send(String topic, CloudEvent event, SendCallback callback) {
+ Message msg = new Message();
+ msg.setTopic(topic);
+ msg.setBody(CloudEventMapper.toBytes(event));
+ // 不设置 tags / keys(这些是 MQ 语义,EventMesh 不暴露)
+
+ producer.send(msg, (sendResult, exception) -> {
+ if (exception != null) callback.onError(exception);
+ else callback.onSuccess(sendResult.getOffsetMsgId());
+ });
+ }
+
+ @Override
+ public List poll(String topic, int maxEvents, long timeoutMs) {
+ // RocketMQ PushConsumer 模式:
+ // EventMesh 消费全部消息,然后按自己的订阅规则分发
+ // 不使用 RocketMQ 的 tag 过滤(那是 MQ 语义)
+ List msgs = pullFromRocketMQ(topic, maxEvents, timeoutMs);
+ return msgs.stream()
+ .map(m -> CloudEventMapper.fromBytes(m.getBody()))
+ .collect(Collectors.toList());
+ }
+}
+```
+
+### 3.5 配置简化
+
+```properties
+# 旧配置(暴露 MQ 语义)
+eventmesh.storage.rocketmq.producer.group=EventMeshProducer
+eventmesh.storage.rocketmq.consumer.group=EventMeshConsumer
+eventmesh.storage.rocketmq.consumer.tag=*
+
+# 新配置(EventMesh 管理)
+eventmesh.storage.type=kafka # kafka / rocketmq / pulsar / s3stream(§15.8)
+eventmesh.storage.bootstrap.servers=localhost:9092
+eventmesh.storage.consumer.auto.offset.reset=earliest
+
+# S3Stream 存储后端(§15.8,多后端并列)
+# eventmesh.storage.type=s3stream
+# eventmesh.storage.s3stream.endpoint=https://s3.xxx.com
+# eventmesh.storage.s3stream.bucket=eventmesh-wal
+# eventmesh.storage.s3stream.region=us-east-1
+```
+
+---
+
+### 3.6 S3Stream 存储后端与跨后端语义对齐(§15.8)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:❌ **S3Stream 后端未实现**(仅本文档规划,无 `S3StreamStoragePlugin` 代码)。当前存储后端仅 Kafka + RocketMQ。跨后端语义对齐表(§3.6.4)是目标设计,待 S3Stream 落地时验证。
+
+§15.8 决策新增 S3Stream 作为 StoragePlugin 实现,与 Kafka/RocketMQ 并列。本节定义其适配方式与三种后端的语义对齐。
+
+#### 3.6.1 S3Stream 是什么、为何契合"完全自主协调"
+
+S3Stream 是 AutoMQ 的流式存储:**无状态 compute broker + 对象存储(S3)分级**。数据/offset 在 S3,broker 只算不存。这与 §15.1 "EventMesh 完全自主协调"高度契合:
+
+```
+契合点:
+ · S3Stream broker 无状态 → EventMesh 实例可随意扩缩(compute 弹性)
+ · 分区是逻辑概念 → EventMesh 的分区分配协议(§13.2.8)可平滑套用
+ · offset 真相在 S3 → 但 EventMesh 分发 offset 仍自主管(§13.2.4,不冲突,见下)
+
+注意(姿态 A 的边界,§15.8):
+ · S3Stream 本身有 compute 调度能力,但本方案不复用它
+ · EventMesh 在 S3Stream 之上叠自己的分区分配(Meta 主导,§13.2.3)
+ · S3Stream 退化为"数据 WAL + 物理分区"——其弹性优势仍在(存储无状态),调度优势不用
+ → 这是 §15.8 选"姿态 A 最小集成"的代价:换取 §15.1 铁律不破
+```
+
+#### 3.6.2 两种集成深度
+
+```
+v1(推荐起步):S3StreamStoragePlugin 薄包装 Kafka 线协议
+ · S3Stream 兼容 Kafka wire protocol → 复用 KafkaStoragePlugin 的 Producer/Consumer
+ · S3StreamStoragePlugin 仅换 bootstrap 指向 S3Stream endpoint + 配置 S3 参数
+ · 零新协议代码,最快落地
+ · 代价:受 Kafka client 行为约束(如 offset 提交语义),但 §13.2.4 已绕过(EventMesh 自管 offset)
+
+v2(深度):原生 S3Stream SDK
+ · 用 S3Stream 原生 SDK(S3Stream 的 Stream 语义,非 Kafka 兼容层)
+ · 直接操作 Stream/Offset,绕过 Kafka 兼容层开销
+ · 可利用 S3Stream 的分级存储特性(热数据 SSD / 冷数据 S3)
+ · 代价:新协议适配代码,与 Kafka/RocketMQ 实现分叉
+```
+
+#### 3.6.3 S3StreamStoragePlugin 实现(v1 薄包装)
+
+```java
+public class S3StreamStoragePlugin implements MeshStoragePlugin {
+
+ // v1:复用 Kafka client(S3Stream 兼容 Kafka 线协议)
+ private KafkaProducer producer;
+ private KafkaConsumer consumer;
+
+ public S3StreamStoragePlugin(S3StreamConfig config) {
+ // bootstrap 指向 S3Stream endpoint,其余同 Kafka
+ Properties p = new Properties();
+ p.put("bootstrap.servers", config.getEndpoint()); // s3stream endpoint
+ p.put("s3.endpoint", config.getS3Endpoint()); // S3 后端
+ p.put("s3.bucket", config.getBucket());
+ p.put("s3.region", config.getRegion());
+ // 不设 group.id(§3.2 MQ 无语义);enable.auto.commit=false(EventMesh 自管 offset)
+ p.put("enable.auto.commit", "false");
+ this.producer = new KafkaProducer<>(p);
+ this.consumer = new KafkaConsumer<>(p);
+ }
+
+ @Override
+ public void send(String topic, CloudEvent event, SendCallback callback) {
+ // 同 KafkaStoragePlugin:CloudEvent → bytes → ProducerRecord
+ byte[] bytes = CloudEventMapper.toBytes(event);
+ producer.send(new ProducerRecord<>(topic, bytes), (m, e) -> {
+ if (e != null) callback.onError(e);
+ else callback.onSuccess(m.offset());
+ });
+ }
+
+ @Override
+ public List poll(String topic, int partition, long startOffset, int maxEvents, long timeoutMs) {
+ // 按 EventMesh 分配的分区 + 自管 offset 拉取(§13.2.3 assignPartitions + §13.2.4 offset)
+ consumer.assign(Collections.singletonList(new TopicPartition(topic, partition)));
+ consumer.seek(new TopicPartition(topic, partition), startOffset);
+ ConsumerRecords records = consumer.poll(Duration.ofMillis(timeoutMs));
+ // 不 commit(EventMesh offset 独立于 MQ offset,§12.6.6)
+ return records.stream().map(r -> CloudEventMapper.fromBytes(r.value())).collect(Collectors.toList());
+ }
+
+ @Override
+ public void assignPartitions(String topic, List partitions) {
+ // §13.2.3:EventMesh 分区分配结果 → consumer.assign
+ consumer.assign(partitions.stream()
+ .map(p -> new TopicPartition(topic, p)).collect(Collectors.toList()));
+ }
+ // ... start/shutdown/commitOffset(commitOffset 为 no-op,EventMesh 自管)
+}
+```
+
+#### 3.6.4 跨后端语义对齐表
+
+三种后端通过 `MeshStoragePlugin` 抽象对齐,但底层语义有差异,须显式标注:
+
+| 语义维度 | Kafka | RocketMQ | S3Stream(v1) | EventMesh 如何抹平 |
+|---------|-------|----------|--------------|-------------------|
+| 分区概念 | partition | queue(=partition 等价) | partition | 统一抽象为 partition,`assignPartitions(topic, List)` |
+| offset 类型 | long(分区单调) | long(queue 内单调) | long | 统一 long,§13.2.4 自管 |
+| offset 提交 | `__consumer_offsets` | broker 端 | S3 | **EventMesh 不提交 MQ offset**(§12.6.6),自管 RocksDB+Meta |
+| Tag/过滤 | 无(partition 内全量) | Tag 过滤 | 无 | EventMesh MULTICAST 自实现(§4.2.3),不用 MQ Tag |
+| 广播 | consumer group=每消费者独立 | MessageModel.BROADCAST | 无原生 | EventMesh BROADCAST 自实现(§4.2.2),MQ 只存 1 份 |
+| Producer Group | 无(Kafka 无) | ProducerGroup | 无 | 全删(§3.2 MQ 无语义) |
+| 顺序 | partition 内 FIFO | queue 内 FIFO | partition 内 FIFO | EventMesh STICKY 保 partitionKey→同分区(§13.3.3) |
+| 事务消息 | 有(txn API) | 有(半消息) | — | 明确不支持(§13.3.6) |
+
+> **关键对齐**:三种后端 EventMesh 都**不提交 MQ offset、不用 MQ Tag、不用 MQ 广播/Group**——MQ 退化为"按 partition + offset 的 FIFO WAL"。这是"MQ 无语义"铁律在三种后端上的统一落实。S3Stream 的存算分离优势(存储弹性)保留,但 compute 调度不用(姿态 A)。
+
+#### 3.6.5 多后端选型建议
+
+```
+S3Stream:新部署、追求存储成本(S3 比 Kafka broker 便宜)+ 弹性扩缩存算分离
+Kafka :已有 Kafka 集群、低延迟(本地 broker,无 S3 RTT)、强生态
+RocketMQ:已有 RocketMQ、需国产化/特定特性(但 EventMesh 已抹平其 Group/Tag 语义)
+→ 三者通过 MeshStoragePlugin 并列,eventmesh.storage.type 切换(§3.5)
+→ 混合部署:不同 topic 可绑不同后端(StoragePlugin 按 topic 路由,v2 增强)
+```
+
+#### 3.6.6 v2 原生 S3Stream SDK 细节(深度集成,非 v1 必须)
+
+§3.6.2 的 v2 路线用 S3Stream 原生 SDK(非 Kafka 兼容层)。本节给出 Stream/Offset API 与差异。
+
+**原生 API 模型(vs Kafka 兼容层):**
+
+```
+S3Stream 原生概念:
+ · Stream:逻辑流(≈ topic+partition),有独立的 offset 空间
+ · Offset:Stream 内单调递增,由 S3 持久化(真相源)
+ · 分级存储:热数据 SSD 缓存 / 冷数据 S3(自动分级)
+
+原生 SDK 伪 API(示意):
+ S3StreamClient client = S3StreamClient.builder().endpoint(...).build();
+ Stream stream = client.openStream("tenantA.orders", ReadWrite);
+ long nextOffset = stream.append(cloudEventBytes); // 写入,返回 offset
+ List records = stream.fetch(startOffset, maxCount); // 按 offset 范围读
+ // 无 consumer group / 无 rebalance / 无自动 commit——纯流式 WAL
+
+与 Kafka 兼容层(v1)的差异:
+ ┌────────────────┬──────────────────────┬──────────────────────┐
+ │ 维度 │ v1 Kafka 兼容层 │ v2 原生 SDK │
+ ├────────────────┼──────────────────────┼──────────────────────┤
+ │ 协议 │ Kafka wire protocol │ S3Stream 原生协议 │
+ │ 抽象 │ TopicPartition │ Stream │
+ │ offset 提交 │ Kafka client 行为 │ 不提交(EventMesh 自管)│
+ │ 分级存储利用 │ 不暴露 │ 可配热/冷策略 │
+ │ 性能开销 │ Kafka 兼容层额外开销 │ 直达,更低延迟 │
+ │ 实现成本 │ 零(复用 KafkaStorage)│ 新适配代码 │
+ └────────────────┴──────────────────────┴──────────────────────┘
+```
+
+**v2 S3StreamStoragePlugin 实现(原生):**
+
+```java
+public class S3StreamStoragePlugin implements MeshStoragePlugin {
+ private S3StreamClient client;
+ private Map streams = new ConcurrentHashMap<>(); // topic → Stream
+
+ @Override
+ public void send(String topic, CloudEvent event, SendCallback cb) {
+ Stream stream = streams.computeIfAbsent(topic,
+ t -> client.openStream(t, ReadWrite));
+ long offset = stream.append(CloudEventMapper.toBytes(event));
+ cb.onSuccess(offset); // 同步返回 offset(S3Stream append 通常同步)
+ }
+
+ @Override
+ public List poll(String topic, int partition, long startOffset,
+ int maxEvents, long timeoutMs) {
+ Stream stream = streams.get(topic);
+ // 原生 fetch 按 offset 范围,无 assign/rebalance(§13.2.3 由 EventMesh 决定读哪些)
+ List records = stream.fetch(startOffset, maxEvents);
+ return records.stream().map(r -> CloudEventMapper.fromBytes(r.value())).collect(toList());
+ }
+
+ @Override
+ public void assignPartitions(String topic, List partitions) {
+ // S3Stream 原生无 partition 概念(Stream 即逻辑分区)
+ // 映射:EventMesh partition → S3Stream Stream(如 topic#partition → 独立 Stream)
+ // 或单 Stream + EventMesh 在上层做分区路由
+ // 设计选择见下"语义映射"
+ }
+}
+```
+
+**语义映射(EventMesh partition ↔ S3Stream Stream):**
+
+```
+两种映射策略:
+ A. 一 Stream 一 partition:tenantA.orders#0 → Stream "tenantA.orders.0"
+ · 优势:partition 独立 offset 空间,与 §13.2.8 分区分配天然对齐
+ · assignPartitions(topic, [0,1]) → openStream(topic.0), openStream(topic.1)
+ B. 一 Stream 多 partition:tenantA.orders → 单 Stream,partition 编码进 record key
+ · 优势:Stream 数少,管理简单
+ · 劣势:partition 分配需 EventMesh 上层过滤,违背"分区独立拉取"
+ → 推荐 A,与 Kafka/RocketMQ 的 partition 模型一致,§13.2.8 协议无需改
+
+分级存储利用(v2 独有):
+ · 配置 Stream 的 tiering 策略:热数据 N 小时留 SSD,之后转 S3
+ · EventMesh 读取历史消息(replay)时,S3Stream 自动从 S3 拉取
+ · 降低长保留期成本(合规审计场景受益)
+```
+
+**何时上 v2:**
+
+```
+v1(Kafka 兼容层):快速落地、验证 S3Stream 存储可行性
+v2(原生 SDK):需要 ①更低延迟(去兼容层)②分级存储降成本 ③绕过 Kafka client 限制
+→ 建议先 v1 上线,压测确认瓶颈在 Kafka 兼容层后再迁 v2
+```
+
+---
+
+### 4.1 核心职责
+
+这是本次重构最核心的新组件:**EventMesh 自主维护订阅关系和分发策略,不再委托给 MQ 的 Consumer Group。**
+
+```java
+public class SubscriptionManager {
+
+ // 订阅关系:topic → 订阅者列表
+ private final Map> subscriptions = new ConcurrentHashMap<>();
+
+ // 订阅者:一个 HTTP 客户端的订阅上下文
+ static class Subscription {
+ String subscriptionId; // 订阅唯一ID
+ String clientId; // 客户端标识
+ DistributionMode mode; // 分发模式
+ CloudEventFilter filter; // 过滤条件(CloudEvents header / type 匹配)
+ HttpResponseEmitter emitter; // HTTP 长轮询响应通道
+ long lastHeartbeat; // 心跳时间
+ }
+
+ enum DistributionMode {
+ LOAD_BALANCE, // 负载均衡:每条消息只发给一个订阅者(RoundRobin)
+ BROADCAST, // 广播:每条消息发给所有订阅者
+ MULTICAST // 多播:按 CloudEvents type/source/header 匹配发给对应订阅者
+ }
+}
+```
+
+### 4.2 三种分发模式详解
+
+#### 4.2.1 负载均衡(LOAD_BALANCE)
+
+```
+场景:订单处理,同一订单的消息只需要一个消费者处理
+
+EventMesh Topic: "orders" (MQ 存储层)
+订阅者 A: mode=LOAD_BALANCE, clientId=worker-1
+订阅者 B: mode=LOAD_BALANCE, clientId=worker-2
+订阅者 C: mode=LOAD_BALANCE, clientId=worker-3
+
+消息分发:
+ order-001 → worker-1 (RoundRobin 第1轮)
+ order-002 → worker-2 (RoundRobin 第2轮)
+ order-003 → worker-3 (RoundRobin 第3轮)
+ order-004 → worker-1 (RoundRobin 回到第1个)
+
+关键: 分发逻辑由 EventMesh SubscriptionManager 决定,不走 MQ 的分区机制
+ (MQ 只负责持久化,EventMesh 决定谁能收到哪条消息)
+```
+
+#### 4.2.2 广播(BROADCAST)
+
+```
+场景:配置更新,所有服务节点都需要收到
+
+EventMesh Topic: "config-updates" (MQ 存储层)
+订阅者: service-A, service-B, service-C, service-D (全部 4 个)
+
+消息分发:
+ config-change-v1 → [service-A, service-B, service-C, service-D] (全部 4 份)
+ config-change-v2 → [service-A, service-B, service-C, service-D] (全部 4 份)
+
+实现:
+ · 存储层只需写 1 条消息到 MQ
+ · EventMesh SubscriptionManager 读取后,按订阅列表复制 N 份下发
+ · 每个订阅者通过 HTTP Long-Polling 收到自己的消息流
+```
+
+#### 4.2.3 多播(MULTICAST)
+
+```
+场景:按 CloudEvents type 路由到不同消费者
+
+EventMesh Topic: "events" (MQ 存储层,混合了所有业务事件)
+
+订阅关系:
+ event.type="order.created" → order-service
+ event.type="payment.completed" → payment-service
+ event.type="inventory.changed" → inventory-service
+ event.type="user.registered" → user-service + marketing-service
+
+CloudEvent 过滤匹配(基于 CloudEvents extension):
+ "subject" extension: 匹配资源标识
+ "x-em-destinations" extension: 显式指定目标服务列表
+
+消息分发:
+ CloudEvent(type=order.created, subject=order-123)
+ → order-service
+
+ CloudEvent(type=payment.completed, subject=pay-456)
+ → payment-service
+
+ CloudEvent(type=user.registered, subject=user-789)
+ → [user-service, marketing-service]
+```
+
+### 4.3 SubscriptionManager 完整实现
+
+```java
+public class SubscriptionManager {
+
+ private final Map> topicSubscriptions = new ConcurrentHashMap<>();
+ private final DistributionStrategyRegistry strategyRegistry;
+ private final AtomicInteger roundRobinCounter = new ConcurrentHashMap<>();
+
+ // 注册订阅
+ public String subscribe(SubscribeRequest request, HttpResponseEmitter emitter) {
+ String subId = UUID.randomUUID().toString();
+ Subscription sub = new Subscription(subId, request.getClientId(),
+ request.getMode(), request.getFilter(), emitter);
+
+ topicSubscriptions
+ .computeIfAbsent(request.getTopic(), k -> ConcurrentHashMap.newKeySet())
+ .add(sub);
+
+ return subId;
+ }
+
+ // 注销订阅
+ public boolean unsubscribe(String topic, String subId) {
+ Set subs = topicSubscriptions.get(topic);
+ if (subs == null) return false;
+ return subs.removeIf(s -> s.getSubscriptionId().equals(subId));
+ }
+
+ // 拉取并分发消息(由定时任务调用)
+ public void pollAndDispatch(String topic, MeshStoragePlugin storage, long timeoutMs) {
+ List events = storage.poll(topic, 100, timeoutMs);
+ if (events.isEmpty()) return;
+
+ Set subs = topicSubscriptions.get(topic);
+ if (subs == null || subs.isEmpty()) return;
+
+ for (CloudEvent event : events) {
+ List targets = selectTargets(event, subs);
+ for (Subscription target : targets) {
+ dispatchToSubscriber(event, target);
+ }
+ }
+ }
+
+ // 按分发模式选择目标订阅者
+ private List selectTargets(CloudEvent event, Set allSubs) {
+ // 移除心跳过期的订阅者
+ allSubs.removeIf(s -> s.isExpired(maxIdleMs));
+
+ List activeSubs = new ArrayList<>(allSubs);
+ if (activeSubs.isEmpty()) return Collections.emptyList();
+
+ // 按分发模式路由
+ DistributionMode mode = inferMode(activeSubs);
+ switch (mode) {
+ case BROADCAST:
+ return activeSubs; // 全部下发
+
+ case LOAD_BALANCE: {
+ // RoundRobin
+ int idx = Math.abs(roundRobinCounter.incrementAndGet()) % activeSubs.size();
+ return Collections.singletonList(activeSubs.get(idx));
+ }
+
+ case MULTICAST: {
+ // 按 CloudEvents 属性过滤
+ return activeSubs.stream()
+ .filter(s -> s.getFilter().match(event))
+ .collect(Collectors.toList());
+ }
+
+ default:
+ return Collections.emptyList();
+ }
+ }
+
+ // 通过 HTTP Long-Polling 下发
+ private void dispatchToSubscriber(CloudEvent event, Subscription sub) {
+ try {
+ sub.getEmitter().send(event); // HTTP 响应
+ } catch (Exception e) {
+ // 客户端断开:移除订阅
+ removeSubscription(sub.getTopic(), sub.getSubscriptionId());
+ }
+ }
+}
+```
+
+---
+
+## 五、SDK 极简化:HTTP 家族 CloudEvents SDK
+
+### 5.1 目标
+
+当前 EventMesh SDK 有三个版本(TCP / HTTP / gRPC),各自有不同的对象模型和 API 风格:
+
+```
+当前 SDK:
+├─ eventmesh-sdk-java (TCP)
+│ ├─ Proxy tcpClient = Proxy.builder().build()
+│ ├─ tcpClient.createClient(group, topics)
+│ ├─ tcpClient.subscribe(topic, group)
+│ └─ 发送 Package 二进制帧
+│
+├─ eventmesh-sdk-java (HTTP)
+│ ├─ EventMeshHttpClient client = EventMeshHttpClient.builder().build()
+│ ├─ eventMeshHttpClient.publish(message)
+│ ├─ eventMeshHttpClient.subscribe(handler)
+│ └─ 发送 EventMeshMessage JSON
+│
+└─ eventmesh-sdk-java (gRPC)
+ ├─ EventMeshGrpcClient client = EventMeshGrpcClient.builder().build()
+ ├─ client.publish(event)
+ └─ 发送 proto CloudEvent
+```
+
+**重构后:只保留一个 `eventmesh-sdk-java`(HTTP 家族),API 设计遵循 CloudEvents 语义。** 共 4 个核心方法:
+
+```java
+// 4 个核心 API
+public class CloudEventsClient {
+
+ // 1. 异步发布事件
+ CompletableFuture publish(CloudEvent event);
+
+ // 2. 同步请求-应答(对齐 TCP 同步调用语义,详见 §17)
+ CompletableFuture request(CloudEvent event, Duration timeout);
+
+ // 3. 订阅事件(推送模式由传输层决定,见 §5.3)
+ void subscribe(String topic, Consumer handler);
+
+ // 4. 取消订阅
+ void unsubscribe(String topic);
+
+ // Builder(传输层可插拔)
+ static CloudEventsClientBuilder builder();
+}
+```
+
+### 5.1.1 传输层可插拔(用户按场景选)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:❌ **三传输仅到位 2 种。** SSE✅(`SseConnection` + `/events/stream`)、Long-Polling✅(`LongPollingChannel` + `/events/poll`);**WebSocket(本节默认主传输)完全未实现(G1)**。SDK 侧 `CloudEventsClient` 也仅 Long-Polling,无 WS/SSE 客户端。
+
+SDK 仍是"HTTP SDK",但传输层抽象为三种**用户可选**的 HTTP 家族协议(均不违背 §15.3 "无 gRPC / 无自定义 TCP"铁律)。不同场景选不同协议:
+
+```java
+// 传输层选择(builder 配置)
+CloudEventsClient.builder()
+ .runtimeUrl("http://eventmesh-server:8080")
+ .clientId("order-service-1")
+ .transport(Transport.LONG_POLLING) // 或 WEBSOCKET / SSE,默认 WEBSOCKET
+ .encoding(Encoding.BINARY) // 或 STRUCTURED(JSON),默认 BINARY
+ .build();
+```
+
+| 传输 | 适用场景 | 特点 |
+|------|---------|------|
+| **WebSocket**(默认推送主传输) | 持久订阅事件流、双向控制(unsubscribe/ACK)、高吞吐 | 双向长连接,持续推多消息,可同连接发控制命令 |
+| **SSE** | 单向流式输出(如代理大模型 token 流、A2A agent 流式回传) | 单向服务端→客户端,穿墙极佳,浏览器/LLM 生态成熟,自动重连 |
+| **Long-Polling** | 防火墙严苛、WS 被禁的环境降级 | 兼容性最好,1 RTT 间隙延迟 |
+| HTTP 请求-响应 | publish、request-reply(控制面 + 同步调用) | 非推送,请求即响应 |
+
+> **选型逻辑**:推送主传输默认 WebSocket(双向持久订阅流);若场景是"一次请求触发的单向流式回传"(典型如 LLM token 流)用 SSE;WS 被网络环境拦截时降级 Long-Polling。控制面(publish/subscribe 控制请求)与 request-reply 始终走 HTTP 请求-响应。详见 §13.7.1。
+
+### 5.2 使用示例
+
+```java
+// 初始化客户端(默认 WebSocket 推送 + 二进制编码)
+CloudEventsClient client = CloudEventsClient.builder()
+ .runtimeUrl("http://eventmesh-server:8080")
+ .clientId("order-service-1")
+ .build();
+
+// 1. 发布事件(CloudEvents 格式)
+CloudEvent event = CloudEventBuilder.v1()
+ .withId(UUID.randomUUID().toString())
+ .withSource(URI.create("order-service"))
+ .withType("order.created")
+ .withDataContentType("application/json")
+ .withData(GsonJsonFormat.toJson(data))
+ .build();
+
+client.publish(event);
+
+// 2. 同步请求-应答(对齐 TCP 同步调用)
+CloudEvent request = CloudEventBuilder.v1()
+ .withType("order.query")
+ .withExtension("x-em-reply-to", "reply." + reqId)
+ .withExtension("x-em-correlation-id", reqId)
+ .withData(queryJson)
+ .build();
+CloudEvent reply = client.request(request, Duration.ofSeconds(5)).join(); // 阻塞等应答,超时失败
+
+// 3. 订阅事件
+client.subscribe("order-events", receivedEvent -> {
+ String type = receivedEvent.getType();
+ if ("order.created".equals(type)) {
+ processOrder(receivedEvent);
+ }
+});
+```
+
+### 5.3 内部实现
+
+```
+CloudEventsClient 内部实现:
+ publish():
+ HTTP POST /events/publish
+ body: application/cloudevents-batch+json 或 binary 编码(Encoding.BINARY)
+ 返回: 202 Accepted
+
+ request(): ← 新增:同步请求-应答(详见 §17)
+ HTTP POST /events/request (请求挂起,阻塞等应答)
+ header: x-em-correlation-id, x-em-reply-to
+ 返回: 应答 CloudEvent;超时 → 请求失败
+
+ subscribe(): 推送传输三选一(Transport 配置):
+ WEBSOCKET:
+ 升级 GET /events/stream → ws 长连接
+ 服务端持续推 CloudEvent 帧;客户端同连接发 unsubscribe/ACK 控制帧
+ 适用:持久订阅、高吞吐、双向控制
+ SSE:
+ GET /events/stream Accept: text/event-stream
+ 服务端单向推 data: \n\n,断线自动重连
+ 适用:单向流式输出(LLM token 流、A2A 流式回传)
+ LONG_POLLING:
+ 循环 GET /events/poll?clientId=xxx&topics=xxx&timeout=30s
+ 收到消息 → 回调 handler;循环拉取
+ 适用:WS 被禁的降级场景
+
+ unsubscribe():
+ HTTP POST /events/unsubscribe body: { topic, clientId }
+ (WS 模式下也可走控制帧)
+```
+
+### 5.4 删除清单与不可逆风险注记
+
+| 删除项 | 理由 |
+|--------|------|
+| TCP SDK (`tcp/` 子包) | 协议复杂,维护成本高;HTTP 家族(WS/SSE/Long-Polling)可覆盖全部场景 |
+| gRPC SDK (`grpc/` 子包) | HTTP 请求-响应 + WebSocket 推送可替代;减少依赖 |
+| `EventMeshMessage` 类 | 替换为标准 CloudEvents |
+| `Package` 类 | TCP 协议帧,SDK 简化后不需要 |
+| `HttpCommand` 类 | 旧 HTTP 协议,已被 CloudEvents 替代 |
+| OpenMessaging API (`io.openmessaging.api.*`) | 与 MQ Group 语义绑定,违反"MQ 无语义"原则 |
+| `MeshMessageProtocolAdaptor` | TCP SDK 的 Adaptor |
+| `OpenMessageProtocolAdaptor` | OpenMessaging 的 Adaptor |
+
+> **⚠️ 不可逆风险注记(承接 §15.3)**:全删 TCP+gRPC SDK 是不可逆动作。 当前**主要使用 TCP 协议**,存量 TCP 客户端须迁移到 HTTP 家族 SDK(WebSocket/SSE/Long-Polling + request-reply)。落地前必须:
+> 1. 排查存量 TCP 客户端的**实时性/吞吐/同步调用**需求,确认三种 HTTP 传输可覆盖(毫秒级延迟用 WebSocket、同步调用用 request-reply);
+> 2. 对高 TPS 链路做 **WebSocket+二进制 vs TCP** 的压测对比,确认吞吐可接受;
+> 3. 排查 TCP 特有运维操作(redirect/reject)在新 Admin 面(§13.5.4)的覆盖情况。
+> 以上依赖落地基线(§15.4)确定后启动。
+
+---
+
+## 六、Runtime 入方向重构:统一 IngressHandler
+
+### 6.1 当前 IngressProcessor 的问题
+
+当前 `IngressProcessor` 是 102 个 Processor 类的统一入口,设计良好,但仍有遗留负担:
+
+1. **兼容旧协议**:仍处理 `EventMeshMessage` / `Package` / `HttpCommand` 等旧格式
+2. **多协议路由**:TCP/HTTP/gRPC 复用,但 gRPC SDK 被删除后,gRPC Adaptor 仅剩服务端接收能力
+3. **ProtocolAdaptor 种类过多**:5 个 Adaptor 中 2 个(TCP 的 MeshMessageAdaptor、OpenMessagingAdaptor)在新架构下不再需要
+
+### 6.2 简化后的 IngressHandler
+
+```java
+@HttpHandler("/events")
+public class UnifiedIngressHandler {
+
+ private final IngressPipeline pipeline;
+ private final SubscriptionManager subscriptionManager;
+ private final MeshStoragePlugin storagePlugin;
+
+ /**
+ * POST /events/publish
+ * 客户端发送 CloudEvent 到 EventMesh
+ */
+ public void publish(HttpRequest request, HttpResponse response) {
+ // 1. 解析 CloudEvents
+ CloudEvent event = parseCloudEvent(request);
+
+ // 2. 经过 Ingress Pipeline
+ PipelineResult result = pipeline.process(event);
+
+ // 3. 写入 MQ
+ if (result.getAction() == PipelineResult.Action.CONTINUE) {
+ String topic = determineTopic(event);
+ storagePlugin.send(topic, result.getEvent(), new SendCallback() {
+ @Override
+ public void onSuccess(Object metadata) {
+ response.writeSuccess(202, "Accepted");
+ }
+ @Override
+ public void onError(Throwable t) {
+ response.writeError(500, t.getMessage());
+ }
+ });
+ } else {
+ response.writeError(result.getAction().toHttpStatus(), "Rejected");
+ }
+ }
+
+ /**
+ * POST /events/subscribe
+ * 客户端注册订阅关系
+ */
+ public void subscribe(HttpRequest request, HttpResponse response) {
+ SubscribeRequest req = parse(request, SubscribeRequest.class);
+ String subId = subscriptionManager.subscribe(req, createEmitter(response));
+ response.writeJson(200, new SubscribeResponse(subId));
+ }
+
+ /**
+ * POST /events/unsubscribe
+ * 客户端取消订阅
+ */
+ public void unsubscribe(HttpRequest request, HttpResponse response) {
+ UnsubscribeRequest req = parse(request, UnsubscribeRequest.class);
+ subscriptionManager.unsubscribe(req.getTopic(), req.getSubscriptionId());
+ response.writeSuccess(200, "OK");
+ }
+
+ /**
+ * GET /events/poll
+ * HTTP Long-Polling:客户端拉取已下发的消息
+ * (由 SubscriptionManager 主动填充 emitter,poll handler 读取队列)
+ */
+ public void poll(HttpRequest request, HttpResponse response) {
+ String clientId = request.getParam("clientId");
+ String topics = request.getParam("topics");
+ long timeout = Long.parseLong(request.getParam("timeout", "30000"));
+
+ // 创建挂起的 HTTP 响应(不立即返回)
+ AsyncContext ctx = AsyncContext.startAsync(request, response);
+ subscriptionManager.registerPollChannel(clientId, topics, ctx, timeout);
+ }
+
+ // ── CloudEvents 解析 ──
+ private CloudEvent parseCloudEvent(HttpRequest request) {
+ String contentType = request.getHeader("Content-Type");
+ if (contentType.contains("cloudevents")) {
+ // 标准 CloudEvents 1.0
+ return new CloudEventDecoder().decode(request.getBody());
+ } else if (contentType.contains("json")) {
+ // 兼容:JSON body → CloudEvent(通过 extension 字段扩展)
+ return jsonToCloudEvent(request.getBody());
+ }
+ throw new ProtocolHandleException("Unsupported Content-Type: " + contentType);
+ }
+
+ private String determineTopic(CloudEvent event) {
+ // Topic 来源优先级:
+ // 1. CloudEvents "subject" extension(业务 topic)
+ // 2. 配置文件默认 topic
+ String topic = event.getExtension("subject");
+ return topic != null ? topic : DEFAULT_TOPIC;
+ }
+}
+```
+
+### 6.3 简化的 ProtocolAdaptor 体系
+
+```
+当前(5 个 Adaptor):
+├─ HttpProtocolAdaptor ← 保留(HTTP SDK 入口)
+├─ CloudEventsProtocolAdaptor ← 保留(CloudEvents 规范入口)
+├─ MeshMessageProtocolAdaptor ← 删除(TCP SDK)
+├─ OpenMessageProtocolAdaptor ← 删除(OpenMessaging)
+└─ EnhancedA2AProtocolAdaptor ← 保留(A2A Agent 通信)
+
+重构后(3 个 Adaptor):
+├─ HttpCloudEventsAdaptor ← HTTP → CloudEvent(主流)
+├─ A2AAdaptor ← A2A JSON → CloudEvent(复用 HTTP Adaptor)
+└─ (CloudEvents 直接流通) ← CloudEvents SDK 直接发送,不需要 Adaptor
+```
+
+**ProtocolAdaptor 的职责也简化了:** 只负责"把非 CloudEvents 格式转成 CloudEvents"。HTTP SDK 发送的本身就是 CloudEvents JSON,Adaptor 只做解析,不做格式转换。
+
+---
+
+## 七、Runtime 出方向重构:PushService 替代旧 Consumer 体系
+
+### 7.1 当前 Consumer 体系的问题
+
+当前 EventMesh 的消费者模型混乱:
+
+```
+当前 Consumer 体系:
+├─ TCP Consumer:
+│ ├─ Session 管理(ClientSessionMap)
+│ ├─ 下发模式:Session.send() 直接写 TCP Socket
+│ └─ 订阅管理: ClientGroupPackManagement
+│
+├─ HTTP PushConsumer:
+│ ├─ EventMeshHttpServer.abstractHTTPServer
+│ ├─ LRUCache
+│ └─ 订阅管理: EventMeshHTTPServer.localSubscriptionInfoMap
+│
+└─ gRPC PushConsumer:
+ └─ EventMeshGrpcServer.pushToClient()
+```
+
+**三套 Consumer 管理体系,各自独立维护 Session/Subscription 状态。**
+
+### 7.2 统一 PushService
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ `PushService`(per-client 有界缓冲 + ACK callback 跟踪)+ `TransportChannel` 抽象 + `LongPollingChannel`/`SseConnection` 实现✅;**`WebSocketChannel` 未实现(G1)**;`Connection`/`ConnectionPushPump`(drain 缓冲到连接)有。慢消费者状态机见 §13.6.2(G11)。
+
+重构后只有 HTTP 家族 Consumer,推送通道抽象为 `TransportChannel`,支持三种传输(WebSocket / SSE / Long-Polling),用户按场景选(§5.1.1 / §13.7.1):
+
+```java
+public class PushService {
+
+ // 订阅者推送通道(clientId → TransportChannel),统一三种传输
+ private final Map channels = new ConcurrentHashMap<>();
+
+ // 消息缓冲区(通道未连接 / 慢消费者时暂存,防丢;背压上限见 §13.6.2)
+ private final ConcurrentHashMap> pendingEvents =
+ new ConcurrentHashMap<>();
+
+ /**
+ * 由 SubscriptionManager 调用:有一条消息需要下发给订阅者
+ * channel.send() 内部按传输类型走不同下发路径
+ */
+ public void push(String clientId, CloudEvent event) {
+ TransportChannel channel = channels.get(clientId);
+ if (channel != null && channel.isActive()) {
+ // 立即推送:WS 帧 / SSE data: 行 / Long-Polling 响应
+ channel.send(event);
+ } else {
+ // 通道未连接,存入缓冲区(下次连接时 flush)
+ pendingEvents.computeIfAbsent(clientId, k -> new ConcurrentLinkedQueue<>())
+ .add(event);
+ }
+ }
+
+ /**
+ * 客户端建立推送通道(三种传输统一注册)
+ */
+ public void registerChannel(String clientId, TransportType type, ChannelContext ctx, long timeoutMs) {
+ // 先 flush 缓冲区历史消息
+ ConcurrentLinkedQueue buffer = pendingEvents.remove(clientId);
+
+ TransportChannel channel = TransportChannelFactory.create(type, clientId, ctx, buffer, timeoutMs);
+ channels.put(clientId, channel);
+
+ channel.startTimeoutCheck(() -> {
+ channels.remove(clientId);
+ ctx.complete(); // 超时/断开清理
+ });
+ }
+}
+
+// 传输通道抽象(三种传输统一接口)
+interface TransportChannel {
+ boolean isActive();
+ void send(CloudEvent event); // WS 帧 / SSE data: / LP 响应
+ void startTimeoutCheck(Runnable onTimeout);
+}
+// WSChannel / SSEChannel / LongPollingChannel 三种实现
+```
+
+> **与 request-reply 的关系**:request-reply 的应答推送(§17)也复用 `TransportChannel`——若请求方持 WS 通道,应答可走 WS 推回;否则走原挂起的 HTTP 请求响应返回。push() 与 request-reply 应答共享通道,但语义面不同(§17)。
+
+### 7.3 与旧 Consumer 体系的关系
+
+```
+旧 Consumer 体系 新 PushService
+──────────────── ──────────────
+ClientSessionMap (TCP) → 删除(无 TCP SDK)
+LRUCache → channels (clientId → TransportChannel)
+localSubscriptionInfoMap (HTTP) → SubscriptionManager.topicSubscriptions
+ClientGroupPackManagement (TCP sub) → SubscriptionManager
+EventMeshGrpcServer.pushToClient() → 删除(无 gRPC SDK)
+Session.send() (TCP 直写) → TransportChannel.send()(WS/SSE/LP 统一)
+```
+
+---
+
+## 八、Connector Runtime:与 EventMesh Runtime 完全独立
+
+### 8.1 设计原则
+
+**Connector Runtime 与 EventMesh Runtime 是两个完全独立的进程**,通过标准 HTTP + CloudEvents 接口通信,不共享内部组件。
+
+```
+┌─────────────────────────────────────┐ HTTP / CloudEvents ┌──────────────────────────────────────┐
+│ Connector Runtime │ ←──────────────────────→ │ EventMesh Runtime │
+│ │ │ │
+│ ┌──────────────────────────────────┐ │ POST /events/publish │ ┌──────────────────────────────────┐ │
+│ │ Source/Sink Connector 管理器 │ │ ────────────────────────→ │ │ UnifiedIngressHandler │ │
+│ │ · Connector 生命周期管理 │ │ │ │ → IngressPipeline │ │
+│ │ · 外部系统数据拉取/写入 │ │ │ │ → Storage (MQ) │ │
+│ │ · Source offset 管理(独立) │ │ │ └──────────────────────────────────┘ │
+│ │ · Sink offset 管理(独立) │ │ SubscriptionManager │ │
+│ └──────────────────────────────────┘ │ ←─────────────────────── │ ┌──────────────────────────────────┐ │
+│ │ GET /events/poll │ │ SubscriptionManager │ │
+│ Source/Sink Connector 代码结构: │ │ │ · 自主 offset 管理 (RocksDB) │ │
+│ · SourceConnector.poll() → CloudEvent │ │ · 订阅关系 (topic→clientIds) │ │
+│ · SinkConnector.put(CloudEvent) │ │ │ · 分发策略 (LB/Bcast/Mcast) │ │
+│ · 各自维护自己的 offset(独立 RocksDB) │ └──────────────────────────────────┘ │
+└─────────────────────────────────────┘ └──────────────────────────────────────┘
+```
+
+### 8.2 两者的边界
+
+| 职责 | Connector Runtime | EventMesh Runtime |
+|------|-----------------|-------------------|
+| **数据拉取** | Source Connector 从外部系统拉取 | 从 MQ 拉取(供下游客户端订阅) |
+| **数据写入** | Sink Connector 写入外部系统 | 写入 MQ(来自客户端发布) |
+| **Pipeline** | Source/Sink 各自可选的轻量 Transform | 完整的 Ingress/Egress Pipeline(ACL/RateLimit/Filter) |
+| **订阅模型** | 无(只拉外部数据) | 有(LOAD_BALANCE / BROADCAST / MULTICAST) |
+| **Offset 管理** | Source 拉取 offset + Sink 写入确认 offset(各自独立) | Consumer 消费 offset(EventMesh 自主管理,详见 §4.4) |
+| **部署模型** | 独立进程,独立部署 | 独立进程,独立部署 |
+
+### 8.3 Connector → EventMesh 数据流(Source Connector)
+
+```
+外部系统 (MySQL/Redis/MQ/...)
+ ↓
+SourceConnector.poll() ← Connector Runtime 管理
+ ↓ CloudEvent
+HTTP POST /events/publish ← 标准 CloudEvents HTTP
+ ↓
+EventMesh UnifiedIngressHandler
+ ↓
+IngressPipeline (ACL → RateLimit → Filter)
+ ↓
+Storage.send() → Kafka/RocketMQ ← EventMesh Runtime 管理
+```
+
+### 8.4 EventMesh → Connector 数据流(Sink Connector)
+
+```
+Kafka/RocketMQ ← EventMesh Runtime 管理(Storage.poll())
+ ↓
+EgressPipeline (Enrichment → Filter) ← EventMesh Runtime 管理
+ ↓
+HTTP POST /connector/sink/{connectorId} ← 标准 CloudEvents HTTP
+ ↓
+SinkConnector.put(CloudEvent) ← Connector Runtime 管理
+ ↓
+外部系统 (MySQL/Redis/HTTP API/...)
+```
+
+### 8.5 Connector Runtime 内部结构
+
+```java
+public class ConnectorRuntime {
+
+ private final Map sources = new ConcurrentHashMap<>();
+ private final Map sinks = new ConcurrentHashMap<>();
+ private final OffsetStore offsetStore; // Connector 自有的 RocksDB offset store
+ private final HttpClient httpClient; // 向 EventMesh 发送 HTTP 请求
+
+ public void start() {
+ // 1. 加载 Connector 配置(conf/connectors.yaml)
+ List defs = loadConnectorDefs();
+
+ // 2. 启动 Source Connector
+ for (ConnectorDef def : defs.getSources()) {
+ SourceConnector connector = createSourceConnector(def);
+ sources.put(def.getId(), connector);
+ executor.submit(() -> runSource(connector, def));
+ }
+
+ // 3. 启动 Sink Connector(监听 EventMesh 下发)
+ for (ConnectorDef def : defs.getSinks()) {
+ SinkConnector connector = createSinkConnector(def);
+ sinks.put(def.getId(), connector);
+ startSinkListener(connector, def);
+ }
+ }
+
+ // Source: 拉取外部数据 → 发布到 EventMesh
+ private void runSource(SourceConnector connector, ConnectorDef def) {
+ String eventMeshTopic = def.getTargetTopic();
+ while (running) {
+ List events = connector.poll();
+ for (CloudEvent event : events) {
+ try {
+ // HTTP POST 到 EventMesh
+ httpClient.post("/events/publish", event);
+ connector.commit(event); // 提交 Source offset
+ } catch (Exception e) {
+ connector.retry(event); // 重试策略
+ }
+ }
+ }
+ }
+
+ // Sink: 接收 EventMesh 下发 → 写入外部系统
+ private void startSinkListener(SinkConnector connector, ConnectorDef def) {
+ String sourceTopic = def.getSourceTopic();
+ // HTTP Long-Polling 订阅 EventMesh
+ String pollUrl = String.format("/connector/sink/%s/poll", connector.getId());
+
+ // EventMesh SubscriptionManager 按 BROADCAST 模式将 MQ 数据下发
+ // Sink 通过 HTTP Long-Polling 接收
+ while (running) {
+ List events = httpClient.poll(pollUrl, sourceTopic);
+ if (!events.isEmpty()) {
+ connector.put(events);
+ connector.flush();
+ // 确认 offset(写入 Connector 自有的 RocksDB)
+ connector.commitOffset(events.get(events.size() - 1));
+ }
+ }
+ }
+}
+```
+
+### 8.6 Connector Runtime 与 EventMesh Runtime 的部署关系
+
+```
+独立部署(推荐):
+ ConnectorRuntime 进程 EventMeshRuntime 进程
+ ┌─────────────────┐ ┌─────────────────────┐
+ │ Source/Sink │ │ IngressPipeline │
+ │ OffsetStore │ │ SubscriptionManager │
+ │ HTTP Client │ ←──→ │ OffsetStore │
+ └─────────────────┘ │ PushService │
+ │ AdminClient │
+ └─────────────────────┘
+ ↓ ↑
+ Kafka/RocketMQ Kafka/RocketMQ
+ (独立部署) (独立部署)
+
+共用同一 MQ 集群(但各自使用独立 topic,互不干扰)
+```
+
+### 8.7 删除清单(Connector 相关)
+
+| 删除项 | 理由 |
+|--------|------|
+| `ConnectorRuntime` 类 | 独立为 `ConnectorRuntime` 进程,不再是 EventMesh Runtime 内部类 |
+| `ConnectorRuntimeService` | EventMesh Runtime 不再持有 Connector |
+| `SourceRunner` / `SinkRunner` 内部线程 | Connector Runtime 自己管理线程 |
+| `BlockingQueue` 旁路 | Connector → EventMesh 走 HTTP,不再走内存队列 |
+| Connector 的 `System.exit(-1)` Bug(Source/Sink finally 块) | Connector Runtime 重写后不会出现此问题 |
+
+### 8.8 保留清单(Connector Runtime 自身)
+
+| 保留项 | 说明 |
+|--------|------|
+| `SourceConnector` 抽象类 | Connector Runtime 的 Source 基类 |
+| `SinkConnector` 抽象类 | Connector Runtime 的 Sink 基类 |
+| 各 Connector 实现(Kafka/RocketMQ/MySQL/Redis/...) | 独立维护 |
+| Connector 配置管理(config yaml) | Connector Runtime 自己管理 |
+| Connector offset 管理 | Connector Runtime 的 `OffsetStore` 自己管理 |
+
+### 8.9 Connector Runtime 与 Meta / S3Stream 集成(§15.8 补充)
+
+Connector Runtime 是独立进程,但其与 Meta / 存储后端的关系须明确,避免与 EventMesh Runtime 的控制面混淆。
+
+**① 与 Meta 的关系:只读 + 自有 offset,不参与分区协调**
+
+```
+Connector Runtime ≠ EventMesh Runtime,不持有 MQ 分区租约:
+ · 不参与 §13.2.8 分区分配协议(那是 EventMesh Runtime 拉取 MQ 的事)
+ · Connector 的"offset"是外部系统的进度(如 MySQL binlog position、Kafka 源 offset),
+ 与 EventMesh 分发 offset(§13.2.4)完全正交
+
+Connector 与 Meta 的交互(只读 + 上报):
+ · 读:从 Meta 发现 EventMesh Runtime 地址(POST /events/publish 的目标)
+ · 读:从 Meta 读 ACL/限流规则(若 Connector 也过 Pipeline,见 ②)
+ · 写:Connector offset 远程副本上报(独立 key 空间,见 ③)
+ · 不写:分区分配表、订阅视图(那些是 EventMesh Runtime 的)
+```
+
+**② Connector 与存储后端(含 S3Stream)的关系**
+
+```
+Source Connector:
+ 外部系统 → SourceConnector.poll() → CloudEvent
+ → HTTP POST /events/publish → EventMesh Runtime → Storage.send(MQ/S3Stream)
+ · Connector 不直接写 MQ/S3Stream,而是经 EventMesh Runtime(走 IngressPipeline,过 ACL/限流)
+ · 这样 Connector 复用 EventMesh 的安全/限流/trace,不自建
+
+Sink Connector:
+ EventMesh Runtime → Storage.poll() → SubscriptionManager(BROADCAST 模式)
+ → HTTP /connector/sink/{id}/poll 下发 → SinkConnector.put() → 外部系统
+ · Sink 通过 HTTP Long-Polling 从 EventMesh 拉(§8.5),不直接读 MQ/S3Stream
+ · S3Stream 作为 EventMesh 存储后端时,Sink 无感知(对它就是 HTTP CloudEvents)
+```
+
+**③ Connector offset 的 Exactly-Once 落地(§16 层面 A)**
+
+```
+Connector 自有 offset 两级存储(与 EventMesh 分发 offset 独立):
+ ├─ 本地 RocksDB(Connector 进程内)
+ └─ 远程 Admin Server(注意:是 Admin Server,不是 Meta)
+
+ 对比 EventMesh 分发 offset(§13.2.4):
+ EventMesh:本地 RocksDB + Meta(控制面)
+ Connector:本地 RocksDB + Admin Server(管理面)
+ → 两套 offset 互不干扰,各自独立实现 EO
+
+Source EO 流程:
+ 1. poll 外部数据 → 本地 RocksDB 记 source offset
+ 2. HTTP POST /events/publish(等 EventMesh 202 Accepted)
+ 3. publish 成功 → commit source offset 到 Admin Server
+ 4. publish 失败 → 不 commit,重试(本地 offset 未推进,重拉同批)
+ → 至少一次 publish + offset 不超前 = 源端不丢不重
+
+Sink EO 流程:
+ 1. HTTP poll 从 EventMesh 拉一批 CloudEvent
+ 2. SinkConnector.put() 写外部系统(需外部系统幂等或事务)
+ 3. 写成功 → commit sink offset(已处理到哪)到 Admin Server
+ 4. 写失败 → 不 commit,重试(重拉同批,靠外部系统幂等去重)
+ → Sink 的 EO 依赖外部系统幂等(与 §15.2 客户端幂等同理)
+```
+
+**④ Connector 配置(含 S3Stream 间接使用)**
+
+```yaml
+# conf/connectors.yaml(Connector Runtime 自管)
+sources:
+ - id: mysql-cdc-1
+ type: jdbc
+ config: { ... }
+ targetTopic: tenantA.orders # 发布到 EventMesh 的 topic
+ eventmeshRuntime: meta-discovery # 从 Meta 发现 Runtime 地址
+
+sinks:
+ - id: redis-sink-1
+ type: redis
+ config: { ... }
+ sourceTopic: tenantA.orders
+ eventmeshRuntime: meta-discovery
+
+# 注:Connector 不配 S3Stream——它经 EventMesh Runtime 间接使用存储
+# eventmesh.storage.type=s3stream 是 EventMesh Runtime 的配置(§3.5)
+```
+
+> **小结**:Connector Runtime 与 EventMesh Runtime 通过 HTTP+CloudEvents 解耦;Connector 不碰 MQ/S3Stream、不参与分区协调;其 offset 独立于 EventMesh 分发 offset(本地 RocksDB + Admin Server),实现源端 EO。S3Stream 对 Connector 透明。
+
+---
+
+## 九、协议启动入口统一化
+
+### 9.1 当前状态
+
+```
+旧入口:
+├─ EventMeshStartup.main() → EventMeshBootstrap (init/start/shutdown)
+│ ├─ EventMeshHttpBootstrap
+│ ├─ EventMeshTcpBootstrap
+│ ├─ EventMeshGrpcBootstrap
+│ └─ EventMeshAdminBootstrap
+│
+└─ RuntimeInstanceStarter (无 main) → Runtime (init/start/stop)
+ ├─ ConnectorRuntime
+ ├─ FunctionRuntime
+ └─ MeshRuntime
+```
+
+### 9.2 统一启动入口
+
+```java
+// 单一入口
+public class EventMeshApplication {
+
+ public static void main(String[] args) {
+ // 1. 加载配置
+ EventMeshConfig config = loadConfig(args);
+
+ // 2. 初始化组件(可插拔,按需启动)
+ MeshStoragePlugin storage = StoragePluginLoader.load(config.getStorageType());
+
+ // 3. 构造 Runtime 上下文
+ RuntimeContext ctx = RuntimeContext.builder()
+ .config(config)
+ .storage(storage)
+ .ingressPipeline(new IngressPipeline(config))
+ .egressPipeline(new EgressPipeline(config))
+ .subscriptionManager(new SubscriptionManager())
+ .pushService(new PushService())
+ .connectorRuntimeService(new ConnectorRuntimeService(storage))
+ .adminClient(new AdminClient(config))
+ .build();
+
+ // 4. 启动
+ ctx.start();
+
+ // 5. 注册优雅停机
+ Runtime.getRuntime().addShutdownHook(new Thread(ctx::shutdown, "shutdown-hook"));
+
+ System.out.println("EventMesh Runtime started: " + config.getNodeId());
+ }
+}
+```
+
+**删除的旧入口:**
+
+| 删除项 | 理由 |
+|--------|------|
+| `EventMeshStartup` | 替换为 `EventMeshApplication` |
+| `EventMeshBootstrap` 接口 | 替换为 `RuntimeContext` |
+| `EventMeshHttpBootstrap` | 替换为 `UnifiedIngressHandler` |
+| `EventMeshTcpBootstrap` | 无 TCP SDK,删除 |
+| `EventMeshGrpcBootstrap` | 无 gRPC SDK,删除 |
+| `EventMeshAdminBootstrap` | 管理接口整合到 `AdminClient` |
+| `Runtime` / `RuntimeInstanceStarter` | v2 完全废弃,功能整合到统一 Runtime |
+
+---
+
+## 十、文件删除清单
+
+### 10.1 按模块分类
+
+#### eventmesh-sdk-java(大幅精简)
+
+| 删除 | 说明 |
+|------|------|
+| `tcp/` 全部子包 | TCP SDK,无 gRPC SDK |
+| `grpc/` 全部子包 | HTTP SDK 可覆盖 |
+| `producer/impl/EventMeshTCPProducer.java` | TCP 协议 |
+| `producer/impl/EventMeshGrpcProducer.java` | gRPC 协议 |
+| `consumer/EventMeshTCPClient.java` | TCP 客户端 |
+| `consumer/EventMeshHttpClient.java` | 替换为 `CloudEventsClient` |
+| `protocol/` 全部子包 | 自定义协议,非 CloudEvents |
+| `common/` 中的 `EventMeshMessage.java` | 替换为 CloudEvents |
+| `common/` 中的 `Package.java` | TCP 协议帧 |
+| `common/` 中的 `Command.java` | TCP 命令码 |
+
+#### eventmesh-protocol-plugin(精简)
+
+| 删除 | 说明 |
+|------|------|
+| `eventmesh-protocol-meshmessage/` | TCP SDK 的 ProtocolAdaptor |
+| `eventmesh-protocol-openmessage/` | OpenMessaging,与 MQ Group 绑定 |
+| `eventmesh-protocol-grpc/` + `eventmesh-protocol-grpcmessage/` | gRPC SDK 协议插件 |
+
+#### eventmesh-storage-plugin(精简)
+
+| 删除 | 说明 |
+|------|------|
+| `consumerGroup` / `producerGroup` 配置项 | MQ 语义,不暴露 |
+| `MeshMQProducer.createTransactionProducer()` | 事务消息过于复杂 |
+| `subscribe(topic, subExpression)` 的 `subExpression` | MQ Tag 过滤语义 |
+| RocketMQ: `DefaultMQPushConsumer` 的 `consumerGroup` | EventMesh 自己管理订阅 |
+
+#### eventmesh-runtime(精简)
+
+| 删除 | 说明 |
+|------|------|
+| `protocol/tcp/` 全部子包 | 无 TCP SDK |
+| `protocol/grpc/` 全部子包 | 无 gRPC SDK |
+| `processor/tcp/` 全部 Processor | TCP 协议处理器 |
+| `processor/grpc/` 全部 Processor | gRPC 协议处理器 |
+| `processor/http/` 大部分 Processor | 简化为 `UnifiedIngressHandler` |
+| `Session` / `ClientSession` 体系 | TCP Session |
+| `ClientGroupPack` / `ClientGroupPackManagement` | Consumer Group 管理 |
+| `EventMeshTcpServer` | TCP Server |
+| `EventMeshGrpcServer` | gRPC Server |
+| `HelloProcessor` / `GoodbyeProcessor` | TCP 会话管理 |
+| `SubscribeProcessor` / `UnSubscribeProcessor` | TCP 订阅 |
+
+#### eventmesh-common(精简)
+
+| 删除 | 说明 |
+|------|------|
+| `protocol/tcp/Package.java` | TCP 帧格式 |
+| `protocol/http/HttpCommand.java` | 旧 HTTP 协议 |
+| `protocol/http/HttpRequestProtocolRequest.java` | 旧 HTTP 协议 |
+| `common/Message.java` | 旧消息格式 |
+| `protocol/asm/` 全部 | TCP ASM 字节码(会话加密) |
+
+### 10.2 预期代码量变化
+
+| 模块 | 当前行数(估算) | 重构后行数(估算) | 变化 |
+|------|----------------|------------------|------|
+| eventmesh-sdk-java | ~15,000 | ~3,000 | -80% |
+| eventmesh-protocol-plugin | ~6,885 | ~1,500 | -78% |
+| eventmesh-storage-plugin | ~5,000 | ~3,500 | -30% |
+| eventmesh-runtime | ~32,331 | ~15,000 | -54% |
+| eventmesh-common | ~12,000 | ~6,000 | -50% |
+| **合计** | **~71,000** | **~29,000** | **-59%** |
+
+---
+
+## 十一、重构分阶段实施计划
+
+> **v1.1 更新**:原 Phase 1–8 仅覆盖"减法"与单实例核心数据通路。对照 §13 能力缺口,新增 7 个补充阶段(Phase 2.5 / 4.5 / 5.5 / 5.6 / 6.6 / 7.5 / 8.5),分别补齐多实例协调、安全、下发可靠性、可观测性、运维、Admin 重做、接入扩展。**Phase 1–8 完成 ≠ 可上生产**;🔴 阻断/高优先级的补充阶段为生产前硬性前置。
+
+### 阶段总览与依赖
+
+```
+Phase 1 Storage Plugin 重构(MQ 无语义化 + S3Stream 多后端,§15.8)
+ │
+Phase 2 SubscriptionManager 新增(单实例分发逻辑)
+ │
+Phase 2.5 🔴 多实例消费协调(控制面=Meta,§15.5) ← 阻断,生产 HA 前必做
+ │ (Meta 分区分配+租约 / offset 两级存储 / 订阅关系同步 / 实例间转发)
+ │
+Phase 3 SDK 简化(HTTP 家族,4 API 含 request-reply,三传输默认 WS,§15.6/§15.7)
+ │
+Phase 3.5 🔴 request-reply 同步调用 ← 对齐 TCP 同步调用,超时丢弃
+ │
+Phase 4 Runtime 入方向简化(UnifiedIngressHandler)
+ │
+Phase 4.5 🔴 安全 ← TLS/mTLS + 认证鉴权 + 租户隔离 + 签名
+ │
+Phase 5 Runtime 出方向简化(PushService,TransportChannel 三传输 + 虚拟线程,§15.8)
+ │
+Phase 5.5 🔴 下发可靠性 ← ACK + 重试 + DLQ + STICKY 顺序 + 去重
+ │
+Phase 5.6 🟠 可观测性 ← metrics + trace 传播 + 消息轨迹
+ │
+Phase 6 Connector Runtime 独立化
+ │
+Phase 6.6 🟠 运维 ← 限流 + 背压 + 动态配置 + 优雅停机 + 连接管理
+ │
+Phase 7 启动入口统一(Java 21)
+ │
+Phase 7.5 🔴 Admin 管理面重做 ← 替换依赖 TCP/Group 的旧 handler
+ │
+Phase 8 清理收尾
+ │
+Phase 8.5 🟡 接入扩展 ← WebHook 推送 + MQTT 声明(WS/SSE/批量已在 Phase 3/5 核心)
+
+生产就绪门槛 = Phase 1–8 + 2.5 + 3.5 + 4.5 + 5.5 + 7.5 全部完成
+```
+
+> **🔎 实现状态 vs 门槛(v1.11 / 2026-07-06 盘点)**:**当前代码未达此门槛,不可上生产 HA。** 门槛所列 Phase 中,**Phase 2.5(多实例协调)实质未完成(❌,G2–G6)**、Phase 5 的 WebSocket 主传输未实现(G1);Phase 3.5/4.5/5.5/7.5 为 ⚠️ 部分完成。即"单实例快乐路径"可用,"多实例 HA + 默认 WS 推送"待补。逐项状态见各 Phase 标题处标注,证据见附录 F。
+
+| 阶段 | 优先级 | 类型 | 详细设计 |
+|------|--------|------|---------|
+| Phase 1 | P0 | 减法 | §3 / §15.8 |
+| Phase 2 | P0 | 核心 | §4 |
+| **Phase 2.5** | 🔴 P0 | 补充 | §13.2 / §15.5 |
+| Phase 3 | P0 | 核心 | §5 / §15.6 |
+| **Phase 3.5** | 🔴 P0 | 补充 | §17 / §15.7 |
+| Phase 4 | P1 | 核心 | §6 |
+| **Phase 4.5** | 🔴 P1 | 补充 | §13.4 |
+| Phase 5 | P0 | 核心 | §7 / §15.6 / §15.8 |
+| **Phase 5.5** | 🔴 P1 | 补充 | §13.3 |
+| **Phase 5.6** | 🟠 P2 | 补充 | §13.5.1–13.5.3 |
+| Phase 6 | P1 | 独立 | §8 |
+| **Phase 6.6** | 🟠 P2 | 补充 | §13.6 |
+| Phase 7 | P2 | 收口 | §9 / §15.8 |
+| **Phase 7.5** | 🔴 P1 | 补充 | §13.5.4 |
+| Phase 8 | P3 | 收尾 | — |
+| **Phase 8.5** | 🟡 P3 | 补充 | §13.7.2 |
+
+---
+
+### Phase 1:Storage Plugin 重构(最底层,先行)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ `MeshStoragePlugin` 接口(send / poll-by-offset / assignPartitions / commitOffset)+ Kafka/RocketMQ 原生 pull 模式已实现并去 Group 配置;**但 Kafka 仍设 `group.id="eventmesh-storage-internal"` 违反"MQ 无语义"铁律(附录 F G7)**,S3Stream 后端未实现(附录 F.5),`sendBatch` 批量未实现。
+
+**目标**:MQ 无语义化改造,Storage Plugin 只暴露 send/poll,不暴露任何 Group 概念
+
+```
+阶段产出:
+├─ MeshStoragePlugin 接口重定义(send/poll/start/shutdown)
+├─ KafkaStoragePlugin 重实现(单 Producer + 单 Consumer)
+├─ RocketMQStoragePlugin 重实现(单 Producer + 单 PushConsumer)
+└─ 删除 producerGroup / consumerGroup 全部配置项
+```
+
+> **接口预留(为 Phase 2.5 多实例协调)**:本阶段在 `MeshStoragePlugin` 预留 `assignPartitions(topic, partitions)` 与 `poll(topic, partition, startOffset, maxEvents, timeoutMs)` 方法签名(详见 §13.2.3),先返回全量分区,Phase 2.5 再接 Admin Server 分配。
+
+**影响范围**:Storage Plugin 模块内部,不影响 Runtime 或 SDK
+**测试**:单元测试 Storage Plugin 的 send/poll
+**DoD(验收标准)**:
+- [ ] `MeshStoragePlugin` 接口仅含 send/poll/commitOffset/assignPartitions/start/shutdown,无任何 Group/Tag 配置
+- [ ] Kafka/RocketMQ/S3Stream 三实现均通过 send→poll 往返测试
+- [ ] S3Stream v1 薄包装能连真实 S3Stream endpoint 读写(§3.6.3)
+- [ ] 删除全部 producerGroup/consumerGroup/tag 配置项,启动不报缺失
+- [ ] 接口预留 `assignPartitions` / 按 offset 范围 `poll`(供 Phase 2.5)
+
+### Phase 2:SubscriptionManager 新增(核心逻辑)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:✅ 三分发模式(LOAD_BALANCE / BROADCAST / MULTICAST)+ LOAD_BALANCE_STICKY(单实例按 `partitionkey` hash 粘性)+ 心跳过期清理已实现并单元测试(5 测试全绿)。
+
+**目标**:实现 EventMesh 自主订阅分发逻辑
+
+```
+阶段产出:
+├─ SubscriptionManager 完整实现(LOAD_BALANCE/BROADCAST/MULTICAST)
+├─ PushService 实现(HTTP Long-Polling)
+├─ SubscriptionManager 接入 Storage Plugin(MQ poll → 分发)
+└─ SubscriptionManager 接入 PushService(分发 → HTTP 下发)
+```
+
+**影响范围**:新增类,不影响现有代码
+**测试**:SubscriptionManager 单元测试(模拟 Storage poll + 验证分发逻辑)
+**DoD**:
+- [ ] 三种分发模式(LOAD_BALANCE/BROADCAST/MULTICAST)分发结果正确(单元测试覆盖)
+- [ ] selectTargets 过滤心跳过期订阅者
+- [ ] Storage.poll → selectTargets → PushService.push 链路打通(单实例)
+- [ ] 订阅/退订线程安全(并发 subscribe/unsubscribe 无竞态)
+
+### Phase 2.5:多实例消费协调(🔴 阻断,生产 HA 前必做)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:❌ **阻断,实质未完成。** 分区不重叠拉取(G2)、gen fencing 防脑裂(G3)、实例间转发(G4,`EventMeshApplication` 直接 `return false`)、offset 两级存储(G5,仅本地 RocksDB)**均未接**;Meta 仅 Nacos 后端且有 prefix-scan/CAS 缺陷(G6)。`ClusterCoordinator`/`ClusterMembership`/`PartitionAssigner`/`ClusterSubscriptionStore` 有骨架但 boot 未 wire assigner、未调 `assignPartitions`。**当前多实例部署 = 每实例全量拉取 + 重复消费。** DoD 全部 ❌。
+
+**目标**:解决单 Consumer 全量拉取在多实例下的重复消费/竞争问题,让方案可横向扩展。详细设计见 §13.2。
+
+```
+阶段产出:
+├─ 分区分配 + 租约机制
+│ ├─ Admin Server 主导:topic#partition → ownerInstanceId + 租约续约
+│ └─ 降级模式:实例从 MetaService 拿全量列表,按 partition%N 自洽分配
+├─ MeshStoragePlugin 接入分配结果
+│ ├─ assignPartitions(topic, partitions) 实装(替换全量 assign)
+│ └─ poll(topic, partition, startOffset, ...) 按 offset 范围拉取
+├─ Offset 集中存储(两级)
+│ ├─ 本地 RocksDB(缓存 + 降级可用,§12.6)
+│ └─ 远程(Admin Server / MetaService KV)异步同步 + 启动拉取
+│ └─ readOffset = max(local, remote),clientId 迁移不丢进度
+├─ 订阅关系集群同步
+│ ├─ 集群级订阅视图(MetaService KV / Admin Server):topic → Set
+│ ├─ 本地缓存全量视图(MetaService watch 推送变更)
+│ └─ subscribe()/unsubscribe() 写集群级 + 广播失效
+├─ 实例间消息转发
+│ ├─ clientId → 所在实例的路由表(MetaService 维护)
+│ ├─ 分区拥有者拉到消息 → selectTargets → 查目标 clientId 所在实例
+│ └─ 跨实例 HTTP POST /internal/forward 转发到目标实例投递
+└─ 负载均衡全局性
+ ├─ LOAD_BALANCE_STICKY 按 partitionkey 粘性(全局一致,§13.3.3)
+ └─ 纯 RoundRobin 跨实例退化为实例内 + 分区天然分流(可接受)
+```
+
+**影响范围**:SubscriptionManager、MeshStoragePlugin、MetaService 接入;新增分区分配器、实例间转发模块
+**依赖**:Phase 2(SubscriptionManager 已存在)、Meta 注册中心可用(§15.5)
+**测试**:
+- 单实例:分区分配不退化原有功能
+- 多实例:2+ 实例下分区不重叠、无重复消费
+- 故障注入:杀一个实例 → 分区租约到期 → 其他实例接管 → clientId offset 从远程恢复不丢
+- 降级:Meta 不可用 → 实例自洽分配仍能运行
+**DoD(🔴 阻断阶段,生产 HA 前必做)**:
+- [ ] 2+ 实例下分区无重叠拉取(验证不重复消费)
+- [ ] gen fencing 生效:模拟网络分区,旧 owner 自停 poll(§13.2.8 ④)
+- [ ] offset 两级存储:clientId 迁移实例后从 Meta 恢复进度,零重放
+- [ ] 集群级订阅视图:subscribe 打到实例 A,拉消息在实例 B,订阅者仍收到
+- [ ] 实例间转发:跨实例 clientId 能收到下发
+- [ ] 降级:Meta 挂后实例自洽分配,publish/已有下发不中断(§13.2.9)
+- [ ] Meta 恢复后渐进对齐(offset/订阅/分配),无数据丢失
+
+### Phase 3:SDK 简化(客户端影响最大,Phase 2 完成后做)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ `CloudEventsClient` 4 API(publish/request/reply/subscribe/unsubscribe)齐全;**但仅 Long-Polling 一种传输**(无 WebSocket/SSE 客户端,G16)、无批量 `publish(List)`、poll 异常只 log 不自动重连、ACK 在 handler 返回后自动触发(客户端无法控制 ACK 时机做幂等窗口)。TCP/gRPC SDK 已物理删除 ✅。
+
+**目标**:HTTP-only CloudEvents SDK
+
+```
+阶段产出:
+├─ CloudEventsClient(新类)
+├─ CloudEventsClientBuilder
+├─ subscribe/unsubscribe 实现(HTTP Long-Polling 轮询)
+├─ publish 实现(HTTP POST CloudEvents JSON)
+└─ 集成测试(publish → subscribe → 验证收到)
+```
+
+**废弃(不影响 Runtime 运行,仅废弃 SDK API)**:
+- TCP SDK 代码冻结,文档标记 `@Deprecated`
+- gRPC SDK 代码冻结,文档标记 `@Deprecated`
+- OpenMessaging API 标记 `@Deprecated`
+
+**DoD**:
+- [ ] `CloudEventsClient` 4 API(publish/request/subscribe/unsubscribe)可用
+- [ ] 三传输(WebSocket/SSE/Long-Polling)可切换且各自推送正常(§5.1.1)
+- [ ] 二进制 CloudEvents 编码默认(§15.8)
+- [ ] 端到端:publish → subscribe → 收到(三传输各跑通)
+- [ ] SDK 自动重连(连接断开后恢复订阅)
+- [ ] TCP/gRPC/OpenMessaging 标记 `@Deprecated`,编译警告可见
+
+### Phase 3.5:request-reply 同步调用(🔴 高优先级,对齐 TCP 同步调用)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ 同实例 request→reply + 超时丢弃迟到应答 + correlationId 匹配表已实现并测试;**但跨实例自寻址(`x-em-reply-instance` + `/internal/reply-forward`,§17.6)未实装(G10)**——请求方与响应方在不同实例时应答丢失。
+
+**目标**:对齐 TCP 同步调用语义,新增 RPC-over-bus 能力。详细设计见 §17。
+
+```
+阶段产出:
+├─ request() API(§5.1 第 4 个 API)
+├─ HTTP 映射
+│ ├─ POST /events/request(请求挂起,阻塞等应答)
+│ ├─ POST /events/reply(带 correlationId)
+│ └─ 路由:复用 §13.2.5 clientId→instance 路由表(存 Meta)做跨实例应答
+├─ correlationId 匹配表 + 超时清理
+├─ 超时处理:超时即失败,迟到应答默认丢弃(§15.7)
+└─ 语义边界:与 at-least-once pub/sub 独立,不重投/不进 DLQ
+```
+
+**影响范围**:UnifiedIngressHandler 加 request/reply 端点、SDK 加 request() API
+**依赖**:Phase 3(SDK)、Phase 4(IngressHandler)、Phase 2.5(Meta 路由表,跨实例应答)
+**测试**:request→reply 正常返回;超时失败、迟到应答丢弃;跨实例应答路由正确;request 不触发 DLQ
+**DoD(🔴 高优先级)**:
+- [ ] `request(event, timeout)` 正常收到应答
+- [ ] 超时返回失败,迟到应答被丢弃(§15.7)
+- [ ] 跨实例应答:请求方与响应方在不同实例,应答自寻址路由正确(§17.6,不查 Meta 全局表)
+- [ ] request-reply 不重投、不进 DLQ(与 at-least-once 语义隔离)
+- [ ] correlationId 匹配表超时清理无泄漏
+
+### Phase 4:Runtime 入方向简化
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:✅ `UniHttpServer` 端点齐全(publish/subscribe/unsubscribe/poll/ack/request/reply/stream),基于 JDK `HttpServer` + 虚拟线程(注释提 production 可换 netty `AbstractHTTPServer`)。旧 24 个 HTTP Processor 已替换。
+
+**目标**:UnifiedIngressHandler 替代 24 个 HTTP Processor
+
+```
+阶段产出:
+├─ UnifiedIngressHandler(统一 HTTP 入口)
+│ ├─ publish() → IngressPipeline → Storage.send()
+│ ├─ subscribe() → SubscriptionManager.subscribe()
+│ ├─ unsubscribe() → SubscriptionManager.unsubscribe()
+│ └─ poll() → PushService.registerPollChannel()
+├─ 删除 20+ 个旧的 HTTP Processor
+├─ 删除 MeshMessageProtocolAdaptor / OpenMessageProtocolAdaptor
+└─ IngressPipeline 精简(去掉处理 Package / HttpCommand 的分支)
+```
+
+**测试**:集成测试(CloudEvents SDK → UnifiedIngressHandler → Storage → 验证)
+**DoD**:
+- [ ] UnifiedIngressHandler 统一入口(publish/subscribe/unsubscribe/poll/request/reply)
+- [ ] 删除 20+ 旧 HTTP Processor,无残留路由
+- [ ] IngressPipeline 去掉 Package/HttpCommand 分支,仅处理 CloudEvents
+- [ ] CloudEvents binary + structured 两种编码均能解析(§13.8.1)
+
+### Phase 4.5:安全能力(🔴 高优先级,生产前必做)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ `FilterChain` + `IngressFilter` 体系 + `TokenAuthFilter` / `SignatureVerifierFilter`(HMAC-SHA256)已实现并测试;**但 `AclFilter` 是 `Map>` 静态骨架,非文档 §13.4.2 规则模型(无 priority/DENY/action/Meta-watch,G9)**;TLS 有 `TlsContextFactory`(真生成 SSLContext + mTLS truststore)但 `EventMeshApplication.main` **默认不接线** TLS/FilterChain/Legacy(G14),且硬编码 TLSv1.2、truststore 密码不独立。
+
+**目标**:补齐 TLS/mTLS、认证鉴权、租户隔离、CloudEvents 签名。HTTP-only 方案若明文传输则不可上生产。详细设计见 §13.4。
+
+```
+阶段产出:
+├─ TLS / mTLS(复用 develop 的 SslContextFactory + EventMeshTlsConfig)
+│ ├─ HTTPS Long-Polling
+│ ├─ TlsMode:DISABLED / PERMISSIVE(平滑迁移)/ ENFORCING
+│ └─ 双向认证:tls.server.client.auth = NONE/OPTIONAL/REQUIRE + truststore
+├─ AuthFilter(认证:你是谁)—— 内置 IngressFilter(不装 SPI 插件)
+│ ├─ 内置 TokenAuthFilter;扩展认证 = 新增 IngressFilter
+│ ├─ SDK builder 加 .credential(token / username+password)
+│ └─ 每请求 Authorization 头校验,失败 → 401
+├─ AclFilter(鉴权:你能做什么)—— 内置 IngressFilter
+│ ├─ topic 粒度权限:publish / subscribe
+│ ├─ 权限上下文:CloudEvents extension emuserid / emtenantid
+│ └─ 规则经 MetaService 动态下发,失败 → 403
+├─ 租户隔离
+│ ├─ topic 命名空间:.
+│ ├─ AclFilter 按 tenant 隔离订阅关系与消息
+│ └─ SubscriptionManager 按 tenant 过滤订阅视图
+└─ CloudEvents 签名(借鉴 A2A AgentCardSignature)
+ ├─ extension x-em-signature = HMAC-SHA256(secret, canonical(event))
+ └─ 接收方验签,防篡改 + 来源可信
+```
+
+**影响范围**:IngressPipeline 的 AuthFilter / AclFilter 实装、SDK 加 credential、配置项扩展
+**依赖**:Phase 4(UnifiedIngressHandler + Pipeline 已存在)
+**测试**:无凭证/错误凭证 → 401;越权 topic → 403;mTLS 握手失败 → 拒连;签名错误 → 拒收;租户 A 看不到租户 B
+**DoD(🔴 高优先级)**:
+- [ ] HTTPS Long-Polling/WS/SSE 全传输启用 TLS,ENFORCING 模式明文被拒
+- [ ] mTLS 双向认证:无客户端证书 → 拒连(client.auth=REQUIRE)
+- [ ] AuthFilter:无凭证→401,token/basic 校验通过
+- [ ] AclFilter:越权 topic→403,规则 priority 匹配 + DENY 优先生效(§13.4.2)
+- [ ] ACL 规则经 Meta watch 下发,热路径零 RTT(本地缓存匹配)
+- [ ] 租户隔离:tenantA 看不到 tenantB 的订阅/消息(resource 带 tenant 前缀)
+- [ ] CloudEvents 签名验签:篡改→拒收(§13.4.4)
+
+### Phase 5:Runtime 出方向简化(三传输 + 虚拟线程)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:❌ `PushService` + `LongPollingChannel` + `SseConnection`(`/events/stream`)+ Java 21 虚拟线程(`newVirtualThreadPerTaskExecutor`)已实现;**但 WebSocket(文档 §15.6 默认主传输)完全未实现(G1,全仓零 WS 代码)**;`sendBatch` 批量未实现。三传输实际只到位 2 种(SSE + Long-Polling)。
+
+**目标**:PushService + SubscriptionManager 替代旧的 Consumer 体系,推送通道支持三传输,并发用 Java 21 虚拟线程
+
+```
+阶段产出:
+├─ PushService 实现(替代 LRUCache)
+├─ TransportChannel 三传输(§7.2 / §15.6)
+│ ├─ WebSocketChannel(默认推送主传输,双向持久流)
+│ ├─ SSEChannel(单向流式,LLM token 流)
+│ └─ LongPollingChannel(防火墙降级)
+├─ 批量发送 + 二进制 CloudEvents 编码(从 Phase 8.5 提前到核心,§15.8)
+│ ├─ SDK: publish(List) 批量
+│ ├─ Storage: sendBatch(topic, events, callback)
+│ └─ Encoding.BINARY 默认(高 TPS 降开销)
+├─ Java 21 虚拟线程处理挂起连接(§15.8)
+│ └─ 每个待推通道占虚拟线程,挂起成本接近 0
+├─ SubscriptionManager.pollAndDispatch() 定时任务
+│ → Storage.poll() → selectTargets() → PushService.push()
+├─ 删除 ClientSession / SessionManager(TCP)
+├─ 删除 ClientGroupPack / ClientGroupPackManagement(Consumer Group)
+└─ 删除 EventMeshTcpServer / EventMeshGrpcServer
+```
+
+**测试**:端到端测试(SDK subscribe → Runtime 下发 → 验证收到);三传输各自推送;批量 publish TPS 提升;虚拟线程下万级挂起连接稳定
+**DoD**:
+- [ ] TransportChannel 三实现(WS/SSE/LP)统一接口,各自推送正常(§7.2)
+- [ ] WebSocket 为默认推送主传输,毫秒级延迟
+- [ ] 批量 publish(`sendBatch`)TPS 较单条提升(压测对比)
+- [ ] 二进制 CloudEvents 编码默认,JSON 可选
+- [ ] Java 21 虚拟线程承载挂起连接,万级连接下内存/CPU 稳定
+- [ ] 删除 ClientSession/ClientGroupPack/EventMeshTcpServer/EventMeshGrpcServer,编译通过
+
+### Phase 5.5:下发可靠性(🔴 高优先级,生产前必做)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ `ReliableDispatcher` 完整:ACK 后才推进 offset(at-least-once)+ 指数退避(1/2/4/8/16s)+ 超 maxAttempts 转 `.DLQ` + STICKY 顺序 + trace 埋点;**但多实例下 STICKY 退化为 RoundRobin 破坏顺序(G8)、退避无 jitter(G13)**。DLQ 携带 reason/retry-count ✅。
+
+**目标**:废弃 §12.6.4 的"不做 ACK、发送成功即推进 offset"简化版,补齐 ACK + 重试 + DLQ + 顺序 + 去重,建立消息总线基本契约(至少一次)。详细设计见 §13.3。
+
+```
+阶段产出:
+├─ ACK 机制(至少一次)
+│ ├─ POST /events/ack { subId, clientId, topic, partition, offset }
+│ ├─ offset 仅在 ACK 后推进;ACK 超时(ackTimeout,如 30s)→ 视为失败重投
+│ └─ SDK 处理完一批 → 显式 ACK,再 poll 下一批
+├─ 重试与死信队列(内置 ReliableDispatcher,不接 eventmesh-retry SPI 插件)
+│ ├─ 下发失败/ACK 超时 → ReliableDispatcher 重投,指数退避(1s/2s/4s/8s/16s)
+│ ├─ 超 maxAttempts(默认 6 = 初次 + 5)→ 转 DLQ topic: <原topic>.DLQ
+│ └─ 死信携带 emdlqreason / emdlqretrycount + 原始 CloudEvent
+├─ 顺序消息(STICKY 粘性会话)
+│ ├─ 新增 DistributionMode: LOAD_BALANCE_STICKY
+│ ├─ partitionKey = CloudEvents partitionkey extension
+│ └─ target = subscribers[hash(partitionKey) % size],同 key 永远同 worker 保序
+├─ 延迟/定时消息(决策)
+│ ├─ 保留 TTL(CloudEvents time + x-em-ttl,过期丢弃)
+│ └─ 定时投递 v1 不支持(声明 + 列入 roadmap)
+└─ 去重 / 幂等
+ ├─ CloudEvents id 作为 dedupId,重投不变
+ ├─ EventMesh 不做全局去重,靠 at-least-once + 客户端幂等
+ └─ 明确交付语义 = 至少一次(事务消息明确不支持)
+```
+
+**影响范围**:PushService 加 ACK 跟踪、新增重试器与 DLQ 路由、SubscriptionManager 加 STICKY 模式、SDK 加 ACK 调用
+**依赖**:Phase 5(PushService 已存在)
+**测试**:下发后客户端不 ACK → 超时重投;重投超阈值 → 进 DLQ;同 partitionkey 消息 → 同 worker 顺序到达;客户端按 id 幂等不重复处理
+**DoD(🔴 高优先级)**:
+- [ ] ACK 机制:offset 仅在 ACK 后推进(§13.3.1),ACK 超时→重投
+- [ ] Retryer:指数退避(1s/2s/4s/8s/16s + jitter),超 maxRetries 转 DLQ(§13.3.2)
+- [ ] DLQ topic `.DLQ` 可独立订阅,死信带 reason/retry-count
+- [ ] LOAD_BALANCE_STICKY:同 partitionkey → 同 worker,保序(§13.3.3)
+- [ ] 文档/SDK 明确声明交付语义=至少一次,不承诺恰好一次
+- [ ] crash 恢复:offset 未推进的消息重启后重放(靠幂等收敛)
+
+### Phase 5.6:可观测性(🟠 中优先级)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ `UniMetrics`(OTel 仪表,8 项核心:publish/publish_failed/rate_limited/dispatched/dispatch_latency/ack/redeliveries/dlq)+ `UniTrace`(OTel Span:publish/dispatch/ack/retry/dlq,以 CloudEvent id 关联)已埋;**余 8 项 metrics(offset_lag / active_subscribers / pending_queue / slow_consumer / partition_owner 等,附录 F.5)未实现**;traceparent/tracestate/baggage 全链路透传未显式实装。
+
+> **决策(v1.9):可观测只用 OpenTelemetry,不支持其他扩展。** metrics 与 trace 全部走 OTel(Meter / Tracer API),由部署侧配置的 OTel exporter(OTLP、Prometheus-via-OTel 等)导出。**不再**复用/支持 `eventmesh-metrics-prometheus`、`eventmesh-trace-plugin`(zipkin/jaeger/pinpoint) 这些独立插件——它们是 legacy,uni runtime 不接线(见附录 D.8)。实现:`org.apache.eventmesh.runtime.uni.metrics.UniMetrics`(OTel LongCounter/LongHistogram 仪表)。
+
+**目标**:补齐 metrics、分布式 trace 传播、消息轨迹。详细设计见 §13.5.1–13.5.3。
+
+```
+阶段产出:
+├─ Metrics(OpenTelemetry Meter 仪表,唯一路径)
+│ ├─ eventmesh_publish_count / eventmesh_publish_failed_count / eventmesh_rate_limited_count
+│ ├─ eventmesh_dispatched_count / eventmesh_dispatch_latency_nanos
+│ └─ eventmesh_ack_count / eventmesh_redeliveries_count / eventmesh_dlq_count
+├─ 分布式 Trace 传播(OpenTelemetry Tracer,唯一路径)
+│ ├─ W3C traceparent(CloudEvents Distributed Tracing extension)
+│ └─ 链路:SDK publish → IngressPipeline → Storage.send →
+│ pollAndDispatch → 跨实例 forward → push → 客户端 ACK
+│ OTel Span,经 OTLP / OTel-Prometheus exporter 导出
+└─ 消息轨迹(OTel Span,按 CloudEvents id 关联)
+ └─ 关键节点埋点:publish / 入 MQ / dispatch / push / ack / retry / dlq
+ 支持按 CloudEvents id 查询全链路轨迹
+```
+
+**影响范围**:各 Pipeline / PushService / 重试器埋点;OTel 仪表注册新指标;trace context 透传
+**依赖**:Phase 5(出方向通路存在)、Phase 5.5(重试/DLQ 节点需埋点)
+**测试**:OTel exporter(如 OTLP)抓取到各指标与 span;按 id 查到全轨迹
+**DoD**:
+- [ ] 8+ 项 OTel metrics 仪表暴露(经 OTel exporter,含 topic/tenant/mode 标签,§13.5.1)
+- [ ] traceparent/tracestate/baggage 全链路透传(§13.5.2)
+- [ ] 关键节点 Span(publish/ingress/storage/dispatch/push/ack + retry/dlq)可见
+- [ ] 按 CloudEvents id 查询全链路轨迹
+- [ ] uni runtime 不依赖任何 `eventmesh-trace-plugin` / `eventmesh-metrics-prometheus` 类
+
+### Phase 6:Connector Runtime 独立化
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:✅ 独立模块 `eventmesh-connector-runtime` + `ConnectorApplication`(独立 main 进程)+ `RemoteOffsetStore` / `RocksDBConnectorOffsetStore`(本地+远程 EO 双写,§8.9)+ at-least-once commit-on-success 框架。**完成度最高的 Phase。**
+
+**目标**:Connector Runtime 与 EventMesh Runtime 完全分离,各自独立部署
+
+```
+阶段产出:
+├─ ConnectorRuntime 独立为独立 Java 进程(独立 main 入口)
+├─ SourceConnector 拉取外部数据 → HTTP POST /events/publish 到 EventMesh
+├─ SinkConnector 订阅 EventMesh → HTTP Long-Polling 接收 → 写入外部系统
+├─ EventMesh Runtime 删除 ConnectorRuntimeService(不再持有 Connector)
+├─ Connector 自有 OffsetStore(RocksDB,与 EventMesh 的 OffsetStore 完全独立)
+└─ 删除 Connector 的 BlockingQueue 旁路 + System.exit(-1) Bug
+```
+
+**测试**:Connector Runtime 独立启动 → Source 写入 → EventMesh 收到 → 下发 → Sink 收到 → 外部系统确认
+**DoD**:
+- [ ] Connector Runtime 独立 Java 进程(独立 main),不经 EventMesh Runtime 内嵌
+- [ ] Source → HTTP POST /events/publish → EventMesh 收到
+- [ ] Sink → HTTP Long-Polling 接收 → 写外部系统
+- [ ] Connector 自有 OffsetStore(本地 RocksDB + Admin Server 远程,§8.9),与 EventMesh 分发 offset 独立
+- [ ] Source/Sink 的 EO 流程成立(publish 成功才 commit source offset,§16 层面 A)
+- [ ] 删除 BlockingQueue 旁路 + System.exit(-1) Bug
+
+### Phase 6.6:运维与稳定性(🟠 中优先级)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ `TokenBucketRateLimiter`(per-topic)+ 慢消费者四态状态机(HEALTHY/SLOW/STALLED/EVICTED)+ 优雅停机(drain+等在飞 ACK+flush offset)已实现;**但慢消费者状态机有缺陷(无周期采样/溢出仅 1 种策略/STALLED 仍入队,G11)**、停机缺"释放分区租约通知 Meta 重分配"(G12)、动态配置热更新(`EventMeshDynamicConfigManager`)未接线、僵尸 poll 清理未实现。
+
+**目标**:补齐限流、背压、动态配置、优雅停机、连接生命周期管理。详细设计见 §13.6。
+
+```
+阶段产出:
+├─ 限流(复用 Guava RateLimiter + RateLimiterRulerListener)
+│ ├─ RateLimitFilter(Ingress):per topic / per clientId,超限 → 429
+│ ├─ 规则经 MetaService 动态下发
+│ └─ 下发侧限流(新增,develop 仅 TCP 侧有):per clientId 配额,慢消费者不丢
+├─ 背压与慢消费者隔离
+│ ├─ 每 clientId pendingEvents 队列 maxPending 上限(如 10000)
+│ ├─ 超限策略:丢弃最旧 + 记 metric(或转 DLQ)
+│ ├─ 慢消费者检测:poll 间隔超 slowThreshold → 减配额/暂停/告警
+│ └─ 连续 N 周期 slow → 自动 unsubscribe(防泄漏)
+├─ 动态配置与热更新(复用 EventMeshDynamicConfigManager + MetaService)
+│ └─ 订阅/限流/过滤/ACL 规则热更新,不重启
+├─ 优雅停机
+│ ├─ 停新请求(503) → drain pending → 等在飞 ACK(graceful 10s)
+│ ├─ flush offset(本地+远程)→ 释放分区租约(通知 Admin 重分配)
+│ └─ 关闭 Storage / HTTP Server
+│ > **v1.11 实现**: `UniRuntime.shutdown(gracefulMs)` 已实现完整 drain 流程:
+│ > 停 pull-loop → final dispatcher tick(drain pending) → 循环等在飞 ACK(gracefulMs, 默认10s)
+│ > → flush+close offsetStore → close storage. `EventMeshApplication` shutdown hook 调用.
+└─ 连接生命周期管理
+ ├─ poll 超时清理、僵尸 poll 检测(lastHeartbeat 清理)
+ ├─ 客户端断开 → 移除订阅 + 释放资源
+ └─ poll channel 总数上限,超限拒绝
+```
+
+**影响范围**:Pipeline 加 RateLimitFilter、PushService 加背压/慢消费者逻辑、shutdown 流程细化、连接清理调度
+**依赖**:Phase 6、Phase 5.5(DLQ 用于背压溢出)
+**测试**:超限 → 429;慢消费者积压触发背压不拖垮其他订阅者;停机后 offset 不丢、分区被接管;僵尸 poll 被清理
+**DoD**:
+- [ ] RateLimitFilter:per topic/clientId 限流,超限→429,规则经 Meta 动态下发
+- [ ] 背压:每 clientId 有界队列 + 溢出策略,慢消费者状态机(HEALTHY/SLOW/STALLED/EVICTED)生效(§13.6.2)
+- [ ] 广播场景:单慢消费者不阻塞其他订阅者(线程池隔离)
+- [ ] 优雅停机:drain pending → 等在飞 ACK → flush offset → 释放分区租约,offset 不丢
+- [ ] 僵尸 poll / 断连客户端被清理,无连接泄漏
+- [ ] 动态配置热更新(订阅/限流/ACL 规则不重启生效)
+
+### Phase 7:启动入口统一
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:✅ `EventMeshApplication` 单一 main 入口,统一管理 runtime + 流量 HTTP + admin HTTP 三组件 init/start/shutdown 生命周期 + 虚拟线程 + shutdown hook。旧 Tcp/Grpc/Admin Bootstrap + RuntimeInstanceStarter 已删。
+
+**目标**:EventMeshApplication 单一入口
+
+```
+阶段产出:
+├─ EventMeshApplication.main()(替换 EventMeshStartup)
+├─ RuntimeContext(统一组件生命周期管理)
+├─ 删除 EventMeshTcpBootstrap / EventMeshGrpcBootstrap
+├─ 删除 EventMeshAdminBootstrap(管理接口整合)
+└─ 删除 RuntimeInstanceStarter(旧 v2 入口)
+```
+
+**DoD**:
+- [ ] `EventMeshApplication.main()` 单一入口,按需加载组件(Storage/Pipeline/SubscriptionManager/PushService/Meta)
+- [ ] RuntimeContext 统一 init/start/shutdown 生命周期
+- [ ] Java 21 构建(虚拟线程启用,§15.8)
+- [ ] 删除 Tcp/Grpc/Admin Bootstrap + RuntimeInstanceStarter,无残留入口
+- [ ] 优雅停机 hook 注册(§13.6.4)
+
+### Phase 7.5:Admin 管理面重做(🔴 高优先级,生产前必做)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ `UniAdminServer`/`UniAdminService` 实现 ~5/8 接口(subscriptions / offsets / rejectClient / dlqReplay / metrics + pendingDeliveries);**缺 `/admin/clients`、`/admin/dlq/{topic}` browse、`/admin/ratelimit/rules` 下发、`/admin/health`(分区视图)**;数据为**进程内本地视图**,非文档要求的集群级(Meta 聚合)(G15)。
+
+**目标**:纠正原文档"Admin 不变"的错误——旧 admin v1 的 19 个 handler 大量依赖已删的 TCP session / Consumer Group,必须重做。详细设计见 §13.5.4。
+
+```
+阶段产出:
+├─ 新 Admin 面(基于集群级 SubscriptionManager + OffsetStore 视图,经 Admin Server)
+│ ├─ GET /admin/subscriptions 集群订阅关系(按 topic/tenant)
+│ ├─ GET /admin/offsets offset lag(按 topic/clientId)
+│ ├─ GET /admin/clients 在线客户端 + 所属实例
+│ ├─ POST /admin/clients/{id}/reject 踢客户端(清订阅)
+│ ├─ GET /admin/dlq/{topic} 浏览死信
+│ ├─ POST /admin/dlq/{topic}/replay 死信重投
+│ ├─ PUT /admin/ratelimit/rules 下发限流规则
+│ └─ GET /admin/health 实例健康/分区分配视图
+└─ 删除依赖 TCP/Group 的旧 handler
+ (ShowListenClientByTopicHandler / RedirectGroupBatchHandler /
+ RejectClientByIpPortHandler 等已无对应底层)
+```
+
+**影响范围**:admin handler 模块重写,Admin Server 数据模型适配集群级订阅/offset 视图
+**依赖**:Phase 7、Phase 2.5(集群级视图)、Phase 5.5(DLQ 浏览/重投)
+**测试**:订阅关系查询准确;踢客户端生效;死信重投成功;限流规则下发到所有实例
+**DoD(🔴 高优先级)**:
+- [ ] 新 Admin 8 个接口(subscriptions/offsets/clients/reject/dlq browse/dlq replay/ratelimit/health)可用(§13.5.4)
+- [ ] 数据来源是集群级视图(Meta + OffsetStore),非进程内
+- [ ] 踢客户端(reject)后其订阅清除、连接断开
+- [ ] 死信 replay 重新触发下发/WebHook
+- [ ] 限流规则经 Meta 下发到所有实例(最终一致)
+- [ ] 删除依赖 TCP/Group 的旧 admin v1 handler(19 个),无残留引用
+
+### Phase 8:清理收尾
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:✅(v1.10 决策已落实)TCP 兼容桥(`transport.tcp`:`UniTcpServer` + `MeshMessagePackageRouter` 翻译层 + `TcpPushChannel` 出向)+ HTTP 兼容桥(`transport.http`:`LegacyHttpBridge`)到位,老 TCP/HTTP 客户端零改动;gRPC/OpenMessaging 代码已物理删除。**未做**:性能基线压测报告、全量 §18 E2E 套件(多实例类用例因 Phase 2.5 未完成而不成立)。
+
+> **v1.10 决策修正:TCP 不直接删,保留为边缘协议适配器以兼容老客户端。** 原文"物理删除 TCP SDK"会让存量 TCP 客户端无法接入。修正方案:TCP 退化为新架构的 ingress/egress 传输适配器(与 HTTP/WebHook/长轮询并列),**保留**线协议(`Package`/`Command`/`Codec`)+ 翻译层(`TcpMessageProtocolResolver`/`MeshMessageProtocolAdaptor`)+ netty TCP server 骨架;**替换/删除** TCP 自有的核心逻辑(`ClientSession`/`ClientGroupPack`/`ClientGroupPackManagement`/rebalance、Consumer Group 语义)—— 这些由新 `SubscriptionManager`+`ReliableDispatcher` 接管。实现:`org.apache.eventmesh.runtime.uni.transport.tcp`(`TcpPushChannel` 出向 PushChannel、`TcpAckRegistry` 关联客户端 ACK、`TcpIngressBridge` 入向帧→CloudEvent→`UniIngressService`、`TcpFrameCodec`/`TcpFrameDecoder` 抽象)。老 TCP 客户端零改动。gRPC 同理可作边缘适配器(如无存量 gRPC 客户端则可删)。
+
+```
+阶段产出:
+├─ TCP 兼容桥:保留线协议+翻译层,TCP 核心(session/group/rebalance)删,接 UniIngressService
+├─ 删除 gRPC SDK 相关代码(如无存量 gRPC 客户端)
+├─ 删除 OpenMessaging API 相关代码
+├─ 更新文档(README / Quick Start / SDK Guide)
+├─ 性能基线测试(TPS 对比)
+└─ 端到端集成测试套件
+```
+
+**DoD**:
+- [ ] TCP 兼容桥:老 TCP 客户端能 publish/subscribe/收推送/ACK(端到端,§18 E2E-43 TCP 兼容)
+- [ ] TCP 核心(session/group/rebalance)代码物理删除,编译通过;TCP 线协议 + 翻译层保留
+- [ ] gRPC/OpenMessaging 代码物理删除(非仅 @Deprecated),编译通过
+- [ ] 全量集成测试套件通过(§18)
+- [ ] 性能基线:WebSocket+二进制+批量 vs TCP 适配器 vs 旧 TCP 的 TPS/延迟对比报告
+- [ ] 文档(README/Quick Start/SDK Guide)更新为新架构
+- [ ] 无对已删类的残留引用(grep 干净)
+
+### Phase 8.5:接入能力扩展(🟡 中低优先级)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:✅ `WebHookChannel`(HMAC-SHA256 签名头 `X-Em-Signature`/`X-Em-Timestamp`/`X-Em-Delivery-Id` + 指数退避重试 + 转 DLQ + 2xx=ACK)已实现并测试;WS/SSE/批量已提前到 Phase 5 核心(其中 WS 见 G1 未完成)。MQTT 不支持已声明。
+
+**目标**:补齐 WebHook 主动推送、MQTT 声明。(注:WS/SSE/批量/二进制已在 Phase 5 提前到核心,详见 §15.6/§15.8。)详细设计见 §13.7。
+
+```
+阶段产出:
+├─ WebHook 主动推送(补纯 pull/push 模式下无法推第三方的能力)
+│ ├─ subscribe 时配 delivery: { type:webhook, url, secret, retry }
+│ ├─ EventMesh 主动 HTTP POST 推送到 deliveryUrl + x-em-signature 签名
+│ └─ 失败重试(指数退避)+ 转 DLQ,与 WebSocket/SSE/Long-Polling 并存
+└─ MQTT 声明不支持(如需由独立网关转 CloudEvents 接入,不在 Runtime 范围)
+```
+
+**影响范围**:WebHook 投递模块
+**依赖**:Phase 8(核心通路已收尾)、Phase 5.5(WebHook 重试/DLQ 复用)
+**测试**:WebHook 推送失败重试到 DLQ;与三传输并存
+**DoD**:
+- [ ] WebHook delivery:subscribe 配 deliveryUrl,EventMesh 主动 POST 推送
+- [ ] 签名:X-Em-Signature(HMAC-SHA256) + X-Em-Timestamp(防重放) + X-Em-Delivery-Id(去重)(§13.7.2)
+- [ ] WebHook 失败重试(指数退避)+ 转 DLQ
+- [ ] WebHook 与 WS/SSE/LP 并存(订阅者按需选 delivery)
+- [ ] 文档声明不支持 MQTT(需由独立网关转 CloudEvents)
+
+---
+
+## 十二、关键设计决策讨论
+
+### 12.1 MQ 无语义 vs 保留 MQ 某些能力
+
+**用户明确要求**:MQ 只做存储,不暴露 Producer Group / Consumer Group / Tag
+
+这意味着:
+- **丢弃**:MQ 的分区负载均衡语义(MQ 按分区 hash 决定哪个 Consumer 消费),由 EventMesh SubscriptionManager 替代
+- **丢弃**:MQ 的 Tag 过滤(`subExpression = "TAG1 || TAG2"`),由 EventMesh MULTICAST 模式的 CloudEvents 过滤替代
+- **保留**:MQ 的持久化(WAL)能力——这是 MQ 作为"分布式日志"的核心价值
+- **保留**:MQ 的 offset 管理——EventMesh SubscriptionManager 依赖 MQ 的 offset 来实现 replay
+
+### 12.2 HTTP Long-Polling vs WebSocket / SSE
+
+> **⚠️ 本节选型已被 §15.6 取代**:初版选 Long-Polling 默认。结合 负载(毫秒级延迟 + 高吞吐 + TCP 同步调用),§15.6 决策改为 **WebSocket 默认 + SSE 单向流 + Long-Polling 降级,三种用户可选**。详见 §5.1.1 / §7.2 / §13.7.1。本表保留以记录决策演进。
+
+**选择 HTTP Long-Polling**(而非 WebSocket 或 SSE):
+
+| 方案 | 优点 | 缺点 |
+|------|------|------|
+| **HTTP Long-Polling(初版选中,现已降级为备选)** | 简单,兼容性好,穿防火墙/F5/Nginx 无压力,不需要特殊协议升级 | 稍高延迟(max 1 RTT),每次建立连接 |
+| WebSocket(现默认推送主传输) | 实时,低延迟,双向,高吞吐 | 需要代理层特殊配置(部分企业网络禁止)→ 此时降级 Long-Polling |
+| SSE(现单向流式选项) | 服务器推送,简单,穿墙极佳,LLM 生态成熟 | 单向(不能同时 publish)→ 控制命令走 HTTP 请求响应 |
+
+**设计**:§15.6 后为 WebSocket 默认推送,SSE 用于单向流式输出,Long-Polling 作防火墙降级,SDK 内部自动重连。
+
+### 12.3 负载均衡:RoundRobin vs 最小连接数
+
+当前选型 RoundRobin,理由:
+
+1. **实现简单**:RoundRobin 计数器是原子自增,不需要维护连接状态
+2. **足够公平**:在大多数场景下(消费者性能相近)效果等同于最小连接数
+3. **可扩展**:未来可以通过配置切换为"最小连接数"策略
+
+### 12.4 广播模式的消息放大问题
+
+**场景**:1000 个订阅者,广播模式需要向 MQ 写 1 条,但下发时 EventMesh 需要建立 1000 个 HTTP Long-Polling 连接。
+
+**解决**:
+- MQ 只写 1 条(广播效率高)
+- EventMesh 内部通过线程池并发下发(不阻塞 MQ poll)
+- 订阅者端 HTTP SDK 维护重连机制(当下发失败时不阻塞其他订阅者)
+
+### 12.5 多播模式的 CloudEvents 过滤标准
+
+**多播匹配规则**(按优先级):
+
+```
+1. "x-em-subscriptions" extension(显式订阅列表)
+ CloudEvent: { "x-em-subscriptions": ["service-A", "service-B"] }
+ → 只发给 service-A 和 service-B
+
+2. "type" 属性匹配(CloudEvents 标准字段)
+ CloudEvent.type = "order.created"
+ → 订阅了 type=order.created 的订阅者都收到
+
+3. "source" 属性匹配(CloudEvents 标准字段)
+ CloudEvent.source = "/orderservice"
+ → 订阅了 source=/orderservice 的订阅者都收到
+
+4. "subject" extension(业务标识)
+ CloudEvent.subject = "topic-orders"
+ → MQTT Topic 映射,供 SubscriptionManager 路由
+
+优先级:1 > 2 > 3 > 4
+```
+
+### 12.6 Offset 管理:EventMesh 自主 offset 管理(参考 RocketMQ Client 实现)
+
+**核心原则**:EventMesh **完全自主管理 offset**,不依赖 MQ 的 Consumer Group offset 机制。
+
+这意味着 EventMesh 自己维护一张 "topic → MQ offset → 分发状态" 的映射表,每个订阅关系独立追踪。
+
+> **RocksDB 定位(§15.8 澄清)**:本节 RocksDB 为**本地完整 offset 副本**。多实例下 offset 真相源在 **Meta**(§13.2.4 两级存储),RocksDB 的职责是:①高频写本地(每批 ACK)+ 低频刷 Meta(写卸载,防 Meta 被 offset 写压垮);②crash 恢复读本地完整 offset,零重放(即使 Meta flush 滞后);③Meta 不可用时本地兜底。RocksDB 与 Meta 非冗余——是"本地完整副本 + Meta 写缓冲 + 降级兜底",非主存储。
+
+#### 12.6.1 为什么不用 MQ 的 Consumer Group offset
+
+```
+MQ Consumer Group offset(当前 EventMesh 依赖的机制):
+ Kafka: Consumer Group → __consumer_offsets topic(broker 端管理)
+ RocketMQ: Consumer Group → consumeOffset.json(broker 端管理)
+
+问题:
+ · MQ offset 是按 Consumer Group 粒度管理的
+ · EventMesh 的 LOAD_BALANCE 模式中,同一 topic 可能有多个订阅者(不同 clientId)
+ · MQ 只能追踪 "某 Consumer Group 消费到哪",无法追踪 "某 clientId 消费到哪"
+ · EventMesh 在 BROADCAST 模式下,一条消息对多个 clientId 只消费一次(MQ offset 推进一次)
+ 但每个 clientId 的实际分发进度可能不同(有些客户端重连后需要 replay)
+ · MULTICAST 模式下,MQ offset 无法区分"哪个 clientId 消费了哪条消息"
+```
+
+#### 12.6.2 RocketMQ Client 的 Offset 管理模式(参考实现)
+
+RocketMQ 的 `DefaultMQPushConsumerImpl` 使用 `OffsetStore` 接口管理 offset,有两种实现:
+
+```java
+// RocketMQ: OffsetStore 接口
+public interface OffsetStore {
+ long readOffset(final MessageQueue mq, final ReadOffsetType type); // 读取 offset
+ void writeOffset(final MessageQueue mq, long offset); // 持久化 offset
+ void flush(); // 刷盘
+ long load(); // 从磁盘加载
+}
+
+// 两种实现:
+// 1. LocalFileOffsetStore:offset 存本地文件(/root/.rocketmq_offset/)
+// 2. RemoteBrokerOffsetStore:offset 由 broker 管理(通过 RPC 上报)
+
+// LocalFileOffsetStore 关键实现(RocketMQ 4.x):
+public class LocalFileOffsetStore implements OffsetStore {
+
+ private final String storePath; // /root/.rocketmq_offset/{consumerGroup}/{topic}/partitionN
+ private final ConcurrentHashMap offsetTable =
+ new ConcurrentHashMap<>(); // 内存中的 offset 快照
+
+ @Override
+ public long readOffset(MessageQueue mq, ReadOffsetType type) {
+ // 1. 尝试从内存取
+ AtomicLong offset = offsetTable.get(mq);
+ if (offset != null) return offset.get();
+
+ // 2. 内存没有,从本地文件加载
+ String filePath = buildOffsetFilePath(mq);
+ if (Files.exists(Paths.get(filePath))) {
+ String content = Files.readString(Paths.get(filePath));
+ long savedOffset = Long.parseLong(content.trim());
+ offsetTable.put(mq, new AtomicLong(savedOffset));
+ return savedOffset;
+ }
+
+ // 3. 文件也不存在,按 type 决定
+ return type == ReadOffsetType.READ_FROM_STORE ? 0 : -1;
+ }
+
+ @Override
+ public void writeOffset(MessageQueue mq, long offset) {
+ // 先写内存
+ offsetTable.computeIfAbsent(mq, k -> new AtomicLong())
+ .set(offset);
+
+ // 异步刷盘(不是每条消息都刷盘,而是批量异步刷)
+ // 参考 RocketMQ: flushOffsetInterval = 1000ms(可配置)
+ scheduleAsyncFlush(mq, offset);
+ }
+
+ @Override
+ public void flush() {
+ // 同步刷盘(进程退出时调用)
+ for (Map.Entry e : offsetTable.entrySet()) {
+ String path = buildOffsetFilePath(e.getKey());
+ Files.writeString(Paths.get(path), String.valueOf(e.getValue().get()));
+ }
+ }
+}
+```
+
+#### 12.6.3 EventMesh OffsetStore 设计
+
+参考 RocketMQ Client 的 `LocalFileOffsetStore`,EventMesh 实现自己的 `EventMeshOffsetStore`:
+
+```java
+public class EventMeshOffsetStore {
+
+ // 两级结构:topic → clientId → partition → offset
+ // · topic: EventMesh 的逻辑 topic(对应 MQ topic)
+ // · clientId: 订阅者的客户端标识
+ // · partition: MQ 的物理分区(Kafka 有,RocketMQ 的 queueId)
+ // · offset: MQ 的逻辑 offset(EventMesh 自己追踪)
+ private final ConcurrentHashMap> tables =
+ new ConcurrentHashMap<>();
+
+ static class PartitionOffsetTable {
+ AtomicLong currentOffset = new AtomicLong(0);
+ String storePath; // 本地文件路径
+ }
+
+ // 存储介质:RocksDB(参考 RocketMQ 5.0 RemoteBrokerOffsetStore 的 RocksDB 实现)
+ // 选择 RocksDB 而非普通文件的理由:
+ // · 高并发写入(多个 SubscriptionManager 线程并发更新 offset)
+ // · 支持批量写入(WAL + MemTable + SSTable)
+ // · 进程崩溃后自动恢复
+ // · 支持范围查询(按 topic 批量读取所有 clientId 的 offset)
+ private final RocksDB rocksDB;
+
+ // 构造函数:打开 RocksDB 实例
+ public EventMeshOffsetStore(String dataPath) {
+ Options options = new Options()
+ .setCreateIfMissing(true)
+ .setWriteBufferSize(64 * 1024 * 1024) // 64MB MemTable
+ .setMaxWriteBufferNumber(3); // 3 份 MemTable
+
+ this.rocksDB = RocksDB.open(options, dataPath + "/offset_store");
+ }
+
+ // Key 设计:{topic}#{clientId}#{partition}
+ // → 唯一确定一个订阅关系的消费位点
+ // 示例: "orders#worker-1#0" → "1042" (第1042条消息已分发)
+ private byte[] buildKey(String topic, String clientId, int partition) {
+ return (topic + "#" + clientId + "#" + partition).getBytes(StandardCharsets.UTF_8);
+ }
+
+ // 读取 offset(启动时调用,恢复分发进度)
+ public long readOffset(String topic, String clientId, int partition) {
+ byte[] key = buildKey(topic, clientId, partition);
+ byte[] value = rocksDB.get(key);
+ if (value == null) return -1; // -1 表示从未消费过
+ return Long.parseLong(new String(value));
+ }
+
+ // 写入 offset(每批消息处理完成后调用)
+ public void writeOffset(String topic, String clientId, int partition, long offset) {
+ byte[] key = buildKey(topic, clientId, partition);
+ byte[] value = String.valueOf(offset).getBytes(StandardCharsets.UTF_8);
+ rocksDB.put(key, value);
+ }
+
+ // 批量读取某 topic 的所有 clientId offset(用于 Admin Server 查询)
+ public Map readAllOffsets(String topic) {
+ byte[] prefix = (topic + "#").getBytes(StandardCharsets.UTF_8);
+ RocksIterator it = rocksDB.newIterator();
+ Map result = new HashMap<>();
+
+ for (it.seek(prefix); it.isValid(); it.next()) {
+ String key = new String(it.key());
+ if (!key.startsWith(topic + "#")) break;
+ String rest = key.substring(topic.length() + 1);
+ long offset = Long.parseLong(new String(it.value()));
+ result.put(rest, offset);
+ }
+ return result;
+ }
+
+ // 优雅关闭时刷盘
+ public void flush() {
+ rocksDB.flush(new FlushOptions().setWaitForFlush(true));
+ }
+}
+```
+
+#### 12.6.4 SubscriptionManager 与 OffsetStore 集成
+
+```java
+public class SubscriptionManager {
+
+ private final EventMeshOffsetStore offsetStore;
+ private final MeshStoragePlugin storage;
+
+ // 分发消息(按 LOAD_BALANCE 模式)
+ public void pollAndDispatch(String topic, String clientId, int partition, long timeoutMs) {
+ // 1. 读取 EventMesh 自主管理的 offset
+ long lastOffset = offsetStore.readOffset(topic, clientId, partition);
+ long startOffset = lastOffset < 0 ? OffsetResetType.EARLIEST.apply() : lastOffset + 1;
+
+ // 2. 从 MQ 按 offset 范围拉取
+ List events = storage.poll(topic, partition, startOffset, 100, timeoutMs);
+ if (events.isEmpty()) return;
+
+ // 3. 找下一个有效的 offset(MQ 返回的最后一条消息的 offset + 1)
+ long nextOffset = events.get(events.size() - 1).getExtension("x-mq-offset", Long.class) + 1;
+
+ // 4. 发送给订阅者
+ for (CloudEvent event : events) {
+ dispatchToSubscriber(event, clientId);
+ }
+
+ // 5. 收到 ACK 后才更新 offset(可靠下发)
+ // (简化版:发送成功即更新 offset,不做重试和 ACK 确认)
+ offsetStore.writeOffset(topic, clientId, partition, nextOffset);
+ }
+
+ // 消费者重连时,从 offset 恢复
+ public void onClientReconnect(String clientId, String topic) {
+ for (int partition = 0; partition < getPartitionCount(topic); partition++) {
+ long offset = offsetStore.readOffset(topic, clientId, partition);
+ if (offset >= 0) {
+ // 从上次位置继续下发
+ resendFromOffset(topic, clientId, partition, offset + 1);
+ }
+ }
+ }
+}
+```
+
+#### 12.6.5 Offset 管理的三种场景
+
+```
+场景 1:正常消费(LOAD_BALANCE)
+ EventMesh offset 记录: "orders#worker-1#0" = 1042
+ → worker-1 收到 orders topic 第 1043 条消息
+
+场景 2:消费者重启(BROADCAST 恢复)
+ worker-2 重启后,从 offsetStore 读取 "orders#worker-2#0" = 300
+ → 从 MQ offset 300 重新拉取(replay)
+ → 不会漏消息
+
+场景 3:MULTICAST(每个 clientId 独立 offset)
+ topic="events", clientId="order-service", partition=0 → offset=5000
+ topic="events", clientId="payment-service", partition=0 → offset=3200
+ topic="events", clientId="inventory-service", partition=0 → offset=8100
+ → 三个 service 的消费进度完全独立,互不干扰
+```
+
+#### 12.6.6 与 MQ offset 的关系
+
+```
+EventMesh 管理的是 "订阅关系 offset"(谁收到了哪条):
+ topic#clientId#partition → eventMesh_offset
+ · 一个 topic 可能有多个 clientId,每个 clientId 有自己的 offset
+ · EventMesh offset 是"已下发确认"的位置,不等于 MQ 的消费位点
+
+MQ 管理的是 "原始数据位点":
+ topic#partition → mq_offset
+ · Storage.poll() 从指定 MQ offset 拉取
+ · EventMesh 每次 poll(startOffset, maxEvents) 使用的是 MQ offset
+
+EventMesh offset 和 MQ offset 的对应关系:
+ EventMesh offset_store:
+ "orders#worker-1#0" → 1042 (worker-1 已收到第1042条)
+ ↓ 对应
+ Storage.poll(topic, partition=0, startOffset=1043, maxEvents=100)
+ ↓ 拉取
+ MQ 返回 100 条消息(offset 1043~1142)
+ ↓
+ 发送成功 → offset_store.update("orders#worker-1#0" = 1143)
+```
+
+#### 12.6.7 Offset 持久化策略
+
+| 策略 | 说明 | 适用场景 |
+|------|------|---------|
+| **每条消息后写 RocksDB** | 最可靠,但性能差(~1ms/次) | 不推荐 |
+| **批量异步写(每 100 条或每 1s)** | 平衡可靠性和性能 | **推荐(默认)** |
+| **进程退出时 flush** | RocketMQ `LocalFileOffsetStore` 策略 | 不够可靠 |
+| **幂等写(LSM 树批量合并)** | RocksDB WAL + MemTable flush 时合并写入 | **推荐(RocksDB 天然支持)** |
+
+**最终选择**:RocksDB + 批量异步写 + 进程退出时强制 flush。MQ offset 用作"数据可用性"探测,EventMesh offset 用作"分发进度"追踪。
+
+> **多实例补充(§13.2.4 / §15.8)**:RocksDB 是本地完整副本;集群真相源在 Meta。高频写 RocksDB,低频批量刷 Meta;启动/接管时 `readOffset = max(local, remote)`,以 Meta 远程为准(最新集群进度)。Meta 不可用时退化为纯本地 RocksDB。
+
+---
+
+## 十三、能力缺口与设计补充(v1.1)
+
+> 前述章节(§1–§12)完成了"减法"设计:删 TCP/gRPC/OpenMessaging SDK、MQ 无语义化、单 Producer/单 Consumer、统一 HTTP+CloudEvents。但整体只覆盖了**快乐路径**(publish → 存储 → poll → 分发)。经与 develop 分支现有能力逐一对照,以下能力在生产落地前必须补齐设计,否则方案存在架构性 blocker。本节按严重程度排序给出缺口与具体设计补充。
+
+### 13.1 缺口总览
+
+| # | 能力 | develop 分支现状 | 本文档现状 | 严重程度 | 补充章节 |
+|---|------|-----------------|-----------|---------|---------|
+| 1 | 多实例消费协调 | 多 Consumer Group rebalance | **缺失(架构 blocker)** | 🔴 阻断 | §13.2 |
+| 2 | Offset 集中存储 | RocketMQ broker 端 offset | 仅本地 RocksDB | 🔴 阻断 | §13.2.4 |
+| 3 | 订阅关系集群同步 | ClientGroupPack(进程内) | 进程内 Map | 🔴 阻断 | §13.2.5 |
+| 4 | 下发 ACK / 至少一次 | TCP SessionSender 重试 | §12.6.4 自述"不做 ACK" | 🔴 高 | §13.3.1 |
+| 5 | 重试 + 死信队列 | eventmesh-retry + DLQ topic | 仅 Connector 侧一行 retry | 🔴 高 | §13.3.2 |
+| 6 | 顺序消息(粘性会话) | 较弱 | RoundRobin 主动破坏顺序 | 🟠 中高 | §13.3.3 |
+| 7 | 去重 / 幂等 | 无 | 无 | 🟠 中高 | §13.3.5 |
+| 8 | TLS / mTLS | SslContextFactory + TlsConfig | HTTP 明文未提 HTTPS | 🔴 高 | §13.4.1 |
+| 9 | 认证鉴权 | security-plugin(acl/token/basic) | Pipeline 列名零设计 | 🔴 高 | §13.4.2 |
+| 10 | Metrics | metrics-prometheus(OTel) | 完全未提 | 🟠 中 | §13.5.1 |
+| 11 | 分布式 Trace 传播 | trace-plugin(zipkin/jaeger/pinpoint) | 完全未提 | 🟠 中 | §13.5.2 |
+| 12 | Admin 管理面 | admin v1 19 handler + admin-server | 误标"不变",底层已删 | 🔴 高 | §13.5.4 |
+| 13 | 限流 | Guava RateLimiter + meta 动态规则 | RateLimitFilter 列名零设计 | 🟠 中 | §13.6.1 |
+| 14 | 背压 / 慢消费者隔离 | 无显式 | 广播 1000 订阅者无背压 | 🟠 中 | §13.6.2 |
+| 15 | 优雅停机语义 | shutdown hook | 仅一句 ctx::shutdown | 🟡 中低 | §13.6.4 |
+| 16 | 实时推送传输 | gRPC 流式 | Long-Polling 1 RTT 延迟 | 🟡 中低 | §13.7.1 |
+| 17 | WebHook 主动推送 | WebhookPushRequest + connector | 纯 pull 无法主动 push | 🟠 中 | §13.7.2 |
+| 18 | 批量发送 | BatchSendAsyncEventProcessor | send() 单条 | 🟡 中低 | §13.7.3 |
+| 19 | topic/subject 语义 | — | §6.2 与 §12.5 自相矛盾 | 🟠 中 | §13.8.3 |
+| 20 | 过滤表达式语法 | — | CloudEventFilter.match() 未定义 | 🟠 中 | §13.8.4 |
+
+### 13.2 多实例消费协调(架构 blocker,最高优先级)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:❌ **架构 blocker 未解除。** §13.2.3 分区不重叠、§13.2.4 offset 两级存储、§13.2.5 实例间转发、§13.2.8 gen fencing **均未实装**(附录 F G2–G5);§13.2.9 降级时序因此也不成立。代码有 `ClusterCoordinator`/`ClusterMembership`/`PartitionAssigner`/`ClusterSubscriptionStore` 骨架,但 boot 未 wire assigner、转发直接 `return false`。多实例=重复消费。
+
+#### 13.2.1 问题
+
+§3.2 规定"Storage Plugin 内部只有**一个** Consumer 实例(由 EventMesh Runtime 持有),全量拉取所有 topic 的消息"。但生产部署是多 Runtime 实例。若每个实例都 `consumer.assign(全部分区)` 全量拉取:
+
+```
+实例 A ──assign(p0,p1,p2)──→ 拉到 msg-100
+实例 B ──assign(p0,p1,p2)──→ 拉到 msg-100 ← 重复
+实例 C ──assign(p0,p1,p2)──→ 拉到 msg-100 ← 重复
+```
+
+→ **每条消息被 N 个实例重复拉取、重复分发**。方案无法横向扩展,且与"LOAD_BALANCE 每条只发一个订阅者"的语义直接冲突。
+
+#### 13.2.2 方案选型
+
+| 方案 | 机制 | 优点 | 缺点 |
+|------|------|------|------|
+| A. 复用 MQ Consumer Group rebalance | EventMesh 仍用 MQ 的 Group 做 rebalance | 成熟、零开发 | **违反"MQ 无语义"铁律**,把 Group 又暴露回来,本重构作废 |
+| B. Meta 主导分区分配 + 租约 | Meta(注册中心)给每个实例分配分区,实例持租约拉取 | 符合"EventMesh 自主",复用 develop `eventmesh-meta`,强一致 + watch | Meta 成为协调强依赖,需 HA + 降级 |
+| C. 实例自洽分配(Meta 协调) | 实例从 Meta 拿全量实例列表,按 `partition % N == myIndex` 自洽分配 | 无中心分配器,降级友好 | 无负载感知,实例增删需重新分配 |
+| D. 全量拉取 + 下发去重 | 每实例全拉,靠 dedupId 去重 | 无协调 | N 倍 MQ 流量、N 倍 CPU,不可扩展 |
+
+**决策(§15.5):B(在线)+ C(降级)组合,控制面 = Meta 注册中心。** Meta 在线时主导分配(强一致 KV + watch 推送,负载感知);Meta 不可用时降级为 C(实例自洽),保证 Runtime 仍可独立运行(延续"可降级部署"原则)。**不**用 Admin Server 承担协调职责(见 §13.2.7 边界)。
+
+#### 13.2.3 推荐:Meta 主导分区分配 + 实例自洽降级
+
+```
+┌──────────────────────────────────────────────────────────────┐
+│ Meta 注册中心(全局控制面,复用 eventmesh-meta) │
+│ · 分区分配表: topic#partition → ownerInstanceId │
+│ · 实例租约: instanceId → { partitions, leaseExpireAt } │
+│ · 负载感知: 按实例订阅数/TPS 做均衡(可选,v2 增强) │
+│ 强一致 KV + watch 推送(nacos/etcd/consul/zk/raft 任选) │
+└──────────────┬──────────────────────────────┬────────────────┘
+ │ watch 分配变更 │ 心跳续约
+ ▼ ▼
+┌──────────────────────┐ ┌──────────────────────┐
+│ Runtime 实例 A │ │ Runtime 实例 B │
+│ 持有 p0,p1 租约 │ │ 持有 p2 租约 │
+│ consumer.assign(p0,p1)│ │ consumer.assign(p2) │
+│ 仅拉自己负责的分区 │ │ 仅拉自己负责的分区 │
+└──────────────────────┘ └──────────────────────┘
+
+降级模式(Meta 不可用):
+ 实例从最后缓存的全量实例列表 [A,B,C] 自洽分配
+ → A: p0%3==0 → p0;B → p1;C → p2
+ → Meta 恢复后重新对齐到权威分配表
+ 注:降级期间本地 RocksDB offset 仍可用,不丢进度
+```
+
+**Storage Plugin 接口需扩展**(§3.2 的 `poll` 之外):
+
+```java
+public interface MeshStoragePlugin {
+ // ... 原 send/poll/commitOffset ...
+
+ /** 由 SubscriptionManager 告知本实例负责的分区(替代 consumer.assign 全量) */
+ void assignPartitions(String topic, List partitions);
+
+ /** 按 MQ offset 范围拉取(供按 offset 精确 replay) */
+ List poll(String topic, int partition, long startOffset, int maxEvents, long timeoutMs);
+}
+```
+
+#### 13.2.4 Offset 集中存储(升级 §12.6 的本地 OffsetStore)
+
+§12.6 的 `EventMeshOffsetStore` 只存**本地 RocksDB**。多实例下:clientId 原连实例 A,A crash 后 clientId 重连到实例 B,B 的本地 RocksDB 没有该 clientId 的 offset → 从 earliest 重放或丢进度。
+
+**升级为两级存储(远程层 = Meta)**:
+
+```
+EventMeshOffsetStore(两级)
+├─ 本地 RocksDB(缓存 + 降级可用) ← 写穿透第一步
+└─ 远程 Meta KV(控制面,强一致) ← 异步同步,启动时拉取
+
+writeOffset(topic, clientId, partition, offset):
+ 1. rocksDB.put(key, offset) // 立即本地落盘
+ 2. async → meta.put(key, offset) // 批量异步(每 1s 或 100 条)
+
+readOffset(topic, clientId, partition): // 启动 / 接管时
+ 1. local = rocksDB.get(key)
+ 2. remote = meta.get(key) // 以远程为准(最新集群进度)
+ 3. return max(local, remote)
+```
+
+**clientId 迁移**:实例 B 接管 clientId 时,从 Meta 远程读 offset 继续,不丢进度。本地 RocksDB 仅作降级缓存(Meta 不可用时仍能从本地恢复)。
+
+#### 13.2.5 订阅关系集群同步
+
+`subscribe()` 请求可能打到任意实例(HTTP 无亲和),但拉取该 topic 分区的可能是另一个实例。§4.3 的 `topicSubscriptions` 是**进程内 Map**,多实例不互通。
+
+**升级为集群级订阅视图(存 Meta,控制面)**:
+
+```
+订阅关系存储(Meta KV)
+ key: topic
+ value: Set{ subId, clientId, mode, filter, ownerInstance }
+
+每个实例:
+ · 本地缓存全量订阅视图(Meta watch 推送变更)
+ · subscribe()/unsubscribe() 写 Meta + 广播失效
+
+分发决策点(关键):
+ 分区拥有者实例拉到消息
+ → 查全局订阅视图 selectTargets(event, allSubs)
+ → 对每个目标 clientId:
+ · 查 "clientId 当前通道在哪个实例"(连接路由表,存 Meta)
+ · 若在本实例 → 直接 push 到其 TransportChannel
+ · 若在别的实例 → instance-to-instance HTTP 转发到目标实例
+ → 目标实例收到转发 → 投入该 clientId 的 pendingEvents / TransportChannel
+```
+
+```
+拉取实例 A (拥有 p0) 目标实例 B (clientId-X 的通道在此)
+ msg → selectTargets │
+ → clientId-X 在实例 B ───────┼──→ HTTP POST /internal/forward
+ │ { clientId, event }
+ ▼
+ push 到 clientId-X 的 TransportChannel
+```
+
+> 这是本方案最复杂的一环。若 v1 不做多实例,可先单实例部署 + 预留接口;但**生产 HA 必须实现**,否则单点故障。
+
+#### 13.2.6 负载均衡的全局性
+
+§4.3 的 `roundRobinCounter` 是**单实例原子计数器**。多实例下"3 个 worker 分散在 3 个 Runtime"无法做到全局 RoundRobin(每个实例独立计数 → 倾斜)。
+
+**决策**:
+- LOAD_BALANCE 默认按 **partitionKey 粘性**(§13.3.3),天然全局一致(hash 不依赖实例)。
+- 纯 RoundRobin 仅在"所有订阅者连同一实例"时精确;跨实例时退化为"实例内 RoundRobin + 实例间按分区天然分流",可接受。
+
+#### 13.2.7 Meta vs Admin Server 职责边界(控制面 vs 管理面)
+
+文档多处出现 Meta 与 Admin Server,须厘清两者职责,避免把强一致协调塞进管理面:
+
+| | Meta(注册中心 / 全局控制面) | Admin Server(管理面) |
+|---|---|---|
+| 性质 | 强一致 KV + watch 推送 | 独立进程,业务管理 |
+| 实现 | 复用 develop `eventmesh-meta`(nacos/etcd/consul/zk/raft) | develop `eventmesh-admin-server` |
+| 存什么 | 实例注册心跳、分区分配表、clientId 路由表、集群订阅视图、offset 远程副本、动态规则(限流/ACL/过滤) | Job 管理、DLQ 浏览重投、人工运维指令、指标聚合展示 |
+| 谁读写 | Runtime 自动(控制路径,强一致) | 运维/管理员(管理路径) |
+| 可用性 | 强(挂则协调停摆)→ HA + 降级(§13.2.3 C) | 中(挂则运维受影响,Runtime 可降级运行) |
+| 与数据面关系 | 数据面协调的权威源 | 只读 Meta 视图做展示,不参与强一致协调 |
+
+**原则**:控制面的活归 Meta;Admin Server 只做管理面(展示 + 运维指令),**不**承担分区/订阅/offset 的强一致存储。这样控制面强一致靠 Meta,管理面可独立演进,职责不互相耦合。
+
+> **develop 现状梳理(v1.9 决策:弃用 Registry,只用 Meta)**:仓库存在 `eventmesh-meta`(MetaService SPI,5 后端)与 `eventmesh-registry`(仅 nacos,偏实例发现)两套注册抽象。**决定:弃用 `eventmesh-registry`,只用 `eventmesh-meta`(MetaService)作为唯一控制面**,实例发现能力并入 MetaService。不再保留两套并存的注册抽象。
+
+#### 13.2.8 分区分配协议细节(租约 / 重均衡 / 防脑裂)
+
+§13.2.3 给出"Meta 主导分配 + 实例自洽降级"的骨架,本节细化协议机制。核心约束:**MQ 无语义下(§15.1),所有 fencing 必须由 EventMesh 自己实现**——不能依赖 MQ 的 epoch/leader-generation。
+
+**① 确定性分配算法(无中心分配器时的 v1 基线)**
+
+```
+输入:全量实例列表 [I0,I1,...,In-1](Meta 维护,按 instanceId 稳定排序)
+ 全量分区 [P0,P1,...,Pm-1]
+
+v1 算法(环形取模,确定性):
+ 实例 Ii 负责分区 Pj iff (j + rebalanceOffset) % n == i
+ → rebalanceOffset=0 时:P0→I0, P1→I1, ..., Pn→I0(环形)
+
+特性:
+ · 纯函数:相同实例列表 → 相同分配(所有实例算出一致结果)
+ · 增量友好:增删 1 个实例,约 1/n 分区迁移(接近最小迁移)
+ · 无负载感知:v1 不看实例负载,v2 由 leader 增强(见下)
+
+谁来算:v1 每个实例自己算(读 Meta 的实例列表);v2 由 leader 算后写入 Meta,其余实例读
+```
+
+**② 租约 = 心跳(不引入第三套机制)**
+
+```
+租约机制:
+ 实例定期(leaseRenewInterval,如 5s)向 Meta 更新自己的心跳 key:
+ key: /em/instances/
+ value: { lastHeartbeat, ownedPartitions:[...], gen }
+ TTL: leaseTtl(如 15s,3 倍续约间隔)
+
+租约过期:
+ 心跳 TTL 到期未续约 → Meta 视实例下线 → 从实例列表移除 → 触发重均衡
+ → 与"心跳上报"复用同一通道,不为租约另起机制
+```
+
+**③ 重新均衡触发时机**
+
+```
+触发重均衡的事件(实例 watch Meta 感知):
+ · 实例上线(新实例注册)→ 分区可能迁给它
+ · 实例下线(租约过期 / 主动注销)→ 其分区需重分配
+ · 实例列表稳定排序变化(instanceId 变更,罕见)
+ · v2:leader 主动再平衡(负载倾斜超阈值)
+
+重均衡流程:
+ 1. 实例列表变更 → 所有实例重新计算分配(确定性算法,结果一致)
+ 2. 失去分区的实例:停止该分区 poll,但**不立即 commit offset**(见 ④ 迁移)
+ 3. 获得分区的实例:从 Meta 读该分区最新 offset,assignPartitions() 后开始 poll
+ 4. generation +1(fencing,见 ④)
+```
+
+**④ Offset 迁移与 fencing(防脑裂核心)**
+
+最危险场景:实例 A 被判定下线、分区 P0 重新分配给实例 B,但 A 其实没死(网络分区),A 和 B **同时 poll P0** → 重复消费/offset 互相覆盖。
+
+```
+fencing 机制(EventMesh 自实现,MQ 无语义下必需):
+ generation(代次):每次重均衡 +1,写入 Meta 分配表
+ /em/assignments/ = { owner: , gen: <递增> }
+
+ 实例 poll 前 fencing 检查:
+ poll 伪代码:
+ assignment = meta.get("/em/assignments/" + topic + "#" + partition)
+ if (assignment.gen != myGen || assignment.owner != myInstanceId) {
+ // 我已不是该分区 owner,停止 poll(被 fencing)
+ consumer.pause(partition); return;
+ }
+ consumer.poll(...); // 否则正常拉取
+
+ 效果:A 网络分区后无法续租约 → gen 推进 → A 恢复时发现自己 gen 过期 → 自停
+ B 拿到新 gen → 唯一 poller
+ → 不依赖 MQ 的 epoch,纯 EventMesh 侧实现
+```
+
+> **注意**:fencing 只能"软 fencing"(poll 前检查),无法阻止 A 的 MQ client 在底层继续拉。配合 §13.3.5 去重(CloudEvents id 幂等),即使短暂双 poll,下发侧靠客户端幂等兜底。这是"MQ 无语义"铁律的必然代价——换来不绑 MQ rebalance。
+
+**⑤ Offset 迁移的安全交接**
+
+```
+失去分区的实例(旧 owner):
+ · 停 poll → flush 本地 offset 到 Meta(最后一次 commit)
+ · gen 推进前完成的 poll,其 offset 已 commit;之后不再 commit
+
+获得分区的实例(新 owner):
+ · readOffset = max(local, meta) // §13.2.4 两级存储
+ · 从该 offset 继续 poll
+ · 不需要"交接握手"——确定性算法 + Meta offset 真相源,新 owner 自己读进度即可
+
+窗口期:gen 推进到新 owner 开始 poll 之间,可能短暂无人 poll(消息延迟,不丢)
+ 或双 poll(靠 ④ fencing + ⑤ 幂等收敛)
+```
+
+**⑥ v2 增强(leader 主导 + 负载感知,非 v1 必须)**
+
+```
+v1(基线):无 leader,每实例确定性自算分配。简单、无单点,但无负载感知。
+v2(增强):
+ · Meta 选一个实例作 assignment-leader(基于租约的最小 instanceId,或 Raft)
+ · leader 周期性按实例负载(订阅数/TPS/积压)重算分配,写入 Meta
+ · 其余实例读 Meta 分配表(不再自算)
+ · leader 切换:租约失效 → 重新选主(Raft 模式天然容错)
+
+何时上 v2:v1 在实例性能均质时够用;出现明显负载倾斜(如某实例订阅热点 topic)时再上
+```
+
+**⑦ 降级模式与一致性**
+
+```
+Meta 不可用(§13.2.3 降级):
+ · 实例用最后缓存的全量实例列表 + 确定性算法继续分配
+ · gen 不再推进(无法 fencing 新故障)→ 退化为"尽力而为"
+ · offset 仍读写本地 RocksDB(不丢进度)
+ · Meta 恢复后:重算分配 + 推进 gen + 对齐 offset 远程副本
+ → 降级期间可能短暂双 poll,靠幂等收敛;可接受(降级本就是异常态)
+```
+
+**小结**:协议 = 确定性分配 + 租约心跳 + gen fencing + 两级 offset + 幂等兜底。无 MQ 依赖、无中心分配器(v1)、可降级。复杂度集中在 fencing 与重均衡窗口,是"完全自主协调"的必然成本(§15.1 决策已接受)。
+
+#### 13.2.9 降级模式端到端时序(Meta 不可用 → 自洽 → 恢复对齐)
+
+§13.2.3 / §13.2.8 提及降级,本节给出端到端时序,明确各路径在 Meta 挂/恢复期间的行为。
+
+**① Meta 不可用检测与进入降级**
+
+```
+T0 Meta 心跳/watch 失败(连续 K 次,如 3 次/5s)
+T1 所有 Runtime 实例标记 Meta=DEGRADED
+ ├─ 分区分配:冻结在最后缓存的全量实例列表 + 确定性算法(§13.2.8 ①)
+ │ → gen 不再推进(无法 fencing 新故障)
+ ├─ offset 写:仅写本地 RocksDB(远程层不可用)
+ ├─ offset 读:仅读本地(max(local) 退化为 local)
+ ├─ 订阅视图:冻结在最后缓存的全量视图(新 subscribe 仅本实例可见)
+ ├─ ACL/限流规则:冻结在最后缓存
+ └─ 实例间转发:仍可用(实例列表缓存仍在,能找到目标实例地址)
+```
+
+**② 降级期间各路径行为**
+
+```
+路径 A:publish
+ SDK → POST /events/publish → IngressPipeline(用缓存 ACL,最终一致)
+ → Storage.send(MQ/S3Stream) ← MQ 不依赖 Meta,正常写
+ → publish 正常(存储后端独立于 Meta)
+
+路径 B:subscribe / unsubscribe
+ subscribe 写集群级视图失败 → 仅写本实例进程内 Map
+ → 本实例订阅仍工作;跨实例订阅不互通(新订阅者若 poll 到别的实例,收不到)
+ → 影响:新订阅者在降级期间可能丢消息,恢复后对齐
+ → 缓解:客户端 SDK 重试 subscribe + 恢复后重连
+
+路径 C:下发(pollAndDispatch)
+ 分区拥有者实例拉 MQ → selectTargets(用缓存订阅视图)
+ → 本实例订阅者:正常 push
+ → 跨实例订阅者:实例间转发(缓存路由表仍可用)→ 正常 push
+ → 已有订阅的下发基本正常(依赖缓存视图)
+
+路径 D:offset 推进
+ ACK → 写本地 RocksDB(不刷 Meta)
+ → crash 恢复:读本地,不丢进度
+ → 但 clientId 迁移到别的实例:新实例本地无该 offset → 从 earliest 重放或丢进度
+ → 影响:降级期间实例 crash 导致 clientId 迁移时,可能重放(靠幂等收敛,§13.3.5)
+
+路径 E:重均衡
+ 实例增删:无法感知(Meta 挂,租约/列表不更新)
+ → 死实例的分区无人接管(gen 不推进)→ 该分区消息积压
+ → 影响:降级期间实例故障 = 分区暂停(不丢,恢复后继续)
+```
+
+**③ Meta 恢复 → 对齐流程**
+
+```
+T2 Meta 恢复,watch 重连
+T3 各实例标记 Meta=RECOVERING,开始对齐:
+ ├─ 实例列表对齐:重新注册心跳 → Meta 重建全量列表
+ ├─ offset 对齐:本地 RocksDB offset 批量刷 Meta
+ │ → readOffset 回归 max(local, remote)
+ ├─ 订阅视图对齐:本实例进程内 Map 的新订阅 → 重新写 Meta
+ │ → 恢复集群级视图一致性
+ ├─ 分配重算:全量列表就绪 → 确定性算法重算 → gen 推进
+ │ → 死实例的分区重新分配给存活实例 → 接管拉取(从 Meta offset 继续)
+ └─ ACL/规则:watch 推送最新规则,覆盖缓存
+T4 对齐完成,标记 Meta=HEALTHY,退出降级
+
+对齐期间的保护:
+ · gen 推进时,旧 owner 自停(§13.2.8 ④ fencing)
+ · 重放窗口靠幂等收敛(§13.3.5)
+ · 对齐是渐进的,不阻塞正常 publish/下发
+```
+
+**④ 降级保证矩阵**
+
+| 能力 | 降级期间 | 恢复后 |
+|------|---------|--------|
+| publish | ✅ 正常(MQ 独立于 Meta) | ✅ |
+| 已有订阅下发 | ✅ 正常(缓存视图) | ✅ |
+| 新订阅 | ⚠️ 仅本实例可见 | ✅ 对齐到集群 |
+| offset 不丢 | ✅ 本地 RocksDB | ✅ 刷回 Meta |
+| clientId 迁移 | ⚠️ 可能重放(幂等收敛) | ✅ |
+| 实例故障接管 | ❌ 分区暂停(不丢) | ✅ 重分配接管 |
+| ACL/限流 | ⚠️ 冻结最后规则 | ✅ 刷新 |
+
+> **降级哲学**:Meta 挂时"尽力而为 + 不丢数据",牺牲部分一致性(新订阅、故障接管)换取可用性;恢复后渐进对齐,幂等兜底收敛。这是 §15 "可降级部署"原则在多实例协调上的落实。
+
+### 13.3 下发可靠性与消息语义
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ §13.3.1 ACK(offset 仅 ACK 推进)、§13.3.2 重试+DLQ(指数退避+`.DLQ`)、§13.3.5 去重声明、§13.3.6 不支持事务——均已实现(`ReliableDispatcher`)。**缺口**:§13.3.2 退避无 jitter(G13);§13.3.3 STICKY 单实例✅但多实例退化为 RoundRobin(G8);§13.3.4 TTL 过期丢弃未实现(附录 F.5)。
+
+§12.6.4 自述"简化版:发送成功即更新 offset,不做重试和 ACK 确认"——这意味着 **HTTP 下发失败即丢消息**。消息总线的基本契约必须补齐。
+
+#### 13.3.1 ACK 机制(至少一次)
+
+Long-Polling 下发是单向的(poll 响应里塞消息),客户端是否收到/处理完未知。需显式 ACK:
+
+```
+SDK:
+ poll() 返回一批消息 + lastOffset
+ 处理完成后 → POST /events/ack { subId, clientId, topic, partition, offset }
+
+Runtime:
+ offset 仅在 ACK 后推进(at-least-once)
+ ACK 超时(ackTimeout,如 30s)未收到 → 视为下发失败 → 重投
+
+接口:
+ POST /events/ack
+ body: { "subId":"...", "clientId":"...", "topic":"orders",
+ "partition":0, "offset":1143 }
+ resp: 200 OK
+```
+
+```
+下发 → 等 ACK
+ ├─ ACK 到达 → offsetStore.writeOffset(offset) ✅ 推进
+ ├─ ACK 超时 → 重投(同消息同 dedupId) 🔁
+ └─ 重投超 maxRetries → 转 DLQ ☠️
+```
+
+#### 13.3.2 重试与死信队列
+
+> **v1.9 决策:retry 内置,不暴露 SPI 插件扩展。** uni runtime 的重试由内置 `ReliableDispatcher`(指数退避 + DLQ)承担,**不复用/不接线** `eventmesh-retry` 模块(及其 SPI 插件)。重试策略不可插拔(固定为指数退避),无需 SPI 扩展点。
+
+内置 `ReliableDispatcher` + DLQ topic 机制:
+
+```
+下发失败/ACK 超时
+ → ReliableDispatcher 调度重投(指数退避:1s/2s/4s/8s/16s)
+ → 重投
+ → 超过 maxAttempts(默认 6 = 初次 + 5 次重试)
+ → 转 DLQ topic: "<原topic>.DLQ"
+ → DLQ 可被独立订阅(人工/自动消费)+ 告警
+
+DLQ 消息携带:
+ · 原始 CloudEvent(含 id)
+ · 失败原因、重试次数、首次失败时间
+ · 死信 CloudEvent extension: emdlqreason, emdlqretrycount(无连字符,CloudEvents 命名规则)
+```
+
+**重试器实现(`ReliableDispatcher`,内置):**
+
+```
+ReliableDispatcher(单实例,pending 表 + tick 调度):
+ · pending: ConcurrentHashMap,Delivery 记 attempt + nextAttemptAt
+ · tick() 周期推进(由 UniRuntime 调度,或接 HashedWheelTimer):扫过期 Delivery
+ · 超时/nack → 重投;attempt >= maxAttempts → DLQ
+ · offset 仅在 ack() 推进(§13.3.1)
+
+退避(固定指数,不可插拔):
+ · 1s,2s,4s,8s,16s(2^n,封顶 16s)
+ · (jitter 可后续加,防重试风暴同步)
+
+重试流程时序:
+ T0 下发 msg-1 到 clientId-X,记 pending {deliveryId, attempt=1, nextAttemptAt=T0+ackTimeout}
+ T1 ACK 超时(ackTimeout 未收到)→ tick 命中 → 重投,attempt=2,nextAttemptAt=T+退避
+ T2 重投成功(ACK 到达)→ ack() 推进 offset,pending 移除
+ T3 重投又超时 → tick 再重投,attempt++ ... 直到 attempt=maxAttempts
+ T4 attempt>=maxAttempts → tick 命中转 DLQ(写 .DLQ),pending 移除,记 metric
+
+关键:offset 只在 ACK 成功后推进(§13.3.1)。
+ 重投不推进 offset,故 MQ 侧消息不移除——重投的是"同一消息再次下发"
+```
+
+**并发与持久化:**
+
+```
+并发:
+ · Retryer 进程内单例,RetryTask 不可变(msgId+retryCount+nextRetryAt)
+ · 重投通过 PushService.push(线程安全)
+ · 同 msgId 的多次失败:仅一个 RetryTask 在轮(去重入队,按 msgId)
+
+持久化(crash 恢复):
+ · Retryer 是内存时间轮,进程 crash 则重试任务丢失
+ · 恢复策略:crash 后 offset 未推进的消息,由 SubscriptionManager 重新 poll + 下发
+ (因为 offset 没推进,MQ 侧消息还在,重启后自然重拉重发)
+ → 不持久化 Retryer,靠"offset 不超前"保证不丢(crash 后重放,幂等收敛)
+ → 代价:crash 后已重试 N 次的计数丢失,重新从 0 计;可接受(重试计数非关键状态)
+
+与 DLQ 的关系:
+ · DLQ 是持久化 topic(写 MQ),非内存
+ · 转 DLQ 后 offset 推进(视为"已处理",不再重投)
+ · DLQ 消息可被独立订阅消费/重投(Admin 面 §13.5.4 replay)
+```
+
+**配置项:**
+
+```properties
+eventmesh.retry.maxRetries=5 # 最大重试次数
+eventmesh.retry.backoff=EXPONENTIAL # EXPONENTIAL/FIXED/LINEAR
+eventmesh.retry.initialDelayMs=1000 # 初始退避
+eventmesh.retry.maxDelayMs=60000 # 退避封顶
+eventmesh.retry.jitterRatio=0.2 # ±20% jitter
+eventmesh.retry.ackTimeoutMs=30000 # ACK 超时
+eventmesh.dlq.topicSuffix=.DLQ # 死信 topic 后缀
+```
+
+#### 13.3.3 顺序消息(粘性会话)
+
+§4.2.1 的 RoundRobin 会**主动破坏分区顺序**(同 key 消息轮询到不同 worker)。补 STICKY 子模式:
+
+```
+DistributionMode:
+ LOAD_BALANCE // RoundRobin(默认,无序)
+ LOAD_BALANCE_STICKY // 粘性:按 partitionKey 绑定 clientId
+ BROADCAST
+ MULTICAST
+
+STICKY 路由:
+ partitionKey = event.getExtension("partitionkey") // CloudEvents 规范字段
+ target = subscribers[ hash(partitionKey) % subscribers.size() ]
+ → 同 key 永远发同一 worker → 保序
+
+适用:订单状态机(同一订单需顺序处理)、事件溯源
+```
+
+> CloudEvents 1.0 规范已定义 `partitionkey` extension,直接复用。
+
+#### 13.3.4 延迟 / 定时消息
+
+develop 仅 TTL(消息存活时间,过期丢弃),无定时投递。
+
+**决策(v1)**:
+- **保留 TTL**:CloudEvents `time` + `x-em-ttl`,过期消息不下发、直接丢弃。
+- **不支持定时投递**(指定时刻才下发):v1 不实现。如需,后续独立延迟队列 + 时间轮触发,列入 roadmap。
+- 文档明确声明,避免误用。
+
+#### 13.3.5 去重与幂等
+
+单 Consumer 全量拉 + ACK 超时重投 = **必然重复下发**。
+
+**决策**:
+- CloudEvents `id` 作为 **dedupId**,重投时 `id` 不变。
+- EventMesh 侧**不做全局去重**(成本高、需状态存储),靠 **at-least-once + 客户端幂等**。
+- 客户端按 `id` 去重(业务侧幂等表/本地去重窗口)。
+- 文档明确:**交付语义 = 至少一次**,不承诺恰好一次(Exactly-Once 由业务幂等保证)。
+
+#### 13.3.6 事务消息(明确不支持)
+
+develop 无事务消息。本重构删除 `createTransactionProducer`。
+
+**决策**:明确声明**不支持事务消息/半消息/事务回查**。需要事务语义的业务用 saga、本地消息表、Outbox 模式实现。
+
+### 13.4 安全能力(完全缺失,develop 全有)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ §13.4.1 TLS 有 `TlsContextFactory`(真 SSLContext + mTLS)但 boot 默认不接线 + 硬编码 TLSv1.2 + truststore 密码不独立(G14);§13.4.2 `FilterChain`+`TokenAuthFilter`+`SignatureVerifierFilter` 实现,**但 `AclFilter` 是静态 map 骨架、非 §13.4.2 规则模型(G9)**;§13.4.3 租户隔离只剩 topic 字符串匹配(SubscriptionManager 无 tenant 过滤);§13.4.4 签名验签✅。
+
+#### 13.4.1 TLS / mTLS
+
+文档 HTTP-only 却未提 HTTPS,Long-Polling 明文传输。复用 develop 的 `SslContextFactory` + `EventMeshTlsConfig`:
+
+```properties
+# 新增配置
+eventmesh.server.tls.mode=ENFORCING # DISABLED / PERMISSIVE / ENFORCING
+eventmesh.server.tls.ssl.protocol=TLSv1.3
+eventmesh.server.tls.keystore.path=...
+eventmesh.server.tls.keystore.password=...
+# mTLS 双向认证
+eventmesh.server.tls.client.auth=REQUIRE # NONE / OPTIONAL / REQUIRE
+eventmesh.server.tls.truststore.path=...
+```
+
+- Long-Polling 走 HTTPS。
+- PERMISSIVE 模式支持平滑迁移(明文+密文共存)。
+
+#### 13.4.2 认证与鉴权
+
+§2.1 Pipeline 列了 `AuthFilter` / `AclFilter` 名字却零设计。补:
+
+> **v1.9 决策:security 不用 SPI 插件扩展,用 filter 链扩展。** uni runtime 内置 `FilterChain` + `IngressFilter` 接口(`org.apache.eventmesh.runtime.uni.security`)。认证/鉴权/签名各是一个 filter(`TokenAuthFilter` / `AclFilter` / `SignatureVerifierFilter`),**不复用/不接线** `eventmesh-security-plugin`(及其 SPI 插件 jar)。扩展安全能力 = 新增一个 `IngressFilter` 实现(代码扩展,配置进 FilterChain),而非打 SPI 插件包。
+
+```
+AuthFilter(认证:你是谁)—— 实现 IngressFilter
+ · 内置 TokenAuthFilter(token);扩展认证方式 = 新增 IngressFilter(不装 SPI 插件)
+ · SDK builder 加 .credential(token/username+password)
+ · 每请求 Authorization 头校验
+ · 失败 → 401
+
+AclFilter(鉴权:你能做什么)—— 实现 IngressFilter
+ · topic 粒度权限:publish / subscribe
+ · 权限上下文:CloudEvents extension emuserid / emtenantid(无连字符,CloudEvents 命名规则)
+ · 规则可经 MetaService 动态下发
+ · 失败 → 403
+```
+
+**ACL 规则模型细化:**
+
+```
+规则结构(每条 ACL 规则):
+ {
+ principal: "." | ".*" | "*", // 主体(支持通配)
+ resource: "." | ".*" | "*", // 客体 topic(含租户前缀,§13.4.3)
+ action: PUBLISH | SUBSCRIBE | REQUEST | "*", // 操作
+ effect: ALLOW | DENY, // 效果
+ priority: // 匹配优先级(高优先)
+ }
+
+匹配算法(按 priority 降序遍历,首条匹配生效):
+ 1. 规则按 priority 降序排列(DENY 优先原则:同 priority 时 DENY 先匹配)
+ 2. 对请求 (principal, resource, action) 遍历:
+ principal 匹配(精确 > tenantId.* > *)AND
+ resource 匹配(精确 > tenantId.* > *)AND
+ action 匹配(精确 > *)
+ → 首条命中规则的 effect 生效
+ 3. 无任何规则命中 → 默认 DENY(白名单模型)
+
+示例规则集:
+ priority=100 DENY principal=tenantB.* resource=tenantA.* action=* // 跨租户禁止
+ priority=50 ALLOW principal=tenantA.* resource=tenantA.* action=* // 租内全允许
+ priority=10 ALLOW principal=tenantA.svc1 resource=tenantA.orders action=PUBLISH
+ → tenantB 用户访问 tenantA.orders:命中 priority=100 DENY → 403
+ → tenantA.svc1 发布 tenantA.orders:命中 priority=50 ALLOW(10 也允许,但 50 先匹配)
+```
+
+**规则在 Meta 的存储与下发:**
+
+```
+Meta 存储:
+ key: /em/acl/rules
+ value: List(JSON 数组,按 priority 排序)
+ watch: 所有 Runtime 实例 watch 此 key
+
+下发与生效:
+ · 规则变更(Admin/运维写 Meta)→ watch 推送 → 全实例更新本地规则缓存
+ · AuthFilter 认证后,AclFilter 用本地缓存做匹配(无 Meta 查询,热路径零 RTT)
+ · 规则缓存最终一致(watch 推送有 ms 级延迟,可接受)
+
+租户隔离联动(§13.4.3):
+ · resource 带 tenantId 前缀,ACL 天然按租户隔离
+ · 跨租户访问由 priority 最高的 DENY 规则拦截
+ · SubscriptionManager 按 tenantId 过滤订阅视图(看不到别租户订阅)
+```
+
+#### 13.4.3 租户隔离
+
+- topic 命名空间:`.`(如 `tenantA.orders`)。
+- AclFilter 按 tenant 隔离:租户 A 看不到租户 B 的订阅关系与消息。
+- 多租户共享集群,SubscriptionManager 按 tenant 过滤订阅视图。
+
+#### 13.4.4 CloudEvents 签名
+
+借鉴 A2A 的 `AgentCardSignature`。可信来源校验:
+
+```
+CloudEvents extension: x-em-signature = HMAC-SHA256(secret, canonical(event))
+ · canonical = source + type + id + time + data 的规范化拼接
+ · 接收方(Runtime / 订阅者)用预共享密钥验签
+ · 防篡改 + 来源可信
+```
+
+### 13.5 可观测性(完全缺失,develop 全有)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ §13.5.1 metrics 8/16 项核心已实现(OTel 仪表);§13.5.2 trace 关键节点 Span 已埋(`UniTrace`,publish/dispatch/ack/retry/dlq),**但 traceparent/tracestate/baggage 全链路显式透传未做**;§13.5.4 Admin 仅 ~5/8 接口且本地视图(G15)。legacy metrics/trace 插件确未接线(符合 v1.9 决策)。
+
+#### 13.5.1 Metrics
+
+**只用 OpenTelemetry**(Meter API,`org.apache.eventmesh.runtime.uni.metrics.UniMetrics`)。**不**用 `eventmesh-metrics-prometheus`(legacy,不接线)。指标定义(名称 / 类型 / 标签 / 含义):
+
+> 注:下表为目标全集;当前实现已落地核心 8 项(publish/publish_failed/rate_limited/dispatched/dispatch_latency/ack/redeliveries/dlq),其余(带 *)随相应 Phase 补齐。
+
+| 指标 | 类型 | 标签 | 含义 |
+|------|------|------|------|
+| `eventmesh_publish_count` | Counter | topic, tenant | 入方向 publish 数 |
+| `eventmesh_publish_failed_count` | Counter | topic, tenant | publish 失败数 |
+| `eventmesh_rate_limited_count` | Counter | topic | 被限流拒绝的 publish 数 |
+| `eventmesh_dispatched_count` | Counter | topic, mode(LB/Bcast/Mcast), tenant | 下发消息数 |
+| `eventmesh_dispatch_latency_nanos` | Histogram | topic | selectTargets + push 延迟 |
+| `eventmesh_ack_count` | Counter | topic, clientId | ACK 数 |
+| `eventmesh_redeliveries_count` | Counter | topic, clientId | 重投数 |
+| `eventmesh_dlq_count` | Counter | topic, reason | 死信数 |
+| `eventmesh_poll_idle_ratio` * | Gauge | topic | poll 空闲比例(无消息) |
+| `eventmesh_active_subscribers` * | Gauge | topic, tenant | 活跃订阅者数 |
+| `eventmesh_offset_lag` * | Gauge | topic, partition | 分发 offset 落后 MQ offset |
+| `eventmesh_pending_queue_size` * | Gauge | topic, clientId | 每 clientId 背压队列水位 |
+| `eventmesh_slow_consumer_count` * | Gauge | topic, state(SLOW/STALLED) | 慢消费者数(§13.6.2) |
+| `eventmesh_request_reply_count` * | Counter | topic, outcome(ok/timeout) | request-reply 调用数 |
+| `eventmesh_partition_owner` * | Gauge | topic, partition, instance | 分区分配视图(§13.2.8) |
+
+> 标签设计:topic / tenant / mode 为核心维度,便于按租户/topic 切片。所有指标走 OTel API,**默认经 OTel Prometheus exporter 暴露 `/metrics`**(部署侧可换 OTLP 等),不经 legacy `metrics-prometheus` 插件、无 SPI 扩展点。
+
+#### 13.5.2 分布式 Trace 传播
+
+> **v1.11 实现**: `UniTrace` (org.apache.eventmesh.runtime.metrics.UniTrace) 已在 publish/dispatch/ack/retry/dlq 关键节点埋 OTel Span。CloudEvent id/type 作为 span 属性, 链路: publish → dispatch → ack (+ retry/dlq 分支)。
+
+全链路 CloudEvents,trace context 用 **W3C `traceparent` + `tracestate` + `baggage`**(CloudEvents Distributed Tracing extension):
+
+```
+CloudEvent extensions(trace 透传字段):
+ traceparent: 00-{traceId}-{spanId}-{flags} // W3C Trace Context,必传
+ tracestate: vendor1=value,vendor2=value // 厂商扩展(可选)
+ baggage: k1=v1,k2=v2 // 跨服务业务上下文(如 tenantId、userId)
+
+传播规则:
+ · 请求方生成 traceparent(或继承上游),写入 CloudEvent extension
+ · 每跳(Runtime 内部节点)继承 traceId,生成新 spanId
+ · 跨实例 forward、跨 Connector、SDK ↔ Runtime 均透传 traceparent + baggage
+ · baggage 用于透传 tenantId/userId(与 ACL 的 x-em-tenantid 对齐)
+```
+
+**关键节点 Span 设计:**
+
+```
+Span 链路(一条 publish → 下发的完整 trace):
+ [span] sdk.publish (SDK 侧, parent=traceparent)
+ └─ [span] ingress.pipeline (Runtime, AuthFilter→AclFilter→Transformer→Router)
+ └─ [span] storage.send (Runtime, 写 MQ)
+ └─ [span] dispatch.pollAndDispatch (Runtime, 拉取+selectTargets)
+ ├─ [span] push.toClient (Runtime, TransportChannel.send)
+ │ └─ [span] client.ack (SDK 侧)
+ └─ [span] forward.crossInstance (若跨实例转发, §13.2.5)
+
+异常分支 Span:
+ · retry:dispatch.retry(记录重试次数、退避时长)
+ · dlq:dispatch.dlq(记录死信原因)
+ · request-reply:request.send → reply.route → reply.deliver(§17,独立 trace)
+
+Span 属性(attributes):
+ · topic, tenantId, mode, clientId, partition, offset, cloudEventId
+ · error(失败时), retryCount
+```
+
+用 **OpenTelemetry Tracer** 创建 Span,经 OTel exporter(OTLP 等)导出。**不**用 `eventmesh-trace-plugin`(zipkin/jaeger/pinpoint)(legacy,不接线)。按 CloudEvents `id` 关联同一消息的所有 span,形成全链路轨迹。
+
+#### 13.5.3 消息轨迹
+
+用 **OpenTelemetry Span**(同 §13.5.2),在关键节点埋点:publish / 入 MQ / dispatch / push / ack / retry / dlq。支持按 CloudEvents `id` 查询某条消息的全链路轨迹。不再依赖 `eventmesh-trace-plugin`。
+
+#### 13.5.4 Admin 管理面重做(文档误标"不变")
+
+§13(旧)称 Admin "不变",但旧 admin v1 的 19 个 handler 大量依赖 **TCP session / Consumer Group**(如 `ShowListenClientByTopicHandler`、`RedirectGroupBatchHandler`、`RejectClientByIpPortHandler`),底层已删 → **Admin 必须重做**。
+
+新 Admin 面(基于 SubscriptionManager + OffsetStore 集群级视图,经 Admin Server):
+
+| 接口 | 功能 |
+|------|------|
+| `GET /admin/subscriptions` | 查询集群订阅关系(按 topic/tenant) |
+| `GET /admin/offsets` | 查询 offset lag(按 topic/clientId) |
+| `GET /admin/clients` | 查询在线客户端 + 所属实例 |
+| `POST /admin/clients/{id}/reject` | 踢客户端(清除其订阅) |
+| `GET /admin/dlq/{topic}` | 浏览死信 |
+| `POST /admin/dlq/{topic}/replay` | 死信重投 |
+| `PUT /admin/ratelimit/rules` | 下发限流规则 |
+| `GET /admin/health` | 实例健康/分区分配视图 |
+
+### 13.6 运维与稳定性
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ §13.6.1 限流(`TokenBucketRateLimiter` per-topic,但非 Meta 动态下发)、§13.6.2 背压慢消费者四态状态机(有缺陷 G11)、§13.6.4 优雅停机(drain+等ACK+flush,缺租约释放 G12)已实现;**§13.6.3 动态配置热更新、§13.6.5 僵尸 poll 清理/连接上限未实现**。
+
+#### 13.6.1 限流
+
+§2.1 的 `RateLimitFilter` 实化。复用 develop 的 Guava `RateLimiter` + `RateLimiterRulerListener`(meta 动态规则):
+
+```
+RateLimitFilter(Ingress):
+ · per topic / per clientId 限流
+ · 规则经 MetaService 动态下发(RateLimiterRulerListener)
+ · 超限 → 429 Too Many Requests
+
+下发侧限流(新增,develop 仅 TCP 侧有):
+ · per clientId 下发速率上限,防慢消费者拖垮
+ · 超限 → 消息暂存 pendingEvents,不丢
+```
+
+#### 13.6.2 背压与慢消费者隔离
+
+§12.4 广播 1000 订阅者场景,慢消费者会拖垮线程池。本节细化背压机制。
+
+**① 每 clientId 有界队列 + 溢出策略矩阵**
+
+```
+每 clientId 一个独立有界队列(pendingEvents),与分发模式解耦:
+ · 容量 maxPending(默认 10000,可按 topic/clientId 配)
+ · 入队:分发到该 clientId 但其通道未连接 / 来不及消费时
+ · 出队:通道连接时 flush(§7.2 registerChannel)
+
+> **v1.11 实现**: `PushService` 已实现慢消费者四态状态机 (§13.6.2): HEALTHY → SLOW (buffer≥80%) → STALLED (连续3次slow) → EVICTED (连续10次stalled → 自动 unsubscribe)。`getClientState(clientId)` 供 admin 查询。
+
+溢出策略(按订阅属性选,非全局一刀切):
+ ┌──────────────┬──────────────────────────────┬─────────────────────┐
+ │ 策略 │ 行为 │ 适用 │
+ ├──────────────┼──────────────────────────────┼─────────────────────┤
+ │ DROP_OLDEST │ 丢最旧消息 + 记 metric │ 实时流(行情/状态) │
+ │ DROP_NEWEST │ 丢新消息 + 记 metric │ 保历史完整性场景 │
+ │ BLOCK │ 暂停对该 clientId 分发 │ 不可丢(配合超时踢) │
+ │ TO_DLQ │ 溢出消息转 DLQ │ 关键业务消息 │
+ └──────────────┴──────────────────────────────┴─────────────────────┘
+ 默认 DROP_OLDEST + metric;TO_DLQ 复用 §13.3.2 重试/DLQ 通路
+```
+
+**② 慢消费者检测算法(基于 ACK 速率 + 队列水位)**
+
+```
+慢消费者判定指标(每 clientId 周期采样,sampleInterval=10s):
+ · ackRate:单位时间 ACK 数(吞吐)
+ · queueLag:当前 pendingEvents 水位
+ · pollInterval:最近两次 poll 间隔
+ · ackLatency:消息入队到 ACK 的延迟
+
+慢消费者状态机:
+ HEALTHY ──queueLag > highWatermark(80%) 持续 2 周期──→ SLOW
+ SLOW ──queueLag < lowWatermark(50%) 持续 3 周期──→ HEALTHY
+ SLOW ──连续 N(=5) 周期仍 SLOW ──────────────────→ STALLED
+ STALLED ──连续 M(=12) 周期无 ACK ──────────────────→ EVICTED
+
+各级动作:
+ SLOW :降配额(分发速率上限砍半)+ 告警 metric
+ STALLED :暂停分发(不再入队,已有队列按溢出策略处理)+ 告警
+ EVICTED :自动 unsubscribe(防泄漏)+ 通知客户端重连
+```
+
+**③ 与 ACK / 重试 / DLQ 的联动**
+
+```
+分发 → 入 pendingEvents → 通道推送 → 客户端 ACK(§13.3.1)
+ ├─ ACK 到达 → 出队,offset 推进
+ ├─ ACK 超时 → 重投(§13.3.2),原消息重入队尾
+ └─ 重投超 maxRetries → 转 DLQ
+
+背压与重试的冲突处理:
+ · 慢消费者 STALLED 期间,新消息不分发 → 不触发 ACK 超时 → 不重投
+ (避免对慢消费者雪崩重投)
+ · STALLED 解除后,从队列恢复分发;队列溢出的按溢出策略(DROP/TO_DLQ)
+ · EVICTED 后,其未 ACK 消息:LOAD_BALANCE 由其他订阅者接管;BROADCAST 转 DLQ
+```
+
+**④ 广播场景的隔离(§12.4 落实)**
+
+```
+1000 订阅者广播:
+ · 线程池(虚拟线程,§15.8)并发下发,每订阅者独立队列
+ · 单个慢消费者阻塞在自己队列,不阻塞其他 999 个
+ · 慢消费者走 ② 状态机:SLOW 降配额 → STALLED 暂停 → EVICTED 踢出
+ · 踢出后广播集缩为 999,不影响其余
+ · 线程池隔离:每 topic 独立线程池,防某 topic 慢消费者拖垮全 Runtime
+```
+
+**⑤ 配置项**
+
+```properties
+# 背压与慢消费者
+eventmesh.backpressure.maxPending=10000 # 每 clientId 队列上限
+eventmesh.backpressure.overflowPolicy=DROP_OLDEST # DROP_OLDEST/NEWEST/BLOCK/TO_DLQ
+eventmesh.backpressure.sampleIntervalMs=10000 # 采样周期
+eventmesh.backpressure.highWatermark=0.8 # 慢消费者高水位
+eventmesh.backpressure.lowWatermark=0.5 # 恢复低水位
+eventmesh.backpressure.stalledCycles=5 # STALLED 阈值
+eventmesh.backpressure.evictCycles=12 # EVICTED 阈值(无 ACK)
+eventmesh.backpressure.threadPoolPerTopic=true # 每 topic 独立线程池
+```
+
+#### 13.6.3 动态配置与热更新
+
+复用 `EventMeshDynamicConfigManager` + MetaService:
+- 订阅关系、限流规则、过滤规则、ACL 规则**热更新**,不重启。
+- 配置变更经 MetaService watch 推送到所有实例。
+
+#### 13.6.4 优雅停机
+
+§9 仅一句 `ctx::shutdown`,未提停机语义。补:
+
+```
+shutdown 顺序:
+ 1. 停止接受新 publish / 新 poll 请求(返回 503)
+ 2. drain pendingEvents(尽力下发已缓冲消息)
+ 3. 等待 in-flight 消息 ACK(graceful timeout,如 10s)
+ 4. flush offset(本地 RocksDB + 远程同步)
+ 5. 释放分区租约(通知 Admin Server 重新分配 → 其他实例接管)
+ 6. 关闭 Storage / HTTP Server
+
+关键:步骤 4-5 保证不丢进度、不丢分区归属。
+```
+
+#### 13.6.5 连接生命周期管理
+
+- **poll 超时清理**:Long-Polling 超时返回空响应,释放挂起的 AsyncContext。
+- **僵尸 poll 检测**:客户端长期不 poll → 按 `lastHeartbeat` 清理订阅。
+- **客户端断开**:emitter.send() 异常 → 移除订阅 + 释放资源。
+- **连接泄漏防护**:poll channel 总数上限,超限拒绝新 poll。
+
+### 13.7 接入能力补充
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:❌/✅ 混合。§13.7.1 **WebSocket(默认主传输)未实现(G1)**,仅 SSE+Long-Polling;§13.7.2 WebHook✅(`WebHookChannel` 签名+重试+DLQ);§13.7.3 批量发送❌;§13.7.4 MQTT 不支持已声明✅。
+
+#### 13.7.1 三种传输按场景选(用户可选,默认 WebSocket)
+
+§12.2 自承 Long-Polling 有 1 RTT 延迟。结合 负载(毫秒级延迟 + 高吞吐 + TCP 同步调用),传输层抽象为三种**用户可选**的 HTTP 家族协议,默认 WebSocket(详见 §5.1.1 / §7.2 / §15.6):
+
+```
+Transport 接口(抽象,三种实现):
+ ├─ WebSocketTransport (默认推送主传输:持久双向订阅流,高吞吐,同连接发控制命令)
+ ├─ SSETransport (单向流式输出:LLM token 流、A2A agent 流式回传,穿墙极佳)
+ └─ LongPollingTransport(降级备选:WS 被防火墙拦截时)
+
+SDK: CloudEventsClient.builder().transport(WEBSOCKET).build() // 或 SSE / LONG_POLLING
+Runtime: PushService 经 TransportChannel 统一三种传输(§7.2)
+```
+
+| 传输 | 适用场景 | 选型逻辑 |
+|------|---------|---------|
+| **WebSocket**(默认) | 持久订阅事件流、双向控制(unsubscribe/ACK)、高吞吐 | 双向长连接持续推多消息,无 1 RTT 间隙,对齐 TCP 推送实时性 |
+| **SSE** | 一次请求触发的单向流式回传(LLM token 流、A2A 流式) | 单向服务端→客户端,穿墙最好,浏览器/LLM 生态成熟,自动重连 |
+| **Long-Polling** | WS 被企业网络拦截的降级 | 兼容性最好,1 RTT 间隙延迟(机房内 ~1ms) |
+
+> **为什么不是 gRPC**:WebSocket 给到与 gRPC 流式同等的低延迟+高吞吐,但属 HTTP 家族(HTTP 升级),不需要 proto+流+独立 SDK,不违背 §15.3 "无 gRPC"铁律。SSE 比 WS 更轻、穿墙更好,适合单向流式。三者各有所长,故支持三种让用户按场景选,而非"只选一个"。
+
+> **虚拟线程的影响(§15.8)**:Java 21 虚拟线程下,挂起连接(Long-Polling、WS、SSE 的每个待推通道)的成本从"占一个平台线程"降到"占一个轻量虚拟线程"。这使得 Long-Polling 在大连接数下的阻塞成本接近 0,但 **WebSocket 在"消息间隙无 1 RTT 空窗 + 高吞吐多路复用"上的优势依然成立**——故默认仍 WebSocket,虚拟线程是吞吐/连接规模的红利,不改变默认选型。
+
+#### 13.7.2 WebHook 主动推送
+
+> **适用场景边界**:WebHook 是**可选旁路投递通道**,非核心数据通路。与主线推送(WebSocket/SSE/Long-Polling)分工:
+> - **主线推送**:订阅者**主动连** EventMesh(维持 WS/poll),高吞吐、低延迟、双向控制——用于**能跑 SDK 的自家业务服务**。
+> - **WebHook**:EventMesh **主动 POST** 到订阅者的 deliveryUrl,订阅者被动接——用于**不能/不会主动连 EventMesh 的第三方**(企微/钉钉/Slack 机器人、第三方 SaaS、告警系统、外部 HTTP API)。
+>
+> **何时不用 WebHook**:下游全是自家服务、能跑 SDK 维持连接 → 用主线 WebSocket 推送,WebHook 是多余复杂度。WebHook 每条一次 HTTP 往返,吞吐低、延迟高,**不适合高吞吐数据流**,只适合低频通知/第三方集成/告警链路。 高吞吐毫秒级场景以 WebSocket 为主力,WebHook 排 Phase 8.5 低优先级。
+
+纯 pull 模式下,EventMesh 无法主动推送到**不 poll 的第三方**(如第三方 HTTP API、企微机器人)。develop 有 `WebhookPushRequest`。补 push 模式:
+
+```
+subscribe 时可配 deliveryUrl:
+ POST /events/subscribe
+ { "topic":"orders", "mode":"BROADCAST",
+ "delivery": { "type":"webhook", "url":"https://third-party/cb",
+ "secret":"...", "retry":5 } }
+
+EventMesh 主动 HTTP POST 推送到 deliveryUrl:
+ · 带 x-em-signature 签名
+ · 失败重试(指数退避)+ 转 DLQ
+ · 与 WebSocket/SSE/Long-Polling 并存(订阅者按需选一种 delivery 方式)
+```
+
+**WebHook 签名规范细化:**
+
+```
+签名算法:HMAC-SHA256(secret, canonical)
+ canonical = method + "\n"
+ + url_path + "\n"
+ + timestamp + "\n"
+ + sha256(body)
+ 其中 body = CloudEvent 的 structured JSON
+
+请求头:
+ X-Em-Signature: sha256= // HMAC 结果
+ X-Em-Timestamp: // 防重放(接收方校验时间窗,如 ±5min)
+ X-Em-Delivery-Id: // 每次 delivery 唯一,接收方据此去重
+
+接收方验证:
+ 1. 校验 X-Em-Timestamp 在 ±5min 窗口内(防重放)
+ 2. 用预共享 secret 重算 HMAC,比对 X-Em-Signature(常量时间比较)
+ 3. 记录 X-Em-Delivery-Id 做去重(因重试可能重复投递)
+ 4. 返回 2xx 视为成功,非 2xx 或超时视为失败
+```
+
+**重试与 DLQ 集成:**
+
+```
+WebHook 投递失败处理(复用 §13.3.2 Retryer):
+ T0 HTTP POST 推送 → 失败(非 2xx / 超时 / 连接拒绝)
+ T1 入 Retryer,指数退避(1s/2s/4s/8s/16s)
+ · 重试时 X-Em-Delivery-Id 不变(同一 delivery,便于接收方去重)
+ · retryCount 写入 CloudEvent extension x-em-retry-count
+ T2 重试超 maxRetries(subscribe 时配的 retry,默认 5)
+ → 转 DLQ topic: .DLQ
+ → DLQ 消息带 x-em-dlq-reason=webhook_failed, x-em-webhook-url, x-em-dlq-retry-count
+ T3 Admin 面(§13.5.4)可浏览 DLQ + 手动 replay(重新触发 WebHook)
+
+与 ACK 的关系:
+ · WebHook 模式下,HTTP 2xx = ACK(等价 §13.3.1 的 ACK)
+ · offset 在 2xx 后推进;重试期间 offset 不推进
+ · 与 Long-Polling/WS/SSE 的 ACK 语义统一,仅 delivery 通道不同
+```
+
+**幂等与去重:**
+
+```
+WebHook 可能重复投递(重试 + at-least-once):
+ · 接收方按 X-Em-Delivery-Id 去重(短期,如 1h 窗口)
+ · 或按 CloudEvent id 幂等(长期,业务幂等,§13.3.5)
+ · EventMesh 侧不做全局去重,靠接收方(与下发侧 at-least-once 同理)
+```
+
+**配置项:**
+
+```properties
+eventmesh.webhook.connectTimeoutMs=5000
+eventmesh.webhook.readTimeoutMs=10000
+eventmesh.webhook.maxRetries=5 # 默认重试(可被 subscribe 配覆盖)
+eventmesh.webhook.backoff=EXPONENTIAL
+eventmesh.webhook.timestampWindowMs=300000 # ±5min 防重放窗口
+eventmesh.webhook.threadPoolSize=64 # WebHook 投递线程池(虚拟线程可放大)
+```
+
+#### 13.7.3 批量发送
+
+develop 有 `BatchSendAsyncEventProcessor`。`send()` 单条,高 TPS 场景需批量:
+
+```java
+// SDK
+CompletableFuture publish(List events); // 批量
+
+// Storage
+void sendBatch(String topic, List events, SendCallback callback);
+```
+
+降低每条消息的 RTT 开销。
+
+#### 13.7.4 MQTT(声明不支持)
+
+develop/当前分支已无 MQTT 能力。**明确声明不支持 MQTT 协议接入**。如需 MQTT,由独立网关转 CloudEvents 再接入(不在 Runtime 范围)。
+
+### 13.8 协议与格式工程化细节
+
+#### 13.8.1 CloudEvents 编码模式
+
+`parseCloudEvent` 需区分两种编码:
+
+| 编码 | Content-Type | 特点 |
+|------|-------------|------|
+| **binary** | `application/cloudevents+json`(或具体 data 类型) | CloudEvents 属性在 HTTP header,data 在 body |
+| **structured** | `application/cloudevents-batch+json` | 整个 CloudEvent(含 data)在 body |
+
+Runtime 两种都支持,SDK 默认 structured(简单)。
+
+#### 13.8.2 大消息与分片
+
+- 单消息上限(如 `maxMessageSize=1MB`,可配)。
+- 超限:**拒绝 + 明确错误**(413 Payload Too Large),不自动分片。
+- 大数据建议:data 放外部存储(OSS/S3),CloudEvents data 仅放引用 URL。
+
+#### 13.8.3 Topic 映射规则统一(消除 §6.2 与 §12.5 矛盾)
+
+§6.2 用 `subject` extension 当 topic,§12.5 又把 `subject` 当"业务标识/MQTT 映射"——**语义冲突**。统一:
+
+```
+CloudEvents 字段语义(最终):
+ type → 事件类型(如 "order.created")→ MULTICAST 过滤用
+ source → 事件来源(如 "/orderservice")→ MULTICAST 过滤用
+ subject → 业务实体标识(如 "order-123")→ 仅标识,不作 topic
+ id → 消息唯一 ID → 去重 dedupId
+ partitionkey → 分区/粘性路由 key → STICKY 负载均衡用
+ x-em-topic → EventMesh topic(显式指定)→ 路由用
+
+determineTopic(event) 优先级:
+ 1. x-em-topic extension(显式)
+ 2. 配置默认 topic
+ (不再用 subject 当 topic)
+```
+
+#### 13.8.4 过滤表达式语法
+
+§12.5 列了 MULTICAST 4 级匹配优先级,但 `CloudEventFilter.match()` 语法未定义。补:
+
+```
+v1 语法:精确匹配 + 通配
+ filter: { "type": "order.created" } // 精确
+ filter: { "type": "order.*" } // 前缀通配
+ filter: { "source": "/orderservice" } // 精确
+ filter: { "x-em-tenantid": "tenantA" } // extension 精确
+ filter: { "type":"order.*", "source":"/svc" } // AND 组合
+
+匹配规则:
+ · 所有条件 AND
+ · 值支持前缀通配 *(仅尾部)
+ · 不支持正则/CEL(列入后续 roadmap)
+
+match(event) = filter 所有 key 都匹配 event 对应属性
+```
+
+#### 13.8.5 幂等去重键
+
+- CloudEvents `id` = dedupId,重试/重投时 `id` 不变。
+- 客户端按 `id` 幂等处理。
+- EventMesh 不做全局去重(§13.3.5)。
+
+### 13.9 对实施计划(§11)的影响
+
+§11 原 Phase 1–8 只覆盖"减法"与单实例核心数据通路,未含上述缺口。本节列出的 7 类缺口已作为补充阶段正式纳入 §11 实施计划(见 §11「阶段总览与依赖」表),与原 Phase 交错排布:
+
+| 缺口阶段 | 内容 | 详细设计 | 优先级 |
+|---------|------|---------|--------|
+| **Phase 2.5 多实例协调** | 分区分配 + 租约 + offset 集中存储 + 订阅关系同步 + 实例间转发 | §13.2 | 🔴 阻断(生产 HA 前必做) |
+| **Phase 4.5 安全** | TLS/mTLS + AuthFilter + AclFilter + 租户隔离 + 签名 | §13.4 | 🔴 高 |
+| **Phase 5.5 下发可靠性** | ACK + 重试 + DLQ + STICKY 顺序 + 去重 | §13.3 | 🔴 高 |
+| **Phase 5.6 可观测性** | metrics + trace 传播 + 消息轨迹 | §13.5.1–13.5.3 | 🟠 中 |
+| **Phase 6.6 运维** | 限流 + 背压 + 动态配置 + 优雅停机 + 连接管理 | §13.6 | 🟠 中 |
+| **Phase 7.5 Admin 重做** | 新 Admin 面(替换依赖 TCP/Group 的旧 handler) | §13.5.4 | 🔴 高 |
+| **Phase 8.5 接入扩展** | WS/SSE + WebHook 推送 + 批量发送 | §13.7 | 🟡 中低 |
+
+> **关键结论**:§11 的 Phase 1–8 完成后,方案仅具备"单实例快乐路径"能力,**不可直接上生产**。生产就绪门槛 = Phase 1–8 + Phase 2.5 + 3.5 + 4.5 + 5.5 + 7.5 全部完成(§11 依赖图末行)。
+
+---
+
+## 十四、与 unified-runtime-design.md 的关系
+
+本重构方案是 **unified-runtime-design.md** 的进一步演进,核心差异:
+
+| 维度 | unified-runtime-design.md(v2.1) | 本文档(简化版) |
+|------|-----------------------------------|-----------------|
+| SDK 协议 | TCP / HTTP / gRPC / A2A 四种 | **仅 HTTP** |
+| 消息格式 | CloudEvents + EventMeshMessage + Package + proto 混用 | **全链路 CloudEvents** |
+| MQ 角色 | 存储 + 部分语义(分区/广播/订阅) | **纯存储(无 Producer/Consumer Group)** |
+| 订阅分发 | Consumer Group 继承 MQ 语义 | **EventMesh 自主实现(LOAD_BALANCE/BROADCAST/MULTICAST)** |
+| ProtocolAdaptor | 5 个(HTTP/CloudEvents/MeshMessage/OpenMessage/A2A) | **3 个(HTTP CloudEvents/A2A,直接 CloudEvents)** |
+| Processor 类 | 102 个 Processor → IngressProcessor | **~10 个 Handler** |
+| OpenMessaging | 保留(与 MQ Group 解耦后作为 API 层) | **删除**(SDK 简化为 CloudEvents-only) |
+| A2A | A2A Protocol Layer(编程模型) | **保留**(A2A → CloudEvents → Pipeline) |
+| Connector | Source/Sink → Pipeline → Storage | **独立**(Connector Runtime 与 EventMesh Runtime 完全独立,通过 HTTP/CloudEvents 通信) |
+| Admin Server | gRPC BiStream 管理面 | **重做**(旧 v1 handler 依赖 TCP/Group 已删,详见 §13.5.4 / Phase 7.5) |
+| 目标代码量 | ~70,000 行 | **~29,000 行(-59%)** |
+
+**两文档的共同点(不变的部分):**
+
+1. 统一 Runtime(单进程,替代 v1 + v2 双运行时)
+2. Ingress/Egress Pipeline(Filter → Transformer → Router)
+3. AdminClient(gRPC BiStream 管理面,通信通道不变;其上的 admin handler 业务接口重做,见 §13.5.4)
+4. Offset 管理(RocksDB + 批量异步写,参考 RocketMQ Client OffsetStore)
+5. A2A Agent 通信(基于 HTTP + CloudEvents)
+6. 可降级部署(Admin Server 不可用时 Runtime 独立运行)
+7. Connector Runtime 独立部署(不与 EventMesh Runtime 合并,通过 HTTP 接口通信)
+
+---
+
+---
+
+## 十五、用户决策记录(v1.2)
+
+> 以下为方案根基决策,定于 2026-07-02 讨论确认。后续设计与实施以本节为准;如需变更须在此追加新决策并标注取代关系。
+
+### 15.1 MQ 语义边界:完全自主协调
+
+**决策**:严格遵守"MQ 无语义"铁律,EventMesh **完全自主**实现多实例消费协调。
+
+- **不**采用"内部复用 MQ Consumer Group rebalance"的折中方案。
+- Phase 2.5 全量开发:自写分区分配协议 + 租约 + offset 集中存储 + 订阅关系集群同步 + 实例间转发。
+- 代价:开发成本最高。收益:自主性最强、不绑死 MQ rebalance 行为、§3/§4 "自主订阅分发"核心价值完整成立。
+- **影响**:§3.2 Storage Plugin 接口必须扩展 `assignPartitions` / 按 offset 范围 `poll`;§13.2 全部成立。
+
+### 15.2 下发交付语义:至少一次 + 客户端幂等
+
+**决策**:客户端下发侧采用 **At-Least-Once**,配合客户端按 CloudEvents `id` 幂等。**不**对下发侧实现 Exactly-Once。
+
+- §13.3 全部成立:ACK + 重试 + DLQ + STICKY 顺序 + 客户端去重。
+- **与迁移计划文档 EO 的关系**:见 §16。EO 仅适用于 Connector offset 管理(Source/Sink 的内部进度),不延伸到"客户端下发交付"。
+- 代价:客户端需实现幂等(业务侧幂等表 / 本地去重窗口)。收益:避免事务性下发 + 全局去重存储的极高成本。
+
+### 15.3 SDK 删除范围:全删 TCP+gRPC,仅 HTTP
+
+**决策**:保留单一 HTTP-only CloudEvents SDK,**全删** TCP SDK 与 gRPC SDK。
+
+- §5 / §10 全部成立。TCP/gRPC/OpenMessaging 代码标记 `@Deprecated` 后删除。
+- 这是**不可逆动作**,所有存量 TCP/gRPC 客户端必须迁移到 HTTP SDK。
+- 前置假设:业务方接受迁移成本;低延迟场景靠 Phase 8.5 的 WebSocket/SSE 可选传输覆盖。
+- **风险点**:若有强依赖 gRPC 流式 / TCP 低延迟的存量客户端,迁移前需逐户确认(落地基线决策后启动排查)。
+
+### 15.4 落地基线:基于当前工作区(-1.15.0-port),按新架构重调
+
+**决策(2026-07-03 确定)**:代码落地基线 = **当前工作区**(`apache-eventmesh` / `feature/-1.15.0-port` 分支)。不基于 old 仓库(`apache-eventmesh-old`,纯 Apache 1.15.0)也不基于 Apache develop 干净重写。
+
+**依据**(经 old 仓库 masa 子模块盘点得出):
+1. 当前工作区已吸收新架构前置模块——`eventmesh-meta`(5 后端含 raft)/ `eventmesh-openconnect` / `eventmesh-protocol-a2a` / CloudEvents / `eventmesh-retry`——**这些正是新架构要用的**(meta 控制面 / connector / A2A / 可靠性),基线已有,无需重搬。基于 old 仓库要重新搬这些。
+2. 定制集中在 old 仓库的 **`masa-eventmesh` 子模块**(connector-wemq / trace-weapm / trace-mss / registry-namesrv / registry-nacos / security-acl / wemq-access-starter / logappender),是**插件化**实现(EventMesh SPI 接口),边界清晰,可跨基线移植。基线选择不影响定制迁移。
+3. 定制基于**老接口**(MeshProducer / EventMeshMessage / RegistryService),新架构要按新接口(MeshStoragePlugin / MetaService / AclFilter)重写——**这步工基线无关,基于谁都要做**。
+
+**关键原则:已吸收模块"认可但重调"**:
+- meta/openconnect/A2A/raft 等**保留**,但**按新架构重新调整实现**(非原样用):
+ - meta:从"元数据 KV + 限流规则"**扩展为全局控制面**(分区分配/订阅视图/offset 远程/规则,§13.2.7)
+ - openconnect:保留 connector 框架,Connector Runtime **独立进程化**(§8)
+ - A2A:保留,转 CloudEvents → Pipeline(§6.3)
+ - raft:保留作 meta 后端之一
+- TCP/gRPC/OpenMessaging/Group 语义/双 runtime/空模块:**删除**(§10)
+
+** 定制迁移**:masa 子模块作为**定制参考 + 按新接口重写**塞入:
+- connector-wemq → 按 `MeshStoragePlugin` 重写(去 EventMeshMessage/TCP,保留 WeMQ 协议对接、配置中心、RSA 等业务语义)
+- trace-weapm/mss → 按 `EventMeshTraceService`(trace-plugin)重写
+- registry-namesrv/nacos → 并入 `MetaService`(统一控制面,§13.2.7)
+- security-acl → 按 `AclFilter`(§13.4.2)重写
+- wemq-access-starter → 弃(薄壳,新入口用 `EventMeshApplication`,§9)
+
+**上游同步策略**(已分叉,不追求完全合并):
+- 选择性 backport develop 的 security fix / 严重 bugfix / 用到模块的新特性
+- 新架构减法(删 59% + 重写核心)会自动消除大量分叉点,收敛后 backport 更干净
+- develop 作"能力参照",不作地基
+
+**落地前置(必做)**:
+1. 固化当前工作区未提交改动(一堆 `M` 文件)到独立分支,避免与新架构改动混淆
+2. 盘点 定制清单(masa 子模块,已部分完成,见附录 E)
+3. 排查存量 TCP/gRPC 客户端(支撑 §15.3 不可逆决策)
+
+### 15.5 全局控制面:Meta 注册中心(而非 Admin Server)
+
+**决策**:全局控制面归 **Meta 注册中心**(复用 develop `eventmesh-meta`,nacos/etcd/consul/zk/raft),Admin Server 仅做管理面。详见 §13.2.7。
+
+- Meta 存:实例注册心跳、分区分配表、clientId 路由表、集群订阅视图、offset 远程副本、动态规则。
+- Admin Server 存:Job 管理、DLQ 浏览重投、运维指令、指标聚合展示——**不**承担强一致协调。
+- 降级:Meta 不可用 → 实例自洽分区分配(§13.2.3 C);Admin 不可用 → 运维受影响但 Runtime 正常。
+- **develop 两套注册抽象梳理(v1.9 决策:弃用 Registry,只用 Meta)**:`eventmesh-meta`(MetaService SPI,5 后端)+ `eventmesh-registry`(仅 nacos,偏实例发现)。**决定弃用 `eventmesh-registry` 模块**(含 `registry-api` / `registry-nacos`),全局控制面与实例发现**只用 `eventmesh-meta`(MetaService)**。registry 的实例发现能力由 MetaService 承担,避免两套注册抽象并存。 老 `registry-namesrv` / `registry-nacos` 定制按 MetaService 重写,不保留 RegistryService 路径。
+
+### 15.6 传输层:HTTP 家族三选,默认 WebSocket
+
+**决策**:SDK 传输层支持三种 HTTP 家族协议,用户按场景选,默认 WebSocket。
+
+- **WebSocket**(默认):持久双向订阅流,高吞吐,同连接发控制命令——对齐 TCP 推送实时性。
+- **SSE**:单向流式输出(LLM token 流、A2A 流式回传),穿墙极佳。
+- **Long-Polling**:WS 被防火墙拦截时的降级。
+- **无 gRPC**(§15.3 铁律不变):WebSocket 给到与 gRPC 流式同等的低延迟+高吞吐,但属 HTTP 家族,不需 proto+独立 SDK。
+- **request-reply(§15.7)**:控制面 + 同步调用走 HTTP 请求-响应,与推送传输正交。
+- 详见 §5.1.1 / §7.2 / §13.7.1。
+
+### 15.7 新增 request-reply 同步调用
+
+**决策**:新增 `request()` API(§5.1 / §17),对齐 TCP 同步调用语义。
+
+- 请求-应答走 HTTP 请求-响应(挂起),复用 correlationId 匹配 + Meta 路由表做跨实例应答。
+- **超时处理**:超时即失败,**迟到应答默认丢弃**(语义清晰,不写 DLQ)。
+- **语义边界**:request-reply 是 RPC 语义,**不重投、不进 DLQ**(避免重复执行副作用),与 §15.2 的 at-least-once pub/sub 独立。
+
+### 15.8 存储层:S3Stream 多后端 + Java 21 虚拟线程
+
+**决策**:实现基于 **Java 21**;存储层保留多后端,**新增 S3Stream 作为 StoragePlugin 实现**,与 Kafka/RocketMQ 并列。
+
+- **S3Stream 集成姿态(姿态 A,最小集成)**:新增 `S3StreamStoragePlugin` 实现 `MeshStoragePlugin`,EventMesh **仍完全自主协调分区**(§15.1 不变)。S3Stream 作为数据 WAL,其 compute 调度能力不被复用——EventMesh 在其上叠自己的分区分配协议。
+- **保留多后端**:S3Stream + Kafka + RocketMQ 并列,`eventmesh.storage.type` 选择。§3 的 plugin 抽象保留。
+- **offset 真相源不变**:仍存 **Meta**(§13.2.4 不变),S3Stream 只做数据 WAL,不存 EventMesh 分发 offset。RocksDB 作为本地完整副本(见下)。
+- **RocksDB 定位(澄清)**:在 offset 两级存储中,RocksDB 是"**本地完整副本 + Meta 写卸载 + 降级兜底**",非冗余:
+ - 高频写本地 RocksDB(每批 ACK,亚毫秒),低频刷 Meta(批量异步)→ Meta 不被 offset 写压垮。
+ - crash 恢复读本地完整 offset,零重放(即使 Meta flush 滞后)。
+ - Meta 不可用时本地兜底。
+ - (Connector Runtime 的 offset 仍 RocksDB + Admin 双写,§8/§16 层面 A,独立。)
+- **Java 21 虚拟线程**:推送服务(PushService 的挂起通道、Long-Polling/WS/SSE 连接)用虚拟线程处理,挂起连接成本接近 0。这是吞吐/连接规模红利,**不改变默认 WebSocket 选型**(WS 在消息间隙无 1 RTT 空窗 + 多路复用的优势仍成立)。
+
+### 15.9 决策对文档各章的影响校验
+
+| 决策 | 现有文档章节 | 是否一致 | 备注 |
+|------|-------------|---------|------|
+| 15.1 完全自主协调 | §13.2 / Phase 2.5 | ✅ 一致 | S3Stream 姿态 A 不变 |
+| 15.2 至少一次+幂等 | §13.3 | ✅ 一致 | §16 已澄清边界 |
+| 15.3 全删 TCP+gRPC | §5 / §10 / Phase 3/8 | ✅ 一致 | §5.4 已补不可逆风险注记 |
+| 15.4 暂不锁定基线 | 全文 | ✅ 一致 | 不动代码 |
+| 15.5 Meta 控制面 | §13.2.7 | ✅ 一致 | §13.2.2/3/4/5 已改 |
+| 15.6 三传输默认 WS | §5.1.1 / §7.2 / §13.7.1 | ✅ 一致 | §12.2 旧选型表需补注(见下) |
+| 15.7 request-reply | §5.1 / §17 | ✅ 一致 | §17 已新增 |
+| 15.8 S3Stream+J21 | §3 / §13.2.4 | ✅ 一致 | §3.5 配置需补 S3Stream 项;§12.6 RocksDB 定位需改 |
+
+> **遗留 TODO**(非阻断):① §12.2 旧的"Long-Polling vs WebSocket/SSE"选型表与 §15.6 默认 WS 冲突,需补注"已由 §15.6 取代";② §3.5 配置简化补 S3Stream 存储类型项;③ §12.6 把 RocksDB 从"主存储"改为"本地完整副本"定位。
+
+---
+
+## 十六、交付语义边界:At-Least-Once vs Exactly-Once
+
+> §15.2 选定下发侧为 At-Least-Once,但 `eventmesh-unified-runtime-migration-plan.md` 将 Exactly-Once 列为核心卖点。两份文档不矛盾——它们描述的是**两个不同层面**。本节明确边界,避免混淆。
+
+### 16.1 两个层面的交付语义
+
+```
+层面 A:Connector 内部进度(offset 持久化)
+ 范围:Source Connector 拉取 offset / Sink Connector 写入确认 offset
+ 位置:Connector Runtime 内部,不涉及外部客户端
+ 语义:Exactly-Once(本地 RocksDB + 远程 Admin Server 双写 offset)
+ 目的:Connector 重启/故障后不丢、不重复处理外部数据
+ 依据:migration-plan §1.2 / §2.2 / Phase 3
+
+层面 B:客户端下发交付(订阅者收到消息)
+ 范围:EventMesh → SDK 订阅者(publish 后的下发)
+ 位置:SubscriptionManager → PushService → 客户端 poll/ACK
+ 语义:At-Least-Once + 客户端幂等(本次决策 §15.2)
+ 目的:消息不丢;可能重复,靠客户端按 id 幂等
+ 依据:本文件 §13.3
+```
+
+### 16.2 为什么下发侧不沿用 Connector 的 EO
+
+| 维度 | Connector offset(EO 可行) | 客户端下发(EO 代价过高) |
+|------|---------------------------|------------------------|
+| 状态存储 | 单点 offset,RocksDB 双写即可 | 需全局去重表(每 clientId×每 id),存储与查询成本高 |
+| 确认点 | Connector 内部 commit,进程可控 | 客户端 ACK 跨网络,确认语义弱(ACK 丢失=状态不确定) |
+| 重复代价 | 重复处理外部数据(如重复写 DB)需 Connector 幂等 | 重复下发,客户端幂等即可,EO 边际收益低 |
+| 事务性 | offset 与数据处理可本地事务化 | 下发 + offset 推进跨网络,无法本地事务 |
+
+→ 下发侧实现 EO 需"事务性下发 + 全局去重存储",成本与收益不匹配。故 **B 层用 At-Least-Once**。
+
+### 16.3 边界声明(写入文档约束)
+
+- **Connector 内部**(层面 A):保留 migration-plan 的 Exactly-Once offset 双写设计,本文件不推翻。
+- **客户端下发**(层面 B):明确 **At-Least-Once**,文档与 SDK 须如实声明交付语义,不承诺恰好一次。
+- **两层面独立**:Connector offset 的 EO 不蕴含下发侧 EO;反之亦然。
+- 若未来某 topic 需下发侧 EO,按 §13.3.5 备注"可配置"路径演进,但 v1 不实现。
+
+---
+
+## 十七、request-reply 同步调用(v1.3)
+
+> **🔎 实现状态(v1.11 / 2026-07-06 盘点)**:⚠️ §17.1–17.5(HTTP 挂起 + correlationId 匹配 + 超时丢弃 + 不进 DLQ)已实现(`UniIngressService.request/reply` + `/events/request` `/events/reply`);**§17.6 自寻址跨实例路由(`x-em-reply-instance` + `/internal/reply-forward`)未实装(G10)**——请求方与响应方跨实例时应答丢失。
+
+> TCP 协议有同步调用语义(请求方发事件 + 阻塞等应答),简化方案须有等价能力。本节定义 RPC-over-bus 的 request-reply 语义,对齐 §5.1 的 `request()` API。
+
+### 17.1 语义
+
+TCP 同步调用 = 请求方发事件并阻塞等应答,总线按 topic 路由到响应方,响应方处理后回带 correlationId 的应答,总线再路由回请求方解阻塞。这是 **RPC 语义**,与 §15.2 的 at-least-once pub/sub 是**不同语义面**。
+
+### 17.2 HTTP 映射(请求-响应天然挂起)
+
+```
+请求方: POST /events/request
+ body: CloudEvent { ..., x-em-reply-to: "reply.", x-em-correlation-id: reqId }
+ (HTTP 请求挂起,阻塞等应答或超时)
+ ↓ EventMesh 按 event 路由(同 publish 路径:IngressPipeline → Storage.send)
+响应方: 订阅了请求 topic,收到后处理
+ POST /events/reply
+ body: CloudEvent { x-em-correlation-id: reqId, data: 应答体 }
+ ↓ EventMesh 按 correlationId 匹配到挂起的 HTTP 请求
+请求方: 原挂起请求返回应答 ← 解阻塞
+ 超时 → 请求失败(与 TCP 同步调用语义一致)
+```
+
+### 17.3 关键设计点
+
+1. **匹配表**:`correlationId → 挂起的 AsyncContext`,内存表 + 超时清理。
+2. **多实例路由**:应答可能打到非请求实例。复用 §13.2.5 的 clientId→instance 路由表(存 Meta)——按 correlationId 找到"开请求的实例",把应答转发过去。或请求方持 WS/SSE 通道时,应答走通道推回(§7.2 TransportChannel 复用)。
+3. **超时处理(决策 §15.7)**:请求自带 timeout,超时即失败,清理挂起上下文。**迟到应答默认丢弃**(最简单、语义清晰:RPC 一次一应答)。不写 DLQ(避免复杂度)。响应方超时后仍在处理的结果丢失,由业务层重试请求覆盖。
+4. **语义边界(与 at-least-once 隔离)**:request-reply **不重投、不进 DLQ**——重投会导致重复执行副作用(RPC 的副作用通常不可重复)。这与 pub/sub 的 at-least-once+重试+DLQ 是独立路径。
+5. **超时≠丢消息**:请求已路由到响应方,超时只是请求方不等了;响应方可能仍在处理。
+
+### 17.4 API(对齐 §5.1)
+
+```java
+// 同步请求-应答
+CompletableFuture request(CloudEvent event, Duration timeout);
+// 内部:POST /events/request 挂起,收到 /events/reply 或超时
+```
+
+### 17.5 与传输层的关系
+
+- 请求方若持 WebSocket/SSE 通道,应答可走通道推回(低延迟)。
+- 否则走原挂起的 HTTP 请求响应返回(Long-Polling 式挂起)。
+- 控制面(publish/request/reply/subscribe/unsubscribe)始终走 HTTP 请求-响应,与推送传输(WS/SSE/LP)正交。
+
+### 17.6 request-reply 路由:自寻址,无需 Meta
+
+§17.3 初版说"复用 §13.2.5 的 clientId→instance 路由表做跨实例应答"——**这会过度耦合**。request-reply 的应答路由其实**不需要 Meta 全局路由表**,用**自寻址(self-addressed)**即可,比 pub/sub 分发更简单:
+
+**① 原理:应答目标由请求方自带,不查表**
+
+```
+请求方(实例 A 上的 clientId-R)发 request:
+ CloudEvent extensions:
+ x-em-correlation-id: reqId
+ x-em-reply-to: reply. ← 应答"投递地址"
+ x-em-reply-instance: ← 请求方所在实例(自寻址关键)
+ x-em-reply-channel: ws | sse | http ← 应答回传方式
+
+应答方(实例 B)处理完,发 reply:
+ POST /events/reply (可打到任意实例)
+ CloudEvent extensions:
+ x-em-correlation-id: reqId
+ → EventMesh 不查 Meta,直接按 x-em-reply-instance 转发到实例 A
+```
+
+**② 两层映射(仅请求方实例本地,不进 Meta)**
+
+```
+实例 A 本地表(内存,request 发出时建,超时清理):
+ reqId → { AsyncContext(挂起请求), clientId-R, expireAt }
+
+应答到达实例 A(经 /events/reply 或跨实例转发):
+ 按 reqId 查本地表 → 匹配到挂起请求 → 返回应答 → 清理条目
+ 超时未匹配 → 丢弃(§15.7)
+
+→ 路由状态只在"开请求的实例"本地,无需 Meta 全局可见
+→ 与 §13.2.5 的 clientId→instance 路由表(用于 pub/sub 推送分发)是两回事
+```
+
+**③ 跨实例应答转发(轻量,无 Meta 查询)**
+
+```
+应答 /events/reply 打到实例 C(≠ 实例 A):
+ 实例 C 读 x-em-reply-instance = A
+ → HTTP POST /internal/reply-forward 到实例 A(实例列表从 Meta 拿,但只是"找 A 在哪",非 per-reqId 路由)
+ → 实例 A 收到 → 本地表匹配 → 解阻塞
+
+实例列表查询:复用 Meta 的实例注册(§13.2.7),但不为每个 reqId 建路由条目
+→ Meta 只存"实例 A 的地址",不存"reqId 在哪个实例"
+```
+
+**④ 与 pub/sub 推送路由的对比**
+
+| | pub/sub 推送(§13.2.5) | request-reply 应答(本节) |
+|---|---|---|
+| 路由依据 | clientId → instance(订阅者长期位置) | x-em-reply-instance(请求方自带) |
+| 路由表 | 存 Meta(全局,订阅者迁移要更新) | **不存 Meta**,请求方本地内存表 |
+| 转发 | 实例间按 clientId 路由表转发 | 实例间按 reply-instance 一次性转发 |
+| 状态生命周期 | 长期(订阅期) | 短期(reqId 超时即清) |
+| 复杂度 | 高(订阅迁移、全局一致) | 低(自寻址,无迁移) |
+
+**⑤ 请求方实例 crash 的处理**
+
+```
+请求方实例 A 在等待应答时 crash:
+ · 本地 reqId 表随进程消失
+ · 应答迟到到达(转发到 A 的接管者或 A 重启后)→ 本地表无 reqId → 丢弃(§15.7 超时丢弃语义)
+ · 请求方业务层感知请求超时 → 自行重试(新 reqId)
+ → 无需持久化 reqId 表,crash 清理自然
+```
+
+**小结**:request-reply 用自寻址(reply-to + reply-instance),路由状态本地化,**不依赖 Meta 全局路由表**。这纠正了 §17.3 第 2 点的过度耦合——应答路由比 pub/sub 简单,不应套用订阅者路由模型。
+
+> **勘误(§17.3 第 2 点)**:原文"复用 §13.2.5 的 clientId→instance 路由表(存 Meta)做跨实例应答"已被本节取代。正确机制是自寻址:请求方在 CloudEvent 自带 `x-em-reply-instance`,应答按此转发,reqId 匹配表仅存请求方实例本地内存。
+
+---
+
+## 十八、端到端测试用例设计(v1.6)
+
+> 覆盖核心数据通路与关键质量属性。每用例标注:前置条件 / 步骤 / 预期 / 覆盖设计章节。供 §11 Phase 8 集成测试套件落地。
+
+### 18.1 基础数据通路
+
+| ID | 用例 | 前置 | 步骤 | 预期 | 覆盖 |
+|----|------|------|------|------|------|
+| E2E-01 | publish→subscribe 单实例 | SDK 连 Runtime | publish(order.created)→subscribe | 订阅者收到,CloudEvent 字段完整 | §4/§5/§6 |
+| E2E-02 | 三传输推送 | WS/SSE/LP 各一订阅者 | publish 1 条 | 三者均收到同消息 | §5.1.1/§7.2 |
+| E2E-03 | 批量 publish | — | publish(List<100>) | 全部落 MQ,TPS > 单条×N | §13.7.3 |
+| E2E-04 | 二进制 vs JSON 编码 | — | 同消息两种编码 publish | 均能被订阅者解析 | §13.8.1 |
+
+### 18.2 分发模式
+
+| ID | 用例 | 前置 | 步骤 | 预期 | 覆盖 |
+|----|------|------|------|------|------|
+| E2E-05 | LOAD_BALANCE RoundRobin | 3 订阅者同 mode | publish 6 条 | 各收 2 条,无重复 | §4.2.1 |
+| E2E-06 | BROADCAST | 4 订阅者 | publish 1 条 | 全收,各 1 份 | §4.2.2 |
+| E2E-07 | MULTICAST 按 type | 3 订阅者各订不同 type | publish 混合 type | 仅匹配 type 的订阅者收 | §4.2.3/§13.8.4 |
+| E2E-08 | STICKY 顺序 | partitionkey=order-1 发 5 条 | — | 同 worker 顺序收到 | §13.3.3 |
+
+### 18.3 request-reply 同步调用
+
+| ID | 用例 | 前置 | 步骤 | 预期 | 覆盖 |
+|----|------|------|------|------|------|
+| E2E-09 | 正常请求-应答 | 响应方订阅请求 topic | request(order.query) | 收到应答,correlationId 匹配 | §17 |
+| E2E-10 | 超时丢弃 | 响应方不处理 | request(timeout=2s) | 2s 后失败,迟到应答丢弃 | §15.7 |
+| E2E-11 | 跨实例应答 | 请求方/响应方不同实例 | request | 应答自寻址路由正确 | §17.6 |
+| E2E-12 | request 不进 DLQ | 响应方持续失败 | request 超时 | 不触发 DLQ(与 pub/sub 隔离) | §17.3 |
+
+### 18.4 可靠性(ACK/重试/DLQ/幂等)
+
+| ID | 用例 | 前置 | 步骤 | 预期 | 覆盖 |
+|----|------|------|------|------|------|
+| E2E-13 | ACK 推进 offset | — | 下发后不 ACK | 超时重投;ACK 后 offset 推进 | §13.3.1 |
+| E2E-14 | 重试退避 | 订阅者持续失败 | 下发 1 条 | 按 1s/2s/4s/8s/16s 重投 | §13.3.2 |
+| E2E-15 | 转 DLQ | 订阅者失败超 maxRetries | — | 消息进 `.DLQ`,带 reason/retry-count | §13.3.2 |
+| E2E-16 | 幂等去重 | 订阅者按 id 去重 | 重投同 id | 业务不重复执行 | §13.3.5 |
+| E2E-17 | crash 恢复不丢 | 下发后 Runtime crash | 重启 | offset 未推进的消息重放,幂等收敛 | §13.3.2 |
+
+### 18.5 多实例与协调
+
+| ID | 用例 | 前置 | 步骤 | 预期 | 覆盖 |
+|----|------|------|------|------|------|
+| E2E-18 | 分区不重叠 | 3 实例 | publish 大量 | 每条仅一实例拉取,无重复消费 | §13.2.3 |
+| E2E-19 | gen fencing 防脑裂 | 实例 A 网络分区 | A 恢复 | A gen 过期自停 poll,B 唯一 poller | §13.2.8 ④ |
+| E2E-20 | clientId 迁移不丢进度 | clientId 连 A | A crash,重连 B | B 从 Meta 读 offset 继续,零丢 | §13.2.4 |
+| E2E-21 | 跨实例转发 | 订阅者在 B,拉取在 A | publish | A 转发到 B,订阅者收到 | §13.2.5 |
+| E2E-22 | 实例扩缩容 | 2→3 实例 | 增实例 | 分区重分配,约 1/n 迁移,无丢 | §13.2.8 |
+
+### 18.6 降级与恢复
+
+| ID | 用例 | 前置 | 步骤 | 预期 | 覆盖 |
+|----|------|------|------|------|------|
+| E2E-23 | Meta 挂降级 | 运行中 | 断 Meta | 自洽分配,publish/已有下发不中断 | §13.2.9 |
+| E2E-24 | 降级期 offset | Meta 挂 | ACK 多条 | 仅写本地 RocksDB,crash 不丢 | §13.2.9 |
+| E2E-25 | Meta 恢复对齐 | 降级中 | 恢复 Meta | offset/订阅/分配渐进对齐,无丢 | §13.2.9 |
+| E2E-26 | 降级期新订阅 | Meta 挂 | subscribe | 本实例可见,恢复后集群对齐 | §13.2.9 |
+
+### 18.7 安全
+
+| ID | 用例 | 前置 | 步骤 | 预期 | 覆盖 |
+|----|------|------|------|------|------|
+| E2E-27 | TLS 强制 | ENFORCING 模式 | 明文请求 | 拒连 | §13.4.1 |
+| E2E-28 | mTLS 双向 | client.auth=REQUIRE | 无客户端证书 | 拒连 | §13.4.1 |
+| E2E-29 | 认证失败 | — | 无凭证 publish | 401 | §13.4.2 |
+| E2E-30 | ACL 越权 | tenantB 用户 | 访问 tenantA topic | 403(DENY 优先) | §13.4.2 |
+| E2E-31 | 签名验签 | — | 篡改 CloudEvent | 拒收 | §13.4.4 |
+| E2E-32 | 租户隔离 | tenantA/B 各订阅 | 互查 | 看不到对方订阅/消息 | §13.4.3 |
+
+### 18.8 背压与慢消费者
+
+| ID | 用例 | 前置 | 步骤 | 预期 | 覆盖 |
+|----|------|------|------|------|------|
+| E2E-33 | 队列溢出策略 | DROP_OLDEST | 灌满 maxPending | 丢最旧 + metric | §13.6.2 |
+| E2E-34 | 慢消费者状态机 | 1 慢 + 999 快(广播) | 持续不 ACK | 慢者 SLOW→STALLED→EVICTED,快者不受影响 | §13.6.2 |
+| E2E-35 | 限流 429 | RateLimitFilter | 超 TPS 上限 | 429,不丢已入队 | §13.6.1 |
+| E2E-36 | 优雅停机 | 运行中 | shutdown | drain→等ACK→flush offset→释放租约,offset 不丢 | §13.6.4 |
+
+### 18.9 可观测性
+
+| ID | 用例 | 前置 | 步骤 | 预期 | 覆盖 |
+|----|------|------|------|------|------|
+| E2E-37 | metrics 暴露 | — | 抓 /metrics | 16 项指标含标签 | §13.5.1 |
+| E2E-38 | trace 全链路 | — | publish→下发 | zipkin/jaeger 见完整 span 链 | §13.5.2 |
+| E2E-39 | 按 id 查轨迹 | — | 记 CloudEvent id | 查到全链路 span | §13.5.3 |
+
+### 18.10 Connector
+
+| ID | 用例 | 前置 | 步骤 | 预期 | 覆盖 |
+|----|------|------|------|------|------|
+| E2E-40 | Source→EventMesh | MySQL Source | binlog 变更 | EventMesh 收到 CloudEvent | §8.3 |
+| E2E-41 | EventMesh→Sink | Redis Sink | publish | Sink 写入 Redis | §8.4 |
+| E2E-42 | Source EO | publish 失败 | — | source offset 不推进,重拉同批 | §8.9/§16 |
+
+### 18.11 WebHook
+
+| ID | 用例 | 前置 | 步骤 | 预期 | 覆盖 |
+|----|------|------|------|------|------|
+| E2E-43 | WebHook 推送 | deliveryUrl 配置 | publish | 第三方收到 POST + 签名 | §13.7.2 |
+| E2E-44 | WebHook 重试 | 第三方返回 500 | — | 指数退避重试,超阈值转 DLQ | §13.7.2 |
+| E2E-45 | WebHook 去重 | — | 重试同 Delivery-Id | 接收方按 id 去重 | §13.7.2 |
+
+> **测试套件组织**:按 §11 Phase 对应——核心通路(E2E-01~08)在 Phase 2/3/5;可靠性(13~17)在 Phase 5.5;多实例(18~22)在 Phase 2.5;降级(23~26)在 Phase 2.5;安全(27~32)在 Phase 4.5;背压(33~36)在 Phase 6.6;可观测(37~39)在 Phase 5.6;Connector(40~42)在 Phase 6;WebHook(43~45)在 Phase 8.5。全量通过 = Phase 8 收尾 DoD。
+
+---
+
+## 附录 A:配置项总表(v1.7)
+
+> 汇总散落各章的配置项,按模块分类。`#` 列为默认值,章节列指向设计依据。
+
+### A.1 存储(Storage Plugin)
+
+| 配置项 | 默认值 | 说明 | 章节 |
+|--------|--------|------|------|
+| `eventmesh.storage.type` | kafka | kafka / rocketmq / pulsar / s3stream | §3.5/§15.8 |
+| `eventmesh.storage.bootstrap.servers` | localhost:9092 | Kafka/RocketMQ bootstrap | §3.5 |
+| `eventmesh.storage.consumer.auto.offset.reset` | earliest | offset 重置策略 | §3.5 |
+| `eventmesh.storage.s3stream.endpoint` | — | S3Stream endpoint(type=s3stream) | §3.6.3 |
+| `eventmesh.storage.s3stream.bucket` | — | S3 bucket | §3.6.3 |
+| `eventmesh.storage.s3stream.region` | — | S3 region | §3.6.3 |
+
+### A.2 SDK / 传输
+
+| 配置项 | 默认值 | 说明 | 章节 |
+|--------|--------|------|------|
+| `eventmesh.sdk.transport` | WEBSOCKET | WEBSOCKET / SSE / LONG_POLLING | §5.1.1/§15.6 |
+| `eventmesh.sdk.encoding` | BINARY | BINARY / STRUCTURED | §5.1.1/§15.8 |
+
+### A.3 下发可靠性(重试 / DLQ)
+
+| 配置项 | 默认值 | 说明 | 章节 |
+|--------|--------|------|------|
+| `eventmesh.retry.maxRetries` | 5 | 最大重试次数 | §13.3.2 |
+| `eventmesh.retry.backoff` | EXPONENTIAL | EXPONENTIAL/FIXED/LINEAR | §13.3.2 |
+| `eventmesh.retry.initialDelayMs` | 1000 | 初始退避 | §13.3.2 |
+| `eventmesh.retry.maxDelayMs` | 60000 | 退避封顶 | §13.3.2 |
+| `eventmesh.retry.jitterRatio` | 0.2 | ±20% jitter | §13.3.2 |
+| `eventmesh.retry.ackTimeoutMs` | 30000 | ACK 超时 | §13.3.2 |
+| `eventmesh.dlq.topicSuffix` | .DLQ | 死信 topic 后缀 | §13.3.2 |
+
+### A.4 背压与慢消费者
+
+| 配置项 | 默认值 | 说明 | 章节 |
+|--------|--------|------|------|
+| `eventmesh.backpressure.maxPending` | 10000 | 每 clientId 队列上限 | §13.6.2 |
+| `eventmesh.backpressure.overflowPolicy` | DROP_OLDEST | DROP_OLDEST/NEWEST/BLOCK/TO_DLQ | §13.6.2 |
+| `eventmesh.backpressure.sampleIntervalMs` | 10000 | 慢消费者采样周期 | §13.6.2 |
+| `eventmesh.backpressure.highWatermark` | 0.8 | 慢消费者高水位 | §13.6.2 |
+| `eventmesh.backpressure.lowWatermark` | 0.5 | 恢复低水位 | §13.6.2 |
+| `eventmesh.backpressure.stalledCycles` | 5 | STALLED 阈值 | §13.6.2 |
+| `eventmesh.backpressure.evictCycles` | 12 | EVICTED 阈值(无 ACK) | §13.6.2 |
+| `eventmesh.backpressure.threadPoolPerTopic` | true | 每 topic 独立线程池 | §13.6.2 |
+
+### A.5 安全(TLS / 认证 / ACL)
+
+| 配置项 | 默认值 | 说明 | 章节 |
+|--------|--------|------|------|
+| `eventmesh.server.tls.mode` | DISABLED | DISABLED/PERMISSIVE/ENFORCING | §13.4.1 |
+| `eventmesh.server.tls.ssl.protocol` | TLSv1.3 | TLS 协议 | §13.4.1 |
+| `eventmesh.server.tls.keystore.path` | — | keystore 路径 | §13.4.1 |
+| `eventmesh.server.tls.keystore.password` | — | keystore 密码 | §13.4.1 |
+| `eventmesh.server.tls.client.auth` | NONE | NONE/OPTIONAL/REQUIRE(mTLS) | §13.4.1 |
+| `eventmesh.server.tls.truststore.path` | — | truststore 路径(mTLS) | §13.4.1 |
+| `eventmesh.auth.type` | token | auth-token / auth-http-basic | §13.4.2 |
+
+### A.6 WebHook
+
+| 配置项 | 默认值 | 说明 | 章节 |
+|--------|--------|------|------|
+| `eventmesh.webhook.connectTimeoutMs` | 5000 | 连接超时 | §13.7.2 |
+| `eventmesh.webhook.readTimeoutMs` | 10000 | 读超时 | §13.7.2 |
+| `eventmesh.webhook.maxRetries` | 5 | 默认重试(可被 subscribe 覆盖) | §13.7.2 |
+| `eventmesh.webhook.backoff` | EXPONENTIAL | 退避策略 | §13.7.2 |
+| `eventmesh.webhook.timestampWindowMs` | 300000 | ±5min 防重放窗口 | §13.7.2 |
+| `eventmesh.webhook.threadPoolSize` | 64 | 投递线程池 | §13.7.2 |
+
+### A.7 多实例协调(Meta / 租约 / 分配)
+
+| 配置项 | 默认值 | 说明 | 章节 |
+|--------|--------|------|------|
+| `eventmesh.meta.type` | nacos | nacos/etcd/consul/zookeeper/raft | §13.2.7/§15.5 |
+| `eventmesh.meta.address` | — | Meta 注册中心地址 | §13.2.7 |
+| `eventmesh.coordinator.leaseRenewIntervalMs` | 5000 | 租约续约间隔 | §13.2.8 |
+| `eventmesh.coordinator.leaseTtlMs` | 15000 | 租约 TTL(3 倍续约) | §13.2.8 |
+| `eventmesh.coordinator.degradedMetaTimeoutMs` | 15000 | 进入降级的 Meta 超时 | §13.2.9 |
+
+### A.8 Offset 存储
+
+| 配置项 | 默认值 | 说明 | 章节 |
+|--------|--------|------|------|
+| `eventmesh.offset.local.path` | ./data/offset | 本地 RocksDB 路径 | §12.6/§13.2.4 |
+| `eventmesh.offset.remoteFlushIntervalMs` | 1000 | 刷 Meta 间隔(写卸载) | §13.2.4 |
+| `eventmesh.offset.remoteFlushBatch` | 100 | 刷 Meta 批量大小 | §13.2.4 |
+
+### A.9 运行时
+
+| 配置项 | 默认值 | 说明 | 章节 |
+|--------|--------|------|------|
+| `eventmesh.server.http.port` | 8080 | HTTP Server 端口 | §6 |
+| `eventmesh.runtime.nodeId` | — | 实例 ID | §9 |
+| `eventmesh.gracefulShutdown.timeoutMs` | 10000 | 优雅停机等待 ACK | §13.6.4 |
+
+---
+
+## 附录 B:CloudEvents extension 字段全集(v1.7)
+
+> 规范化所有 `x-em-*` 自定义 extension 字段。CloudEvents 1.0 标准字段(id/source/type/time/data/specversion/datacontenttype)不在此列。`必填` 列:请求方填 / 响应方填 / 系统填 / 可选。
+
+### B.1 路由与分发
+
+| 字段 | 类型 | 含义 | 必填 | 章节 |
+|------|------|------|------|------|
+| `x-em-topic` | String | EventMesh topic(显式路由) | 可选(默认 topic 时省略) | §13.8.3 |
+| `partitionkey` | String | 分区/粘性路由 key(CloudEvents 规范字段,非 x-em-) | 可选 | §13.3.3 |
+| `x-em-tenantid` | String | 租户 ID(隔离 + ACL) | 可选(单租户省略) | §13.4.2/§13.4.3 |
+| `x-em-userid` | String | 用户/调用方 ID(ACL 主体) | 可选 | §13.4.2 |
+| `x-em-distribution-mode` | Enum | LB/LB_STICKY/BROADCAST/MULTICAST | 系统填(subscribe 时定) | §4.2/§13.3.3 |
+
+### B.2 request-reply
+
+| 字段 | 类型 | 含义 | 必填 | 章节 |
+|------|------|------|------|------|
+| `x-em-correlation-id` | String | 请求-应答关联 ID | 请求方填 | §17 |
+| `x-em-reply-to` | String | 应答投递地址(reply.\) | 请求方填 | §17.6 |
+| `x-em-reply-instance` | String | 请求方所在实例(自寻址) | 请求方填 | §17.6 |
+| `x-em-reply-channel` | Enum | ws/sse/http(应答回传方式) | 请求方填 | §17.6 |
+
+### B.3 可靠性
+
+| 字段 | 类型 | 含义 | 必填 | 章节 |
+|------|------|------|------|------|
+| `x-em-retry-count` | Int | 已重试次数 | 系统填 | §13.3.2 |
+| `x-em-dlq-reason` | String | 死信原因(ack_timeout/webhook_failed/...) | 系统填(DLQ 消息) | §13.3.2 |
+| `x-em-dlq-retry-count` | Int | 进 DLQ 时的总重试次数 | 系统填(DLQ 消息) | §13.3.2 |
+| `x-em-ttl` | Duration | 消息存活时间(过期丢弃) | 可选 | §13.3.4 |
+
+### B.4 MQ 坐标(内部透传)
+
+| 字段 | 类型 | 含义 | 必填 | 章节 |
+|------|------|------|------|------|
+| `x-em-mq-offset` | Long | MQ 物理 offset(Storage.poll 注入) | 系统填 | §12.6.6 |
+| `x-em-mq-partition` | Int | MQ 分区号 | 系统填 | §12.6.6 |
+
+### B.5 WebHook 投递(HTTP 头,非 CloudEvent extension)
+
+| 头 | 类型 | 含义 | 章节 |
+|----|------|------|------|
+| `X-Em-Signature` | String | sha256=\ HMAC 签名 | §13.7.2 |
+| `X-Em-Timestamp` | Long | epoch_ms(防重放) | §13.7.2 |
+| `X-Em-Delivery-Id` | UUID | 每次 delivery 唯一(去重) | §13.7.2 |
+
+### B.6 Trace(CloudEvents Distributed Tracing extension)
+
+| 字段 | 类型 | 含义 | 章节 |
+|------|------|------|------|
+| `traceparent` | String | W3C Trace Context(00-traceId-spanId-flags) | §13.5.2 |
+| `tracestate` | String | 厂商扩展(可选) | §13.5.2 |
+| `baggage` | String | 跨服务业务上下文(k=v,k=v) | §13.5.2 |
+
+### B.7 多播过滤
+
+| 字段 | 类型 | 含义 | 必填 | 章节 |
+|------|------|------|------|------|
+| `x-em-subscriptions` | List\ | 显式订阅目标列表(多播优先级 1) | 可选 | §12.5 |
+
+> **命名规范**:所有 EventMesh 自定义字段用 `x-em-` 前缀(CloudEvents extension 约定)。`partitionkey`/`traceparent`/`tracestate`/`baggage` 为 CloudEvents 规范字段,不加前缀。MQ 坐标类(`x-em-mq-*`)为内部透传,不下发客户端。
+
+---
+
+## 附录 C:部署拓扑示例(v1.7)
+
+> 四种典型部署拓扑,标注组件、适用场景、关键取舍。
+
+### C.1 单实例(开发/小规模/PoC)
+
+```
+┌─────────────┐ HTTP/CloudEvents ┌─────────────────────────┐
+│ SDK 客户端 │ ───────────────────────→ │ EventMesh Runtime (单实例)│
+│ (WS/SSE/LP) │ ←─────────────────────── │ · Ingress/Egress Pipeline│
+└─────────────┘ │ · SubscriptionManager │
+ │ · PushService │
+ │ · 本地 RocksDB offset │
+ │ · Meta(可选,单实例可省) │
+ └──────────┬──────────────┘
+ │ Storage.send/poll
+ ▼
+ ┌─────────────────────────┐
+ │ Kafka/RocketMQ/S3Stream │
+ └─────────────────────────┘
+```
+- **适用**:开发调试、PoC、小规模(单分区足够)、无 HA 要求
+- **取舍**:无多实例协调(§13.2 可省),Meta 可选(无分区分配需求);crash 即不可用
+- **配置**:`eventmesh.storage.type=kafka`,无 Meta 配置
+
+### C.2 多实例(生产 HA,推荐基线)
+
+```
+ ┌─────────────────────────────────────────┐
+ │ Meta 注册中心 (nacos/etcd,HA 集群) │
+ │ · 实例注册/分区分配/订阅视图/offset远程 │
+ └───┬──────────────┬──────────────┬───────┘
+ │ │ │
+ ┌────────▼───┐ ┌───────▼────┐ ┌──────▼─────┐
+ │ Runtime A │ │ Runtime B │ │ Runtime C │
+ │ 持 p0,p1 │ │ 持 p2 │ │ 持 p3,p4 │
+ │ 本地RocksDB│ │ 本地RocksDB│ │ 本地RocksDB│
+ └──────┬─────┘ └──────┬─────┘ └──────┬─────┘
+ │ │ │
+ └───────────────┼───────────────┘
+ │ Storage.send/poll (各自负责分区)
+ ▼
+ ┌───────────────────────────┐
+ │ Kafka/RocketMQ/S3Stream │
+ │ (多分区,EventMesh 单Producer单Consumer模式)│
+ └───────────────────────────┘
+ ┌───────────────────────────────────┐
+ │ SDK 客户端 (连任意 Runtime, WS/SSE)│
+ └───────────────────────────────────┘
+```
+- **适用**:生产、需 HA、中等规模
+- **取舍**:Meta 强依赖(降级为自洽,§13.2.9);实例间转发跨实例订阅;gen fencing 防脑裂
+- **配置**:`eventmesh.meta.type=nacos`,`eventmesh.coordinator.leaseTtlMs=15000`
+- **关键**:SDK 连任意实例,订阅视图集群级,跨实例转发保证下发
+
+### C.3 多租户(SaaS / 多业务线隔离)
+
+```
+ ┌─────────────────────────────────────────┐
+ │ Meta (含 ACL 规则 + 租户隔离订阅视图) │
+ └───┬─────────────────────────────────────┘
+ │
+ ┌────────▼─────────────────────────────────────┐
+ │ EventMesh Runtime 集群 (多实例,共享) │
+ │ · AclFilter 按 tenant 隔离 (§13.4.2) │
+ │ · SubscriptionManager 按 tenant 过滤订阅视图 │
+ │ · topic 命名: . │
+ └────────┬─────────────────────────────────────┘
+ │
+ ┌────────▼─────────┐ ┌──────────────────────┐
+ │ tenantA.orders │ │ tenantB.orders │ ← topic 按租户隔离
+ │ tenantA.events │ │ tenantB.events │
+ └──────────────────┘ └──────────────────────┘
+ ┌────────────────┐ ┌────────────────┐
+ │ tenantA 客户端 │ │ tenantB 客户端 │
+ │ (token+tenantid)│ │ (token+tenantid)│
+ └────────────────┘ └────────────────┘
+```
+- **适用**:SaaS 平台、多业务线共享集群、需租户隔离
+- **取舍**:共享 Runtime 集群降成本;ACL 必须严格(DENY 跨租户优先级最高);topic 带租户前缀
+- **配置**:`eventmesh.auth.type=token`,ACL 规则经 Meta 下发
+- **关键**:tenantA 看不到 tenantB 的订阅/消息(§13.4.3)
+
+### C.4 S3Stream 存算分离(云原生/低成本/弹性)
+
+```
+ ┌──────────────────────────────────────────────────┐
+ │ Meta (nacos/etcd) │
+ └───┬──────────────────────────────────────────────┘
+ │
+ ┌───▼──────────────────────────────────────────────┐
+ │ EventMesh Runtime 集群 (无状态 compute,可秒级扩缩)│
+ │ · 分区分配协议 (§13.2.8, 不用 S3Stream 调度) │
+ │ · 本地 RocksDB offset + Meta 远程 │
+ └───┬──────────────────────────────────────────────┘
+ │ S3StreamStoragePlugin (v1 Kafka线协议薄包装/v2 原生)
+ ▼
+ ┌──────────────────────────────────────────────────┐
+ │ S3Stream (无状态 broker) │
+ │ · 热数据: 本地 SSD 缓存 │
+ │ · 冷数据: S3 对象存储 (分级,低成本长保留) │
+ └──────────────────────────────────────────────────┘
+ │
+ ▼
+ ┌──────────────┐
+ │ S3 对象存储 │ ← 数据真相源,offset 持久化
+ └──────────────┘
+```
+- **适用**:云原生部署、追求存储成本(S3 比 Kafka broker 便宜)、长保留期(合规审计)、弹性扩缩存算分离
+- **取舍**:S3Stream compute 调度能力不用(姿态 A,§15.8),EventMesh 叠自己分区协议;延迟略高于本地 broker(S3 RTT)
+- **配置**:`eventmesh.storage.type=s3stream`,`eventmesh.storage.s3stream.endpoint=...`
+- **关键**:Runtime 无状态可随意扩缩(compute 弹性),存储在 S3(成本+持久);v1 薄包装起步,压测后可迁 v2 原生(§3.6.6)
+
+### C.5 拓扑选型矩阵
+
+| 拓扑 | 规模 | HA | 隔离 | 成本 | 典型场景 |
+|------|------|----|------|------|---------|
+| C.1 单实例 | 小 | ❌ | — | 低 | 开发/PoC |
+| C.2 多实例 | 中大 | ✅ | — | 中 | 生产基线 |
+| C.3 多租户 | 中大 | ✅ | ✅ | 中(共享降本) | SaaS/多业务线 |
+| C.4 S3Stream | 中大 | ✅ | — | 低(S3) | 云原生/长保留 |
+
+> 可组合:C.2+C.3(多实例多租户)、C.2+C.4(多实例+S3Stream)、C.2+C.3+C.4(全量)。
+
+---
+
+## 附录 D:develop 代码迁移映射表(v1.7)
+
+> 基于 develop 分支现有代码(经 §13 前置盘点),映射到新架构的处理方式:删除 / 替换 / 保留 / 新增。供实施时按模块迁移。标注 `→` 指迁移目标。
+
+### D.1 SDK(eventmesh-sdk-java)
+
+| develop 现有 | 处理 | 迁移目标 | 章节 |
+|-------------|------|---------|------|
+| `tcp/` 全子包(Proxy/Package) | 删除 | — | §5/§10 |
+| `grpc/` 全子包 | 删除 | — | §5/§10 |
+| `EventMeshHttpClient` | 替换 | `CloudEventsClient`(4 API + 三传输) | §5.1 |
+| `EventMeshMessage` | 删除 | CloudEvent | §5/§10 |
+| `Package` / `Command` | 删除 | — | §10 |
+| `HttpCommand` | 删除 | — | §10 |
+| `tcp/impl/openmessage/OpenMessageTCPClient` | 删除 | — | §10 |
+| `http/producer/OpenMessageProducer` | 删除 | `CloudEventsClient.publish` | §5.1 |
+
+### D.2 协议插件(eventmesh-protocol-plugin)
+
+| develop 现有 | 处理 | 迁移目标 | 章节 |
+|-------------|------|---------|------|
+| `eventmesh-protocol-api`(ProtocolAdaptor) | 保留 | toCloudEvent/fromCloudEvent 统一入口 | §6.3 |
+| `eventmesh-protocol-cloudevents` | 保留 | 主流(HTTP SDK 直发 CloudEvents) | §6.3 |
+| `eventmesh-protocol-http` | 保留 | HTTP→CloudEvent | §6.3 |
+| `eventmesh-protocol-a2a`(EnhancedA2A+MCP) | 保留 | A2A→CloudEvent | §6.3/§14 |
+| `eventmesh-protocol-meshmessage` | 删除 | —(TCP SDK 已删) | §6.3/§10 |
+| `eventmesh-protocol-openmessage` | 删除 | —(OpenMessaging 已删) | §6.3/§10 |
+| `eventmesh-protocol-grpc` / `grpcmessage` | 删除 | —(gRPC SDK 已删) | §10 |
+
+### D.3 存储(eventmesh-storage-*)
+
+| develop 现有 | 处理 | 迁移目标 | 章节 |
+|-------------|------|---------|------|
+| `MeshMQProducer`(含 producerGroup) | 替换 | `MeshStoragePlugin.send`(无 Group) | §3.2 |
+| `MeshMQConsumer`(含 consumerGroup) | 替换 | `MeshStoragePlugin.poll`(无 Group) | §3.2 |
+| `createTransactionProducer()` | 删除 | —(不支持事务,§13.3.6) | §3.2/§10 |
+| `subscribe(topic, subExpression)` | 替换 | `poll(topic, partition, offset)`(无 Tag) | §3.2/§13.8.3 |
+| `eventmesh-storage-kafka` | 改造 | `KafkaStoragePlugin`(单 P+C,无 Group) | §3.3 |
+| `eventmesh-storage-rocketmq` | 改造 | `RocketMQStoragePlugin`(单 P+C,无 Group) | §3.4 |
+| `eventmesh-storage-standalone` | 保留/改造 | 单机实现 | §3 |
+| —(新增) | 新增 | `S3StreamStoragePlugin`(v1 薄包装/v2 原生) | §3.6 |
+
+### D.4 运行时(eventmesh-runtime)
+
+| develop 现有 | 处理 | 迁移目标 | 章节 |
+|-------------|------|---------|------|
+| `protocol/tcp/` 全子包 | 删除 | — | §7/§10 |
+| `protocol/grpc/` 全子包 | 删除 | — | §7/§10 |
+| `processor/tcp/` 全部 | 删除 | — | §10 |
+| `processor/grpc/` 全部 | 删除 | — | §10 |
+| `processor/http/` 24 个 | 替换 | `UnifiedIngressHandler` | §6 |
+| `Session`/`ClientSession`/`ClientSessionGroupMapping` | 删除 | `TransportChannel`(§7.2) | §7/§10 |
+| `ClientGroupPack`/`ClientGroupPackManagement` | 删除 | `SubscriptionManager`(集群级视图) | §4/§13.2.5 |
+| `EventMeshTcpServer`/`EventMeshGrpcServer` | 删除 | `EventMeshHttpServer`(唯一传输) | §7/§10 |
+| `Hello/Goodbye/Subscribe/UnSubscribeProcessor` | 删除 | HTTP subscribe/unsubscribe + 心跳即 poll | §6/§10 |
+| `tcp/client/forward/CrossClusterForwardService` | 重设计 | 跨集群转发(去 TCP 依赖) | §13.2 |
+| `tcp/client/session/retry/TcpRetryer` | 替换 | `Retryer`(HashedWheelTimer,§13.3.2) | §13.3.2 |
+| `tcp/client/session/Session`(RateLimiter) | 替换 | `RateLimitFilter`(HTTP 侧) | §13.6.1 |
+| `http/consumer/EventMeshConsumer`(LRUCache) | 替换 | `PushService`(TransportChannel) | §7.2 |
+| `admin/handler/v1/` 19 个 | 重做 | 新 Admin 8 接口(§13.5.4) | §13.5.4 |
+| `boot/EventMeshTlsConfig`/`SslContextFactory` | 保留 | TLS/mTLS(HTTP 侧复用) | §13.4.1 |
+| `configuration/EventMeshDynamicConfigManager` | 保留 | 动态配置热更新 | §13.6.3 |
+
+### D.5 公共(eventmesh-common)
+
+| develop 现有 | 处理 | 迁移目标 | 章节 |
+|-------------|------|---------|------|
+| `protocol/tcp/Package.java` | 删除 | — | §10 |
+| `protocol/http/HttpCommand.java` | 删除 | — | §10 |
+| `common/Message.java` | 删除 | CloudEvent | §10 |
+| `protocol/asm/`(TCP ASM 加密) | 删除 | — | §10 |
+| `ssl/SslContextFactory` | 保留 | TLS/mTLS | §13.4.1 |
+
+### D.6 安全(不用 SPI 插件,filter 链扩展)
+
+> **v1.9 决策:security 不用 SPI 插件扩展,用内置 FilterChain + IngressFilter。** `eventmesh-security-plugin` 模块(及其 SPI jar)不接线。
+
+| develop 现有 | 处理 | 迁移目标 | 章节 |
+|-------------|------|---------|------|
+| `eventmesh-security-api`(AclService/AuthService) | **不复用** | 用内置 `IngressFilter`/`FilterContext`/`FilterVerdict` | §13.4.2 |
+| `eventmesh-security-acl`(AclServiceImpl) | **不复用** | 内置 `AclFilter`(规则模型 §13.4.2) | §13.4.2 |
+| `eventmesh-security-auth-token` | **不复用** | 内置 `TokenAuthFilter` | §13.4.2 |
+| `eventmesh-security-auth-http-basic` | **不复用** | 新增一个 `IngressFilter` 实现(代码扩展,非 SPI 插件) | §13.4.2 |
+| `runtime/acl/Acl.java` | **不复用** | `AclFilter` 内置实现 | §13.4.2 |
+
+扩展方式:新增安全能力 = 新增一个 `org.apache.eventmesh.runtime.uni.security.IngressFilter` 实现并注册进 `FilterChain`,无需打 SPI 插件包。
+
+### D.7 元数据/注册(eventmesh-meta / eventmesh-registry)
+
+> **v1.9 决策:弃用 Registry,只用 Meta。**
+
+| develop 现有 | 处理 | 迁移目标 | 章节 |
+|-------------|------|---------|------|
+| `eventmesh-meta-api`(MetaService SPI) | 保留+扩展 | 唯一全局控制面(分区/订阅/offset/规则/实例发现) | §13.2.7/§15.5 |
+| `eventmesh-meta-nacos/etcd/consul/zookeeper/raft` | 保留 | Meta 5 后端 | §13.2.7 |
+| `eventmesh-meta-api/RateLimiterRulerListener` | 保留 | 限流规则动态下发 | §13.6.1 |
+| `eventmesh-registry-api`(RegistryService) | **弃用** | 实例发现并入 MetaService,删除 RegistryService | §13.2.7 |
+| `eventmesh-registry-nacos` | **弃用** | 由 `eventmesh-meta-nacos` 承担,删除该模块 | §13.2.7 |
+
+### D.8 可观测性(只用 OpenTelemetry,默认 Prometheus)
+
+> **v1.9 决策:可观测只用 OpenTelemetry(默认接 Prometheus),legacy metrics/trace 插件不接线、无 SPI 扩展点。** metrics 内置为 OTel 仪表(`UniMetrics`),默认经 OTel Prometheus exporter 暴露 `/metrics`;trace 走 OTel Tracer。不再有 `eventmesh-metrics-plugin` / `eventmesh-trace-plugin` 的 SPI 插件扩展形式。
+
+| develop 现有 | 处理 | 迁移目标 | 章节 |
+|-------------|------|---------|------|
+| `eventmesh-metrics-api`(MetricsRegistry) | **不复用** | 内置 OTel Meter 仪表(`UniMetrics`),默认 Prometheus exporter | §13.5.1 |
+| `eventmesh-metrics-prometheus` | **不复用** | 由 OTel Prometheus exporter 承担(内置,非 SPI 插件) | §13.5.1 |
+| `eventmesh-trace-api`(EventMeshTraceService) | **不复用** | 内置 OTel Tracer 直接建 Span | §13.5.2 |
+| `eventmesh-trace-zipkin/jaeger/pinpoint` | **不复用** | 由 OTel exporter(OTLP 等)承担 | §13.5.2 |
+| 各 processor 的 trace 接入 | 迁移 | 新 Handler/Pipeline 节点用 OTel Span 埋点 | §13.5.2 |
+
+### D.9 重试(内置,不用 SPI 插件)
+
+> **v1.9 决策:retry 内置 `ReliableDispatcher`,不暴露 SPI 插件扩展。** `eventmesh-retry` 模块不接线。
+
+| develop 现有 | 处理 | 迁移目标 | 章节 |
+|-------------|------|---------|------|
+| `eventmesh-retry-api`(Retryer/HashedWheelTimer) | **不复用** | 内置 `ReliableDispatcher`(指数退避 + DLQ) | §13.3.2 |
+| `eventmesh-retry-rocketmq`(RocketMQRetryStrategyImpl) | **不复用** | 不需要(重试策略固定,无 SPI 扩展) | §13.3.2 |
+
+### D.10 Connector(eventmesh-openconnect / eventmesh-connectors)
+
+| develop 现有 | 处理 | 迁移目标 | 章节 |
+|-------------|------|---------|------|
+| `eventmesh-openconnect-java`(SourceWorker/SinkWorker) | 保留 | Connector Runtime 基类 | §8 |
+| `eventmesh-openconnect-offsetmgmt-*` | 保留 | Connector 自有 OffsetStore | §8.9 |
+| 24 个 connector(kafka/mysql/redis/...) | 保留 | 独立维护 | §8.8 |
+| `eventmesh-runtime` 内 `ConnectorRuntimeService` | 删除/移出 | 移到独立 Connector Runtime 进程 | §8.7/§8.9 |
+| `BlockingQueue` 旁路 | 删除 | 走 HTTP(§8.7) | §8.7 |
+| Source/Sink `System.exit(-1)` Bug | 修复 | 重写后消除 | §8.7 |
+
+### D.11 SPI(eventmesh-spi)
+
+| develop 现有 | 处理 | 迁移目标 | 章节 |
+|-------------|------|---------|------|
+| `eventmesh-spi`(EventMeshSPI/EventMeshExtensionFactory) | 保留 | 插件加载机制 | — |
+| 各 `XxxPluginFactory`(Trace/Metrics/Protocol/Storage/Registry) | 保留 | 静态获取封装 | — |
+
+### D.12 启动入口
+
+| develop 现有 | 处理 | 迁移目标 | 章节 |
+|-------------|------|---------|------|
+| `EventMeshStartup` | 替换 | `EventMeshApplication` | §9 |
+| `EventMeshBootstrap`(Http/Tcp/Grpc/Admin) | 替换 | `RuntimeContext` | §9 |
+| `EventMeshTcpBootstrap`/`EventMeshGrpcBootstrap` | 删除 | — | §9/§10 |
+| `RuntimeInstanceStarter`(v2) | 删除 | 整合到统一 Runtime | §9/§10 |
+
+### D.13 迁移优先级
+
+```
+第一批(Phase 1-2,底层+核心):
+ Storage Plugin 改造(D.3)→ SubscriptionManager 新增
+第二批(Phase 2.5-3.5,多实例+SDK):
+ Meta 控制面(D.7)→ SDK 替换(D.1)→ request-reply
+第三批(Phase 4-5,入出方向):
+ UnifiedIngressHandler(D.4)→ PushService(D.4)→ 删 TCP/gRPC(D.4)
+第四批(Phase 4.5-5.6,质量属性):
+ 安全(D.6)→ 可靠性(D.9)→ 可观测(D.8)
+第五批(Phase 6-7.5,Connector+Admin):
+ Connector 独立(D.10)→ Admin 重做(D.4)→ 启动统一(D.12)
+第六批(Phase 8-8.5,收尾):
+ 删 OpenMessaging/旧协议(D.1/D.2)→ WebHook → 测试套件(§18)
+```
+
+> **迁移原则**:先底层后上层、先核心后质量属性、先新增后删除(新通路跑通再删旧)。每批对应 §11 Phase,DoD 达标方进入下一批。删除类操作集中在 Phase 8(旧通路确认无引用后)。
+
+---
+
+## 附录 E: 定制(masa 子模块)迁移清单
+
+> 已拆分为独立文档:[`-masa-migration-inventory.md`](./-masa-migration-inventory.md)。该文档盘点 `apache-eventmesh-old/masa-eventmesh` 子模块的 8 个 定制(connector-wemq / trace-weapm / trace-mss / registry-namesrv / registry-nacos / security-acl / wemq-access-starter / logappender),映射到新架构的处理方式(重写/合并/保留/弃)、重写要点、迁移批次(对应 §11 Phase)与风险点,作为 §15.4 基线决策的迁移资产登记。
+
+---
+
+## 附录 F:实现差距分析(v1.11,2026-07-06)
+
+> 本附录是设计文档对照实际代码(`uni-architecture` 分支,66 个主类 + 24 个测试类)逐项核对的结果,记录**截至核对时点的实现缺口**。前文 §1–§18 + 附录 A–E 为目标设计;本附录标注"代码现状",二者差异即为后续实施 backlog。复核时须以代码为准重新核对,勿仅凭本附录断言。
+
+> **🔄 v1.12 进度(2026-07-06 实施第一批 quick win,已编译+测试通过)**:**G7**(Kafka `group.id` 已删,消除 MQ 无语义矛盾)、**G8**(`ClusterCoordinator` STICKY 改 partitionkey hash,多实例保序)、**G13**(`ReliableDispatcher` nack 重试加 ±20% jitter,7 参构造启 0.2、6 参=0 保持测试确定性)、**G14-TLS**(`TlsContextFactory` 升 TLSv1.3 + 独立 truststore 密码 + `EventMeshApplication.main` 读 `-Deventmesh.tls.*` 接线 HTTPS)。下表/正文标注保留 v1.11 盘点原文作历史,已修复项以本注记为准。G14 的 Filter/Legacy boot 接线、G1 WebSocket SDK 客户端、G3 fencing / G4 实例间转发 / G6 etcd 后端为后续批次。
+
+> **🔄 v1.12 第二批(多实例核心,已编译+测试通过)**:**G1 服务端**(netty `UniWsServer` + `WsConnection` + boot `-Deventmesh.ws.port` 接线;SDK 客户端归 G16)、**G2** 分区不重叠拉取(`PartitionOwnership` 心跳 + 确定性 assign + `MeshStoragePlugin.partitionCount` + pollLoop 按 owned 分区)、**G12** 优雅停机释放分区租约(`ClusterMembership.leave`)、**G5** offset 两级存储(`MetaBackedOffsetStore` 本地 RocksDB + 异步刷 Meta,`readOffset=max(local,remote)`)。多实例核心(分区不重叠 + 租约 + offset 远程恢复)已具备;G3/G4 已于第三批完成,仅剩 G6 etcd 后端。
+
+> **🔄 v1.12 第三批(多实例协调完整,已编译+测试通过)**:**G3** gen fencing(`PartitionOwnership` 分配表带 generation,Meta 存 `/em/assignments/=|`,被接管时 gen 更大则自 fencing,§13.2.8④)、**G4+G10** 实例间转发(`HttpForwarder` POST `/internal/forward` + `ClusterMembership.addressOf`(heartbeat 值带 `timestamp|address`)+ `UniHttpServer` 端点 + reply 自寻址 `/internal/reply-forward`,§13.2.5/§17.6)。多实例 Phase 2.5 核心完整:分区不重叠 + 租约 + gen fencing + offset 两级 + 跨实例转发 + reply 自寻址。仅剩 G6 etcd Meta 后端(Nacos 的 prefix-scan/CAS 缺陷由此根治)。
+
+> **🔄 v1.12 第四批(P1 安全+运维,已编译+测试通过)**:**G9** AclFilter 规则模型(新增 `AclRule`:principal/resource/action/effect/priority + `*`/`tenantId.*` 通配 + priority 降序 + DENY 优先 + 默认 DENY 白名单 + `setRules` 热更新供 Meta watch 下发,§13.4.2)、**G11** 慢消费者溢出策略矩阵(`PushService.OverflowPolicy` BLOCK/DROP_OLDEST/DROP_NEWEST/TO_DLQ + STALLED/EVICTED 暂停入队避免雪崩重投,§13.6.2①③;独立周期采样线程待后续)。剩余 P2:G15 已于第五批完成;G16 SDK / F.5 零散项待做。
+
+> **🔄 v1.12 第五批(P2 Admin,已编译+测试通过)**:**G15** Admin 补全(`dlqBrowse` 浏览死信 + `/admin/dlq/browse`、`setRateLimit` 下发限流 + `/admin/ratelimit` PUT、`/admin/health` 加分区分配视图)。Admin 现覆盖 §13.5.4 全部 8 接口(subscriptions / offsets / clients / reject / dlq-browse / dlq-replay / ratelimit / health)。剩余 G16 SDK(WS/批量/重连/手动ACK) / F.5 零散项——批量/TTL/大消息已于第六批完成,其余待做。
+
+> **🔄 v1.12 第六批(P2 批量+TTL+大消息,已编译+测试通过)**:**G16 批量** SDK `CloudEventsClient.publish(List)` + runtime `UniIngressService.publishBatch` + `/events/publish-batch` 端点(§13.7.3);**F.5 TTL** `emttl` 过期事件在 dispatch 时丢弃(§13.3.4);**F.5 大消息** `Content-Length > 1MB` 返回 413 Payload Too Large(§13.8.2,不自动分片)。剩余:G16 手动 ACK / poll 重连 / SSE+WS 客户端传输;F.5 租户命名空间 `.` / 动态配置热更新 / 僵尸 poll 清理 / metrics 余 8 项(offset_lag / active_subscribers / pending_queue / slow_consumer / partition_owner 等)——手动 ACK / poll 重连 / request_reply metric 已于第七批完成,其余待做。
+
+> **🔄 v1.12 第七批(P2 metrics+SDK,已编译+测试通过)**:**F.5 metrics** `eventmesh_request_reply_count` counter(§13.5.1,ingress `request` finally 埋点);**G16 手动 ACK** SDK `CloudEventsClient.subscribeWithAck(Predicate)`——handler 返回 `true`=ack(offset 推进)/`false`=不 ack 等 dispatcher 超时重投,给客户端幂等窗口(§13.3.5),旧 `subscribe(Consumer)` 自动 ACK 保留;**G16 poll 重连** 异常后 backoff 1s 重试,避免服务器宕机时紧循环(Phase 3 DoD 自动重连)。剩余:G16 SSE+WS 客户端传输;F.5 租户/动态配置/僵尸poll/metrics Gauge 已于第八批完成(剩 4 项需复杂数据源的 Gauge);P0 G6 etcd Meta 后端。
+
+> **🔄 v1.12 第八批(F.5 运维+可观测,已编译+测试通过)**:**F.5 僵尸 poll 清理** `PushService.lastPollTime` + `getStaleClientIds` + `UniRuntime` 周期 60s 清理过期订阅(§13.6.5);**F.5 metrics Gauge** `UniMetrics.registerGauge`(OTel ObservableGauge)+ `pending_queue_size` / `slow_consumer_count` / `active_topics` 三个 gauge(§13.5.1);**F.5 动态配置热更新** `DynamicConfigWatcher` watch Meta `/em/ratelimit/rules` → `setTopicRateLimit`(§13.6.3 限流热更新;ACL 规则热更新待 G14 Filter boot 接线后接 `AclFilter.setRules`);**F.5 租户命名空间** topic `.` 前缀约定 + G9 `AclFilter` 跨租户 DENY 拦截(代码侧 topic 字符串隔离已够,§13.4.3)。剩余:metrics 3 项 Gauge(`offset_lag` / `partition_owner` / `poll_idle_ratio`,需带标签 ObservableGauge / MQ-offset 对比 / poll 统计,复杂,标注 roadmap);P0 G6 etcd。G16 SSE+WS 客户端已于第九批完成。
+
+> **🔄 v1.12 第九批(P2 收尾:SDK 传输客户端,已编译+测试通过)**:**G16 SSE 客户端** `CloudEventsClient.subscribeSse`(HttpURLConnection 读 `text/event-stream` 流,解析 `data:` 帧)+ **G16 WS 客户端** `subscribeWs`(`java.net.http.WebSocket`,§15.6 默认主传输,onText 回调解析帧 + shutdown 正常关闭)+ **F.5 `active_subscribers` gauge**。G16 三传输客户端齐全(Long-Polling / SSE / WebSocket),§5.1.1 三传输选型完整落地。F.5 3 项复杂 Gauge 已于第十批完成(F.5 全部完成,§13.5.1 全 16 项 metrics 落地)。
+
+> **🔄 v1.12 第十批(F.5 可观测收尾:3 项复杂 Gauge,已编译+测试通过)**:**F.5 metrics** 3 项带标签 ObservableGauge——`poll_idle_ratio`(per topic,poll 空闲比例 per-mille)+ `partition_owner`(per `topic#partition#instance`,本实例 owned 分区标记 1)+ `offset_lag`(per `topic#partition`,MQ `endOffset` − 分发 offset),需 `UniMetrics.registerLabelledGauge` + `LabelledLong` + `MeshStoragePlugin.endOffset`(Kafka 用 `consumer.endOffsets` 实装,default -1 兜底)。§13.5.1 全 16 项 metrics 落地。**F.5 全部完成,P2 全部收尾**;G6 已于第十一批完成(复用 Nacos NamingService)。
+
+> **🔄 v1.12 第十一批(G6 多实例收尾:复用 Nacos NamingService,已编译+测试通过)**:**G6** `NacosMetaStore` 对 `/em/instances/` prefix 改用 Nacos **NamingService**(`registerInstance` / `selectInstances(svc, healthy)` / `deregisterInstance`)做实例发现——根治 ConfigService 无 prefix-scan 导致多实例 `liveInstances()` 拿不全、分区分配失效(每实例全量拉取重复消费)的缺陷。其他 prefix(`/em/assignments/*` / `/em/offsets/*` / `/em/acl/rules`)仍走 ConfigService(单 key get/put + per-key watch,无 prefix-scan 需求)。复用现有 `nacos-client` 依赖,无新依赖;生产用 Nacos 直接可用,多实例 HA 名副其实。**多实例 Phase 2.5 全部完成,所有 P0/P1/P2 缺口收尾。**
+
+### F.1 总体状态
+
+新架构已是**可运行系统**:`EventMeshApplication.main` 启动 runtime + 流量 HTTP(`UniHttpServer`,JDK `HttpServer` + 虚拟线程)+ 独立 admin HTTP(`UniAdminServer`)。单实例快乐路径完整:publish → `MeshStoragePlugin.send` → `poll` → `SubscriptionManager.targetsFor` → `ReliableDispatcher.deliver` → `PushService` → 客户端 `ack` → `OffsetStore` 推进 offset。可靠性(ACK/指数退避重试/DLQ/STICKY)、SSE、TLS、TCP 兼容桥、HTTP 兼容桥均有真实代码。
+
+但距离 §11 自定的**生产就绪门槛**(Phase 1–8 + 2.5 + 3.5 + 4.5 + 5.5 + 7.5)仍有差距,集中在 **Phase 2.5(多实例协调)整体只是骨架**——而这是文档自己定的 🔴 阻断项。下文按"严重程度 + 是否文档-代码矛盾"分类。
+
+### F.2 🔴 阻断生产 / 文档-代码矛盾
+
+| # | 缺口 | 文档要求 | 代码现状 | 证据 |
+|---|------|---------|---------|------|
+| G1 | **WebSocket 传输缺失** | §5.1.1/§15.6:"默认 WebSocket 推送主传输",三传输 WS/SSE/LP 用户可选 | 全仓无 WebSocket/WsConnection/handshaker 代码(grep 仅命中 LICENSE 文档)。实际只有 `SseConnection`(`/events/stream`)+ `LongPollingChannel`。SSE 实现为 `while(isOpen){pumpOnce(100); Thread.sleep(20)}` 忙等 | `UniHttpServer.stream`;SDK `CloudEventsClient` 仅 long-polling |
+| G2 | **多实例分区不重叠未实装** | §13.2.3/§13.2.8:每实例只 assign 自己负责的分区 | 每实例 `assign(全部分区)` 全量拉取;`EventMeshApplication.enableCluster` 未建 `PartitionAssigner`、未调 `storage.assignPartitions` | `KafkaMeshStoragePlugin.poll`(lazy assign 全部分区);`EventMeshApplication.enableCluster` |
+| G3 | **gen fencing 防脑裂未实装** | §13.2.8④:poll 前查 generation/owner,过期自停 poll | 无 generation 概念。`ClusterMembership` 只有心跳时间戳 + TTL,`PartitionAssigner` 注释自承"production Meta-led path 可以覆盖"=未做 | `ClusterMembership`;`PartitionAssigner` |
+| G4 | **实例间转发未实装** | §13.2.5:跨实例 HTTP POST `/internal/forward` | `Forwarder` 直接 `log.warn("...not yet implemented"); return false` | `EventMeshApplication` 第 69–74 行 |
+| G5 | **offset 两级存储未实装** | §13.2.4:本地 RocksDB + 远程 Meta,`readOffset=max(local,remote)` | `OffsetStore` 接口纯本地,只有 `InMemoryOffsetStore`/`RocksDBOffsetStore`,无远程 Meta 层 | `OffsetStore` 接口;`UniAdminService` 注释"reflect the local instance until that layer lands" |
+| G6 | **Meta 后端仅 Nacos(且 Nacos 实现有缺陷)** | §15.5/§13.2.7:nacos/etcd/consul/zk/raft **5 后端** | 仅 `NacosMetaStore`(真接 nacos `ConfigService`)+ `InMemoryMetaStore`,etcd/consul/zk/raft 全无。且 Nacos 配置模型有结构性缺陷:无 prefix-scan(`getWithPrefix` 仅返回本地见过的 key,非集群视图)、无 CAS(`putIfAbsent` 非原子)、无真 prefix-watch | `NacosMetaStore`(注释自承限制) |
+| G7 | **Kafka 设了 group.id,违反"MQ 无语义"铁律** | §3.2/§13.2 铁律:不暴露 Consumer Group | `KafkaMeshStoragePlugin.init` 设 `GROUP_ID_CONFIG="eventmesh-storage-internal"`,而同类注释第 72 行写"NO group.id"。虽 `enable.auto.commit=false`,但同 group.id 的多实例 consumer 会触发 Kafka rebalance,与"自主协调、不用 MQ rebalance"冲突 | `KafkaMeshStoragePlugin` 第 80 行 |
+
+> **影响**:E2E-02(三传输,仅 2 种)、E2E-18/19/20/21/23–26(多实例、fencing、迁移、转发、降级)在当前代码下**均不成立**。多实例部署 = 每实例全量拉取 + 重复消费。即 §11 自定的"生产 HA 前必做"的 Phase 2.5 实质未完成。
+
+### F.3 🟠 已实现但方案需完善
+
+| # | 缺口 | 文档要求 | 代码现状 | 证据 |
+|---|------|---------|---------|------|
+| G8 | **STICKY 多实例破坏顺序** | §13.3.3:同 partitionkey → 同 worker,保序 | 单实例 `SubscriptionManager.stickyIndex` 按 `partitionkey` hashCode 取模(✓);但 `ClusterCoordinator.selectByMode` 的 `LOAD_BALANCE_STICKY` 走 RoundRobin(与 LB 同分支) | `ClusterCoordinator` 第 96–98 行 vs `SubscriptionManager.stickyIndex` |
+| G9 | **AclFilter 是骨架** | §13.4.2:规则模型(principal/resource/action/effect/priority + DENY 优先 + 默认 DENY + Meta watch 下发) | `Map>` 静态 map,注释自承"skeleton... static map for simplicity"。无 priority/DENY/action 区分/Meta 下发 | `AclFilter` |
+| G10 | **request-reply 跨实例自寻址未实装** | §17.6:`x-em-reply-instance` + 跨实例 `/internal/reply-forward` | `UniIngressService.reply` 只查本地 `pendingRequests`;`UniHttpServer.reply` 无 reply-instance 转发 | `UniIngressService.reply`;`UniHttpServer.reply` |
+| G11 | **慢消费者状态机有缺陷** | §13.6.2②:周期采样 ackRate/queueLag + 溢出策略矩阵 | (a) 阈值用 offer **之前**的 size,80% 判定滞后;(b) 只由 `offer` 驱动,无独立周期采样→客户端不 poll 时永远 HEALTHY;(c) 溢出只有"nack 重投"一种,无 DROP_OLDEST/NEWEST/BLOCK/TO_DLQ 矩阵;(d) STALLED 期间仍 offer,与"暂停分发避免雪崩重投"矛盾 | `PushService.updateClientState` |
+| G12 | **优雅停机缺"释放分区租约"** | §13.6.4 第 5 步:flush offset 后释放租约、通知 Meta 重分配 | `UniRuntime.shutdown` 有 drain+等 ACK+flush+close,无租约释放→多实例下停机分区需等 TTL(默认 15s)超时才被接管 | `UniRuntime.shutdown` |
+| G13 | **重试无 jitter** | §13.3.2/A.3:`jitterRatio=0.2` ±20% | `backoffMs` 纯 `2^n`,无 jitter,重试风暴风险 | `ReliableDispatcher.backoffMs` |
+| G14 | **TLS 小问题** | A.5:TLSv1.3 + 独立 truststore 密码 | `TlsContextFactory` 硬编码 `TLSv1.2`;truststore 用 `keystorePass` 解密(不能独立配);`EventMeshApplication.main` 默认不接线 TLS/FilterChain/LegacyBridge → 默认启动明文+无鉴权 | `TlsContextFactory` 第 57、62 行;`EventMeshApplication.main` |
+
+### F.4 Admin / SDK / Connector 完整度
+
+| # | 项 | 文档 | 代码现状 |
+|---|------|------|---------|
+| G15 | **Admin 8 接口仅 ~5 个** | §13.5.4:subscriptions/offsets/clients/reject/dlq-browse/dlq-replay/ratelimit/health | `UniAdminService` 有 subscriptions/offsets/pendingDeliveries/rejectClient/dlqReplay/metrics;**缺** clients/dlq-browse/ratelimit 下发/health。数据为**进程内本地视图**,非集群级(Meta 聚合) |
+| G16 | **SDK 仅 Long-Polling、无批量、无重连、ACK 不可控** | §5.1.1 三传输、§13.7.3 批量、Phase3 自动重连、§13.3.5 客户端幂等 | `CloudEventsClient` 4 API 齐全✓,但仅 long-polling、无 `publish(List)`、poll 异常只 log 不重连、ACK 在 handler 返回后自动触发(客户端无法控 ACK 时机做幂等窗口) |
+| G17 | **Connector Runtime ✓ 基本到位** | §8/§8.9 | 独立模块 `eventmesh-connector-runtime` + `ConnectorApplication`(独立 main)✓;`RemoteOffsetStore`+`RocksDBConnectorOffsetStore`(EO 双写)✓;at-least-once commit-on-success 框架在。完成度最高 |
+
+### F.5 🟡 其他缺失
+
+| 项 | 文档 | 代码 |
+|----|------|------|
+| 批量 sendBatch(storage + SDK) | §13.7.3 | ❌ 均无 |
+| TTL 过期丢弃(emttl) | §13.3.4 | ❌ 无处理 |
+| 大消息限制 413 | §13.8.2 | ❌ 无 maxMessageSize 检查 |
+| 租户 topic 命名空间 `.` + 订阅视图 tenant 过滤 | §13.4.3 | ❌ SubscriptionManager 无 tenant 过滤 |
+| 动态配置热更新 | §13.6.3 | ❌ 无 EventMeshDynamicConfigManager 接线 |
+| 僵尸 poll 清理 / poll channel 上限 | §13.6.5 | ❌ 无 lastHeartbeat 清理、无连接上限 |
+| 16 项 metrics | §13.5.1 | ⚠️ 8 项核心有,8 项带 *(lag/active_subscribers/pending_queue/slow_consumer/...)无 |
+| S3Stream 存储后端 | §3.6/§15.8 | ❌ 仅文档规划,无实现 |
+| HTTP server 生产化 | §6(注释提 netty 可换) | ⚠️ 用 JDK `com.sun.net.httpserver.HttpServer`,注释承认"production 可换 netty" |
+
+### F.6 已确认实现(对照 backlog 避免误判)
+
+以下项经核对**确已实现**,复核时可跳过:单实例三分发模式(LB/BROADCAST/MULTICAST/STICKY)、`ReliableDispatcher`(ACK 推进 offset + 指数退避 + DLQ + trace 埋点)、`OffsetStore`(RocksDB key=`topic#clientId#partition`)、`UniTrace`(OTel span:publish/dispatch/ack/retry/dlq)、`TokenBucketRateLimiter`、`FilterChain`/`IngressFilter` 体系、`SignatureVerifierFilter`(HMAC-SHA256)、TCP 兼容桥(`UniTcpServer`+翻译层)、HTTP 兼容桥(`LegacyHttpBridge`)、`WebHookChannel`(签名头+重试+DLQ)、`ClusterCoordinator`(本地/转发路由框架,转发本身见 G4)、Java 21 虚拟线程(`UniHttpServer` 用 `newVirtualThreadPerTaskExecutor`)。
+
+### F.7 建议优先级
+
+**P0(修矛盾 / 阻断项声明)**
+- G7 删 Kafka `group.id`(1 行,铁律矛盾)或文档说明其用途
+- G1 WebSocket:补实现 **或** 修文档降级为 roadmap(当前文档-代码背离最严重)
+- G2–G6 Phase 2.5:补 fencing+转发+两级 offset+多 Meta 后端 **或** 文档明确"当前仅单实例可用,Phase 2.5 待完成",避免误判可上生产
+
+**P1(已实现但方案完善)**
+- G8 ClusterCoordinator STICKY 改 partitionkey hash
+- G10 request-reply 跨实例 reply-forward
+- G9 AclFilter 规则模型 / G11 慢消费者周期采样 / G13 重试 jitter / G14 TLS 独立 truststore 密码 + TLSv1.3
+- G14 `EventMeshApplication` boot 接线 TLS/FilterChain/Legacy(配置驱动)
+
+**P2(补全)**
+- G15 Admin 补 clients/dlq-browse/ratelimit/health + 集群级视图
+- G16 SDK 批量 + 重连 + 手动 ACK
+- G6 补 etcd Meta 后端 或 文档限定 Nacos 缺陷
+- F.5 其余项
+
+> **说明**:本附录是**核对时点快照**。代码持续演进,复核前请重读相关源码——本文档正文 §1–§18 + 附录 A–E 是目标设计,本附录 F 是"代码现状",二者差异随实现推进收敛。
+
+---
+
+*文档版本:v1.10 | EventMesh 简化重构方案 | 基于 unified-runtime-design.md v2.1 演进 | 2026-07-03*
+*v1.10 变更:**老协议不直接删——保留为边缘协议适配器以兼容老客户端。** ① TCP 退化为新架构 ingress/egress 传输(与 HTTP/WebHook/长轮询并列):保留线协议(`Package`/`Command`/`Codec`)+翻译层(`TcpMessageProtocolResolver`/`MeshMessageProtocolAdaptor`)+netty server 骨架,删除 TCP 自有核心(`ClientSession`/`ClientGroupPack`/rebalance、Consumer Group)。新增 `transport.tcp` 包(`TcpPushChannel`/`TcpAckRegistry`/`TcpIngressBridge`/`TcpFrameCodec`+`TcpFrameDecoder`)。② legacy HTTP(`EventMeshHttpClient`/`EventMeshMessage`/`/eventmesh/*` webhook-push)同理:`transport.http.legacy` 包(`LegacyHttpBridge`/`LegacyHttpCodec`),老 HTTP subscribe(url+topics) 映射为 `WebHookChannel` 出向推送,publish 翻译为 CloudEvent→核心。老 TCP/HTTP 客户端零改动。更新 Phase 8 DoD。*
+*v1.9 变更:四项决策固化——① **可观测只用 OpenTelemetry,默认接 Prometheus**(metrics 走 OTel Meter 仪表、trace 走 OTel Tracer Span,默认经 OTel Prometheus exporter 暴露;legacy `eventmesh-metrics-prometheus` / `eventmesh-trace-plugin`(zipkin/jaeger/pinpoint) 不再接线、无 SPI 扩展点,附录 D.8);② **弃用 `eventmesh-registry`,只用 `eventmesh-meta`(MetaService)** 作为唯一控制面(附录 D.7 registry 行改为弃用);③ **retry 内置 `ReliableDispatcher`,不暴露 SPI 插件扩展**(`eventmesh-retry` 不接线,附录 D.9);④ **security 不用 SPI 插件扩展,用内置 FilterChain + IngressFilter**(`eventmesh-security-plugin` 不接线,扩展 = 新增 IngressFilter 实现,附录 D.6)。更新 §13.5、§13.3.2、§13.4.2、§13.2.7、§15.5、Phase 4.5/5.5/5.6。实现侧 `UniMetrics`(OTel 仪表)、`ReliableDispatcher`(内置重试)、`FilterChain`/`IngressFilter`(filter 安全)均已落地。*
+*v1.1 变更:新增 §13「能力缺口与设计补充」——对照 develop 分支现有能力,补齐多实例协调、下发可靠性、安全、可观测性、运维、接入扩展、协议工程化 7 类缺口设计,并更新 §14 实施 Phase 影响。*
+*v1.2 变更:新增 §15「用户决策记录」(固化 MQ 完全自主协调 / At-Least-Once+幂等 / 全删 TCP+gRPC / 暂不锁定落地基线 四项根基决策)、§16「交付语义边界」(澄清 Connector offset EO 与客户端下发 at-least-once 为不同层面,消除与 migration-plan 的表面矛盾);修正 §14 Admin Server 对比行为"重做"并澄清 AdminClient 通道不变。*
+*v1.3 变更:SDK 传输层升级为 HTTP 家族三选(WebSocket 默认/SSE 单向流/Long-Polling 降级,§5.1.1/§7.2/§13.7.1);新增 §17「request-reply 同步调用」(对齐 TCP 同步调用语义,超时丢弃);§13.2 控制面归 Meta 注册中心(新增 §13.2.7 Meta vs Admin 边界);新增 §15.6–15.8 决策(三传输 / S3Stream 多后端 / Java21 虚拟线程);明确 RocksDB 在 offset 两级存储中的"本地完整副本+Meta写卸载+降级兜底"定位;§11 Phase 计划更新。*
+*v1.4 变更:深化三处核心设计——新增 §13.2.8「分区分配协议细节」(确定性分配+租约心跳+gen fencing 防脑裂+offset 迁移+v2 leader 增强,MQ 无语义下自实现 fencing);新增 §17.6「request-reply 路由自寻址」(纠正 §17.3 过度耦合,应答无需 Meta 全局路由表,用 reply-to+reply-instance 自寻址);新增 §3.6「S3Stream 存储后端」(v1 Kafka 线协议薄包装 / v2 原生 SDK、跨后端语义对齐表、三种后端统一抹平 MQ 语义)。*
+*v1.5 变更:深化五处——§13.6.2 背压细化(有界队列+溢出策略矩阵+慢消费者状态机 HEALTHY/SLOW/STALLED/EVICTED+与 ACK/重试/DLQ 联动+广播隔离+配置项);§13.4.2 ACL 规则模型(主体/客体/操作/效果+priority 匹配+DENY 优先+Meta 存储与 watch 下发+租户隔离联动);§13.5 可观测性(16 项 metrics 指标定义表含标签、traceparent+tracestate+baggage 透传、关键节点 Span 链路设计);§8.9 Connector 与 Meta/S3Stream 集成(只读 Meta 不参与分区协调、经 Runtime 间接用存储、Connector offset 独立 EO 本地RocksDB+AdminServer);§13.2.9 降级端到端时序(Meta 挂→自洽各路径行为→恢复对齐→保证矩阵)。*
+*v1.6 变更:深化五处——§13.3.2 重试器时间轮实现(HashedWheelTimer+退避策略+jitter+与 ACK/offset/DLQ 时序+crash 靠 offset 不超前恢复+配置项);§13.7.2 WebHook 签名/重试/DLQ 集成(HMAC-SHA256 canonical 签名+X-Em-Timestamp 防重放+X-Em-Delivery-Id 去重+2xx=ACK+offset 推进+配置项);§3.6.6 v2 原生 S3Stream SDK(Stream/Offset API+与 Kafka 兼容层差异表+partition↔Stream 映射策略+分级存储利用);§11 各 Phase 补 DoD 验收标准(16 个 Phase 全覆盖,🔴 阶段详列);新增 §18 端到端测试用例设计(45 个用例分 11 类,标注前置/步骤/预期/覆盖章节+Phase 对应)。*
+*v1.7 变更:§13.7.2 补 WebHook 适用场景边界说明(主线推送 vs WebHook 旁路,何时用/不用);新增四附录——附录 A 配置项总表(9 类配置项汇总,散落各章归一)、附录 B CloudEvents extension 字段全集(x-em-* 规范化,7 类字段含类型/必填/章节)、附录 C 部署拓扑示例(单实例/多实例/多租户/S3Stream 四拓扑+选型矩阵+可组合)、附录 D develop 代码迁移映射表(13 类模块的删除/替换/保留/新增+6 批迁移优先级)。*
+*v1.8 变更:§15.4 落地基线决策确定(基于当前工作区 -1.15.0-port,认可已吸收的 meta/openconnect/A2A/raft 但按新架构重调, 定制按新接口重写,上游选择性 backport 不追求完全合并);新增附录 E「 定制(masa 子模块)迁移清单」(盘点 apache-eventmesh-old/masa-eventmesh 8 个子模块:connector-wemq/trace-weapm/trace-mss/registry-namesrv/registry-nacos/security-acl/wemq-access-starter/logappender,标注老接口→新架构对应、重写要点、迁移批次、风险点,最复杂为 connector-wemq 内嵌 RocketMQ patch+EventMeshMessage 依赖)。*
diff --git a/docs/production-readiness.md b/docs/production-readiness.md
new file mode 100644
index 0000000000..d2426e66a5
--- /dev/null
+++ b/docs/production-readiness.md
@@ -0,0 +1,207 @@
+# EventMesh Uni-Architecture 生产准入清单
+
+> 状态: 2026-07-10 (更新: §2 阻塞点全部解决 — remoting 插件 offset 游标 + 多 broker queue 寻址修复并验证). 基于真 RocketMQ (127.0.0.1:9876) + 真 Nacos (127.0.0.1:8848) 集成测试.
+
+## 1. 已验证 ✅
+
+| 能力 | 验证方式 | 结果 |
+|---|---|---|
+| 发布/订阅/轮询/ACK | RealBrokerIntegrationTest (真 RocketMQ) | ✅ 全链路 |
+| 客户端 SDK (HTTP CloudEvents) | ClientBrokerIntegrationTest | ✅ publish/subscribe + request/reply |
+| 老 TCP 协议兼容 | LegacyTcpClientIntegrationTest + LegacyTcpClusterBrokerIntegrationTest | ✅ 老 SDK 零改动 |
+| 动态 connector 调度 | ConnectorSchedulingIntegrationTest | ✅ runtime push /control/start |
+| 多实例无重复消费 | MultiInstanceRocketMqIntegrationTest (2 实例) | ✅ 5 条恰好投递一次 |
+| 跨实例 forward (Nacos) | NacosClusterForwardIntegrationTest | ✅ sub 在 A, publish 在 B, A 收到 |
+| ACK 超时重投递 + DLQ | AckTimeoutRedeliveryIntegrationTest | ✅ at-least-once + DLQ |
+| 限流 (429) | RateLimitIntegrationTest | ✅ admin + metrics |
+| TLS/HTTPS | TlsIntegrationTest | ✅ 端到端 |
+| 死信 replay | DlqIntegrationTest | ✅ DLQ -> 回原 topic |
+| 吞吐 | LoadThroughputIntegrationTest (真实多 broker, 修复后实测) | ~101 ev/s, 0 丢失, 0 重复 (500 事件突发) |
+
+## 2. 阻塞点 ⛔ → ✅ 已全部解决
+
+> 截至 2026-07-10,针对 **RocketMQ 部署无已知硬阻塞**。以下两项曾阻塞上生产,现已定位真因并修复 + 验证。
+
+### 2.1 消息丢失(订阅/重启窗口)✅ 已解决
+- **最初现象** (2026-07-08, 旧 DefaultLitePullConsumer 插件): 500 条突发, 57 条 (11%) 丢失, 0 重复。当时归因为 RocketMQ `CONSUME_FROM_LAST_OFFSET` + rebalance 延迟。
+- **真因** (2026-07-10 定位): `RocketMQRemotingStoragePlugin` 两个 latent bug —— remoting 插件号称"解决 offset-seek 丢失",实则未根治:
+ 1. **pull 游标卡在 offset 0**: `poll()` 仅在 `SUCCESS` 时记录 `nextBeginOffset`; broker 对越界 offset 返回 `OFFSET_MOVED` (code 20/21) 被静默丢弃 → 游标反复请求 offset 0, 永远拉不到新消息。(当 topic 有历史、min offset > 0 时触发。)
+ 2. **多 broker queue 寻址错误**: `poll`/`send` 把全局扁平 queueId 当 `header.queueId`, 但 RocketMQ queueId 按 broker 局部编号 → 跨 broker 的 topic 有 ~80% 队列映射到无效局部 id → 返回 null → **静默不消费** (每跑 2080 条 ERROR 日志)。
+- **为何之前没暴露**: LoadThroughput 用无历史的新 topic (min offset=0, 不触发 bug 1) 且多为单 broker 路由 (不触发 bug 2), 故显示 0 丢失; ClientBroker 的 topic `em-it-client` 跨多次运行有历史 + TBW102 自动创建 topic 扩散到 5 个 broker → 同时触发两个 bug → pub-1 收不到。
+- **修复**:
+ 1. 始终用 `nextBeginOffset` 推进游标 (NO_NEW_MSG=空操作 / OFFSET_MOVED=修正 / FOUND=前进) + null-`nextBeginOffset` guard。
+ 2. `getBrokerForQueue` 返回 `QueueLoc(brokerAddr, localQueueId)` (按 `readQueueNums` 扁平, 每 broker 局部 id 从 0 起), send/poll 用 localQueueId。
+ - 附带: `RPC_TIMEOUT_MS` 100→1000ms (100ms 伪超时); 去掉单次 pull 失败永久拉黑 broker。
+- **验证**: `ClientBrokerIntegrationTest` GREEN (真实多 broker, pub/sub + request-reply); offset 文件从仅 `#0–#3` 变为 `#0–#19` (20 队列全消费); 幽灵队列 ERROR 2080→0; 全套 runtime 82 tests 0 failures。
+
+### 2.2 offset 持久化(重启恢复)✅ 已解决
+- **现象**: autoCommit=false + 不 commit MQ offset → 重启后位置不持久 → CONSUME_FROM_LAST_OFFSET (跳到最新, 丢窗口) 或 FIRST_OFFSET (从 0 重放, 重复)。
+- **修复**: remoting 插件自管 pull offset (per `topic#queueId`), 持久化到 `./data/offset/rocketmq-pull-offsets.properties`, 重启加载 + 始终从 `nextBeginOffset` 续传 (见 2.1)。
+- **验证**: 多次测试运行间 offset 正确续传 (load 1 topic → persist 1 topic, offset 持续前进)。
+
+## 3. 已知限制 ⚠️ (可灰度, 需文档化)
+
+| 项 | 说明 |
+|---|---|
+| RocketMQ 分区分配 | subscribe-mode + 共享 consumer group -> broker rebalance (稳态无重复). EventMesh PartitionOwnership 旁路 (partitionCount=-1). 设计如此. |
+| Gen fencing | 软 fence (Meta gen, 能读 Meta 时生效) + lease gate (Meta 不可达 -> 停 poll). 网络分区期间仍有 at-least-once 重复窗口 (直到 lease 过期). |
+| WebSocket 推送 | `UniWsServer` **已实现** (netty 握手 + 每连接 `ConnectionPushPump` + ACK 控制帧 + wss; SDK `subscribeWs`), 但**无集成测试** (端到端 WS 推送未验证); SSE + long-polling 已验证. |
+| Nacos watch 时序 | naming.subscribe push 延迟波动 -> 测试需 30s 等待. 多实例 watch 在组合跑时偶发 flaky. |
+| Kafka 分区 | assign+seek (EventMesh 自管). 未对真 Kafka 实测 (无 Kafka broker). |
+
+## 4. 可观测性 📊
+
+### 4.1 Metrics (已有)
+- `GET /admin/metrics` -> JSON: publishCount, publishFailed, rateLimited, eventsDispatched, ackCount, redeliveries, dlqCount, pendingDeliveries. (**唯一内置读出**)
+- OTel instruments (LongCounter/LongHistogram) 已埋点, 名如 `eventmesh_publish_count` / `eventmesh_publish_failed_count` / `eventmesh_rate_limited_count` / `eventmesh_dispatched_count` / `eventmesh_ack_count` / `eventmesh_redeliveries_count` / `eventmesh_dlq_count` / `eventmesh_dispatch_latency_nanos`. **运行时未捆绑 exporter** —— 需部署侧挂 OTel SDK/agent (Prometheus 或 OTLP collector); 旧 `eventmesh-metrics-prometheus` 插件未接.
+- `GET /admin/subscriptions?topic=` -> live subscriptions.
+- `GET /admin/offsets?topic=` -> distribution offsets.
+- `GET /admin/health` -> liveness + pending + partition ownership.
+
+### 4.2 需补
+- [ ] OTel metrics exporter 接入 (Prometheus / OTLP) —— 当前仅 `/admin/metrics` JSON, 无 Prometheus scrape 端点.
+- [x] Grafana dashboard 模板 (§7.4).
+- [x] 告警规则 (§7.3: 发布失败率 / DLQ 率 / 积压 / 实例 down).
+- [ ] 端到端 distributed tracing (OTel spans 存在: startPublish/startDispatch/startAck/startDlq; 需接 collector).
+- [x] SLO 定义 (§7).
+
+### 4.3 Admin API 一览
+| Endpoint | 方法 | 用途 |
+|---|---|---|
+| `/admin/metrics` | GET | 运行指标 |
+| `/admin/health` | GET | 存活 + pending + 分区 |
+| `/admin/subscriptions?topic=` | GET | 活跃订阅 |
+| `/admin/offsets?topic=` | GET | 分发 offset |
+| `/admin/clients?topic=` | GET | 在线客户端 + pending |
+| `/admin/client/reject?clientId=` | POST | 驱逐客户端 |
+| `/admin/dlq/replay?topic=&max=` | POST | 死信回放 |
+| `/admin/dlq/browse?topic=&max=` | GET | 死信浏览 |
+| `/admin/ratelimit` | PUT | 设限流规则 |
+| `/admin/connectors` | GET/POST/DELETE | connector CRUD |
+| `/admin/connector-workers` | GET/POST | worker 注册/心跳 |
+| `/connector/offset?connectorId=` | GET/POST | connector offset |
+
+## 5. 运维 Runbook
+
+### 5.1 部署
+```
+# runtime 镜像
+docker run -p 8080:8080 -p 8081:8081 \
+ -e EVENTMESH_STORAGE_TYPE=kafka \
+ -e JAVA_OPTS="-Deventmesh.meta.type=nacos -Deventmesh.meta.addr=nacos:8848" \
+ -v $PWD/conf:/data/app/eventmesh/conf \
+ eventmesh:uni
+
+# connector 镜像
+docker run -e EVENTMESH_RUNTIME_URL=http://runtime:8080 \
+ -e CONNECTOR_OPTS="-Dconnector.1.class=...KafkaSourceConnector -Dconnector.1.mode=source -Dconnector.1.topic=t1" \
+ eventmesh-connector:uni
+```
+
+### 5.2 关键配置
+| 配置 | 默认 | 说明 |
+|---|---|---|
+| `eventmesh.storage.type` | standalone | kafka/rocketmq (standalone 已删) |
+| `eventmesh.http.port` | 8080 | 流量 HTTP |
+| `eventmesh.admin.port` | 8081 | 管理 HTTP |
+| `eventmesh.offset.path` | ./data/offset | RocksDB offset 目录 |
+| `eventmesh.meta.type` | (空) | nacos (空=单实例 InMemory) |
+| `eventmesh.meta.addr` | (空) | nacos 地址 |
+| `eventmesh.tls.keystore` | (空) | TLS keystore 路径 |
+| `eventmesh.ws.port` | (空) | WebSocket (-1=关) |
+
+### 5.3 存储配置
+- Kafka: `conf/kafka-client.properties` -> `bootstrap.servers`
+- RocketMQ: `conf/eventmesh.properties` -> `eventMesh.server.rocketmq.namesrvAddr`
+
+### 5.4 常见故障
+| 症状 | 排查 | 处理 |
+|---|---|---|
+| 启动报 "no MeshStoragePlugin" | `eventmesh.storage.type` 错或 SPI jar 不在 classpath | 设正确 type + 确保 storage jar 在 `plugin/storage//` |
+| 启动报 "topic not exist" (CODE 17) | broker autoCreateTopicEnable=false | 预建 topic (4+ queues) |
+| 消息不投递 | `/admin/health` 查 partitionOwnership + pendingDeliveries | 消费者未分配 / ACK 积压 |
+| DLQ 堆积 | `/admin/dlq/browse?topic=` 查死信 | 修消费者 -> `/admin/dlq/replay` 回放 |
+| 限流 | `/admin/metrics` 查 rateLimited | `PUT /admin/ratelimit` 调整 |
+| 多实例重复 | 查 `partitionOwnership` (null=poll-all, broker rebalance) | 确认 broker rebalance 正常 |
+| Meta 不可达 | 日志 "heartbeat failed - lease invalid" | 修 Nacos; 实例自动停 poll (lease gate) |
+
+### 5.5 升级/回滚
+- 滚动重启: 逐实例 `docker stop` -> `docker run` (新镜像). Lease gate 确保停 poll 期间不重复.
+- 回滚: 同上, 换旧镜像. offset 兼容 (RocksDB 格式不变).
+- **注意**: offset 持久化 + 续传已修 (§2.1/§2.2), 滚动重启不丢窗口内消息。Lease gate 确保实例停 poll 期间不重复。
+
+## 6. 下一步优先级
+
+1. ✅ ~~消息丢失 / offset-seek (§2.1)~~ -- 真因: pull 游标卡 offset 0 + 多 broker queue 寻址; 已修并验证 (ClientBroker IT GREEN).
+2. ✅ ~~offset persist + 续传 (§2.2)~~ -- pull-offset 持久化 + 始终从 nextBeginOffset 续传.
+3. 📊 OTel exporter 接入 (metrics + trace → Prometheus / OTLP collector): 仪器 + spans 已埋点, 但**运行时未捆绑 exporter** (§4.2); dashboard / 告警 / SLO 模板已就绪 (§7), 接 exporter 后才可实测.
+4. 🧪 broker/Nacos failover 混沌测试.
+5. 🔒 mTLS 双向认证: 单向 TLS 已实现+测试 (`TlsContextFactory`+`withTls`); **client-auth 强制未接** (`UniHttpServer` 用默认 `HttpsConfigurator`, 未设 `needClientAuth`). auth filter (Token/ACL/HMAC) 已实现.
+6. 🧪 WebSocket 集成测试: `UniWsServer` 已实现 + wss, 但无 IT (SDK `subscribeWs` 端到端未验证).
+
+## 7. SLO 定义
+
+### 7.1 指标采集 (已有)
+| 指标 | 来源 | 用途 |
+|---|---|---|
+| `publishCount` / `publishFailed` | `/admin/metrics` | 发布成功率 |
+| `eventsDispatched` / `ackCount` | `/admin/metrics` | 投递成功率 |
+| `redeliveries` / `dlqCount` | `/admin/metrics` | 可靠性(重投/DLQ 率)|
+| `pendingDeliveries` | `/admin/metrics` | 积压 |
+| `rateLimited` | `/admin/metrics` | 限流频率 |
+| `consumeLag` | `storage.poll` offset vs `endOffset` | 消费延迟 |
+
+### 7.2 SLO 目标(建议初值,灰度后调整)
+
+| SLO | 目标 | 告警阈值 | 计算方式 |
+|---|---|---|---|
+| **发布可用性** | ≥ 99.9% | publishFailed / publishCount > 0.1% 持续 5min | `1 - publishFailed / publishCount` |
+| **投递延迟 P99** | ≤ 500ms | P99 > 1s 持续 5min | publish 时间戳 -> ACK 时间戳 |
+| **端到端延迟 P99** | ≤ 2s | P99 > 5s 持续 5min | publish -> subscriber 收到 |
+| **DLQ 率** | ≤ 0.01% | dlqCount / eventsDispatched > 0.1% | `dlqCount / eventsDispatched` |
+| **积压** | ≤ 1000 | pendingDeliveries > 5000 持续 5min | `pendingDeliveries` |
+| **消费延迟** | ≤ 10s | consumeLag > 60s 持续 5min | `endOffset - pollOffset` |
+| **限流频率** | ≤ 1% | rateLimited / publishCount > 5% | `rateLimited / publishCount` |
+| **可用性** | ≥ 99.9% | `/admin/health` 非 UP 持续 3min | HTTP 200 |
+
+### 7.3 告警规则(Prometheus / Alertmanager)
+
+> **前提**: 需先接 OTel → Prometheus exporter (§4.2, 当前未捆绑). counter 仪器名 `eventmesh_publish_count` 经 Prometheus exporter 导出为 `eventmesh_publish_count_total` (monotonic counter 自动加 `_total`).
+
+```yaml
+# publish failure rate
+- alert: PublishFailureRateHigh
+ expr: rate(eventmesh_publish_failed_total[5m]) / rate(eventmesh_publish_count_total[5m]) > 0.001
+ for: 5m
+ labels: { severity: critical }
+ annotations: { summary: "Publish failure rate > 0.1%" }
+
+# DLQ rate
+- alert: DlqRateHigh
+ expr: rate(eventmesh_dlq_count_total[5m]) / rate(eventmesh_events_dispatched_total[5m]) > 0.001
+ for: 5m
+ labels: { severity: warning }
+ annotations: { summary: "DLQ rate > 0.1%" }
+
+# pending deliveries backlog
+- alert: PendingDeliveriesHigh
+ expr: eventmesh_pending_deliveries > 5000
+ for: 5m
+ labels: { severity: warning }
+ annotations: { summary: "Pending deliveries > 5000" }
+
+# health
+- alert: EventMeshDown
+ expr: up{job="eventmesh"} == 0
+ for: 3m
+ labels: { severity: critical }
+ annotations: { summary: "EventMesh instance down" }
+```
+
+### 7.4 Grafana dashboard 模板
+
+Dashboard 建议包含 4 行:
+1. **发布**: publishCount (rate) + publishFailed (rate) + rateLimited (rate)
+2. **投递**: eventsDispatched (rate) + ackCount (rate) + redeliveries (rate)
+3. **积压**: pendingDeliveries (gauge) + dlqCount (counter)
+4. **健康**: health (UP/DOWN) + consumeLag per topic
diff --git a/docs/uni-architecture-features-and-tests.md b/docs/uni-architecture-features-and-tests.md
new file mode 100644
index 0000000000..37b45677a1
--- /dev/null
+++ b/docs/uni-architecture-features-and-tests.md
@@ -0,0 +1,73 @@
+# uni-architecture 分支:功能与测试总结
+
+> 快照时间:2026-07-10 | 分支:`uni-architecture`
+
+---
+
+# 架构定位
+
+**MQ-as-stateless-WAL + HTTP-only CloudEvents SDK + EventMesh 自管订阅/offset** 的重写(`docs/eventmesh-uni-architecture-redesign.md`)。采用**叠加式**策略:新核心并行存在,旧 TCP/HTTP **保留为兼容适配层**(老客户端零改动),物理删除(Phase 8)是最后切换。
+
+# 模块(活跃 gradle,共 ~38 个)
+
+| 类别 | 模块 |
+|---|---|
+| 核心 | `eventmesh-runtime`(75 主类)、`common`、`spi` |
+| SDK | `sdk-java`(新 `CloudEventsClient` + 旧 TCP/HTTP/gRPC SDK) |
+| 存储 | `storage-api`、`-kafka`、`-rocketmq`(rocketmq = remoting 直连 RPC 插件) |
+| 协议 | `protocol-api`、`-meshmessage`、`-grpc`、`-a2a` |
+| 连接器 | `connector-runtime` + **24 个 connector 插件**(rocketmq/kafka/rabbitmq/redis/mongodb/pulsar/s3/pravega/knative/jdbc/file/spring/prometheus/dingtalk/lark/wecom/slack/wechat/http/chatgpt/canal/mcp/openfunction…) |
+
+已删除:旧 security/meta/metrics/trace/retry/registry/function/v1/v2/admin/starter/examples/operator + storage-{standalone,pulsar,redis,rabbitmq}。
+
+# 核心功能(runtime 按包)
+
+- **boot**: `UniRuntime`(pull/tick 调度循环)、`EventMeshApplication`(runtime+http+admin 一体启动,支持 TLS / WS / 集群 / 连接器调度)
+- **ingress**: `UniIngressService` — publish / subscribe / poll / ack / **request-reply**(`emcorrelationid`) 编排核心
+- **subscription**: LOAD_BALANCE / BROADCAST / MULTICAST / STICKY + 心跳清理
+- **delivery**: `ReliableDispatcher` — ACK 跟踪 + 指数退避重试 + **DLQ**(offset 仅 ACK 后前进,at-least-once)
+- **offset**: 内存 / **RocksDB**(崩溃恢复)/ Meta 两级
+- **push**: 长轮询 / **SSE** / ConnectionPushPump
+- **http**: `UniHttpServer`(新 `/events/*` + 旧 `/eventmesh/*`)、`UniWsServer`、`TlsContextFactory`
+- **transport.tcp** (12): `UniTcpServer` + netty pipeline — **旧 TCP 客户端零改动**
+- **transport.http**: `LegacyHttpBridge` — **旧 HTTP 客户端零改动**
+- **cluster** (15): `NacosMetaStore` + `ClusterMembership`(心跳租约) + `PartitionOwnership`(确定性分区 + gen fencing) + `ClusterCoordinator`(本地投递/跨实例 forward) → **多实例无重复**
+- **security**: FilterChain(Token / ACL / HMAC 签名),无 SPI
+- **ratelimit**: 令牌桶(每 topic,超额返回 429)
+- **metrics**: OTel(默认 Prometheus 导出)+ `UniTrace`
+- **connector**: `ConnectorScheduler`(runtime-push 动态调度连接器到 worker)
+- **admin**: 独立端口的 `/admin/*`(metrics / dlq replay / ratelimit / connectors…)
+
+# 测试情况
+
+## 单元/进程内测试(无 broker,跑在常规套件里) — 全绿
+
+- runtime:**38 个测试类 / 82 @Test**,刚跑 **0 failures**(8 skipped = 下面那些 gated IT)
+- sdk-java:29 @Test | connector-runtime:13 @Test
+- 合计 **~124 @Test**
+- 覆盖:subscription / offset(RocksDB 崩溃恢复) / delivery(ACK+nack+超时+DLQ+退避) / push / ratelimit / security / cluster / TLS / DLQ / 限流 / cluster-forward(2 实例 in-memory)/ 连接器调度 / 旧 TCP+HTTP 兼容
+
+## 真 broker/Nacos 集成测试(gated,已在多 broker 实测中验证 GREEN)
+
+- `-Dit.storage=rocketmq`(真 RocketMQ `127.0.0.1:9876`):
+ - `RealBrokerIntegrationTest` ✅
+ - `ClientBrokerIntegrationTest` ✅(**本次刚修绿** — pull 游标卡 offset 0 + 多 broker queue 寻址)
+ - `LoadThroughputIntegrationTest` ✅(500 事件,0 丢失,0 重复)
+ - `AckTimeoutRedeliveryIntegrationTest` ✅(ACK 超时→重投→DLQ)
+ - `BrokerListTest` ✅
+- `-Dit.nacos`(真 RocketMQ + Nacos `127.0.0.1:8848`):
+ - `LegacyTcpClusterBrokerIntegrationTest` ✅(旧 TCP SDK + 真 broker + Nacos)
+ - `MultiInstanceRocketMqIntegrationTest` ✅(2 实例无重复)
+ - `NacosClusterForwardIntegrationTest` ✅(跨实例订阅转发)
+
+## 已知待办(需环境/更大改动,已记录)
+
+- WebSocket netty 握手的服务端接线(SSE 通道已就绪)
+- 真 Meta MetaStore:etcd 适配(nacos 已完成)
+- Java 21 虚拟线程(项目源码级还是 Java 8)
+- Phase 8 物理删除旧 TCP/gRPC SDK(最终切换,需旧 runtime 全量回归)
+- `UniHttpServer.publish` 对 `RateLimitedException` 仍走 catch-all 返回 500(应为 429;测试靠 metrics 断言,未覆盖状态码)
+
+## 一句话
+
+新架构已是**可运行系统**,全部单元/进程内测试 + 7 个真 broker/Nacos 集成测试均 GREEN;老 TCP/HTTP 客户端零改动接入新核心。剩余项均为需环境或最终切换的收尾工作。
diff --git a/eventmesh-admin-server/.gitignore b/eventmesh-admin-server/.gitignore
deleted file mode 100644
index b63da4551b..0000000000
--- a/eventmesh-admin-server/.gitignore
+++ /dev/null
@@ -1,42 +0,0 @@
-.gradle
-build/
-!gradle/wrapper/gradle-wrapper.jar
-!**/src/main/**/build/
-!**/src/test/**/build/
-
-### IntelliJ IDEA ###
-.idea/modules.xml
-.idea/jarRepositories.xml
-.idea/compiler.xml
-.idea/libraries/
-*.iws
-*.iml
-*.ipr
-out/
-!**/src/main/**/out/
-!**/src/test/**/out/
-
-### Eclipse ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-bin/
-!**/src/main/**/bin/
-!**/src/test/**/bin/
-
-### NetBeans ###
-/nbproject/private/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-
-### VS Code ###
-.vscode/
-
-### Mac OS ###
-.DS_Store
\ No newline at end of file
diff --git a/eventmesh-admin-server/bin/start-admin.sh b/eventmesh-admin-server/bin/start-admin.sh
deleted file mode 100644
index 1633036617..0000000000
--- a/eventmesh-admin-server/bin/start-admin.sh
+++ /dev/null
@@ -1,200 +0,0 @@
-#!/bin/bash
-#
-# Licensed to Apache Software Foundation (ASF) under one or more contributor
-# license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright
-# ownership. Apache Software Foundation (ASF) licenses this file to you under
-# the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-#===========================================================================================
-# Java Environment Setting
-#===========================================================================================
-set -e
-# Server configuration may be inconsistent, add these configurations to avoid garbled code problems
-export LANG=en_US.UTF-8
-export LC_CTYPE=en_US.UTF-8
-export LC_ALL=en_US.UTF-8
-
-TMP_JAVA_HOME="/customize/your/java/home/here"
-
-# Detect operating system.
-OS=$(uname)
-
-function is_java8_or_11 {
- local _java="$1"
- [[ -x "$_java" ]] || return 1
- [[ "$("$_java" -version 2>&1)" =~ 'java version "1.8' || "$("$_java" -version 2>&1)" =~ 'openjdk version "1.8' || "$("$_java" -version 2>&1)" =~ 'java version "11' || "$("$_java" -version 2>&1)" =~ 'openjdk version "11' ]] || return 2
- return 0
-}
-
-function extract_java_version {
- local _java="$1"
- local version=$("$_java" -version 2>&1 | awk -F '"' '/version/ {print $2}' | awk -F '.' '{if ($1 == 1 && $2 == 8) print "8"; else if ($1 == 11) print "11"; else print "unknown"}')
- echo "$version"
-}
-
-# 0(not running), 1(is running)
-#function is_proxyRunning {
-# local _pid="$1"
-# local pid=`ps ax | grep -i 'org.apache.eventmesh.runtime.boot.EventMeshStartup' |grep java | grep -v grep | awk '{print $1}'|grep $_pid`
-# if [ -z "$pid" ] ; then
-# return 0
-# else
-# return 1
-# fi
-#}
-
-function get_pid {
- local ppid=""
- if [ -f ${EVENTMESH_ADMIN_HOME}/bin/pid-admin.file ]; then
- ppid=$(cat ${EVENTMESH_ADMIN_HOME}/bin/pid-admin.file)
- # If the process does not exist, it indicates that the previous process terminated abnormally.
- if [ ! -d /proc/$ppid ]; then
- # Remove the residual file.
- rm ${EVENTMESH_ADMIN_HOME}/bin/pid-admin.file
- echo -e "ERROR\t EventMesh process had already terminated unexpectedly before, please check log output."
- ppid=""
- fi
- else
- if [[ $OS =~ Msys ]]; then
- # There is a Bug on Msys that may not be able to kill the identified process
- ppid=`jps -v | grep -i "org.apache.eventmesh.admin.server.ExampleAdminServer" | grep java | grep -v grep | awk -F ' ' {'print $1'}`
- elif [[ $OS =~ Darwin ]]; then
- # Known problem: grep Java may not be able to accurately identify Java processes
- ppid=$(/bin/ps -o user,pid,command | grep "java" | grep -i "org.apache.eventmesh.admin.server.ExampleAdminServer" | grep -Ev "^root" |awk -F ' ' {'print $2'})
- else
- if [ $DOCKER ]; then
- # No need to exclude root user in Docker containers.
- ppid=$(ps -C java -o user,pid,command --cols 99999 | grep -w $EVENTMESH_ADMIN_HOME | grep -i "org.apache.eventmesh.admin.server.ExampleAdminServer" | awk -F ' ' {'print $2'})
- else
- # It is required to identify the process as accurately as possible on Linux.
- ppid=$(ps -C java -o user,pid,command --cols 99999 | grep -w $EVENTMESH_ADMIN_HOME | grep -i "org.apache.eventmesh.admin.server.ExampleAdminServer" | grep -Ev "^root" | awk -F ' ' {'print $2'})
- fi
- fi
- fi
- echo "$ppid";
-}
-
-#===========================================================================================
-# Locate Java Executable
-#===========================================================================================
-
-if [[ -d "$TMP_JAVA_HOME" ]] && is_java8_or_11 "$TMP_JAVA_HOME/bin/java"; then
- JAVA="$TMP_JAVA_HOME/bin/java"
- JAVA_VERSION=$(extract_java_version "$TMP_JAVA_HOME/bin/java")
-elif [[ -d "$JAVA_HOME" ]] && is_java8_or_11 "$JAVA_HOME/bin/java"; then
- JAVA="$JAVA_HOME/bin/java"
- JAVA_VERSION=$(extract_java_version "$JAVA_HOME/bin/java")
-elif is_java8_or_11 "$(which java)"; then
- JAVA="$(which java)"
- JAVA_VERSION=$(extract_java_version "$(which java)")
-else
- echo -e "ERROR\t Java 8 or 11 not found, operation abort."
- exit 9;
-fi
-
-echo "EventMesh using Java version: $JAVA_VERSION, path: $JAVA"
-
-EVENTMESH_ADMIN_HOME=$(cd "$(dirname "$0")/.." && pwd)
-export EVENTMESH_ADMIN_HOME
-
-EVENTMESH_ADMIN_LOG_HOME="${EVENTMESH_ADMIN_HOME}/logs"
-export EVENTMESH_ADMIN_LOG_HOME
-
-echo -e "EVENTMESH_ADMIN_HOME : ${EVENTMESH_ADMIN_HOME}\nEVENTMESH_ADMIN_LOG_HOME : ${EVENTMESH_ADMIN_LOG_HOME}"
-
-function make_logs_dir {
- if [ ! -e "${EVENTMESH_ADMIN_LOG_HOME}" ]; then mkdir -p "${EVENTMESH_ADMIN_LOG_HOME}"; fi
-}
-
-error_exit ()
-{
- echo -e "ERROR\t $1 !!"
- exit 1
-}
-
-export JAVA_HOME
-
-#===========================================================================================
-# JVM Configuration
-#===========================================================================================
-#if [ $1 = "prd" -o $1 = "benchmark" ]; then JAVA_OPT="${JAVA_OPT} -server -Xms2048M -Xmx4096M -Xmn2048m -XX:SurvivorRatio=4"
-#elif [ $1 = "sit" ]; then JAVA_OPT="${JAVA_OPT} -server -Xms256M -Xmx512M -Xmn256m -XX:SurvivorRatio=4"
-#elif [ $1 = "dev" ]; then JAVA_OPT="${JAVA_OPT} -server -Xms128M -Xmx256M -Xmn128m -XX:SurvivorRatio=4"
-#fi
-
-GC_LOG_FILE="${EVENTMESH_ADMIN_LOG_HOME}/eventmesh_admin_gc_%p.log"
-
-JAVA_OPT="${JAVA_OPT} -server -Xms1g -Xmx1g"
-JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:SurvivorRatio=8 -XX:MaxGCPauseMillis=50"
-JAVA_OPT="${JAVA_OPT} -verbose:gc"
-if [[ "$JAVA_VERSION" == "8" ]]; then
- # Set JAVA_OPT for Java 8
- JAVA_OPT="${JAVA_OPT} -Xloggc:${GC_LOG_FILE} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"
- JAVA_OPT="${JAVA_OPT} -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"
-elif [[ "$JAVA_VERSION" == "11" ]]; then
- # Set JAVA_OPT for Java 11
- XLOG_PARAM="time,level,tags:filecount=5,filesize=30m"
- JAVA_OPT="${JAVA_OPT} -Xlog:gc*:${GC_LOG_FILE}:${XLOG_PARAM}"
- JAVA_OPT="${JAVA_OPT} -Xlog:safepoint:${GC_LOG_FILE}:${XLOG_PARAM} -Xlog:ergo*=debug:${GC_LOG_FILE}:${XLOG_PARAM}"
-fi
-JAVA_OPT="${JAVA_OPT} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${EVENTMESH_ADMIN_LOG_HOME} -XX:ErrorFile=${EVENTMESH_ADMIN_LOG_HOME}/hs_err_%p.log"
-JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"
-JAVA_OPT="${JAVA_OPT} -XX:+AlwaysPreTouch"
-JAVA_OPT="${JAVA_OPT} -XX:MaxDirectMemorySize=8G"
-JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages -XX:-UseBiasedLocking"
-JAVA_OPT="${JAVA_OPT} -Dio.netty.leakDetectionLevel=advanced"
-JAVA_OPT="${JAVA_OPT} -Dio.netty.allocator.type=pooled"
-JAVA_OPT="${JAVA_OPT} -Djava.security.egd=file:/dev/./urandom"
-JAVA_OPT="${JAVA_OPT} -Dlog4j.configurationFile=${EVENTMESH_ADMIN_HOME}/conf/log4j2.xml"
-JAVA_OPT="${JAVA_OPT} -Deventmesh.log.home=${EVENTMESH_ADMIN_LOG_HOME}"
-JAVA_OPT="${JAVA_OPT} -DconfPath=${EVENTMESH_ADMIN_HOME}/conf"
-JAVA_OPT="${JAVA_OPT} -DconfigurationPath=${EVENTMESH_ADMIN_HOME}/conf"
-JAVA_OPT="${JAVA_OPT} -Dlog4j2.AsyncQueueFullPolicy=Discard"
-JAVA_OPT="${JAVA_OPT} -Drocketmq.client.logUseSlf4j=true"
-JAVA_OPT="${JAVA_OPT} -DeventMeshPluginDir=${EVENTMESH_ADMIN_HOME}/plugin"
-
-#if [ -f "pid.file" ]; then
-# pid=`cat pid.file`
-# if ! is_proxyRunning "$pid"; then
-# echo "proxy is running already"
-# exit 9;
-# else
-# echo "err pid$pid, rm pid.file"
-# rm pid.file
-# fi
-#fi
-
-pid=$(get_pid)
-if [[ $pid == "ERROR"* ]]; then
- echo -e "${pid}"
- exit 9
-fi
-if [ -n "$pid" ]; then
- echo -e "ERROR\t The server is already running (pid=$pid), there is no need to execute start.sh again."
- exit 9
-fi
-
-make_logs_dir
-
-echo "Using Java version: $JAVA_VERSION, path: $JAVA" >> ${EVENTMESH_ADMIN_LOG_HOME}/eventmesh-admin.out
-
-EVENTMESH_ADMIN_MAIN=org.apache.eventmesh.admin.server.ExampleAdminServer
-if [ $DOCKER ]; then
- $JAVA $JAVA_OPT -classpath ${EVENTMESH_ADMIN_HOME}/conf:${EVENTMESH_ADMIN_HOME}/apps/*:${EVENTMESH_ADMIN_HOME}/lib/* $EVENTMESH_ADMIN_MAIN >> ${EVENTMESH_ADMIN_LOG_HOME}/eventmesh-admin.out
-else
- $JAVA $JAVA_OPT -classpath ${EVENTMESH_ADMIN_HOME}/conf:${EVENTMESH_ADMIN_HOME}/apps/*:${EVENTMESH_ADMIN_HOME}/lib/* $EVENTMESH_ADMIN_MAIN >> ${EVENTMESH_ADMIN_LOG_HOME}/eventmesh-admin.out 2>&1 &
-echo $!>${EVENTMESH_ADMIN_HOME}/bin/pid-admin.file
-fi
-exit 0
diff --git a/eventmesh-admin-server/bin/stop-admin.sh b/eventmesh-admin-server/bin/stop-admin.sh
deleted file mode 100644
index 207531d7fa..0000000000
--- a/eventmesh-admin-server/bin/stop-admin.sh
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/bin/bash
-#
-# Licensed to Apache Software Foundation (ASF) under one or more contributor
-# license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright
-# ownership. Apache Software Foundation (ASF) licenses this file to you under
-# the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-# Detect operating system
-OS=$(uname)
-
-EVENTMESH_ADMIN_HOME=`cd $(dirname $0)/.. && pwd`
-
-export EVENTMESH_ADMIN_HOME
-
-function get_pid {
- local ppid=""
- if [ -f ${EVENTMESH_ADMIN_HOME}/bin/pid-admin.file ]; then
- ppid=$(cat ${EVENTMESH_ADMIN_HOME}/bin/pid-admin.file)
- # If the process does not exist, it indicates that the previous process terminated abnormally.
- if [ ! -d /proc/$ppid ]; then
- # Remove the residual file and return an error status.
- rm ${EVENTMESH_ADMIN_HOME}/bin/pid-admin.file
- echo -e "ERROR\t EventMesh admin process had already terminated unexpectedly before, please check log output."
- ppid=""
- fi
- else
- if [[ $OS =~ Msys ]]; then
- # There is a Bug on Msys that may not be able to kill the identified process
- ppid=`jps -v | grep -i "org.apache.eventmesh.admin.server.ExampleAdminServer" | grep java | grep -v grep | awk -F ' ' {'print $1'}`
- elif [[ $OS =~ Darwin ]]; then
- # Known problem: grep Java may not be able to accurately identify Java processes
- ppid=$(/bin/ps -o user,pid,command | grep "java" | grep -i "org.apache.eventmesh.admin.server.ExampleAdminServer" | grep -Ev "^root" |awk -F ' ' {'print $2'})
- else
- # It is required to identify the process as accurately as possible on Linux
- ppid=$(ps -C java -o user,pid,command --cols 99999 | grep -w $EVENTMESH_ADMIN_HOME | grep -i "org.apache.eventmesh.admin.server.ExampleAdminServer" | grep -Ev "^root" |awk -F ' ' {'print $2'})
- fi
- fi
- echo "$ppid";
-}
-
-pid=$(get_pid)
-if [[ $pid == "ERROR"* ]]; then
- echo -e "${pid}"
- exit 9
-fi
-if [ -z "$pid" ];then
- echo -e "ERROR\t No EventMesh admin server running."
- exit 9
-fi
-
-kill ${pid}
-echo "Send shutdown request to EventMesh admin(${pid}) OK"
-
-[[ $OS =~ Msys ]] && PS_PARAM=" -W "
-stop_timeout=60
-for no in $(seq 1 $stop_timeout); do
- if ps $PS_PARAM -p "$pid" 2>&1 > /dev/null; then
- if [ $no -lt $stop_timeout ]; then
- echo "[$no] server shutting down ..."
- sleep 1
- continue
- fi
-
- echo "shutdown server timeout, kill process: $pid"
- kill -9 $pid; sleep 1; break;
- echo "`date +'%Y-%m-%-d %H:%M:%S'` , pid : [$pid] , error message : abnormal shutdown which can not be closed within 60s" > ../logs/shutdown.error
- else
- echo "shutdown server ok!"; break;
- fi
-done
-
-if [ -f "pid-admin.file" ]; then
- rm pid-admin.file
-fi
-
-
diff --git a/eventmesh-admin-server/build.gradle b/eventmesh-admin-server/build.gradle
deleted file mode 100644
index fdfe1bffe8..0000000000
--- a/eventmesh-admin-server/build.gradle
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-dependencies {
- implementation project(":eventmesh-spi")
- implementation project(":eventmesh-common")
- implementation project(":eventmesh-registry:eventmesh-registry-api")
- implementation project(":eventmesh-registry:eventmesh-registry-nacos")
- implementation project(":eventmesh-openconnect:eventmesh-openconnect-offsetmgmt-plugin:eventmesh-openconnect-offsetmgmt-api")
- implementation "com.alibaba.nacos:nacos-client"
- implementation("org.springframework.boot:spring-boot-starter-web") {
- exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
- }
- implementation 'org.springframework.boot:spring-boot-starter-jetty'
- implementation "io.grpc:grpc-core"
- implementation "io.grpc:grpc-protobuf"
- implementation "io.grpc:grpc-stub"
- implementation "io.grpc:grpc-netty-shaded"
-
- // https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter
- implementation "com.baomidou:mybatis-plus-boot-starter"
-
- // https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter
- implementation "com.alibaba:druid-spring-boot-starter"
- compileOnly 'com.mysql:mysql-connector-j'
- compileOnly 'org.projectlombok:lombok'
- testImplementation 'junit:junit:4.13.2'
- testImplementation 'org.projectlombok:lombok'
- annotationProcessor 'org.projectlombok:lombok'
-}
-
-configurations.implementation {
- exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
-}
-
-sourceSets {
- main {
- resources {
- srcDirs = ['src/main/resources', 'conf']
- }
- }
-}
-
diff --git a/eventmesh-admin-server/conf/application.yaml b/eventmesh-admin-server/conf/application.yaml
deleted file mode 100644
index 7765d90ce8..0000000000
--- a/eventmesh-admin-server/conf/application.yaml
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-spring:
- datasource:
- url: jdbc:mysql://localhost:3306/eventmesh?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
- username: //db_username
- password: //db_password
- driver-class-name: com.mysql.cj.jdbc.Driver
- initialSize: 1
- minIdle: 1
- maxActive: 20
- maxWait: 10000
- timeBetweenEvictionRunsMillis: 60000
- minEvictableIdleTimeMillis: 300000
- validationQuery: SELECT 1 FROM DUAL
- testWhileIdle: true
- testOnBorrow: false
- testOnReturn: false
- poolPreparedStatements: false
- maxPoolPreparedStatementPerConnectionSize: 20
- filters: stat
- connectionProperties: "druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000"
-# secret keys
-sysPubKey:
-appPrivKey:
-
-mybatis-plus:
- mapper-locations: classpath:mapper/*.xml
- configuration:
- map-underscore-to-camel-case: false
- log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-# http server port
-server:
- port: 8082
-event-mesh:
- admin-server:
- serviceName: DEFAULT_GROUP@@em_adm_server
- # grpc server port
- port: 8081
- adminServerList:
- R1: http://localhost:8082;http://localhost:8082
- R2: http://localhost:8092;http://localhost:8092
- region: R1
\ No newline at end of file
diff --git a/eventmesh-admin-server/conf/eventmesh-admin.properties b/eventmesh-admin-server/conf/eventmesh-admin.properties
deleted file mode 100644
index 30507ec02c..0000000000
--- a/eventmesh-admin-server/conf/eventmesh-admin.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-eventMesh.registry.plugin.type=nacos
-eventMesh.registry.plugin.server-addr=localhost:8848
diff --git a/eventmesh-admin-server/conf/eventmesh.sql b/eventmesh-admin-server/conf/eventmesh.sql
deleted file mode 100644
index 4d11ab1585..0000000000
--- a/eventmesh-admin-server/conf/eventmesh.sql
+++ /dev/null
@@ -1,185 +0,0 @@
--- Licensed to the Apache Software Foundation (ASF) under one
--- or more contributor license agreements. See the NOTICE file
--- distributed with this work for additional information
--- regarding copyright ownership. The ASF licenses this file
--- to you under the Apache License, Version 2.0 (the
--- "License"); you may not use this file except in compliance
--- with the License. You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing,
--- software distributed under the License is distributed on an
--- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
--- KIND, either express or implied. See the License for the
--- specific language governing permissions and limitations
--- under the License.
-
-/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
-/*!40101 SET NAMES utf8 */;
-/*!50503 SET NAMES utf8 */;
-/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
-/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
-/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-
-
--- export eventmesh database
-CREATE DATABASE IF NOT EXISTS `eventmesh` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_bin */ /*!80016 DEFAULT ENCRYPTION='N' */;
-USE `eventmesh`;
-
--- export table eventmesh.event_mesh_data_source structure
-CREATE TABLE IF NOT EXISTS `event_mesh_data_source` (
- `id` int unsigned NOT NULL AUTO_INCREMENT,
- `dataType` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '',
- `description` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
- `configuration` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
- `configurationClass` varchar(200) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
- `region` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `createUid` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '',
- `updateUid` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '',
- `createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `updateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-
--- export table eventmesh.event_mesh_job_info structure
-CREATE TABLE IF NOT EXISTS `event_mesh_job_info` (
- `id` int unsigned NOT NULL AUTO_INCREMENT,
- `jobID` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
- `jobDesc` varchar(50) COLLATE utf8_bin NOT NULL,
- `taskID` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '',
- `transportType` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '',
- `sourceData` int NOT NULL DEFAULT '0',
- `targetData` int NOT NULL DEFAULT '0',
- `jobState` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
- `jobType` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
- `fromRegion` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `runningRegion` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `createUid` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `updateUid` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `updateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`) USING BTREE,
- UNIQUE KEY `jobID` (`jobID`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-
--- export table eventmesh.event_mesh_mysql_position structure
-CREATE TABLE IF NOT EXISTS `event_mesh_mysql_position` (
- `id` int unsigned NOT NULL AUTO_INCREMENT,
- `jobID` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '',
- `serverUUID` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
- `address` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
- `position` bigint DEFAULT NULL,
- `gtid` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
- `currentGtid` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
- `timestamp` bigint DEFAULT NULL,
- `journalName` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
- `createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `updateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`),
- UNIQUE KEY `jobID` (`jobID`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=DYNAMIC;
-
--- export table eventmesh.event_mesh_position_reporter_history structure
-CREATE TABLE IF NOT EXISTS `event_mesh_position_reporter_history` (
- `id` bigint NOT NULL AUTO_INCREMENT,
- `job` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '',
- `record` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
- `address` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
- `createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`),
- KEY `job` (`job`),
- KEY `address` (`address`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='record position reporter changes';
-
--- export table eventmesh.event_mesh_runtime_heartbeat structure
-CREATE TABLE IF NOT EXISTS `event_mesh_runtime_heartbeat` (
- `id` bigint unsigned NOT NULL AUTO_INCREMENT,
- `adminAddr` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
- `runtimeAddr` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
- `jobID` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `reportTime` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'runtime local report time',
- `updateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`),
- KEY `jobID` (`jobID`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-
--- export table eventmesh.event_mesh_runtime_history structure
-CREATE TABLE IF NOT EXISTS `event_mesh_runtime_history` (
- `id` bigint NOT NULL AUTO_INCREMENT,
- `job` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '',
- `address` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
- `createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`),
- KEY `address` (`address`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=DYNAMIC COMMENT='record runtime task change history';
-
--- export table eventmesh.event_mesh_task_info structure
-CREATE TABLE IF NOT EXISTS `event_mesh_task_info` (
- `id` int unsigned NOT NULL AUTO_INCREMENT,
- `taskID` varchar(50) COLLATE utf8_bin NOT NULL,
- `taskName` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
- `taskDesc` varchar(50) COLLATE utf8_bin NOT NULL,
- `taskState` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT 'taskstate',
- `sourceRegion` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `targetRegion` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `createUid` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '',
- `updateUid` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '',
- `createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `updateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`) USING BTREE,
- UNIQUE KEY `taskID` (`taskID`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-
--- export table eventmesh.event_mesh_verify structure
-CREATE TABLE IF NOT EXISTS `event_mesh_verify` (
- `id` int unsigned NOT NULL AUTO_INCREMENT,
- `taskID` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `jobID` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `recordID` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `recordSig` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `connectorName` varchar(200) COLLATE utf8_bin DEFAULT NULL,
- `connectorStage` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `position` text COLLATE utf8_bin DEFAULT NULL,
- `createTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-
--- eventmesh.event_mesh_weredis_position definition
-CREATE TABLE `event_mesh_weredis_position` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `jobID` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '',
- `address` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `clusterName` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `partitionName` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `masterReplid` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `host` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `replOffset` bigint(20) NOT NULL DEFAULT '-1',
- `createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `updateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`),
- UNIQUE KEY `jobID` (`jobID`)
-) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=DYNAMIC;
-
-
-CREATE TABLE `event_mesh_monitor` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `taskID` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `jobID` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `address` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `transportType` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `connectorStage` varchar(50) COLLATE utf8_bin DEFAULT NULL,
- `totalReqNum` bigint DEFAULT NULL,
- `totalTimeCost` bigint DEFAULT NULL,
- `maxTimeCost` bigint DEFAULT NULL,
- `avgTimeCost` bigint DEFAULT NULL,
- `tps` double DEFAULT NULL,
- `createTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-
-/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
-/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
-/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
-/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;
diff --git a/eventmesh-admin-server/conf/log4j2.xml b/eventmesh-admin-server/conf/log4j2.xml
deleted file mode 100644
index acc6acb8ba..0000000000
--- a/eventmesh-admin-server/conf/log4j2.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/eventmesh-admin-server/conf/mapper/EventMeshDataSourceMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshDataSourceMapper.xml
deleted file mode 100644
index 50e6ad82cc..0000000000
--- a/eventmesh-admin-server/conf/mapper/EventMeshDataSourceMapper.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- id,dataType,description,
- configuration,configurationClass,region,
- createUid,updateUid,createTime,updateTime
-
-
diff --git a/eventmesh-admin-server/conf/mapper/EventMeshJobInfoMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshJobInfoMapper.xml
deleted file mode 100644
index a053d1c838..0000000000
--- a/eventmesh-admin-server/conf/mapper/EventMeshJobInfoMapper.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- id,jobID,jobDesc,
- taskID,transportType,sourceData,
- targetData,jobState,jobType,
- fromRegion,runningRegion,createUid,
- updateUid,createTime,updateTime
-
-
diff --git a/eventmesh-admin-server/conf/mapper/EventMeshMonitorMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshMonitorMapper.xml
deleted file mode 100644
index f77fb8ba77..0000000000
--- a/eventmesh-admin-server/conf/mapper/EventMeshMonitorMapper.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- id,taskID,jobID,address,transportType,connectorStage,
- totalReqNum,totalTimeCost,maxTimeCost,avgTimeCost,
- tps,createTime
-
-
diff --git a/eventmesh-admin-server/conf/mapper/EventMeshMysqlPositionMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshMysqlPositionMapper.xml
deleted file mode 100644
index 9bcc7f42bb..0000000000
--- a/eventmesh-admin-server/conf/mapper/EventMeshMysqlPositionMapper.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- id,jobID,serverUUID,
- address,position,gtid,
- currentGtid,timestamp,journalName,
- createTime,updateTime
-
-
diff --git a/eventmesh-admin-server/conf/mapper/EventMeshPositionReporterHistoryMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshPositionReporterHistoryMapper.xml
deleted file mode 100644
index a9e4fe6f1b..0000000000
--- a/eventmesh-admin-server/conf/mapper/EventMeshPositionReporterHistoryMapper.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- id,job,record,
- address,createTime
-
-
diff --git a/eventmesh-admin-server/conf/mapper/EventMeshRuntimeHeartbeatMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshRuntimeHeartbeatMapper.xml
deleted file mode 100644
index 200b1bf54a..0000000000
--- a/eventmesh-admin-server/conf/mapper/EventMeshRuntimeHeartbeatMapper.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- id,adminAddr,runtimeAddr,
- jobID,reportTime,updateTime,
- createTime
-
-
diff --git a/eventmesh-admin-server/conf/mapper/EventMeshRuntimeHistoryMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshRuntimeHistoryMapper.xml
deleted file mode 100644
index 281cce30f9..0000000000
--- a/eventmesh-admin-server/conf/mapper/EventMeshRuntimeHistoryMapper.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- id,job,address,
- createTime
-
-
diff --git a/eventmesh-admin-server/conf/mapper/EventMeshTaskInfoMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshTaskInfoMapper.xml
deleted file mode 100644
index c3514fd945..0000000000
--- a/eventmesh-admin-server/conf/mapper/EventMeshTaskInfoMapper.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- id,taskID,taskName,
- taskDesc,taskState,sourceRegion,targetRegion,
- createUid,updateUid,createTime,
- updateTime
-
-
diff --git a/eventmesh-admin-server/conf/mapper/EventMeshVerifyMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshVerifyMapper.xml
deleted file mode 100644
index 45727498cc..0000000000
--- a/eventmesh-admin-server/conf/mapper/EventMeshVerifyMapper.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- id,taskID,jobID,recordID,
- recordSig,connectorName,connectorStage,
- position,createTime
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/AdminServerProperties.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/AdminServerProperties.java
deleted file mode 100644
index 2e6d3c018a..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/AdminServerProperties.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server;
-
-import java.util.Map;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-import lombok.Getter;
-import lombok.Setter;
-
-@ConfigurationProperties("event-mesh.admin-server")
-@Getter
-@Setter
-public class AdminServerProperties {
-
- private int port;
- private boolean enableSSL;
- private String configurationPath;
- private String configurationFile;
- private String serviceName;
- private Map adminServerList;
- private String region;
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/AdminServerRuntimeException.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/AdminServerRuntimeException.java
deleted file mode 100644
index e68d05100f..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/AdminServerRuntimeException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server;
-
-import lombok.Getter;
-
-@Getter
-public class AdminServerRuntimeException extends RuntimeException {
- private final int code;
-
- public AdminServerRuntimeException(int code, String message) {
- super(message);
- this.code = code;
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/ExampleAdminServer.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/ExampleAdminServer.java
deleted file mode 100644
index d0f2111041..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/ExampleAdminServer.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server;
-
-import org.apache.eventmesh.admin.server.constants.AdminServerConstants;
-import org.apache.eventmesh.common.config.ConfigService;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-@SpringBootApplication(scanBasePackages = "org.apache.eventmesh.admin.server", exclude = {DataSourceAutoConfiguration.class})
-public class ExampleAdminServer {
-
- public static void main(String[] args) throws Exception {
- ConfigService.getInstance().setConfigPath(AdminServerConstants.EVENTMESH_CONF_HOME).setRootConfig(AdminServerConstants.EVENTMESH_CONF_FILE);
- SpringApplication.run(ExampleAdminServer.class);
- log.info("admin start success.");
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/constants/AdminServerConstants.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/constants/AdminServerConstants.java
deleted file mode 100644
index 8ed079fd31..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/constants/AdminServerConstants.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.constants;
-
-public class AdminServerConstants {
- public static final String CONF_ENV = "configurationPath";
-
- public static final String EVENTMESH_CONF_HOME = System.getProperty(CONF_ENV, System.getenv(CONF_ENV));
-
- public static final String EVENTMESH_CONF_FILE = "eventmesh-admin.properties";
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/BaseServer.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/BaseServer.java
deleted file mode 100644
index 9bbe4ce305..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/BaseServer.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web;
-
-import org.apache.eventmesh.common.ComponentLifeCycle;
-import org.apache.eventmesh.common.remote.payload.PayloadFactory;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-public abstract class BaseServer implements ComponentLifeCycle {
-
- static {
- PayloadFactory.getInstance().init();
- }
-
- @PostConstruct
- public void init() throws Exception {
- log.info("[{}] server starting at port [{}]", this.getClass().getSimpleName(), getPort());
- start();
- log.info("[{}] server started at port [{}]", this.getClass().getSimpleName(), getPort());
- }
-
- @PreDestroy
- public void shutdown() throws Exception {
- log.info("[{}] server will destroy", this.getClass().getSimpleName());
- stop();
- log.info("[{}] server has be destroy", this.getClass().getSimpleName());
- }
-
- public abstract int getPort();
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/GrpcServer.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/GrpcServer.java
deleted file mode 100644
index d2a0330355..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/GrpcServer.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web;
-
-import org.apache.eventmesh.admin.server.AdminServerProperties;
-import org.apache.eventmesh.admin.server.web.service.AdminGrpcServer;
-
-import java.util.concurrent.TimeUnit;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-
-import io.grpc.Server;
-import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Controller
-@Slf4j
-public class GrpcServer extends BaseServer {
-
- @Autowired
- AdminGrpcServer adminGrpcServer;
-
- @Autowired
- AdminServerProperties properties;
-
- private Server server;
-
- @Override
- public void start() throws Exception {
- NettyServerBuilder serverBuilder = NettyServerBuilder.forPort(getPort()).addService(adminGrpcServer);
- if (properties.isEnableSSL()) {
- serverBuilder.sslContext(null);
- }
- server = serverBuilder.build();
- server.start();
- }
-
- @Override
- public void stop() {
- try {
- if (server != null) {
- server.shutdown();
- if (!server.awaitTermination(30, TimeUnit.SECONDS)) {
- log.warn("[{}] server don't graceful stop in 30s, it will shutdown now", this.getClass().getSimpleName());
- server.shutdownNow();
- }
- }
- } catch (InterruptedException e) {
- log.warn("destroy [{}] server fail", this.getClass().getSimpleName(), e);
- }
- }
-
- @Override
- public int getPort() {
- return properties.getPort();
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/HttpServer.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/HttpServer.java
deleted file mode 100644
index 0a20d8645e..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/HttpServer.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web;
-
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshTaskInfoService;
-import org.apache.eventmesh.admin.server.web.service.monitor.MonitorBizService;
-import org.apache.eventmesh.admin.server.web.service.task.TaskBizService;
-import org.apache.eventmesh.admin.server.web.service.verify.VerifyBizService;
-import org.apache.eventmesh.common.remote.request.CreateTaskRequest;
-import org.apache.eventmesh.common.remote.request.QueryTaskInfoRequest;
-import org.apache.eventmesh.common.remote.request.QueryTaskMonitorRequest;
-import org.apache.eventmesh.common.remote.request.ReportMonitorRequest;
-import org.apache.eventmesh.common.remote.request.ReportVerifyRequest;
-import org.apache.eventmesh.common.remote.request.TaskBachRequest;
-import org.apache.eventmesh.common.remote.request.TaskIDRequest;
-import org.apache.eventmesh.common.remote.response.CreateTaskResponse;
-import org.apache.eventmesh.common.remote.response.HttpResponseResult;
-import org.apache.eventmesh.common.remote.response.QueryTaskInfoResponse;
-import org.apache.eventmesh.common.remote.response.QueryTaskMonitorResponse;
-import org.apache.eventmesh.common.remote.response.SimpleResponse;
-import org.apache.eventmesh.common.utils.JsonUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-
-import lombok.extern.slf4j.Slf4j;
-
-@RestController
-@RequestMapping("/eventmesh/admin")
-@Slf4j
-public class HttpServer {
-
- @Autowired
- private TaskBizService taskService;
-
- @Autowired
- private VerifyBizService verifyService;
-
- @Autowired
- private MonitorBizService monitorService;
-
- @Autowired
- private EventMeshTaskInfoService taskInfoService;
-
- @RequestMapping(value = "/createTask", method = RequestMethod.POST)
- public String createOrUpdateTask(@RequestBody CreateTaskRequest task) {
- log.info("receive http proto create task:{}", task);
- CreateTaskResponse createTaskResponse = taskService.createTask(task);
- log.info("receive http proto create task result:{}", createTaskResponse);
- SimpleResponse simpleResponse = new SimpleResponse();
- simpleResponse.setData(createTaskResponse);
- return JsonUtils.toJSONString(simpleResponse);
- }
-
-
- @RequestMapping(value = "/reportVerify", method = RequestMethod.POST)
- public String reportVerify(@RequestBody ReportVerifyRequest request) {
- log.info("receive http proto report verify request:{}", request);
- boolean result = verifyService.reportVerifyRecord(request);
- log.info("receive http proto report verify result:{}", result);
- SimpleResponse simpleResponse = new SimpleResponse();
- simpleResponse.setData(result);
- return JsonUtils.toJSONString(simpleResponse);
- }
-
- @RequestMapping(value = "/reportMonitor", method = RequestMethod.POST)
- public String reportMonitor(@RequestBody ReportMonitorRequest request) {
- log.info("receive http proto report monitor request:{}", request);
- boolean result = monitorService.reportMonitorRecord(request);
- log.info("receive http proto report monitor result:{}", result);
- SimpleResponse simpleResponse = new SimpleResponse();
- simpleResponse.setData(result);
- return JsonUtils.toJSONString(simpleResponse);
- }
-
- @RequestMapping(value = "/queryTaskMonitor", method = RequestMethod.POST)
- public String queryTaskMonitor(@RequestBody QueryTaskMonitorRequest request) {
- log.info("receive http proto query task monitor request:{}", request);
- QueryTaskMonitorResponse result = monitorService.queryTaskMonitors(request);
- log.info("receive http proto query task monitor result:{}", result);
- SimpleResponse simpleResponse = new SimpleResponse();
- simpleResponse.setData(result);
- return JsonUtils.toJSONString(simpleResponse);
- }
-
- @RequestMapping(value = "/queryTaskInfo", method = RequestMethod.POST)
- public HttpResponseResult queryTaskInfo(@RequestBody QueryTaskInfoRequest taskInfoRequest) {
- log.info("receive http query task info request:{}", taskInfoRequest);
- List taskInfosResponse = taskService.queryTaskInfo(taskInfoRequest);
- log.info("receive http query task info taskInfosResponse:{}", taskInfoRequest);
- if (taskInfosResponse.isEmpty()) {
- return HttpResponseResult.failed("NOT FOUND");
- }
- return HttpResponseResult.success(taskInfosResponse);
- }
-
- @RequestMapping(value = "/deleteTask", method = RequestMethod.DELETE)
- public HttpResponseResult deleteTask(@RequestBody TaskIDRequest taskIDRequest) {
- log.info("receive need to delete taskID:{}", taskIDRequest.getTaskID());
- boolean result = taskService.deleteTaskByTaskID(taskIDRequest);
- if (result) {
- return HttpResponseResult.success();
- } else {
- return HttpResponseResult.failed();
- }
- }
-
- @RequestMapping(value = "/startTask", method = RequestMethod.POST)
- public HttpResponseResult startTask(@RequestBody TaskIDRequest taskIDRequest) {
- log.info("receive start task ID:{}", taskIDRequest.getTaskID());
- taskService.startTask(taskIDRequest);
- return HttpResponseResult.success();
- }
-
- @RequestMapping(value = "/restartTask", method = RequestMethod.POST)
- public HttpResponseResult restartTask(@RequestBody TaskIDRequest taskIDRequest) {
- log.info("receive restart task ID:{}", taskIDRequest.getTaskID());
- taskService.restartTask(taskIDRequest);
- return HttpResponseResult.success();
- }
-
- @RequestMapping(value = "/stopTask", method = RequestMethod.POST)
- public HttpResponseResult stopTask(@RequestBody TaskIDRequest taskIDRequest) {
- log.info("receive stop task ID:{}", taskIDRequest.getTaskID());
- taskService.stopTask(taskIDRequest);
- return HttpResponseResult.success();
- }
-
- @RequestMapping(value = "/restartBatch", method = RequestMethod.POST)
- public HttpResponseResult restartBatch(@RequestBody List taskBachRequestList) {
- log.info("receive restart batch task IDs:{}", taskBachRequestList);
- List errorNames = new ArrayList<>();
- taskService.restartBatchTask(taskBachRequestList, errorNames);
- if (!errorNames.isEmpty()) {
- return HttpResponseResult.failed(errorNames);
- }
- return HttpResponseResult.success();
- }
-
- @RequestMapping(value = "stopBatch", method = RequestMethod.POST)
- public HttpResponseResult stopBatch(@RequestBody List taskBachRequestList) {
- log.info("receive stop batch task IDs:{}", taskBachRequestList);
- List errorNames = new ArrayList<>();
- taskService.stopBatchTask(taskBachRequestList, errorNames);
- if (!errorNames.isEmpty()) {
- return HttpResponseResult.failed(errorNames);
- }
- return HttpResponseResult.success();
- }
-
- @RequestMapping(value = "/startBatch", method = RequestMethod.POST)
- public HttpResponseResult startBatch(@RequestBody List taskBachRequestList) {
- log.info("receive start batch task IDs:{}", taskBachRequestList);
- List errorNames = new ArrayList<>();
- taskService.startBatchTask(taskBachRequestList, errorNames);
- if (!errorNames.isEmpty()) {
- return HttpResponseResult.failed(errorNames);
- }
- return HttpResponseResult.success();
- }
-
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/Request.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/Request.java
deleted file mode 100644
index 9484e986f1..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/Request.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web;
-
-public class Request {
- private String uid;
- private T data;
-
- public String getUid() {
- return uid;
- }
-
- public void setUid(String uid) {
- this.uid = uid;
- }
-
- public T getData() {
- return data;
- }
-
- public void setData(T data) {
- this.data = data;
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/Response.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/Response.java
deleted file mode 100644
index d573c3bac4..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/Response.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web;
-
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-
-import lombok.Getter;
-import lombok.Setter;
-
-@Getter
-@Setter
-public class Response {
-
- private int code;
-
- private boolean success;
-
- private String desc;
-
- private T data;
-
- public static Response success() {
- Response response = new Response<>();
- response.success = true;
- response.code = ErrorCode.SUCCESS;
- return response;
- }
-
- public static Response success(T data) {
- Response response = new Response<>();
- response.success = true;
- response.data = data;
- return response;
- }
-
- public static Response fail(int code, String desc) {
- Response response = new Response<>();
- response.success = false;
- response.code = code;
- response.desc = desc;
- return response;
- }
-
- public static Response fail(int code, String desc, T data) {
- Response response = new Response<>();
- response.success = false;
- response.code = code;
- response.desc = desc;
- response.data = data;
- return response;
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/ServerController.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/ServerController.java
deleted file mode 100644
index 5623cbad33..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/ServerController.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web;
-
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-@RequestMapping("/eventmesh/admin")
-public class ServerController {
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/config/MybatisPlusConfig.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/config/MybatisPlusConfig.java
deleted file mode 100644
index 15d362bcd0..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/config/MybatisPlusConfig.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.config;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import com.baomidou.mybatisplus.annotation.DbType;
-import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
-import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
-
-@Configuration
-public class MybatisPlusConfig {
-
- @Bean
- public MybatisPlusInterceptor paginationInterceptor() {
- MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
- PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
- pageInterceptor.setMaxLimit(500L);
- interceptor.addInnerInterceptor(pageInterceptor);
- return interceptor;
- }
-
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/DBThreadPool.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/DBThreadPool.java
deleted file mode 100644
index 277ea66656..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/DBThreadPool.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db;
-
-import org.apache.eventmesh.common.EventMeshThreadFactory;
-
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.PreDestroy;
-
-import org.springframework.stereotype.Component;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Component
-@Slf4j
-public class DBThreadPool {
-
- private final ThreadPoolExecutor executor =
- new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors() * 2,
- Runtime.getRuntime().availableProcessors() * 2, 0L, TimeUnit.SECONDS,
- new LinkedBlockingQueue<>(1000), new EventMeshThreadFactory("admin-server-db"),
- new ThreadPoolExecutor.DiscardOldestPolicy());
-
-
- private final ScheduledThreadPoolExecutor checkScheduledExecutor =
- new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), new EventMeshThreadFactory("admin-server-check-scheduled"),
- new ThreadPoolExecutor.DiscardOldestPolicy());
-
- @PreDestroy
- private void destroy() {
- if (!executor.isShutdown()) {
- try {
- executor.shutdown();
- if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
- log.info("wait handler thread pool shutdown timeout, it will shutdown immediately");
- executor.shutdownNow();
- }
- } catch (InterruptedException e) {
- log.warn("wait handler thread pool shutdown fail");
- }
- }
-
- if (!checkScheduledExecutor.isShutdown()) {
- try {
- checkScheduledExecutor.shutdown();
- if (!checkScheduledExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
- log.info("wait scheduled thread pool shutdown timeout, it will shutdown immediately");
- checkScheduledExecutor.shutdownNow();
- }
- } catch (InterruptedException e) {
- log.warn("wait scheduled thread pool shutdown fail");
- }
- }
- }
-
- public ThreadPoolExecutor getExecutors() {
- return executor;
- }
-
- public ScheduledThreadPoolExecutor getCheckExecutor() {
- return checkScheduledExecutor;
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/DruidDataSource.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/DruidDataSource.java
deleted file mode 100644
index fb26d44d30..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/DruidDataSource.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db;
-
-
-import org.apache.eventmesh.admin.server.web.utils.EncryptUtil;
-import org.apache.eventmesh.admin.server.web.utils.ParamType;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.io.IOException;
-import java.sql.SQLException;
-
-import javax.sql.DataSource;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Primary;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Configuration
-@ComponentScan
-@Slf4j
-public class DruidDataSource {
-
- @Value("${spring.datasource.url}")
- private String dbUrl;
-
- @Value("${spring.datasource.username}")
- private String username;
-
- @Value("${spring.datasource.password}")
- private String password;
-
- @Value("${spring.datasource.driver-class-name}")
- private String driverClassName;
-
- @Value("${spring.datasource.initialSize}")
- private int initialSize;
-
- @Value("${spring.datasource.minIdle}")
- private int minIdle;
-
- @Value("${spring.datasource.maxActive}")
- private int maxActive;
-
- @Value("${spring.datasource.maxWait}")
- private int maxWait;
-
- @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
- private int timeBetweenEvictionRunsMillis;
-
- @Value("${spring.datasource.minEvictableIdleTimeMillis}")
- private int minEvictableIdleTimeMillis;
-
- @Value("${spring.datasource.validationQuery}")
- private String validationQuery;
-
- @Value("${spring.datasource.testWhileIdle}")
- private boolean testWhileIdle;
-
- @Value("${spring.datasource.testOnBorrow}")
- private boolean testOnBorrow;
-
- @Value("${spring.datasource.testOnReturn}")
- private boolean testOnReturn;
-
- @Value("${spring.datasource.poolPreparedStatements}")
- private boolean poolPreparedStatements;
-
- @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
- private int maxPoolPreparedStatementPerConnectionSize;
-
- @Value("${spring.datasource.filters}")
- private String filters;
-
- @Value("{spring.datasource.connectionProperties}")
- private String connectionProperties;
-
- @Value("${sysPubKey}")
- private String sysPubKeyStr;
-
- @Value("${appPrivKey}")
- private String appPrivKeyStr;
-
-
- @Bean
- @Primary
- public DataSource dataSource() throws Exception {
- try (com.alibaba.druid.pool.DruidDataSource datasource = new com.alibaba.druid.pool.DruidDataSource()) {
- datasource.setUrl(this.dbUrl);
- datasource.setUsername(username);
- datasource.setPassword(rsaDecrypt(sysPubKeyStr, appPrivKeyStr, password));
- datasource.setDriverClassName(driverClassName);
- datasource.setInitialSize(initialSize);
- datasource.setMinIdle(minIdle);
- datasource.setMaxActive(maxActive);
- datasource.setMaxWait(maxWait);
- datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
- datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
- datasource.setValidationQuery(validationQuery);
- datasource.setTestWhileIdle(testWhileIdle);
- datasource.setTestOnBorrow(testOnBorrow);
- datasource.setTestOnReturn(testOnReturn);
- datasource.setPoolPreparedStatements(poolPreparedStatements);
- datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
- try {
- datasource.setFilters(filters);
- } catch (SQLException e) {
- log.error("druid configuration initialization filter", e);
- }
- datasource.setConnectionProperties(connectionProperties);
-
- return datasource;
- }
- }
-
- public static String rsaDecrypt(String sysPubKeyStr, String appPrivKeyStr, String encrtyptText) throws IOException {
- if (StringUtils.isNotBlank(encrtyptText) && encrtyptText.length() > "{RSA}".length() && encrtyptText.startsWith("{RSA}")) {
- String text = encrtyptText.startsWith("{RSA}") ? encrtyptText.substring("{RSA}".length()) : encrtyptText;
-
- try {
- return EncryptUtil.decrypt(ParamType.STRING, sysPubKeyStr, ParamType.STRING, appPrivKeyStr, ParamType.STRING, text);
- } catch (Exception e) {
- throw new RuntimeException("decrypt error", e);
- }
- } else {
- return encrtyptText;
- }
- }
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshDataSource.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshDataSource.java
deleted file mode 100644
index e6e328984c..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshDataSource.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.entity;
-
-import java.io.Serializable;
-import java.util.Date;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-
-import lombok.Data;
-
-/**
- * TableName event_mesh_data_source
- */
-@TableName(value = "event_mesh_data_source")
-@Data
-public class EventMeshDataSource implements Serializable {
- @TableId(type = IdType.AUTO)
- private Integer id;
-
- private String dataType;
-
- private String description;
-
- private String configuration;
-
- private String configurationClass;
-
- private String region;
-
- private String createUid;
-
- private String updateUid;
-
- private Date createTime;
-
- private Date updateTime;
-
- private static final long serialVersionUID = 1L;
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshJobInfo.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshJobInfo.java
deleted file mode 100644
index a77eaaaca2..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshJobInfo.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.entity;
-
-import java.io.Serializable;
-import java.util.Date;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-
-import lombok.Data;
-
-/**
- * TableName event_mesh_job_info
- */
-@TableName(value = "event_mesh_job_info")
-@Data
-public class EventMeshJobInfo implements Serializable {
- @TableId(type = IdType.AUTO)
- private Integer id;
-
- private String jobID;
-
- private String jobDesc;
-
- private String taskID;
-
- private String transportType;
-
- private Integer sourceData;
-
- private Integer targetData;
-
- private String jobState;
-
- private String jobType;
-
- // job request from region
- private String fromRegion;
-
- // job actually running region
- private String runningRegion;
-
- private String createUid;
-
- private String updateUid;
-
- private Date createTime;
-
- private Date updateTime;
-
- private static final long serialVersionUID = 1L;
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshMonitor.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshMonitor.java
deleted file mode 100644
index 0507464b5b..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshMonitor.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.entity;
-
-import java.io.Serializable;
-import java.util.Date;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-
-import lombok.Data;
-
-/**
- * TableName event_mesh_monitor
- */
-@TableName(value = "event_mesh_monitor")
-@Data
-public class EventMeshMonitor implements Serializable {
-
- @TableId(type = IdType.AUTO)
- private Integer id;
-
- private String taskID;
- private String jobID;
- private String address;
- private String transportType;
- private String connectorStage;
- private Long totalReqNum;
- private Long totalTimeCost;
- private Long maxTimeCost;
- private Long avgTimeCost;
- private Double tps;
- private Date createTime;
-
- private static final long serialVersionUID = 1L;
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshMysqlPosition.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshMysqlPosition.java
deleted file mode 100644
index 5e5d5745c1..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshMysqlPosition.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.entity;
-
-import java.io.Serializable;
-import java.util.Date;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-
-import lombok.Data;
-
-/**
- * TableName event_mesh_mysql_position
- */
-@TableName(value = "event_mesh_mysql_position")
-@Data
-public class EventMeshMysqlPosition implements Serializable {
- @TableId(type = IdType.AUTO)
- private Integer id;
-
- private String jobID;
-
- private String serverUUID;
-
- private String address;
-
- private Long position;
-
- private String gtid;
-
- private String currentGtid;
-
- private Long timestamp;
-
- private String journalName;
-
- private Date createTime;
-
- private Date updateTime;
-
- private static final long serialVersionUID = 1L;
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshPositionReporterHistory.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshPositionReporterHistory.java
deleted file mode 100644
index 8518c38918..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshPositionReporterHistory.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.entity;
-
-import java.io.Serializable;
-import java.util.Date;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-
-import lombok.Data;
-
-/**
- * TableName event_mesh_position_reporter_history
- */
-@TableName(value = "event_mesh_position_reporter_history")
-@Data
-public class EventMeshPositionReporterHistory implements Serializable {
- @TableId(type = IdType.AUTO)
- private Long id;
-
- private String job;
-
- private String record;
-
- private String address;
-
- private Date createTime;
-
- private static final long serialVersionUID = 1L;
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshRuntimeHeartbeat.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshRuntimeHeartbeat.java
deleted file mode 100644
index 95e6c5e261..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshRuntimeHeartbeat.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.entity;
-
-import java.io.Serializable;
-import java.util.Date;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-
-import lombok.Data;
-
-/**
- * TableName event_mesh_runtime_heartbeat
- */
-@TableName(value = "event_mesh_runtime_heartbeat")
-@Data
-public class EventMeshRuntimeHeartbeat implements Serializable {
- @TableId(type = IdType.AUTO)
- private Long id;
-
- private String adminAddr;
-
- private String runtimeAddr;
-
- private String jobID;
-
- private String reportTime;
-
- private Date updateTime;
-
- private Date createTime;
-
- private static final long serialVersionUID = 1L;
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshRuntimeHistory.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshRuntimeHistory.java
deleted file mode 100644
index ea7e10cbad..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshRuntimeHistory.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.entity;
-
-import java.io.Serializable;
-import java.util.Date;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-
-import lombok.Data;
-
-/**
- * TableName event_mesh_runtime_history
- */
-@TableName(value = "event_mesh_runtime_history")
-@Data
-public class EventMeshRuntimeHistory implements Serializable {
- @TableId(type = IdType.AUTO)
- private Long id;
-
- private String job;
-
- private String address;
-
- private Date createTime;
-
- private static final long serialVersionUID = 1L;
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshTaskInfo.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshTaskInfo.java
deleted file mode 100644
index 2d40f4a082..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshTaskInfo.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.entity;
-
-import java.io.Serializable;
-import java.util.Date;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-
-import lombok.Data;
-
-/**
- * TableName event_mesh_task_info
- */
-@TableName(value = "event_mesh_task_info")
-@Data
-public class EventMeshTaskInfo implements Serializable {
- @TableId(type = IdType.AUTO)
- private Integer id;
-
- private String taskID;
-
- private String taskName;
-
- private String taskDesc;
-
- private String taskState;
-
- private String sourceRegion;
-
- private String targetRegion;
-
- private String createUid;
-
- private String updateUid;
-
- private Date createTime;
-
- private Date updateTime;
-
- private static final long serialVersionUID = 1L;
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshVerify.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshVerify.java
deleted file mode 100644
index c5a6c35f8d..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshVerify.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.entity;
-
-import java.io.Serializable;
-import java.util.Date;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-
-import lombok.Data;
-
-/**
- * TableName event_mesh_verify
- */
-@TableName(value = "event_mesh_verify")
-@Data
-public class EventMeshVerify implements Serializable {
-
- @TableId(type = IdType.AUTO)
- private Integer id;
-
- private String taskID;
-
- private String jobID;
-
- private String recordID;
-
- private String recordSig;
-
- private String connectorName;
-
- private String connectorStage;
-
- private String position;
-
- private Date createTime;
-
- private static final long serialVersionUID = 1L;
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshWeredisPosition.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshWeredisPosition.java
deleted file mode 100644
index 2117230826..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/entity/EventMeshWeredisPosition.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.entity;
-
-import java.io.Serializable;
-import java.util.Date;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-
-import lombok.Data;
-
-/**
- * TableName event_mesh_weredis_position
- */
-@TableName(value = "event_mesh_weredis_position")
-@Data
-public class EventMeshWeredisPosition implements Serializable {
- @TableId(type = IdType.AUTO)
- private Integer id;
-
- private String jobID;
-
- // connection run address
- private String address;
-
- private String clusterName;
-
- private String partitionName;
-
- private String masterReplid;
-
- //weredis run host
- private String host;
-
- private Long replOffset = -1L;
-
- private Date createTime;
-
- private Date updateTime;
-
- private static final long serialVersionUID = 1L;
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshDataSourceMapper.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshDataSourceMapper.java
deleted file mode 100644
index c59e28428f..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshDataSourceMapper.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.mapper;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshDataSource;
-
-import org.apache.ibatis.annotations.Mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-/**
- * for table 'event_mesh_data_source' db operation
- * 2024-05-09 15:52:49
- * entity.db.web.server.admin.eventmesh.apache.org.EventMeshDataSource
- */
-@Mapper
-public interface EventMeshDataSourceMapper extends BaseMapper {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshJobInfoExtMapper.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshJobInfoExtMapper.java
deleted file mode 100644
index c04c4e3748..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshJobInfoExtMapper.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.mapper;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo;
-
-import org.apache.ibatis.annotations.Insert;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Param;
-
-import java.util.List;
-
-import org.springframework.transaction.annotation.Transactional;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-/**
- * etx operator for table event_mesh_job_info
- */
-@Mapper
-public interface EventMeshJobInfoExtMapper extends BaseMapper {
-
- @Insert("")
- @Transactional(rollbackFor = Exception.class)
- int saveBatch(@Param("jobs") List jobInfoList);
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshJobInfoMapper.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshJobInfoMapper.java
deleted file mode 100644
index 39f8a4aed6..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshJobInfoMapper.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.mapper;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo;
-
-import org.apache.ibatis.annotations.Mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-/**
- * for table 'event_mesh_job_info' db operation
- * entity org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo
- */
-@Mapper
-public interface EventMeshJobInfoMapper extends BaseMapper {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshMonitorMapper.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshMonitorMapper.java
deleted file mode 100644
index db77224637..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshMonitorMapper.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.mapper;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshMonitor;
-
-import org.apache.ibatis.annotations.Mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-/**
- * event_mesh_monitor
- * Entity org.apache.eventmesh.admin.server.web.db.entity.EventMeshMonitor
- */
-@Mapper
-public interface EventMeshMonitorMapper extends BaseMapper {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshMysqlPositionMapper.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshMysqlPositionMapper.java
deleted file mode 100644
index f0a0467d76..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshMysqlPositionMapper.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.mapper;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshMysqlPosition;
-
-import org.apache.ibatis.annotations.Mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-/**
- * for table `event_mesh_mysql_position` db operation
- * 2024-05-14 17:15:03
- * entity.db.web.server.admin.eventmesh.apache.org.EventMeshMysqlPosition
- */
-@Mapper
-public interface EventMeshMysqlPositionMapper extends BaseMapper {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshPositionReporterHistoryMapper.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshPositionReporterHistoryMapper.java
deleted file mode 100644
index adc6723b79..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshPositionReporterHistoryMapper.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.mapper;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshPositionReporterHistory;
-
-import org.apache.ibatis.annotations.Mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-/**
- * for table 'event_mesh_position_reporter_history' db operation
- * 2024-05-14 17:15:03
- * entity.db.web.server.admin.eventmesh.apache.org.EventMeshPositionReporterHistory
- */
-@Mapper
-public interface EventMeshPositionReporterHistoryMapper extends BaseMapper {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshRuntimeHeartbeatMapper.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshRuntimeHeartbeatMapper.java
deleted file mode 100644
index 813769d2ab..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshRuntimeHeartbeatMapper.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.mapper;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshRuntimeHeartbeat;
-
-import org.apache.ibatis.annotations.Mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-/**
- * for table 'event_mesh_runtime_heartbeat' db operation
- * 2024-05-14 17:15:03
- * entity.db.web.server.admin.eventmesh.apache.org.EventMeshRuntimeHeartbeat
- */
-@Mapper
-public interface EventMeshRuntimeHeartbeatMapper extends BaseMapper {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshRuntimeHistoryMapper.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshRuntimeHistoryMapper.java
deleted file mode 100644
index 5fc3a21f54..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshRuntimeHistoryMapper.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.mapper;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshRuntimeHistory;
-
-import org.apache.ibatis.annotations.Mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-/**
- * for table 'event_mesh_runtime_history' db operation
- * 2024-05-14 17:15:03
- * entity.db.web.server.admin.eventmesh.apache.org.EventMeshRuntimeHistory
- */
-@Mapper
-public interface EventMeshRuntimeHistoryMapper extends BaseMapper {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshTaskInfoMapper.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshTaskInfoMapper.java
deleted file mode 100644
index d1d472b8c4..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshTaskInfoMapper.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.mapper;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshTaskInfo;
-
-import org.apache.ibatis.annotations.Mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-/**
- * event_mesh_task_info
- * Entity org.apache.eventmesh.admin.server.web.db.entity.EventMeshTaskInfo
- */
-@Mapper
-public interface EventMeshTaskInfoMapper extends BaseMapper {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshVerifyMapper.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshVerifyMapper.java
deleted file mode 100644
index b444d1e4b4..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/mapper/EventMeshVerifyMapper.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.mapper;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshVerify;
-
-import org.apache.ibatis.annotations.Mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-/**
- * event_mesh_verify
- * Entity org.apache.eventmesh.admin.server.web.db.entity.EventMeshVerify
- */
-@Mapper
-public interface EventMeshVerifyMapper extends BaseMapper {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshDataSourceService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshDataSourceService.java
deleted file mode 100644
index 29e2b8122e..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshDataSourceService.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshDataSource;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-
-/**
-*
-* for table 'event_mesh_data_source' db operation
-* 2024-05-09 15:52:49
-*/
-public interface EventMeshDataSourceService extends IService {
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshJobInfoExtService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshJobInfoExtService.java
deleted file mode 100644
index 22fc5ae299..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshJobInfoExtService.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo;
-
-import java.util.List;
-
-/**
- * ext operator for table event_mesh_job
- */
-public interface EventMeshJobInfoExtService {
- int batchSave(List jobs);
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshJobInfoService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshJobInfoService.java
deleted file mode 100644
index 572e451ceb..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshJobInfoService.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-
-/**
- * event_mesh_job_info
- */
-public interface EventMeshJobInfoService extends IService {
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshMonitorService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshMonitorService.java
deleted file mode 100644
index 4180f82a97..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshMonitorService.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshMonitor;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-
-/**
- * event_mesh_monitor
- */
-public interface EventMeshMonitorService extends IService {
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshMysqlPositionService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshMysqlPositionService.java
deleted file mode 100644
index 2bf34ab922..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshMysqlPositionService.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshMysqlPosition;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-
-/**
-* for table 'event_mesh_mysql_position' db operation
-* 2024-05-14 17:15:03
-*/
-public interface EventMeshMysqlPositionService extends IService {
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshPositionReporterHistoryService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshPositionReporterHistoryService.java
deleted file mode 100644
index 976eec94ea..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshPositionReporterHistoryService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshPositionReporterHistory;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-
-/**
-* for table 'event_mesh_position_reporter_history' db operation
-* 2024-05-14 17:15:03
-*/
-public interface EventMeshPositionReporterHistoryService extends IService {
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshRuntimeHeartbeatService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshRuntimeHeartbeatService.java
deleted file mode 100644
index f0e5d1c61b..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshRuntimeHeartbeatService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshRuntimeHeartbeat;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-
-/**
- * for table 'event_mesh_runtime_heartbeat' db operation
- * 2024-05-14 17:15:03
- */
-public interface EventMeshRuntimeHeartbeatService extends IService {
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshRuntimeHistoryService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshRuntimeHistoryService.java
deleted file mode 100644
index 1f6db0e12e..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshRuntimeHistoryService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshRuntimeHistory;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-
-/**
- * for table 'event_mesh_runtime_history' db operation
- * 2024-05-14 17:15:03
- */
-public interface EventMeshRuntimeHistoryService extends IService {
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshTaskInfoService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshTaskInfoService.java
deleted file mode 100644
index 04da6a7952..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshTaskInfoService.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshTaskInfo;
-import org.apache.eventmesh.common.remote.request.QueryTaskInfoRequest;
-import org.apache.eventmesh.common.remote.response.QueryTaskInfoResponse;
-
-import java.util.List;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-
-/**
- * event_mesh_task_info
- */
-public interface EventMeshTaskInfoService extends IService {
-
- List queryTaskInfo(QueryTaskInfoRequest taskInfoRequest);
-
- // boolean deleteTaskByTaskID(String taskID);
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshVerifyService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshVerifyService.java
deleted file mode 100644
index 97f2d7268e..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/EventMeshVerifyService.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshVerify;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-
-/**
- * event_mesh_verify
- */
-public interface EventMeshVerifyService extends IService {
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshDataSourceServiceImpl.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshDataSourceServiceImpl.java
deleted file mode 100644
index f703425c94..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshDataSourceServiceImpl.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service.impl;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshDataSource;
-import org.apache.eventmesh.admin.server.web.db.mapper.EventMeshDataSourceMapper;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshDataSourceService;
-
-import org.springframework.stereotype.Service;
-
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-
-/**
- * for table 'event_mesh_data_source' db operation
- * 2024-05-09 15:52:49
- */
-@Service
-public class EventMeshDataSourceServiceImpl extends ServiceImpl
- implements EventMeshDataSourceService {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshJobInfoExtServiceImpl.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshJobInfoExtServiceImpl.java
deleted file mode 100644
index 6cf0ebf6b2..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshJobInfoExtServiceImpl.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service.impl;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo;
-import org.apache.eventmesh.admin.server.web.db.mapper.EventMeshJobInfoExtMapper;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshJobInfoExtService;
-
-import java.util.List;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-@Service
-public class EventMeshJobInfoExtServiceImpl implements EventMeshJobInfoExtService {
- @Autowired
- EventMeshJobInfoExtMapper mapper;
-
- @Override
- public int batchSave(List jobs) {
- return mapper.saveBatch(jobs);
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshJobInfoServiceImpl.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshJobInfoServiceImpl.java
deleted file mode 100644
index 4613e0809d..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshJobInfoServiceImpl.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service.impl;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo;
-import org.apache.eventmesh.admin.server.web.db.mapper.EventMeshJobInfoMapper;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshJobInfoService;
-
-import org.springframework.stereotype.Service;
-
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-
-/**
- * event_mesh_job_info
- */
-@Service
-public class EventMeshJobInfoServiceImpl extends ServiceImpl
- implements EventMeshJobInfoService {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshMonitorServiceImpl.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshMonitorServiceImpl.java
deleted file mode 100644
index ebb4220000..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshMonitorServiceImpl.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service.impl;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshMonitor;
-import org.apache.eventmesh.admin.server.web.db.mapper.EventMeshMonitorMapper;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshMonitorService;
-
-import org.springframework.stereotype.Service;
-
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-
-/**
- * event_mesh_monitor
- */
-@Service
-public class EventMeshMonitorServiceImpl extends ServiceImpl
- implements EventMeshMonitorService {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshMysqlPositionServiceImpl.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshMysqlPositionServiceImpl.java
deleted file mode 100644
index 353443b78d..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshMysqlPositionServiceImpl.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service.impl;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshMysqlPosition;
-import org.apache.eventmesh.admin.server.web.db.mapper.EventMeshMysqlPositionMapper;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshMysqlPositionService;
-
-import org.springframework.stereotype.Service;
-
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * for table 'event_mesh_mysql_position' db operation
- * 2024-05-14 17:15:03
- */
-@Service
-@Slf4j
-public class EventMeshMysqlPositionServiceImpl extends ServiceImpl
- implements EventMeshMysqlPositionService {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshPositionReporterHistoryServiceImpl.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshPositionReporterHistoryServiceImpl.java
deleted file mode 100644
index d546e412ba..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshPositionReporterHistoryServiceImpl.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service.impl;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshPositionReporterHistory;
-import org.apache.eventmesh.admin.server.web.db.mapper.EventMeshPositionReporterHistoryMapper;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshPositionReporterHistoryService;
-
-import org.springframework.stereotype.Service;
-
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-
-/**
- * for table 'event_mesh_position_reporter_history' db operation
- * 2024-05-14 17:15:03
- */
-@Service
-public class EventMeshPositionReporterHistoryServiceImpl extends ServiceImpl
- implements EventMeshPositionReporterHistoryService {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshRuntimeHeartbeatServiceImpl.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshRuntimeHeartbeatServiceImpl.java
deleted file mode 100644
index 452569e3a6..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshRuntimeHeartbeatServiceImpl.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service.impl;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshRuntimeHeartbeat;
-import org.apache.eventmesh.admin.server.web.db.mapper.EventMeshRuntimeHeartbeatMapper;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshRuntimeHeartbeatService;
-
-import org.springframework.stereotype.Service;
-
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * for table 'event_mesh_runtime_heartbeat' db operation
- * 2024-05-14 17:15:03
- */
-@Service
-@Slf4j
-public class EventMeshRuntimeHeartbeatServiceImpl extends ServiceImpl
- implements EventMeshRuntimeHeartbeatService {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshRuntimeHistoryServiceImpl.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshRuntimeHistoryServiceImpl.java
deleted file mode 100644
index d39e868ce5..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshRuntimeHistoryServiceImpl.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service.impl;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshRuntimeHistory;
-import org.apache.eventmesh.admin.server.web.db.mapper.EventMeshRuntimeHistoryMapper;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshRuntimeHistoryService;
-
-import org.springframework.stereotype.Service;
-
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-
-/**
- * for table 'event_mesh_runtime_history' db operation
- * 2024-05-14 17:15:03
- */
-@Service
-public class EventMeshRuntimeHistoryServiceImpl extends ServiceImpl
- implements EventMeshRuntimeHistoryService {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshTaskInfoServiceImpl.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshTaskInfoServiceImpl.java
deleted file mode 100644
index 91acb51a76..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshTaskInfoServiceImpl.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service.impl;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshDataSource;
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo;
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshMysqlPosition;
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshTaskInfo;
-import org.apache.eventmesh.admin.server.web.db.mapper.EventMeshTaskInfoMapper;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshDataSourceService;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshJobInfoService;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshMysqlPositionService;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshTaskInfoService;
-import org.apache.eventmesh.common.remote.JobState;
-import org.apache.eventmesh.common.remote.TaskState;
-import org.apache.eventmesh.common.remote.request.QueryTaskInfoRequest;
-import org.apache.eventmesh.common.remote.response.QueryTaskInfoResponse;
-
-import org.apache.commons.lang3.ObjectUtils;
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * event_mesh_task_info
- */
-@Slf4j
-@Service
-public class EventMeshTaskInfoServiceImpl extends ServiceImpl
- implements EventMeshTaskInfoService {
-
- @Autowired
- private EventMeshTaskInfoMapper taskInfoMapper;
-
- @Autowired
- private EventMeshJobInfoService jobInfoService;
-
- @Autowired
- private EventMeshDataSourceService dataSourceService;
-
- @Autowired
- private EventMeshMysqlPositionService mysqlPositionService;
-
- @Override
- public List queryTaskInfo(QueryTaskInfoRequest taskInfoRequest) {
-
- log.info("receive query task info request:{}", taskInfoRequest);
-
- List queryTaskInfoResponseList = new ArrayList<>();
-
- Integer currentPage = taskInfoRequest.getCurrentPage();
- Integer pageSize = taskInfoRequest.getPageSize();
-
- // query by page
- if (StringUtils.isEmpty(taskInfoRequest.getTaskID())
- && currentPage != null
- && pageSize != null) {
-
- Page page = new Page<>();
- page.setCurrent(currentPage);
- page.setSize(pageSize);
- List eventMeshTaskInfoList = taskInfoMapper.selectPage(page, Wrappers.query()
- .ne("taskState", TaskState.DELETE.name())).getRecords();
- queryTaskInfoResponseList = getQueryTaskInfoResponses(eventMeshTaskInfoList, queryTaskInfoResponseList);
-
- }
-
- if (StringUtils.isNotEmpty(taskInfoRequest.getTaskID()) || StringUtils.isNotEmpty(taskInfoRequest.getTaskID())) {
- queryTaskInfoResponseList = eventMeshTaskInfoList(taskInfoRequest);
- }
-
- // if (StringUtils.isNotEmpty(taskInfoRequest.getJobType())) {
- //
- // }
- //
- // if (StringUtils.isNotEmpty(taskInfoRequest.getSourceDataID())) {
- //
- // }
- //
- // if (StringUtils.isNotEmpty(taskInfoRequest.getTargetDataID())) {
- //
- // }
- //
- // if (StringUtils.isNotEmpty(taskInfoRequest.getIp())) {
- //
- // }
- //
- // if (StringUtils.isNotEmpty(taskInfoRequest.getSourceTableName())) {
- //
- // }
- //
- // if (StringUtils.isNotEmpty(taskInfoRequest.getTaskMathID())) {
- //
- // }
-
- log.info("query event mesh task info response result:{}", queryTaskInfoResponseList);
-
- return queryTaskInfoResponseList;
- }
-
- @Transactional
- private List eventMeshTaskInfoList(QueryTaskInfoRequest taskInfoRequest) {
-
- List eventMeshTaskInfoList = new ArrayList<>();
-
- Page page = new Page<>();
- page.setCurrent(taskInfoRequest.getCurrentPage());
- page.setSize(taskInfoRequest.getPageSize());
-
- if (StringUtils.isNotEmpty(taskInfoRequest.getTaskID())) {
- eventMeshTaskInfoList = taskInfoMapper.selectPage(page, Wrappers.query()
- .eq("taskID", taskInfoRequest.getTaskID())
- .ne("taskState", TaskState.DELETE.name()))
- .getRecords();
- }
-
- if (StringUtils.isNotEmpty(taskInfoRequest.getTaskDesc())) {
- eventMeshTaskInfoList = taskInfoMapper.selectPage(page, Wrappers.query()
- .like("taskDesc", taskInfoRequest.getTaskDesc())
- .ne("jobState", JobState.DELETE.name()))
- .getRecords();
- }
-
- List eventMeshTaskInfos = new ArrayList<>();
-
- List queryTaskInfoResponse = getQueryTaskInfoResponses(eventMeshTaskInfoList, eventMeshTaskInfos);
- log.info("query task info result queryTaskInfoResponse:{}", queryTaskInfoResponse);
-
- return queryTaskInfoResponse;
- }
-
- private List getQueryTaskInfoResponses(List eventMeshTaskInfoList,
- List eventMeshTaskInfos) {
-
- for (EventMeshTaskInfo meshTaskInfo : eventMeshTaskInfoList) {
- QueryTaskInfoResponse eventMeshTaskInfo = initEventMeshTaskInfo(meshTaskInfo);
- eventMeshTaskInfos.add(eventMeshTaskInfo);
- }
-
- if (!eventMeshTaskInfoList.isEmpty()) {
- List eventMeshJobInfoList = new ArrayList<>();
- for (QueryTaskInfoResponse eventMeshTaskInfo : eventMeshTaskInfos) {
- List eventMeshJobInfos = jobInfoService.list(Wrappers.query()
- .eq("taskID", eventMeshTaskInfo.getTaskID())
- .ne("jobState", JobState.DELETE.name()));
-
- for (EventMeshJobInfo eventMeshJobInfo : eventMeshJobInfos) {
- QueryTaskInfoResponse.EventMeshJobInfo eventMeshJobInfoCovert = initEventMeshJobInfo(eventMeshJobInfo);
- eventMeshJobInfoList.add(eventMeshJobInfoCovert);
- }
-
- if (!eventMeshJobInfoList.isEmpty()) {
- for (QueryTaskInfoResponse.EventMeshJobInfo eventMeshJobInfo : eventMeshJobInfoList) {
- QueryTaskInfoResponse.EventMeshDataSource dataSource = covertEventMeshDataSource(
- querySourceOrSinkData(eventMeshJobInfo.getSourceData()));
- QueryTaskInfoResponse.EventMeshDataSource dataSink = covertEventMeshDataSource(
- querySourceOrSinkData(eventMeshJobInfo.getTargetData()));
-
- EventMeshMysqlPosition eventMeshMysqlPosition = mysqlPositionService.getOne(Wrappers.query().eq(
- "jobID",
- eventMeshJobInfo.getJobID()
- ));
-
-
- QueryTaskInfoResponse.EventMeshMysqlPosition mysqlPosition = covertEventMeshMysqlPosition(eventMeshMysqlPosition);
-
- eventMeshJobInfo.setEventMeshMysqlPosition(mysqlPosition);
- eventMeshJobInfo.setDataSource(dataSource);
- eventMeshJobInfo.setDataSink(dataSink);
- }
- }
-
- // set job info to same taskID
- eventMeshTaskInfo.setEventMeshJobInfoList(eventMeshJobInfoList);
- }
- }
-
- List queryTaskInfoResponse = new ArrayList<>();
- if (!eventMeshTaskInfos.isEmpty()) {
- queryTaskInfoResponse.addAll(eventMeshTaskInfos);
- }
-
- return queryTaskInfoResponse;
- }
-
- /**
- * QueryTaskInfoResponse.EventMeshDataSource covert
- *
- * @param eventMeshData EventMeshDataSource
- * @return meshData
- */
- private static QueryTaskInfoResponse.EventMeshDataSource covertEventMeshDataSource(EventMeshDataSource eventMeshData) {
- QueryTaskInfoResponse.EventMeshDataSource meshData = new QueryTaskInfoResponse.EventMeshDataSource();
- if (ObjectUtils.isEmpty(eventMeshData)) {
- return null;
- }
- meshData.setId(eventMeshData.getId());
- meshData.setDataType(eventMeshData.getDataType());
- meshData.setConfiguration(eventMeshData.getConfiguration());
- meshData.setConfigurationClass(eventMeshData.getConfigurationClass());
- meshData.setDescription(eventMeshData.getDescription());
- meshData.setRegion(eventMeshData.getRegion());
- meshData.setCreateUid(eventMeshData.getCreateUid());
- meshData.setUpdateUid(eventMeshData.getUpdateUid());
- meshData.setCreateTime(eventMeshData.getCreateTime());
- meshData.setUpdateTime(eventMeshData.getUpdateTime());
- return meshData;
- }
-
- /**
- * getSourceOrSinkData
- *
- * @param id id
- * @return EventMeshDataSource
- */
- private EventMeshDataSource querySourceOrSinkData(Integer id) {
- return dataSourceService.getOne(Wrappers.query().eq(
- "id",
- id));
- }
-
- /**
- * QueryTaskInfoResponse.EventMeshMysqlPosition
- *
- * @param mysqlPosition EventMeshMysqlPosition
- * @return position
- */
- private static QueryTaskInfoResponse.EventMeshMysqlPosition covertEventMeshMysqlPosition(EventMeshMysqlPosition mysqlPosition) {
- QueryTaskInfoResponse.EventMeshMysqlPosition position = new QueryTaskInfoResponse.EventMeshMysqlPosition();
- if (ObjectUtils.isEmpty(mysqlPosition)) {
- return null;
- }
- position.setId(mysqlPosition.getId());
- position.setJobID(mysqlPosition.getJobID());
- position.setServerUUID(mysqlPosition.getServerUUID());
- position.setAddress(mysqlPosition.getAddress());
- position.setPosition(mysqlPosition.getPosition());
- position.setGtid(mysqlPosition.getGtid());
- position.setCurrentGtid(mysqlPosition.getCurrentGtid());
- position.setTimestamp(mysqlPosition.getTimestamp());
- position.setJournalName(mysqlPosition.getJournalName());
- position.setCreateTime(mysqlPosition.getCreateTime());
- position.setUpdateTime(mysqlPosition.getUpdateTime());
- return position;
- }
-
- /**
- * EventMeshJobInfo covert
- *
- * @param eventMeshJobInfo EventMeshJobInfo
- * @return QueryTaskInfoResponse.EventMeshJobInfo
- */
- private static QueryTaskInfoResponse.EventMeshJobInfo initEventMeshJobInfo(EventMeshJobInfo eventMeshJobInfo) {
- QueryTaskInfoResponse.EventMeshJobInfo eventMeshJobInfoCovert = new QueryTaskInfoResponse.EventMeshJobInfo();
- if (ObjectUtils.isEmpty(eventMeshJobInfo)) {
- return null;
- }
- eventMeshJobInfoCovert.setId(eventMeshJobInfo.getId());
- eventMeshJobInfoCovert.setJobID(eventMeshJobInfo.getJobID());
- eventMeshJobInfoCovert.setJobDesc(eventMeshJobInfo.getJobDesc());
- eventMeshJobInfoCovert.setTaskID(eventMeshJobInfo.getTaskID());
- eventMeshJobInfoCovert.setTransportType(eventMeshJobInfo.getTransportType());
- eventMeshJobInfoCovert.setSourceData(eventMeshJobInfo.getSourceData());
- eventMeshJobInfoCovert.setTargetData(eventMeshJobInfo.getTargetData());
- eventMeshJobInfoCovert.setJobState(eventMeshJobInfo.getJobState());
- eventMeshJobInfoCovert.setJobType(eventMeshJobInfo.getJobType());
- eventMeshJobInfoCovert.setFromRegion(eventMeshJobInfo.getFromRegion());
- eventMeshJobInfoCovert.setRunningRegion(eventMeshJobInfo.getRunningRegion());
- eventMeshJobInfoCovert.setCreateUid(eventMeshJobInfo.getCreateUid());
- eventMeshJobInfoCovert.setUpdateUid(eventMeshJobInfo.getUpdateUid());
- eventMeshJobInfoCovert.setCreateTime(eventMeshJobInfo.getCreateTime());
- eventMeshJobInfoCovert.setUpdateTime(eventMeshJobInfo.getUpdateTime());
- return eventMeshJobInfoCovert;
- }
-
- /**
- * EventMeshTaskInfo covert
- *
- * @param meshTaskInfo EventMeshTaskInfo
- * @return QueryTaskInfoResponse
- */
- private static QueryTaskInfoResponse initEventMeshTaskInfo(EventMeshTaskInfo meshTaskInfo) {
- QueryTaskInfoResponse eventMeshTaskInfo = new QueryTaskInfoResponse();
- eventMeshTaskInfo.setId(meshTaskInfo.getId());
- eventMeshTaskInfo.setTaskID(meshTaskInfo.getTaskID());
- eventMeshTaskInfo.setTaskDesc(meshTaskInfo.getTaskDesc());
- eventMeshTaskInfo.setTaskState(meshTaskInfo.getTaskState());
- eventMeshTaskInfo.setSourceRegion(meshTaskInfo.getSourceRegion());
- eventMeshTaskInfo.setTargetRegion(meshTaskInfo.getTargetRegion());
- eventMeshTaskInfo.setCreateUid(meshTaskInfo.getCreateUid());
- eventMeshTaskInfo.setUpdateUid(meshTaskInfo.getUpdateUid());
- eventMeshTaskInfo.setCreateTime(meshTaskInfo.getCreateTime());
- eventMeshTaskInfo.setUpdateTime(meshTaskInfo.getUpdateTime());
- return eventMeshTaskInfo;
- }
-
-}
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshVerifyServiceImpl.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshVerifyServiceImpl.java
deleted file mode 100644
index 5e49ba32ea..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/db/service/impl/EventMeshVerifyServiceImpl.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.db.service.impl;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshVerify;
-import org.apache.eventmesh.admin.server.web.db.mapper.EventMeshVerifyMapper;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshVerifyService;
-
-import org.springframework.stereotype.Service;
-
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-
-/**
- * event_mesh_verify
- */
-@Service
-public class EventMeshVerifyServiceImpl extends ServiceImpl
- implements EventMeshVerifyService {
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/BaseRequestHandler.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/BaseRequestHandler.java
deleted file mode 100644
index 7f08f388c9..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/BaseRequestHandler.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.handler;
-
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Metadata;
-import org.apache.eventmesh.common.remote.request.BaseRemoteRequest;
-import org.apache.eventmesh.common.remote.response.BaseRemoteResponse;
-
-public abstract class BaseRequestHandler {
-
- public BaseRemoteResponse handlerRequest(T request, Metadata metadata) {
- return handler(request, metadata);
- }
-
- protected abstract S handler(T request, Metadata metadata);
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/RequestHandlerFactory.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/RequestHandlerFactory.java
deleted file mode 100644
index 9375fb537e..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/RequestHandlerFactory.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.handler;
-
-import org.apache.eventmesh.common.remote.request.BaseRemoteRequest;
-import org.apache.eventmesh.common.remote.response.BaseRemoteResponse;
-
-import java.lang.reflect.ParameterizedType;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.springframework.context.ApplicationListener;
-import org.springframework.context.event.ContextRefreshedEvent;
-import org.springframework.stereotype.Component;
-
-@Component
-public class RequestHandlerFactory implements ApplicationListener {
-
- private final Map> handlers =
- new ConcurrentHashMap<>();
-
- public BaseRequestHandler getHandler(String type) {
- return handlers.get(type);
- }
-
- @Override
- @SuppressWarnings({"rawtypes", "unchecked"})
- public void onApplicationEvent(ContextRefreshedEvent event) {
- Map beans =
- event.getApplicationContext().getBeansOfType(BaseRequestHandler.class);
-
- for (BaseRequestHandler requestHandler : beans.values()) {
- Class> clazz = requestHandler.getClass();
- boolean skip = false;
- while (!clazz.getSuperclass().equals(BaseRequestHandler.class)) {
- if (clazz.getSuperclass().equals(Object.class)) {
- skip = true;
- break;
- }
- clazz = clazz.getSuperclass();
- }
- if (skip) {
- continue;
- }
-
- Class c = (Class) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
- handlers.putIfAbsent(c.getSimpleName(), requestHandler);
- }
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/FetchJobRequestHandler.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/FetchJobRequestHandler.java
deleted file mode 100644
index 3392084c28..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/FetchJobRequestHandler.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.handler.impl;
-
-import org.apache.eventmesh.admin.server.web.handler.BaseRequestHandler;
-import org.apache.eventmesh.admin.server.web.pojo.JobDetail;
-import org.apache.eventmesh.admin.server.web.service.job.JobInfoBizService;
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Metadata;
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-import org.apache.eventmesh.common.remote.job.JobConnectorConfig;
-import org.apache.eventmesh.common.remote.request.FetchJobRequest;
-import org.apache.eventmesh.common.remote.response.FetchJobResponse;
-import org.apache.eventmesh.common.utils.JsonUtils;
-
-import org.apache.commons.lang3.StringUtils;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Component
-@Slf4j
-public class FetchJobRequestHandler extends BaseRequestHandler {
-
- @Autowired
- JobInfoBizService jobInfoBizService;
-
- @Override
- public FetchJobResponse handler(FetchJobRequest request, Metadata metadata) {
- if (StringUtils.isBlank(request.getJobID())) {
- return FetchJobResponse.failResponse(ErrorCode.BAD_REQUEST, "job id is empty");
- }
- FetchJobResponse response = FetchJobResponse.successResponse();
- JobDetail detail = jobInfoBizService.getJobDetail(request.getJobID());
- if (detail == null) {
- return response;
- }
- response.setId(detail.getJobID());
- JobConnectorConfig config = new JobConnectorConfig();
- config.setSourceConnectorConfig(JsonUtils.objectToMap(detail.getSourceDataSource().getConf()));
- config.setSourceConnectorDesc(detail.getSourceConnectorDesc());
- config.setSinkConnectorConfig(JsonUtils.objectToMap(detail.getSinkDataSource().getConf()));
- config.setSinkConnectorDesc(detail.getSinkConnectorDesc());
- response.setConnectorConfig(config);
- response.setTransportType(detail.getTransportType());
- response.setState(detail.getState());
- response.setPosition(detail.getPositions());
- response.setType(detail.getJobType());
- return response;
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/FetchPositionHandler.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/FetchPositionHandler.java
deleted file mode 100644
index 85ef0e6113..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/FetchPositionHandler.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.handler.impl;
-
-import org.apache.eventmesh.admin.server.web.db.DBThreadPool;
-import org.apache.eventmesh.admin.server.web.handler.BaseRequestHandler;
-import org.apache.eventmesh.admin.server.web.service.position.PositionBizService;
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Metadata;
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-import org.apache.eventmesh.common.remote.request.FetchPositionRequest;
-import org.apache.eventmesh.common.remote.response.FetchPositionResponse;
-
-import org.apache.commons.lang3.StringUtils;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Component
-@Slf4j
-public class FetchPositionHandler extends BaseRequestHandler {
-
- @Autowired
- DBThreadPool executor;
-
- @Autowired
- PositionBizService positionBizService;
-
- @Override
- protected FetchPositionResponse handler(FetchPositionRequest request, Metadata metadata) {
- if (request.getDataSourceType() == null) {
- return FetchPositionResponse.failResponse(ErrorCode.BAD_REQUEST, "illegal data type, it's empty");
- }
- if (StringUtils.isBlank(request.getJobID())) {
- return FetchPositionResponse.failResponse(ErrorCode.BAD_REQUEST, "illegal job id, it's empty");
- }
- return FetchPositionResponse.successResponse(positionBizService.getPosition(request, metadata));
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportHeartBeatHandler.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportHeartBeatHandler.java
deleted file mode 100644
index 26a9b430b7..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportHeartBeatHandler.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.handler.impl;
-
-import org.apache.eventmesh.admin.server.web.db.DBThreadPool;
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshRuntimeHeartbeat;
-import org.apache.eventmesh.admin.server.web.handler.BaseRequestHandler;
-import org.apache.eventmesh.admin.server.web.service.heatbeat.RuntimeHeartbeatBizService;
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Metadata;
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-import org.apache.eventmesh.common.remote.request.ReportHeartBeatRequest;
-import org.apache.eventmesh.common.remote.response.SimpleResponse;
-import org.apache.eventmesh.common.utils.IPUtils;
-
-import org.apache.commons.lang3.StringUtils;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Component
-@Slf4j
-public class ReportHeartBeatHandler extends BaseRequestHandler {
-
- @Autowired
- RuntimeHeartbeatBizService heartbeatBizService;
-
- @Autowired
- DBThreadPool executor;
-
- @Override
- protected SimpleResponse handler(ReportHeartBeatRequest request, Metadata metadata) {
- if (StringUtils.isBlank(request.getJobID()) || StringUtils.isBlank(request.getAddress())) {
- log.info("request [{}] id or reporter address is empty", request);
- return SimpleResponse.fail(ErrorCode.BAD_REQUEST, "request id or reporter address is empty");
- }
- executor.getExecutors().execute(() -> {
- EventMeshRuntimeHeartbeat heartbeat = new EventMeshRuntimeHeartbeat();
- heartbeat.setJobID(request.getJobID());
- heartbeat.setReportTime(request.getReportedTimeStamp());
- heartbeat.setAdminAddr(IPUtils.getLocalAddress());
- heartbeat.setRuntimeAddr(request.getAddress());
- try {
- if (!heartbeatBizService.saveOrUpdateByRuntimeAddress(heartbeat)) {
- log.warn("save or update heartbeat request [{}] fail", request);
- }
- } catch (Exception e) {
- log.warn("save or update heartbeat request [{}] fail", request, e);
- }
- });
-
- return SimpleResponse.success();
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportJobRequestHandler.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportJobRequestHandler.java
deleted file mode 100644
index c876014f63..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportJobRequestHandler.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.handler.impl;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo;
-import org.apache.eventmesh.admin.server.web.handler.BaseRequestHandler;
-import org.apache.eventmesh.admin.server.web.pojo.TaskDetail;
-import org.apache.eventmesh.admin.server.web.service.job.JobInfoBizService;
-import org.apache.eventmesh.admin.server.web.service.position.PositionBizService;
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Metadata;
-import org.apache.eventmesh.common.remote.JobState;
-import org.apache.eventmesh.common.remote.TransportType;
-import org.apache.eventmesh.common.remote.datasource.DataSourceType;
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-import org.apache.eventmesh.common.remote.offset.RecordPosition;
-import org.apache.eventmesh.common.remote.request.RecordPositionRequest;
-import org.apache.eventmesh.common.remote.request.ReportJobRequest;
-import org.apache.eventmesh.common.remote.response.SimpleResponse;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.List;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import lombok.extern.slf4j.Slf4j;
-
-
-@Component
-@Slf4j
-public class ReportJobRequestHandler extends BaseRequestHandler {
-
- @Autowired
- JobInfoBizService jobInfoBizService;
-
- @Autowired
- PositionBizService positionBizService;
-
- @Override
- public SimpleResponse handler(ReportJobRequest request, Metadata metadata) {
- log.info("receive report job request:{}", request);
- if (StringUtils.isBlank(request.getJobID())) {
- return SimpleResponse.fail(ErrorCode.BAD_REQUEST, "illegal job id, it's empty");
- }
- EventMeshJobInfo jobInfo = jobInfoBizService.getJobInfo(request.getJobID());
- if (jobInfo == null) {
- return SimpleResponse.fail(ErrorCode.BAD_REQUEST, "illegal job id, not exist target job,jobID:" + request.getJobID());
- }
- boolean recordResult = recordPosition(request, metadata, jobInfo);
- boolean result = recordResult && jobInfoBizService.updateJobState(jobInfo.getJobID(), request.getState());
- if (result) {
- return SimpleResponse.success();
- } else {
- return SimpleResponse.fail(ErrorCode.INTERNAL_ERR, "update job failed.");
- }
- }
-
- private boolean recordPosition(ReportJobRequest request, Metadata metadata, EventMeshJobInfo jobInfo) {
- if (!jobInfo.getJobState().equalsIgnoreCase(JobState.INIT.name()) || !JobState.RUNNING.name().equalsIgnoreCase(request.getState().name())) {
- log.info("skip record position because of job state not from init change to running.jobID:{}", jobInfo.getJobID());
- return true;
- }
-
- TaskDetail taskDetail = jobInfoBizService.getTaskDetail(jobInfo.getTaskID(), DataSourceType.MYSQL);
- if (taskDetail.getFullTask() == null || taskDetail.getIncreaseTask() == null) {
- log.info("skip record position because of not exist full and increase job.jobID:{}", jobInfo.getJobID());
- return true;
- }
-
- List recordPositionList =
- positionBizService.getPositionByJobID(taskDetail.getIncreaseTask().getJobID(), DataSourceType.MYSQL);
- if (!recordPositionList.isEmpty()) {
- log.info("skip record position because of increase job has exist position.jobID:{},position list size:{}", jobInfo.getJobID(),
- recordPositionList.size());
- return true;
- }
-
- RecordPositionRequest recordPositionRequest = new RecordPositionRequest();
- recordPositionRequest.setFullJobID(taskDetail.getFullTask().getJobID());
- recordPositionRequest.setIncreaseJobID(taskDetail.getIncreaseTask().getJobID());
- recordPositionRequest.setUpdateState(request.getState());
- recordPositionRequest.setAddress(request.getAddress());
- TransportType currentTransportType = TransportType.getTransportType(jobInfo.getTransportType());
- recordPositionRequest.setDataSourceType(currentTransportType.getSrc());
- return positionBizService.recordPosition(recordPositionRequest, metadata);
- }
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportMonitorHandler.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportMonitorHandler.java
deleted file mode 100644
index a36939bb88..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportMonitorHandler.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.handler.impl;
-
-import org.apache.eventmesh.admin.server.AdminServerProperties;
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo;
-import org.apache.eventmesh.admin.server.web.handler.BaseRequestHandler;
-import org.apache.eventmesh.admin.server.web.service.job.JobInfoBizService;
-import org.apache.eventmesh.admin.server.web.service.monitor.MonitorBizService;
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Metadata;
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-import org.apache.eventmesh.common.remote.request.ReportMonitorRequest;
-import org.apache.eventmesh.common.remote.response.SimpleResponse;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Component;
-import org.springframework.web.client.RestTemplate;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Component
-@Slf4j
-public class ReportMonitorHandler extends BaseRequestHandler {
-
- @Autowired
- private MonitorBizService monitorService;
-
- @Autowired
- JobInfoBizService jobInfoBizService;
-
- @Autowired
- private AdminServerProperties properties;
-
- @Override
- protected SimpleResponse handler(ReportMonitorRequest request, Metadata metadata) {
- if (StringUtils.isAnyBlank(request.getTaskID(), request.getJobID(), request.getAddress())) {
- log.info("report monitor request [{}] illegal", request);
- return SimpleResponse.fail(ErrorCode.BAD_REQUEST, "request task id,job id or address is none");
- }
-
- String jobID = request.getJobID();
- EventMeshJobInfo jobInfo = jobInfoBizService.getJobInfo(jobID);
- if (jobInfo == null || StringUtils.isBlank(jobInfo.getFromRegion())) {
- log.info("report monitor job info [{}] illegal", request);
- return SimpleResponse.fail(ErrorCode.BAD_REQUEST, "job info is null or fromRegion is blank,job id:" + jobID);
- }
- String fromRegion = jobInfo.getFromRegion();
- String transportType = jobInfo.getTransportType();
- if (StringUtils.isEmpty(request.getTransportType())) {
- request.setTransportType(transportType);
- }
- String localRegion = properties.getRegion();
- log.info("report monitor request from region:{},localRegion:{},request:{}", fromRegion, localRegion, request);
- if (fromRegion.equalsIgnoreCase(localRegion)) {
- return monitorService.reportMonitorRecord(request) ? SimpleResponse.success() :
- SimpleResponse.fail(ErrorCode.INTERNAL_ERR, "save monitor "
- + "request fail");
- } else {
- List adminServerList = Arrays.asList(properties.getAdminServerList().get(fromRegion).split(";"));
- if (adminServerList == null || adminServerList.isEmpty()) {
- throw new RuntimeException("No admin server available for region: " + fromRegion);
- }
- String targetUrl = adminServerList.get(new Random().nextInt(adminServerList.size())) + "/eventmesh/admin/reportMonitor";
- log.info("start transfer monitor request to from region admin server. from region:{}, targetUrl:{}", fromRegion, targetUrl);
- RestTemplate restTemplate = new RestTemplate();
- ResponseEntity response = restTemplate.postForEntity(targetUrl, request, String.class);
- if (!response.getStatusCode().is2xxSuccessful()) {
- log.error("transfer monitor request to from region admin server error. from region:{}, targetUrl:{}", fromRegion, targetUrl);
- return SimpleResponse.fail(ErrorCode.INTERNAL_ERR,
- "save monitor request fail,code:" + response.getStatusCode() + ",msg:" + response.getBody());
- }
- return SimpleResponse.success();
- }
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportPositionHandler.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportPositionHandler.java
deleted file mode 100644
index 7a30bef80a..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportPositionHandler.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.handler.impl;
-
-import org.apache.eventmesh.admin.server.web.db.DBThreadPool;
-import org.apache.eventmesh.admin.server.web.handler.BaseRequestHandler;
-import org.apache.eventmesh.admin.server.web.pojo.JobDetail;
-import org.apache.eventmesh.admin.server.web.service.job.JobInfoBizService;
-import org.apache.eventmesh.admin.server.web.service.position.PositionBizService;
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Metadata;
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-import org.apache.eventmesh.common.remote.request.ReportPositionRequest;
-import org.apache.eventmesh.common.remote.response.SimpleResponse;
-
-import org.apache.commons.lang3.StringUtils;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Component
-@Slf4j
-public class ReportPositionHandler extends BaseRequestHandler {
-
- @Autowired
- private JobInfoBizService jobInfoBizService;
-
- @Autowired
- private DBThreadPool executor;
-
- @Autowired
- private PositionBizService positionBizService;
-
- @Override
- protected SimpleResponse handler(ReportPositionRequest request, Metadata metadata) {
- log.info("receive report position request:{}", request);
- if (StringUtils.isBlank(request.getJobID())) {
- log.info("request [{}] illegal job id", request);
- return SimpleResponse.fail(ErrorCode.BAD_REQUEST, "illegal job id, it's empty");
- }
- if (request.getDataSourceType() == null) {
- log.info("request [{}] illegal data type", request);
- return SimpleResponse.fail(ErrorCode.BAD_REQUEST, "illegal data type, it's empty");
- }
- if (StringUtils.isBlank(request.getJobID())) {
- log.info("request [{}] illegal job id", request);
- return SimpleResponse.fail(ErrorCode.BAD_REQUEST, "illegal job id, it's empty");
- }
- if (request.getRecordPositionList() == null || request.getRecordPositionList().isEmpty()) {
- log.info("request [{}] illegal record position", request);
- return SimpleResponse.fail(ErrorCode.BAD_REQUEST, "illegal record position list, it's empty");
- }
-
- positionBizService.isValidatePositionRequest(request.getDataSourceType());
-
- executor.getExecutors().execute(() -> {
- try {
- boolean reported = positionBizService.reportPosition(request, metadata);
- if (reported) {
- if (log.isDebugEnabled()) {
- log.debug("handle runtime [{}] report data type [{}] job [{}] position [{}] success",
- request.getAddress(), request.getDataSourceType(), request.getJobID(),
- request.getRecordPositionList());
- }
- } else {
- log.warn("handle runtime [{}] report data type [{}] job [{}] position [{}] fail",
- request.getAddress(), request.getDataSourceType(), request.getJobID(),
- request.getRecordPositionList());
- }
- } catch (Exception e) {
- log.warn("handle position request fail, request [{}]", request, e);
- } finally {
- try {
- JobDetail detail = jobInfoBizService.getJobDetail(request.getJobID());
- if (detail != null && !detail.getState().equals(request.getState()) && !jobInfoBizService.updateJobState(request.getJobID(),
- request.getState())) {
- log.warn("update job [{}] old state [{}] to [{}] fail", request.getJobID(), detail.getState(), request.getState());
- }
- } catch (Exception e) {
- log.warn("update job id [{}] type [{}] state [{}] fail", request.getJobID(),
- request.getDataSourceType(), request.getState(), e);
- }
- }
- });
- return SimpleResponse.success();
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportVerifyHandler.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportVerifyHandler.java
deleted file mode 100644
index e7f1d1257f..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/handler/impl/ReportVerifyHandler.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.handler.impl;
-
-import org.apache.eventmesh.admin.server.AdminServerProperties;
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo;
-import org.apache.eventmesh.admin.server.web.handler.BaseRequestHandler;
-import org.apache.eventmesh.admin.server.web.service.job.JobInfoBizService;
-import org.apache.eventmesh.admin.server.web.service.verify.VerifyBizService;
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Metadata;
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-import org.apache.eventmesh.common.remote.request.ReportVerifyRequest;
-import org.apache.eventmesh.common.remote.response.SimpleResponse;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Component;
-import org.springframework.web.client.RestTemplate;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Component
-@Slf4j
-public class ReportVerifyHandler extends BaseRequestHandler {
-
- @Autowired
- private VerifyBizService verifyService;
-
- @Autowired
- JobInfoBizService jobInfoBizService;
-
- @Autowired
- private AdminServerProperties properties;
-
- @Override
- protected SimpleResponse handler(ReportVerifyRequest request, Metadata metadata) {
- if (StringUtils.isAnyBlank(request.getTaskID(), request.getJobID(), request.getRecordSig(), request.getRecordID(),
- request.getConnectorStage())) {
- log.info("report verify request [{}] illegal", request);
- return SimpleResponse.fail(ErrorCode.BAD_REQUEST, "request task id,job id, sign, record id or stage is none");
- }
-
- String jobID = request.getJobID();
- EventMeshJobInfo jobInfo = jobInfoBizService.getJobInfo(jobID);
- if (jobInfo == null || StringUtils.isBlank(jobInfo.getFromRegion())) {
- log.info("report verify job info [{}] illegal", request);
- return SimpleResponse.fail(ErrorCode.BAD_REQUEST, "job info is null or fromRegion is blank,job id:" + jobID);
- }
-
- String fromRegion = jobInfo.getFromRegion();
- String localRegion = properties.getRegion();
- log.info("report verify request from region:{},localRegion:{},request:{}", fromRegion, localRegion, request);
- if (fromRegion.equalsIgnoreCase(localRegion)) {
- return verifyService.reportVerifyRecord(request) ? SimpleResponse.success() : SimpleResponse.fail(ErrorCode.INTERNAL_ERR, "save verify "
- + "request fail");
- } else {
- log.info("start transfer report verify to from region admin server. from region:{}", fromRegion);
- List adminServerList = Arrays.asList(properties.getAdminServerList().get(fromRegion).split(";"));
- if (adminServerList == null || adminServerList.isEmpty()) {
- throw new RuntimeException("No admin server available for region: " + fromRegion);
- }
- String targetUrl = adminServerList.get(new Random().nextInt(adminServerList.size())) + "/eventmesh/admin/reportVerify";
- RestTemplate restTemplate = new RestTemplate();
- ResponseEntity response = restTemplate.postForEntity(targetUrl, request, String.class);
- if (!response.getStatusCode().is2xxSuccessful()) {
- return SimpleResponse.fail(ErrorCode.INTERNAL_ERR,
- "save verify request fail,code:" + response.getStatusCode() + ",msg:" + response.getBody());
- }
- return SimpleResponse.success();
- }
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/pojo/BinlogPosition.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/pojo/BinlogPosition.java
deleted file mode 100644
index 5bd8daab10..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/pojo/BinlogPosition.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.pojo;
-
-
-import lombok.Data;
-
-@Data
-public class BinlogPosition {
- private String file;
- private Long position;
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/pojo/JobDetail.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/pojo/JobDetail.java
deleted file mode 100644
index 0e2fa64878..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/pojo/JobDetail.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.pojo;
-
-import org.apache.eventmesh.common.remote.TaskState;
-import org.apache.eventmesh.common.remote.TransportType;
-import org.apache.eventmesh.common.remote.datasource.DataSource;
-import org.apache.eventmesh.common.remote.job.JobType;
-import org.apache.eventmesh.common.remote.offset.RecordPosition;
-
-import java.util.Date;
-import java.util.List;
-
-import lombok.Data;
-
-@Data
-public class JobDetail {
- private Integer id;
-
- private String jobID;
-
- private String jobDesc;
-
- private String taskID;
-
- private TaskState state;
-
- private JobType jobType;
-
- private Date createTime;
-
- private Date updateTime;
-
- private String createUid;
-
- private String updateUid;
-
- // job request from region
- private String fromRegion;
-
- // job actually running region
- private String runningRegion;
-
- private DataSource sourceDataSource;
-
- private String sourceConnectorDesc;
-
- private DataSource sinkDataSource;
-
- private String sinkConnectorDesc;
-
- private TransportType transportType;
-
- private List positions;
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/pojo/TaskDetail.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/pojo/TaskDetail.java
deleted file mode 100644
index 2b174209e2..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/pojo/TaskDetail.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.pojo;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo;
-
-import lombok.Data;
-
-/**
- * Description:
- */
-@Data
-public class TaskDetail {
-
- private EventMeshJobInfo fullTask;
-
- private EventMeshJobInfo increaseTask;
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/AdminGrpcServer.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/AdminGrpcServer.java
deleted file mode 100644
index bc822ad6c3..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/AdminGrpcServer.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.service;
-
-import org.apache.eventmesh.admin.server.AdminServerRuntimeException;
-import org.apache.eventmesh.admin.server.web.handler.BaseRequestHandler;
-import org.apache.eventmesh.admin.server.web.handler.RequestHandlerFactory;
-import org.apache.eventmesh.common.protocol.grpc.adminserver.AdminServiceGrpc;
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Payload;
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-import org.apache.eventmesh.common.remote.payload.PayloadUtil;
-import org.apache.eventmesh.common.remote.request.BaseRemoteRequest;
-import org.apache.eventmesh.common.remote.response.BaseRemoteResponse;
-import org.apache.eventmesh.common.remote.response.SimpleResponse;
-
-import org.apache.commons.lang3.StringUtils;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import io.grpc.stub.ServerCallStreamObserver;
-import io.grpc.stub.StreamObserver;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Service
-@Slf4j
-public class AdminGrpcServer extends AdminServiceGrpc.AdminServiceImplBase {
-
- @Autowired
- RequestHandlerFactory handlerFactory;
-
- private Payload process(Payload value) {
- if (value == null || StringUtils.isBlank(value.getMetadata().getType())) {
- return PayloadUtil.from(SimpleResponse.fail(ErrorCode.BAD_REQUEST, "bad request: type not exists"));
- }
- try {
- BaseRequestHandler handler = handlerFactory.getHandler(value.getMetadata().getType());
- if (handler == null) {
- return PayloadUtil.from(SimpleResponse.fail(ErrorCode.BAD_REQUEST, "not match any request handler"));
- }
- BaseRemoteResponse response = handler.handlerRequest((BaseRemoteRequest) PayloadUtil.parse(value), value.getMetadata());
- if (response == null) {
- log.warn("received request type [{}] handler [{}], then replay empty response", value.getMetadata().getType(),
- handler.getClass().getName());
- response = SimpleResponse.success();
- }
- return PayloadUtil.from(response);
- } catch (Exception e) {
- log.warn("process payload {} fail", value.getMetadata().getType(), e);
- if (e instanceof AdminServerRuntimeException) {
- return PayloadUtil.from(SimpleResponse.fail(((AdminServerRuntimeException) e).getCode(), e.getMessage()));
- }
- return PayloadUtil.from(SimpleResponse.fail(ErrorCode.INTERNAL_ERR, "admin server internal err"));
- }
- }
-
- public StreamObserver invokeBiStream(StreamObserver responseObserver) {
- return new StreamObserver() {
- @Override
- public void onNext(Payload value) {
- Payload payload = process(value);
- if (payload == null) {
- return;
- }
- responseObserver.onNext(payload);
- }
-
- @Override
- public void onError(Throwable t) {
- if (responseObserver instanceof ServerCallStreamObserver) {
- if (!((ServerCallStreamObserver) responseObserver).isCancelled()) {
- log.warn("admin gRPC server fail", t);
- }
- }
- }
-
- @Override
- public void onCompleted() {
- responseObserver.onCompleted();
- }
- };
- }
-
- public void invoke(Payload request, StreamObserver responseObserver) {
- responseObserver.onNext(process(request));
- responseObserver.onCompleted();
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/AdminServer.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/AdminServer.java
deleted file mode 100644
index fd7582800d..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/AdminServer.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.service;
-
-import org.apache.eventmesh.admin.server.AdminServerProperties;
-import org.apache.eventmesh.admin.server.AdminServerRuntimeException;
-import org.apache.eventmesh.common.ComponentLifeCycle;
-import org.apache.eventmesh.common.Constants;
-import org.apache.eventmesh.common.config.CommonConfiguration;
-import org.apache.eventmesh.common.config.ConfigService;
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-import org.apache.eventmesh.common.utils.IPUtils;
-import org.apache.eventmesh.registry.RegisterServerInfo;
-import org.apache.eventmesh.registry.RegistryFactory;
-import org.apache.eventmesh.registry.RegistryService;
-
-import org.apache.commons.lang3.StringUtils;
-
-import javax.annotation.PostConstruct;
-
-import org.springframework.boot.context.event.ApplicationReadyEvent;
-import org.springframework.context.ApplicationListener;
-import org.springframework.stereotype.Service;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Service
-@Slf4j
-public class AdminServer implements ComponentLifeCycle, ApplicationListener {
- private final RegistryService registryService;
-
- private final RegisterServerInfo adminServeInfo;
-
- private final CommonConfiguration configuration;
-
- public AdminServer(AdminServerProperties properties) {
- configuration =
- ConfigService.getInstance().buildConfigInstance(CommonConfiguration.class);
- if (configuration == null) {
- throw new AdminServerRuntimeException(ErrorCode.STARTUP_CONFIG_MISS, "common configuration file miss");
- }
- this.adminServeInfo = new RegisterServerInfo();
-
- adminServeInfo.setHealth(true);
- adminServeInfo.setAddress(IPUtils.getLocalAddress() + ":" + properties.getPort());
- String name = Constants.ADMIN_SERVER_REGISTRY_NAME;
- if (StringUtils.isNotBlank(properties.getServiceName())) {
- name = properties.getServiceName();
- }
- adminServeInfo.setServiceName(name);
- registryService = RegistryFactory.getInstance(configuration.getEventMeshRegistryPluginType());
- }
-
- @Override
- @PostConstruct
- public void start() {
- if (configuration.isEventMeshRegistryPluginEnabled()) {
- registryService.init();
- }
- }
-
- @Override
- public void stop() {
- if (configuration.isEventMeshRegistryPluginEnabled()) {
- registryService.unRegister(adminServeInfo);
- try {
- Thread.sleep(3000);
- } catch (InterruptedException ignore) {
- log.warn("interrupted when sleep");
- Thread.currentThread().interrupt();
- }
- registryService.shutdown();
- }
- }
-
- @Override
- public void onApplicationEvent(ApplicationReadyEvent event) {
- if (configuration.isEventMeshRegistryPluginEnabled()) {
- log.info("application is started and registry plugin is enabled, it's will register admin self");
- registryService.register(adminServeInfo);
- }
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/datasource/DataSourceBizService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/datasource/DataSourceBizService.java
deleted file mode 100644
index 4d2d670100..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/datasource/DataSourceBizService.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.service.datasource;
-
-import org.apache.eventmesh.admin.server.AdminServerRuntimeException;
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshDataSource;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshDataSourceService;
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-import org.apache.eventmesh.common.remote.request.CreateOrUpdateDataSourceReq;
-import org.apache.eventmesh.common.utils.JsonUtils;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-@Service
-public class DataSourceBizService {
-
- @Autowired
- private EventMeshDataSourceService dataSourceService;
-
- public EventMeshDataSource createDataSource(CreateOrUpdateDataSourceReq dataSource) {
- EventMeshDataSource entity = new EventMeshDataSource();
- entity.setConfiguration(JsonUtils.toJSONString(dataSource.getConfig()));
- entity.setConfigurationClass(dataSource.getConfigClass());
- entity.setDataType(dataSource.getType().name());
- entity.setCreateUid(dataSource.getOperator());
- entity.setUpdateUid(dataSource.getOperator());
- entity.setRegion(dataSource.getRegion());
- entity.setDescription(dataSource.getDesc());
- if (dataSourceService.save(entity)) {
- return entity;
- }
- throw new AdminServerRuntimeException(ErrorCode.BAD_DB_DATA, "save data source fail");
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/heatbeat/RuntimeHeartbeatBizService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/heatbeat/RuntimeHeartbeatBizService.java
deleted file mode 100644
index 95dff6e5b3..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/heatbeat/RuntimeHeartbeatBizService.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.service.heatbeat;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshRuntimeHeartbeat;
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshRuntimeHistory;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshRuntimeHeartbeatService;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshRuntimeHistoryService;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * for table 'event_mesh_runtime_heartbeat' db operation 2024-05-14 17:15:03
- */
-@Service
-@Slf4j
-public class RuntimeHeartbeatBizService {
-
- @Autowired
- EventMeshRuntimeHistoryService historyService;
-
- @Autowired
- EventMeshRuntimeHeartbeatService heartbeatService;
-
- public boolean saveOrUpdateByRuntimeAddress(EventMeshRuntimeHeartbeat entity) {
- EventMeshRuntimeHeartbeat old = heartbeatService.getOne(Wrappers.query().eq(
- "runtimeAddr",
- entity.getRuntimeAddr()));
- if (old == null) {
- return heartbeatService.save(entity);
- } else {
- if (Long.parseLong(old.getReportTime()) >= Long.parseLong(entity.getReportTime())) {
- log.info("update heartbeat record ignore, current report time late than db, job [{}], remote [{}]", entity.getJobID(),
- entity.getRuntimeAddr());
- return true;
- }
- try {
- return heartbeatService.update(entity, Wrappers.update().eq("updateTime",
- old.getUpdateTime()));
- } finally {
- if (old.getJobID() != null && !old.getJobID().equals(entity.getJobID())) {
- EventMeshRuntimeHistory history = new EventMeshRuntimeHistory();
- history.setAddress(old.getAdminAddr());
- history.setJob(old.getJobID());
- try {
- historyService.save(history);
- } catch (Exception e) {
- log.warn("save runtime job changed history fail", e);
- }
-
- log.info("runtime [{}] changed job, old job [{}], now [{}]", entity.getRuntimeAddr(), old.getJobID(),
- entity.getJobID());
- }
- }
- }
- }
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/job/JobInfoBizService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/job/JobInfoBizService.java
deleted file mode 100644
index c200d9801a..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/job/JobInfoBizService.java
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.service.job;
-
-import org.apache.eventmesh.admin.server.AdminServerProperties;
-import org.apache.eventmesh.admin.server.AdminServerRuntimeException;
-import org.apache.eventmesh.admin.server.web.db.DBThreadPool;
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshDataSource;
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshJobInfo;
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshRuntimeHeartbeat;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshDataSourceService;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshJobInfoExtService;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshJobInfoService;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshRuntimeHeartbeatService;
-import org.apache.eventmesh.admin.server.web.pojo.JobDetail;
-import org.apache.eventmesh.admin.server.web.pojo.TaskDetail;
-import org.apache.eventmesh.admin.server.web.service.datasource.DataSourceBizService;
-import org.apache.eventmesh.admin.server.web.service.position.PositionBizService;
-import org.apache.eventmesh.common.config.connector.Config;
-import org.apache.eventmesh.common.remote.JobState;
-import org.apache.eventmesh.common.remote.TaskState;
-import org.apache.eventmesh.common.remote.TransportType;
-import org.apache.eventmesh.common.remote.datasource.DataSource;
-import org.apache.eventmesh.common.remote.datasource.DataSourceType;
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-import org.apache.eventmesh.common.remote.job.JobType;
-import org.apache.eventmesh.common.remote.request.CreateOrUpdateDataSourceReq;
-import org.apache.eventmesh.common.utils.JsonUtils;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.time.Duration;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.PostConstruct;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * for table 'event_mesh_job_info' db operation
- */
-@Service
-@Slf4j
-public class JobInfoBizService {
-
- @Autowired
- private EventMeshJobInfoService jobInfoService;
-
- @Autowired
- private EventMeshJobInfoExtService jobInfoExtService;
-
- @Autowired
- private DataSourceBizService dataSourceBizService;
-
- @Autowired
- private EventMeshDataSourceService dataSourceService;
-
- @Autowired
- private PositionBizService positionBizService;
-
- @Autowired
- private AdminServerProperties properties;
-
- @Autowired
- EventMeshRuntimeHeartbeatService heartbeatService;
-
- private final long heatBeatPeriod = Duration.ofMillis(5000).toMillis();
-
- @Autowired
- DBThreadPool executor;
-
- @PostConstruct
- public void init() {
- log.info("init check job info scheduled task.");
- executor.getCheckExecutor().scheduleAtFixedRate(new Runnable() {
- @Override
- public void run() {
- checkJobInfo();
- }
- }, 10, 10, TimeUnit.SECONDS);
- }
-
- public boolean updateJobState(String jobID, TaskState state) {
- if (jobID == null || state == null) {
- return false;
- }
- EventMeshJobInfo jobInfo = new EventMeshJobInfo();
- jobInfo.setJobState(state.name());
- return jobInfoService.update(jobInfo, Wrappers.update().eq("jobID", jobID).ne("jobState", JobState.DELETE.name()));
- }
-
- public boolean updateJobState(String jobID, JobState state) {
- if (jobID == null || state == null) {
- return false;
- }
- EventMeshJobInfo jobInfo = new EventMeshJobInfo();
- jobInfo.setJobState(state.name());
- return jobInfoService.update(jobInfo, Wrappers.update().eq("jobID", jobID).ne("jobState", JobState.DELETE.name()));
- }
-
- @Transactional
- public List createJobs(List jobs) {
- if (jobs == null || jobs.isEmpty() || jobs.stream().anyMatch(job -> StringUtils.isBlank(job.getTaskID()))) {
- log.warn("when create jobs, task id is empty or jobs config is empty ");
- return null;
- }
- List entityList = new LinkedList<>();
-
- for (JobDetail job : jobs) {
- // if running region not equal with admin region continue
- if (!job.getRunningRegion().equals(properties.getRegion())) {
- continue;
- }
- EventMeshJobInfo entity = new EventMeshJobInfo();
- entity.setJobState(TaskState.INIT.name());
- entity.setTaskID(job.getTaskID());
- entity.setJobType(job.getJobType().name());
- entity.setJobDesc(job.getJobDesc());
- String jobID = UUID.randomUUID().toString();
- entity.setJobID(jobID);
- entity.setTransportType(job.getTransportType().name());
- entity.setCreateUid(job.getCreateUid());
- entity.setUpdateUid(job.getUpdateUid());
- entity.setFromRegion(job.getFromRegion());
- entity.setRunningRegion(job.getRunningRegion());
- CreateOrUpdateDataSourceReq source = new CreateOrUpdateDataSourceReq();
- source.setType(job.getTransportType().getSrc());
- source.setOperator(job.getCreateUid());
- source.setRegion(job.getSourceDataSource().getRegion());
- source.setDesc(job.getSourceConnectorDesc());
- Config sourceConfig = job.getSourceDataSource().getConf();
- source.setConfig(sourceConfig);
- source.setConfigClass(job.getSourceDataSource().getConfClazz().getName());
- EventMeshDataSource createdSource = dataSourceBizService.createDataSource(source);
- entity.setSourceData(createdSource.getId());
-
- CreateOrUpdateDataSourceReq sink = new CreateOrUpdateDataSourceReq();
- sink.setType(job.getTransportType().getDst());
- sink.setOperator(job.getCreateUid());
- sink.setRegion(job.getSinkDataSource().getRegion());
- sink.setDesc(job.getSinkConnectorDesc());
- Config sinkConfig = job.getSinkDataSource().getConf();
- sink.setConfig(sinkConfig);
- sink.setConfigClass(job.getSinkDataSource().getConfClazz().getName());
- EventMeshDataSource createdSink = dataSourceBizService.createDataSource(sink);
- entity.setTargetData(createdSink.getId());
-
- entityList.add(entity);
- }
- int changed = jobInfoExtService.batchSave(entityList);
- if (changed != entityList.size()) {
- throw new AdminServerRuntimeException(ErrorCode.INTERNAL_ERR, String.format("create [%d] jobs of not match expect [%d]",
- changed, jobs.size()));
- }
- return entityList;
- }
-
-
- public JobDetail getJobDetail(String jobID) {
- if (jobID == null) {
- return null;
- }
- EventMeshJobInfo job = jobInfoService.getOne(Wrappers.query().eq("jobID", jobID));
- if (job == null) {
- return null;
- }
- JobDetail detail = new JobDetail();
- detail.setTaskID(job.getTaskID());
- detail.setJobID(job.getJobID());
- EventMeshDataSource source = dataSourceService.getById(job.getSourceData());
- EventMeshDataSource target = dataSourceService.getById(job.getTargetData());
- if (source != null) {
- if (!StringUtils.isBlank(source.getConfiguration())) {
- try {
- DataSource sourceDataSource = new DataSource();
- Class> configClass = Class.forName(source.getConfigurationClass());
- sourceDataSource.setConf((Config) JsonUtils.parseObject(source.getConfiguration(), configClass));
- detail.setSourceDataSource(sourceDataSource);
- } catch (Exception e) {
- log.warn("parse source config id [{}] fail", job.getSourceData(), e);
- throw new AdminServerRuntimeException(ErrorCode.BAD_DB_DATA, "illegal source data source config");
- }
- }
- detail.setSourceConnectorDesc(source.getDescription());
- if (source.getDataType() != null) {
- detail.setPositions(positionBizService.getPositionByJobID(job.getJobID(),
- DataSourceType.getDataSourceType(source.getDataType())));
-
- }
- }
- if (target != null) {
- if (!StringUtils.isBlank(target.getConfiguration())) {
- try {
- DataSource sinkDataSource = new DataSource();
- Class> configClass = Class.forName(target.getConfigurationClass());
- sinkDataSource.setConf((Config) JsonUtils.parseObject(target.getConfiguration(), configClass));
- detail.setSinkDataSource(sinkDataSource);
- } catch (Exception e) {
- log.warn("parse sink config id [{}] fail", job.getSourceData(), e);
- throw new AdminServerRuntimeException(ErrorCode.BAD_DB_DATA, "illegal target data sink config");
- }
- }
- detail.setSinkConnectorDesc(target.getDescription());
- }
-
- TaskState state = TaskState.fromIndex(job.getJobState());
- if (state == null) {
- throw new AdminServerRuntimeException(ErrorCode.BAD_DB_DATA, "illegal job state in db");
- }
- detail.setState(state);
- detail.setTransportType(TransportType.getTransportType(job.getTransportType()));
- detail.setJobType(JobType.fromIndex(job.getJobType()));
- detail.setJobDesc(job.getJobDesc());
- return detail;
- }
-
- public EventMeshJobInfo getJobInfo(String jobID) {
- if (jobID == null) {
- return null;
- }
- return jobInfoService.getOne(Wrappers.query().eq("jobID", jobID));
- }
-
- public List getJobsByTaskID(String taskID) {
- if (taskID == null) {
- return null;
- }
- return jobInfoService.list(Wrappers.query().eq("taskID", taskID));
- }
-
- public void checkJobInfo() {
- List eventMeshJobInfoList = jobInfoService.list(Wrappers.query().eq("jobState", JobState.RUNNING.name()));
- log.info("start check job info.to check job size:{}", eventMeshJobInfoList.size());
- for (EventMeshJobInfo jobInfo : eventMeshJobInfoList) {
- String jobID = jobInfo.getJobID();
- if (StringUtils.isEmpty(jobID)) {
- continue;
- }
- List heartbeatList = heartbeatService.list((Wrappers.query().eq("jobID", jobID)));
- if (heartbeatList == null || heartbeatList.size() == 0) {
- continue;
- }
- // if last heart beat update time have delay three period.print job heart beat delay warn
- long currentTimeStamp = System.currentTimeMillis();
- if (currentTimeStamp - heartbeatList.get(0).getUpdateTime().getTime() > 3 * heatBeatPeriod) {
- log.warn("current job heart heart has delay.jobID:{},currentTimeStamp:{},last update time:{}", jobID, currentTimeStamp,
- heartbeatList.get(0).getUpdateTime());
- }
- }
- }
-
- public TaskDetail getTaskDetail(String taskID, DataSourceType dataSourceType) {
- TaskDetail taskDetail = new TaskDetail();
- List jobInfoList = getJobsByTaskID(taskID);
- if (jobInfoList == null || jobInfoList.size() == 0) {
- return taskDetail;
- }
- for (EventMeshJobInfo jobInfo : jobInfoList) {
- TransportType currentTransportType = TransportType.getTransportType(jobInfo.getTransportType());
- JobType jobType = JobType.fromIndex(jobInfo.getJobType());
- if (currentTransportType.getSrc().equals(dataSourceType)) {
- if (jobType.name().equalsIgnoreCase(JobType.FULL.name())) {
- taskDetail.setFullTask(jobInfo);
- }
- if (jobType.name().equalsIgnoreCase(JobType.INCREASE.name())) {
- taskDetail.setIncreaseTask(jobInfo);
- }
- }
- }
- return taskDetail;
- }
-
-
-}
-
-
-
-
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/monitor/MonitorBizService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/monitor/MonitorBizService.java
deleted file mode 100644
index 3377334144..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/monitor/MonitorBizService.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.service.monitor;
-
-import org.apache.eventmesh.admin.server.web.db.entity.EventMeshMonitor;
-import org.apache.eventmesh.admin.server.web.db.service.EventMeshMonitorService;
-import org.apache.eventmesh.common.remote.request.QueryTaskMonitorRequest;
-import org.apache.eventmesh.common.remote.request.ReportMonitorRequest;
-import org.apache.eventmesh.common.remote.response.QueryTaskMonitorResponse;
-import org.apache.eventmesh.common.remote.task.TaskMonitor;
-import org.apache.eventmesh.common.utils.JsonUtils;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Service
-@Slf4j
-public class MonitorBizService {
-
- @Autowired
- private EventMeshMonitorService monitorService;
-
- public boolean reportMonitorRecord(ReportMonitorRequest request) {
- EventMeshMonitor monitor = new EventMeshMonitor();
- monitor.setTaskID(request.getTaskID());
- monitor.setJobID(request.getJobID());
- monitor.setAddress(request.getAddress());
- monitor.setTransportType(request.getTransportType());
- monitor.setConnectorStage(request.getConnectorStage());
- monitor.setTotalReqNum(request.getTotalReqNum());
- monitor.setTotalTimeCost(request.getTotalTimeCost());
- monitor.setMaxTimeCost(request.getMaxTimeCost());
- monitor.setAvgTimeCost(request.getAvgTimeCost());
- monitor.setTps(request.getTps());
- return monitorService.save(monitor);
- }
-
- public QueryTaskMonitorResponse queryTaskMonitors(QueryTaskMonitorRequest request) {
- if (StringUtils.isBlank(request.getTaskID())) {
- throw new RuntimeException("task id is empty");
- }
- long limit = request.getLimit();
- if (limit <= 0) {
- log.info("query task monitor limit:{},use default value:{}", limit, 10);
- limit = 10;
- }
-
- Page queryPage = new Page<>();
- queryPage.setCurrent(1);
- queryPage.setSize(limit);
- queryPage.addOrder(OrderItem.desc("createTime"));
-
- QueryWrapper queryWrapper = new QueryWrapper();
- queryWrapper.eq("taskID", request.getTaskID());
- if (StringUtils.isNotEmpty(request.getJobID())) {
- queryWrapper.eq("jobID", request.getJobID());
- }
- List eventMeshMonitors = monitorService.list(queryPage, queryWrapper);
- List taskMonitorList = new ArrayList<>();
- if (eventMeshMonitors != null) {
- log.info("query event mesh monitor size:{}", eventMeshMonitors.size());
- if (log.isDebugEnabled()) {
- log.debug("query event mesh monitor content:{}", JsonUtils.toJSONString(eventMeshMonitors));
- }
- for (EventMeshMonitor eventMeshMonitor : eventMeshMonitors) {
- TaskMonitor monitor = new TaskMonitor();
- monitor.setTaskID(eventMeshMonitor.getTaskID());
- monitor.setJobID(eventMeshMonitor.getJobID());
- monitor.setAddress(eventMeshMonitor.getAddress());
- monitor.setTransportType(eventMeshMonitor.getTransportType());
- monitor.setConnectorStage(eventMeshMonitor.getConnectorStage());
- monitor.setTotalReqNum(eventMeshMonitor.getTotalReqNum());
- monitor.setTotalTimeCost(eventMeshMonitor.getTotalTimeCost());
- monitor.setMaxTimeCost(eventMeshMonitor.getMaxTimeCost());
- monitor.setAvgTimeCost(eventMeshMonitor.getAvgTimeCost());
- monitor.setTps(eventMeshMonitor.getTps());
- monitor.setCreateTime(eventMeshMonitor.getCreateTime());
- taskMonitorList.add(monitor);
- }
- }
- QueryTaskMonitorResponse queryTaskMonitorResponse = new QueryTaskMonitorResponse();
- queryTaskMonitorResponse.setTaskMonitors(taskMonitorList);
- return queryTaskMonitorResponse;
- }
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/position/IFetchPositionHandler.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/position/IFetchPositionHandler.java
deleted file mode 100644
index 2c039062f3..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/position/IFetchPositionHandler.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.service.position;
-
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Metadata;
-import org.apache.eventmesh.common.remote.offset.RecordPosition;
-import org.apache.eventmesh.common.remote.request.FetchPositionRequest;
-
-import java.util.List;
-
-/**
- * IFetchPositionHandler
- */
-public interface IFetchPositionHandler {
-
- List handler(FetchPositionRequest request, Metadata metadata);
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/position/IRecordPositionHandler.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/position/IRecordPositionHandler.java
deleted file mode 100644
index fa38e14320..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/position/IRecordPositionHandler.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.service.position;
-
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Metadata;
-import org.apache.eventmesh.common.remote.request.RecordPositionRequest;
-
-/**
- * IRecordPositionHandler
- */
-public interface IRecordPositionHandler {
-
- boolean handler(RecordPositionRequest request, Metadata metadata);
-
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/position/IReportPositionHandler.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/position/IReportPositionHandler.java
deleted file mode 100644
index 75f392e395..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/position/IReportPositionHandler.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.service.position;
-
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Metadata;
-import org.apache.eventmesh.common.remote.request.ReportPositionRequest;
-
-/**
- * IReportPositionHandler
- */
-public interface IReportPositionHandler {
-
- boolean handler(ReportPositionRequest request, Metadata metadata);
-}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/position/PositionBizService.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/position/PositionBizService.java
deleted file mode 100644
index 0c4cd7a423..0000000000
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/service/position/PositionBizService.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.eventmesh.admin.server.web.service.position;
-
-import org.apache.eventmesh.admin.server.AdminServerRuntimeException;
-import org.apache.eventmesh.common.protocol.grpc.adminserver.Metadata;
-import org.apache.eventmesh.common.remote.datasource.DataSourceType;
-import org.apache.eventmesh.common.remote.exception.ErrorCode;
-import org.apache.eventmesh.common.remote.offset.RecordPosition;
-import org.apache.eventmesh.common.remote.request.FetchPositionRequest;
-import org.apache.eventmesh.common.remote.request.RecordPositionRequest;
-import org.apache.eventmesh.common.remote.request.ReportPositionRequest;
-
-import java.util.List;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import lombok.extern.slf4j.Slf4j;
-
-@Service
-@Slf4j
-public class PositionBizService {
-
- @Autowired
- PositionHandlerFactory factory;
-
- // called isValidateReportRequest before call this
- public List getPosition(FetchPositionRequest request, Metadata metadata) {
- if (request == null) {
- return null;
- }
- isValidatePositionRequest(request.getDataSourceType());
- IFetchPositionHandler handler = factory.getHandler(request.getDataSourceType());
- return handler.handler(request, metadata);
- }
-
- public void isValidatePositionRequest(DataSourceType type) {
- if (type == null) {
- throw new AdminServerRuntimeException(ErrorCode.BAD_REQUEST, "data source type is null");
- }
- IReportPositionHandler handler = factory.getHandler(type);
- if (handler == null) {
- throw new AdminServerRuntimeException(ErrorCode.BAD_REQUEST,
- String.format("illegal data base type [%s], it not match any report position handler", type));
- }
- }
-
- // called isValidateReportRequest before call this
- public boolean reportPosition(ReportPositionRequest request, Metadata metadata) {
- if (request == null) {
- return false;
- }
- isValidatePositionRequest(request.getDataSourceType());
- IReportPositionHandler handler = factory.getHandler(request.getDataSourceType());
- return handler.handler(request, metadata);
- }
-
- public List