zhaohai666 opened a new pull request, #1232:
URL: https://github.com/apache/rocketmq-dashboard/pull/1232
# [RIP-2] ProxyAdminService control-plane integration: client visibility
(CLIENT-01), route & batch diagnostics, ProxyAdmin naming
> Branch: `feature/studio-rip2-integration`
> Base: `rocketmq-studio` (`7b7ce9bf`)
> Commits: `d60cde23` (feat) + `dbb69e05` (refactor) — 14 files, +1426 / −12
## Background
RocketMQ 5.x gRPC clients (the `rocketmq-client-java` SDK, RIP-1 data plane)
attach to the
**proxy** and never register on the brokers, so the broker-side admin
surface cannot see them.
CLIENT-01 requires the Studio dashboard to expose these online clients.
The proxy ships a dedicated gRPC control-plane service — `ProxyAdminService`
(`apache.rocketmq.v2`, defined in `rocketmq-apis` `admin.proto`) — which has
a **cluster-wide**
view of all clients attached to the proxy. This change wires that surface
into the Studio
backend:
- a gRPC client (`ProxyAdminClient`) speaking `ProxyAdminService` with ACL
2.0 request signing;
- a REST controller (`/api/proxy-admin/*`) exposing route observation and
diagnostics;
- PROXY instances routed through RIP-2 for client listing, so the dashboard
can show clients
that broker-side admin can never see.
## Feature table
| Endpoint | Method | Purpose | Files | Tests |
| --- | --- | --- | --- | --- |
| `GET /api/clients?instanceId=` (PROXY) | GET | List online gRPC clients
via `ProxyAdminService.ListClients` (scope ALL_PROXIES); DIRECT instances keep
the broker-admin path | `RocketMQClientProvider`,
`ProxyAdminClient.listClients` | `ProxyAdminClientTest`,
`RocketMQClientProviderTest` |
| `GET /api/proxy-admin/route-topology?instanceId=&topic=` | GET |
Proxy→broker route links + per-broker queue load | `ProxyAdminController`,
`ProxyAdminClient.describeRouteTopology` | `ProxyAdminControllerTest` |
| `GET /api/proxy-admin/route-events?instanceId=&windowSeconds=&maxEvents=`
| GET | Bounded-window route change events (initial `ROUTE_SNAPSHOT` replay
included) | `ProxyAdminController`, `ProxyAdminClient.collectRouteEvents` |
`ProxyAdminControllerTest`, `ProxyAdminClientTest` |
| `GET
/api/proxy-admin/pop-receipt-handles?instanceId=&group=&topic=&pageNum=&pageSize=`
| GET | POP receipt handles of a batch-consume group | `ProxyAdminController`,
`ProxyAdminClient.describePopReceiptHandles` | `ProxyAdminControllerTest` |
| `GET
/api/proxy-admin/batch-consume-diagnostics?instanceId=&group=&topic=&clientId=`
| GET | Per-client batch-consume diagnostics for a group |
`ProxyAdminController`, `ProxyAdminClient.describeBatchConsumeDiagnostics` |
`ProxyAdminControllerTest` |
| Broker-admin ACL hook (AUTH-01) | n/a | When
`studio.rocketmq.acl.access-key/secret-key` are set, every `DefaultMQAdminExt`
created by the studio attaches an `AclClientRPCHook` automatically |
`MqAdminExtFactory`, `RocketMQAdminConfig`, `RocketMQProperties` |
`MqAdminExtFactoryTest` |
| Dev profile H2 schema init | n/a | `spring.sql.init` executes
`classpath:db/schema.sql` so the H2 in-memory store is bootstrapped |
`application-dev.yml` | `RocketMQClientProviderTest` (context) |
### New configuration keys
| Key | Env var | Default | Description |
| --- | --- | --- | --- |
| `studio.proxyadmin.username` | `STUDIO_PROXYADMIN_USERNAME` | empty | ACL
2.0 account for the `ProxyAdminService` (least privilege: read-only is
sufficient) |
| `studio.proxyadmin.password` | `STUDIO_PROXYADMIN_PASSWORD` | empty | ACL
2.0 secret for the above |
| `studio.rocketmq.acl.access-key` | `STUDIO_ROCKETMQ_ACL_ACCESS_KEY` |
empty | Broker-admin ACL 2.0 access key (AUTH-01) |
| `studio.rocketmq.acl.secret-key` | `STUDIO_ROCKETMQ_ACL_SECRET_KEY` |
empty | Broker-admin ACL 2.0 secret |
## Testing
### Backend (offline, JDK 21)
```
cd server &&
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-21.jdk/Contents/Home \
mvn test -Dtest='!StudioApplicationTest'
```
Result: **802 tests, 802 passed** (0 failures, 0 errors).
`StudioApplicationTest` is excluded —
it boots the full Spring context against a live cluster.
### End-to-end (local 5.5.0 cluster, ACL 2.0 enabled)
1. **RIP-1 data plane** (`rocketmq-client-java` 5.0.7, account `rip2sdk`):
producer sends and
consumer receives on `rip2_chain_topic` / `rip2_chain_grp` — full
send/consume/ack loop OK.
2. **RIP-2 control plane** (dashboard, account `rip2ro`, read-only):
- `GET /api/clients?instanceId=instance-proxy-1` → 200, returns 2 online
gRPC clients
(Producer + Consumer, protocol=gRPC, version=5.0.7, attached to proxy
`:8081`);
- `GET /api/proxy-admin/route-topology` → 200 (proxy→broker-a links
healthy, queue load);
- `GET /api/proxy-admin/route-events?windowSeconds=2` → 200 with
`ROUTE_SNAPSHOT` events
(the previous `DEADLINE_EXCEEDED` 502 is fixed by consuming the
server-stream on a daemon
reader thread over a bounded window instead of blocking to the gRPC
deadline);
3. **Least-privilege enforcement** (negative): account `rip2sdk` (data-plane
only, no
`proxy.admin.*` grants) calling `ListClients` → `PERMISSION_DENIED ... no
matched policies`;
`rip2ro` succeeds — proving read-only isolation.
## Design notes
- **ACL 2.0 signing.** Requests carry `MQv2-HMAC-SHA1
Credential=<user>/<dateTime>,Signature=<hex>`
plus `x-mq-date-time` headers (identical scheme to the data plane).
Credentials come from
`studio.proxyadmin.*`; an empty username skips the interceptor (open
clusters).
- **Server-streaming without deadline flakiness.** `subscribeRouteEvents` is
a gRPC
server-stream; the dashboard consumes it on a daemon reader thread and
returns events
collected within the requested window. `DEADLINE_EXCEEDED`/`CANCELLED` on
window close are
treated as a normal stop, not an error.
- **Naming.** The RIP-2 surface was renamed from the `Rip2*` prefix to
semantic `ProxyAdmin*`
(`org.apache.rocketmq.studio.proxyadmin`, `ProxyAdminClient` /
`ProxyAdminController` / `ProxyAdminDiagnosticsVO`). The public REST paths
(`/api/proxy-admin/*`) and the broker-side account names are unchanged.
## Breaking changes / migration
- Env vars `STUDIO_RIP2_USERNAME` / `STUDIO_RIP2_PASSWORD` are **renamed** to
`STUDIO_PROXYADMIN_USERNAME` / `STUDIO_PROXYADMIN_PASSWORD`. Deployments
that set the old
names must update their environment.
- `server/pom.xml` adds `rocketmq-proto:2.3.0` (locally built from
`rocketmq-apis`) and
`grpc-netty-shaded:1.53.0` (matches the proxy's gRPC transport, avoids
raw-netty conflicts).
## Items not done (and why)
- **Broker-side ACL seeding** (`mqadmin createUser` / `updateAcl` and the
`seed_acl.sh` script)
lives outside this repo/PR — it is cluster provisioning, not Studio code.
- **`db/schema.sql` seed-data fix** (PROXY instance endpoints pointing at
real addresses) is a
local, environment-specific change and is intentionally **not** included
in this commit.
- **`StudioApplicationTest`** requires a live broker and is excluded from
the offline suite.
- **`pop-receipt-handles`** returns empty for push consumers (no POP receipt
handles), matching
the proxy's semantics — expected, not a defect.
## Verification commands
```bash
# unit tests
cd server &&
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-21.jdk/Contents/Home \
mvn test -Dtest='!StudioApplicationTest'
# run the dashboard against an ACL-enabled cluster (read-only account)
STUDIO_PROXYADMIN_USERNAME=rip2ro STUDIO_PROXYADMIN_PASSWORD=rip2ro123 \
java -jar server/target/rocketmq-studio-1.0.0.jar --server.port=8888
# smoke
curl 'http://127.0.0.1:8888/api/clients?instanceId=instance-proxy-1'
curl
'http://127.0.0.1:8888/api/proxy-admin/route-topology?instanceId=instance-proxy-1'
curl
'http://127.0.0.1:8888/api/proxy-admin/route-events?instanceId=instance-proxy-1&windowSeconds=2'
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]