fix: 首次握手失败停止重连并修正 Java 限速在途计数
This commit is contained in:
@@ -143,6 +143,12 @@ public final class Client {
|
||||
return lastStopCode == null ? "" : lastStopCode;
|
||||
}
|
||||
|
||||
int inflightSendsForTest() {
|
||||
synchronized (lock) {
|
||||
return inflightSends;
|
||||
}
|
||||
}
|
||||
|
||||
void setSendQueueLimitForTest(int n) {
|
||||
sendQueueLimit = n;
|
||||
}
|
||||
@@ -211,6 +217,19 @@ public final class Client {
|
||||
}
|
||||
}
|
||||
if (handshakeError != null) {
|
||||
synchronized (lock) {
|
||||
stopReconnect = true;
|
||||
wantConnected = false;
|
||||
if (lastStopCode == null || lastStopCode.isEmpty()) {
|
||||
if (handshakeError instanceof NixMsgException) {
|
||||
lastStopCode = ((NixMsgException) handshakeError).getCode();
|
||||
lastStopErr = (NixMsgException) handshakeError;
|
||||
} else {
|
||||
lastStopCode = "not_connected";
|
||||
lastStopErr = new NixMsgException("not_connected", handshakeError.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (handshakeError instanceof NixMsgException) {
|
||||
throw (NixMsgException) handshakeError;
|
||||
}
|
||||
@@ -229,6 +248,8 @@ public final class Client {
|
||||
synchronized (lock) {
|
||||
stopReconnect = true;
|
||||
wantConnected = false;
|
||||
lastStopCode = "not_connected";
|
||||
lastStopErr = new NixMsgException("not_connected", "连接未成功: " + state);
|
||||
}
|
||||
throw new NixMsgException("not_connected", "连接未成功: " + state);
|
||||
}
|
||||
@@ -704,7 +725,9 @@ public final class Client {
|
||||
transport.publish(Protocol.upTopic(endpointId), Protocol.dumps(hello));
|
||||
await(p.future, connectTimeoutMs);
|
||||
if (p.error != null) {
|
||||
throw p.error instanceof RuntimeException ? (RuntimeException) p.error : new NixMsgException("busy", p.error.getMessage());
|
||||
throw p.error instanceof RuntimeException
|
||||
? (RuntimeException) p.error
|
||||
: new NixMsgException("not_connected", p.error.getMessage());
|
||||
}
|
||||
if (p.response == null) {
|
||||
throw new NixMsgException("not_connected", "握手超时");
|
||||
@@ -883,9 +906,9 @@ public final class Client {
|
||||
if (!Boolean.TRUE.equals(frame.get("ok"))) {
|
||||
Map<String, Object> err = asMap(frame.get("error"));
|
||||
if ("rate_limited".equals(str(err.get("code"), ""))) {
|
||||
// 在途计数已在上方减过一次;只换新 rid 再排队,勿再减。
|
||||
synchronized (lock) {
|
||||
pending.remove(rid);
|
||||
inflightSends = Math.max(0, inflightSends - 1);
|
||||
p.rid = "";
|
||||
p.response = null;
|
||||
p.error = null;
|
||||
|
||||
@@ -41,7 +41,7 @@ public class K00Test {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testK00FirstConnectTimeout() {
|
||||
public void testK00FirstConnectTimeout() throws Exception {
|
||||
FakeTransport tr = new FakeTransport();
|
||||
tr.autoHello = null;
|
||||
client = new Client(tr, true, Types.DEFAULT_MAX_FRAME, Types.CLIENT_NAME, 150);
|
||||
@@ -51,6 +51,9 @@ public class K00Test {
|
||||
} catch (NixMsgException e) {
|
||||
assertEquals("not_connected", e.getCode());
|
||||
}
|
||||
int n = tr.connects.size();
|
||||
Thread.sleep(350);
|
||||
assertEquals("首次握手失败后不得自动重连", n, tr.connects.size());
|
||||
tr.autoHello = new LinkedHashMap<String, Object>();
|
||||
tr.autoHello.put("server_time_ms", 1750000000000L);
|
||||
tr.autoHello.put("server_version", "0.1.0");
|
||||
@@ -63,6 +66,108 @@ public class K00Test {
|
||||
tr.autoHello.put("session_token", "nst_test_token");
|
||||
client.connectSync("ws://example.test/mqtt", "ep1", "p", null, false);
|
||||
assertEquals(ConnectionState.ONLINE, client.getState());
|
||||
assertTrue(tr.connects.size() > n);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testK00RateLimitedInflightOnce() throws Exception {
|
||||
Types.ReconnectBackoff.disableJitterForTest();
|
||||
FakeTransport tr = new FakeTransport();
|
||||
tr.autoSendOk = false;
|
||||
final List<String> holdRids = new CopyOnWriteArrayList<String>();
|
||||
final List<String> limitedRids = new CopyOnWriteArrayList<String>();
|
||||
tr.onUp(new java.util.function.Function<Map<String, Object>, Map<String, Object>>() {
|
||||
@Override
|
||||
public Map<String, Object> apply(Map<String, Object> frame) {
|
||||
if (!"send".equals(String.valueOf(frame.get("type")))) {
|
||||
return null;
|
||||
}
|
||||
String rid = String.valueOf(frame.get("rid"));
|
||||
String id = String.valueOf(frame.get("id"));
|
||||
if ("hold".equals(id)) {
|
||||
holdRids.add(rid);
|
||||
return null; // 保持在途
|
||||
}
|
||||
limitedRids.add(rid);
|
||||
if (limitedRids.size() == 1) {
|
||||
Map<String, Object> err = new LinkedHashMap<String, Object>();
|
||||
err.put("code", "rate_limited");
|
||||
err.put("message", "slow");
|
||||
Map<String, Object> resp = new LinkedHashMap<String, Object>();
|
||||
resp.put("v", 1);
|
||||
resp.put("type", "resp");
|
||||
resp.put("rid", rid);
|
||||
resp.put("ok", false);
|
||||
resp.put("error", err);
|
||||
return resp;
|
||||
}
|
||||
Map<String, Object> data = new LinkedHashMap<String, Object>();
|
||||
data.put("id", frame.get("id"));
|
||||
data.put("send_at_ms", frame.get("send_at_ms"));
|
||||
data.put("state", "scheduled");
|
||||
Map<String, Object> resp = new LinkedHashMap<String, Object>();
|
||||
resp.put("v", 1);
|
||||
resp.put("type", "resp");
|
||||
resp.put("rid", rid);
|
||||
resp.put("ok", true);
|
||||
resp.put("data", data);
|
||||
return resp;
|
||||
}
|
||||
});
|
||||
connectOnline(tr);
|
||||
Thread holder = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
SendOptions opt = new SendOptions();
|
||||
opt.messageId = "hold";
|
||||
opt.sendAtMs = 1700000000001L;
|
||||
client.sendSync(new Target("endpoint", "ep2"), new Body("hold"), opt);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
holder.setDaemon(true);
|
||||
holder.start();
|
||||
long deadline = System.currentTimeMillis() + 2000;
|
||||
while (holdRids.isEmpty() && System.currentTimeMillis() < deadline) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
assertFalse(holdRids.isEmpty());
|
||||
assertEquals(1, client.inflightSendsForTest());
|
||||
final SendOptions opt = new SendOptions();
|
||||
opt.messageId = "lim";
|
||||
opt.sendAtMs = 1700000000002L;
|
||||
Thread sender = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
client.sendSync(new Target("endpoint", "ep2"), new Body("lim"), opt);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
sender.setDaemon(true);
|
||||
sender.start();
|
||||
deadline = System.currentTimeMillis() + 3000;
|
||||
while (limitedRids.size() < 1 && System.currentTimeMillis() < deadline) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
assertTrue(limitedRids.size() >= 1);
|
||||
// rate_limited 后只应减 1:仍剩 hold 那一条在途
|
||||
deadline = System.currentTimeMillis() + 500;
|
||||
int seen = -1;
|
||||
while (System.currentTimeMillis() < deadline) {
|
||||
seen = client.inflightSendsForTest();
|
||||
if (seen == 1) {
|
||||
break;
|
||||
}
|
||||
Thread.sleep(10);
|
||||
}
|
||||
assertEquals("rate_limited 后在途应只减 1", 1, seen);
|
||||
client.close();
|
||||
holder.join(1000);
|
||||
sender.join(1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user