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]

Reply via email to