Chrisp12138 opened a new issue, #7168:
URL: https://github.com/apache/shenyu/issues/7168
## Description
When using the Spring websocket client
(`shenyu-spring-boot-starter-client-spring-websocket`) in `isFull=true` mode,
the websocket selector is never created in shenyu-admin and the ws route never
becomes available. The admin logs an endless 5s retry loop of `selectorDO
object is null, wait to retry`.
The http (springmvc) client in the same `isFull` mode works, because it
publishes **both** a `MetaDataRegisterDTO` and a `URIRegisterDTO`. The
websocket client publishes **only** the `URIRegisterDTO`, while the admin side
**never creates** a selector from a URI report — it requires the selector to
already exist.
## Environment
* ShenYu version(s): 2.7.2-SNAPSHOT (master as of 2026-09), same code paths
exist in 2.7.0/2.7.1
* Client: Spring Boot 3.5.x (spring-websocket handler endpoints, **no**
`@ShenyuServerEndpoint` annotations)
* Register type: `http` (`shenyu.register.registerType=http`)
## Steps to reproduce
1. Start shenyu-admin; enable the `websocket` plugin (it is enabled by
default).
2. Start a service with `shenyu-spring-boot-starter-client-spring-websocket`
on the classpath and:
```yaml
shenyu:
register:
registerType: http
serverLists: http://127.0.0.1:9095
props:
username: admin
password: 123456
client:
websocket:
props:
appName: my-service
contextPath: /my-service
port: 8101
protocol: ws://
isFull: true
```
3. Watch the admin log and the `selector` table for plugin `websocket`.
**Actual result:** every ~5 s the admin logs
```
Register exception: cause: doRegister Failed to execute, because selectorDO
object is null, wait to retry.
```
and no row is ever created in `selector` for plugin `websocket`. WS
handshakes through the gateway fail with `{"code":-107,"message":"websocket:Can
not find selector..."}` (and after a manually pre-created selector,
`{"code":-102,"message":"websocket:Rule not found!"}` until a rule is also
created by hand).
## Root cause
1. `SpringWebSocketClientEventListener#getBeans` — the `isFull` branch
publishes **only** a `URIRegisterDTO` and returns an empty bean map, so no
metadata is ever reported:
```java
//
shenyu-client-websocket/shenyu-client-spring-websocket/.../SpringWebSocketClientEventListener.java
public Map<String, Object> getBeans(final ApplicationContext context) {
if (Boolean.TRUE.equals(isFull)) {
if (!markRegistered()) {
return Collections.emptyMap();
}
LOG.info("init spring websocket client success with isFull mode");
List<String> namespaceIds = super.getNamespace();
namespaceIds.forEach(namespaceId ->
getPublisher().publishEvent(buildURIRegisterDTO(context,
Collections.emptyMap(), namespaceId)));
return Collections.emptyMap(); // <-- no
MetaDataRegisterDTO ever published
}
...
}
```
2. On the admin side, a URI report **updates** an existing selector but does
not create one:
```java
//
shenyu-admin/.../AbstractShenyuClientRegisterServiceImpl.java#doRegisterURI
SelectorDO selectorDO =
selectorService.findByNameAndPluginNameAndNamespaceId(selectorName, pluginName,
namespaceId);
if (Objects.isNull(selectorDO)) {
throw new ShenyuException("doRegister Failed to execute, because
selectorDO object is null, wait to retry.");
}
```
3. The http client's `isFull` branch publishes metadata **and** URI (the
correct pattern) — see `SpringMvcClientEventListener#getBeans`
(`publishEvent(MetaDataRegisterDTO.builder()...)` followed by
`publishEvent(buildURIRegisterDTO(...))`). The websocket client is asymmetric
with it.
Since no metadata ever arrives, the selector is never created and the 5 s
`FallbackRegisterTask` retry (unlimited attempts) never converges.
## Expected result
`isFull=true` on the websocket client should create the websocket selector +
default rule (context-path based) from a metadata report, and then fill the
handle from the URI report — exactly like the springmvc client.
## Suggested fix
In `SpringWebSocketClientEventListener#getBeans` (`isFull` branch), publish
a `MetaDataRegisterDTO` before the URI report, mirroring
`SpringMvcClientEventListener`:
```java
getPublisher().publishEvent(MetaDataRegisterDTO.builder()
.appName(getAppName())
.contextPath(getContextPath())
.path(contextPath) // registerContextPath creates selector
+ default rule
.rpcType(RpcTypeEnum.WEB_SOCKET.getName())
.enabled(true)
.namespaceId(namespaceId)
.build());
```
(Alternatively, `doRegisterURI` could lazily create the selector + default
rule when missing — but aligning the client with the http client's isFull
behavior is the smaller, symmetric change.)
## Workaround we used
Manually pre-create the websocket selector + rule (admin REST), after which
the pending URI report is accepted and the handle is filled with
`ws://host:port`.
--
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]