MQTTセッション読み取りecnnreset
mqttx を使用して mqtt サーバーに接続する場合の READ ECONNRESET のトラブルシューティング
会社は少し前に新しい mqtt サーバーを追加しました。最初のテストでは問題はありませんでしたが、数値が高いほど問題はありませんでした。接続数が増加し、数日おきに READ ECONNRESET が発生し、プロジェクトが正常に使用できなくなったので確認したところ、
インターネット上の回答をもとに調べてみたところ、次の記事
https://blog.csdn.net/slxz001/article/details/123368088
- 分析では、mqtt セッション キューがいっぱいで、mqtt セッション キューのパラメーターが変更された可能性があります。
- プロジェクト コード内の接続が多すぎる可能性もあります。これには、プロジェクト コードの接続ロジックを変更する必要があります。同じ mqtt クライアントを使用して mqtt に接続し、複数のトピックを同時に監視してください。これにより、接続が多すぎることを回避できます。この問題を回避するには、mqtt に接続してください。
問題を回避するために、正式な展開の前に一定数の接続をテストすることをお勧めします。
以下は小さなテストコードです
/**
* 需要自己添加application.yml配置
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
@ConfigurationProperties(prefix = "mqtt")
@Component
public class MqttProperties {
// mqtt服务器url
private String uri;
//登录用户名
private String username;
// 密码
private String password;
// 暂时没用,主题会在代码里动态获取
private String topic;
}
@RunWith(SpringRunner.class)
@SpringBootTest
@Log
public class MqttProducerTest {
@Autowired
private MqttProperties mqttProperties;
private static final String WATCH_PREFIX = "TEST";
/**
* 设备心跳客户端列表
*/
static Map<String, MqttClient> clientMap;
private static final ThreadPoolExecutor POOL_EXECUTOR;
static {
POOL_EXECUTOR = new ThreadPoolExecutor(2, 2, 30 * 1000,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(10000),
new CustomizableThreadFactory("pool"));
clientMap = new HashMap<>();
}
@Test
public void testConnect() {
int count = 10000;
int success = 0;
AtomicInteger fail = new AtomicInteger();
for (int i = 0; i < count; i++) {
log.info(String.format("准备创建第%d个client", i + 1));
// MQTT
String urlFrontSuffix = mqttProperties.getUri();
String clientId = "CLIENT-" + (System.currentTimeMillis() + "").substring(6);
/* TOPIC:TEST */
String watchTopic = WATCH_PREFIX + (i * 100);
System.out.println("clientId: " + clientId + " watchTopic: " + watchTopic);
try {
MemoryPersistence memoryPersistence = new MemoryPersistence();
MqttClient client = new MqttClient(urlFrontSuffix, clientId, memoryPersistence);
MqttConnectOptions options = getOptions();
// 设置回调函数,当订阅到信息时调用此方法
client.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable throwable) {
System.out.println(watchTopic + " Connection Lost.");
fail.incrementAndGet();
}
@Override
public void messageArrived(String str, MqttMessage mqttMessage) throws Exception {
// 订阅成功,并接受信息时调用
String payload = new String(mqttMessage.getPayload()); // 获取消息内容
log.info(String.format("accepted: %s channel:%s", payload, watchTopic));
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
System.out.println("deliveryComplete" + iMqttDeliveryToken);
}
});
client.connect(options);
client.subscribe(watchTopic);
log.info("connected the mqtt client" + clientId);
// mqtt客户端
clientMap.put(watchTopic, client);
if (client.isConnected()) {
success++;
log.info(String.format("第%d个client连接成功", success));
}
} catch (Exception e) {
e.printStackTrace();
}
}
log.info(String.format("总共启动%d, 成功%d, 失败%d", count, success, fail.get()));
}
private MqttConnectOptions getOptions() {
MqttConnectOptions options = new MqttConnectOptions();
// 设置客户端和服务器是否应在重新启动和重新连接期间记住状态 默认false
options.setCleanSession(true);
// 设置超时时间
options.setConnectionTimeout(10);
// 设置会话心跳时间
options.setKeepAliveInterval(20);
// 设置超时时间
options.setConnectionTimeout(10);
// 设置会话心跳时间
options.setKeepAliveInterval(20);
options.setUserName(mqttProperties.getUsername());
options.setPassword(mqttProperties.getPassword().toCharArray());
return options;
}
}
操作結果:
控制台- 10:35:55.099 [main] INFO c.f.a.c.m.MqttProducerTest - [testConnect,58] -准备创建第700个client
clientId: JAVA-CLIENT-4155099 watchTopic: xxx
已断开连接 (32109) - java.io.EOFException
at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:197)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.EOFException
at java.io.DataInputStream.readByte(DataInputStream.java:267)
at org.eclipse.paho.client.mqttv3.internal.wire.MqttInputStream.readMqttWireMessage(MqttInputStream.java:92)
at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:137)
... 1 more
控制台- 10:35:55.119 [main] INFO c.f.a.c.m.MqttProducerTest - [testConnect,58] -准备创建第701个client
clientId: JAVA-CLIENT-4155119 watchTopic: xxx
已断开连接 (32109) - java.io.EOFException
Java コードを使用してテスト ケースを作成しました。テストを実行すると、接続数が約 700 まで確立されたときに問題が発生し、java.io.EOFException エラー メッセージがスローされたことがわかりました。
java.io.EOF例外
EOFException: この例外は、入力中にファイルまたはストリームの終わりに予期せず到達した場合にスローされます。
この例外は主にデータ入力ストリームによって、ストリームの終わりに到達したことを示すために使用されます。他の多くの入力操作は、例外をスローするのではなく、ストリームの終わりに到達したことを示す特別な値を返すことに注意してください。
原因:
- データ ストリームにデータが書き込まれる順序は、データが読み取られる順序と一致しません。
- UTF は 2 バイトエンコーディングであり、writeChars メソッドは文字形式に従って書き込みを行うため、ファイル内で占有されるスペースは Unicode でエンコードされた同じ文字列よりも小さいため、readUTF メソッドを使用して読み取ると、EOF エラーが発生します。起こる。