fix:主备切换测试

This commit is contained in:
waner
2025-05-30 16:21:39 +08:00
parent 085d71feb9
commit fbcbe79388
11 changed files with 543 additions and 157 deletions
@@ -0,0 +1,135 @@
package com.sunyard.pool;
import racal.sunyard.main.SydApi4j;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.*;
public class ConnectionManager {
// 单例实例(静态内部类实现线程安全延迟加载)
private static class Holder {
static final ConnectionManager INSTANCE = new ConnectionManager();
}
// 私有构造函数(禁止外部实例化)
private ConnectionManager() {
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
}
// 全局访问点
public static ConnectionManager getInstance() {
return Holder.INSTANCE;
}
// 使用线程安全的ConcurrentHashMap存储连接
private final ConcurrentHashMap<String, SydApi4j> connectionCache = new ConcurrentHashMap<>();
/**
* 获取或创建指定URL的连接
* @param url 目标服务地址
* @return 已存在的或新建的SydReq连接
* @throws IOException 当创建连接失败时抛出
*/
public SydApi4j getConnection(String url, int timeout) throws Exception {
// 使用compute方法保证原子操作
return connectionCache.compute(url, (key, existingConn) -> {
try {
// 验证现有连接有效性
if (isConnectionValid(existingConn)) {
return existingConn;
}
// 关闭失效连接(如果存在)
closeConnectionQuietly(existingConn);
// 创建新连接
return createNewConnection(url, timeout);
} catch (Exception e) {
throw new RuntimeException("连接创建失败", e);
}
});
}
// 连接有效性验证
private boolean isConnectionValid(SydApi4j conn) {
return conn != null && conn.isActive();
}
// 安全关闭连接
private void closeConnectionQuietly(SydApi4j conn) {
try {
if (conn != null) {
conn.disconnect();
}
} catch (Exception e) {
}
}
// 创建新连接(提取共用逻辑)
private SydApi4j createNewConnection(String url, int timeout) throws Exception {
try {
String[] addressParts = parseUrl(url);
String ip = addressParts[0];
int port = Integer.parseInt(addressParts[1]);
SydApi4j newConn = new SydApi4j();
newConn.connect(ip, port, null, timeout);
return newConn;
} catch (Exception e) {
throw new RuntimeException("连接创建失败: " + url, e);
}
}
// URL解析验证
private String[] parseUrl(String url) throws IllegalArgumentException {
String[] parts = url.split(":");
if (parts.length != 2) {
throw new IllegalArgumentException("无效的URL格式: " + url);
}
return parts;
}
/**
* 关闭指定URL的连接并从缓存移除
* @param url 需要关闭的服务地址
*/
public void closeConnection(String url) {
SydApi4j conn = connectionCache.remove(url);
if (conn != null) {
try {
conn.disconnect(); // 假设这是关闭连接的方法
System.out.println("连接已关闭: " + url);
} catch (Exception e) {
System.err.println("关闭连接异常 [" + url + "]: " + e.getMessage());
}
}
}
/**
* 关闭所有连接并清空缓存
*/
public void closeAllConnections() {
// 创建当前URL的副本避免并发修改
Set<String> urls = ConcurrentHashMap.newKeySet();
urls.addAll(connectionCache.keySet());
urls.forEach(this::closeConnection);
}
/**
* 获取当前缓存中的连接数量
*/
public int size() {
return connectionCache.size();
}
private void shutdown() {
connectionCache.forEach((url, conn) -> {
conn.disconnect();
});
connectionCache.clear();
}
}
@@ -9,6 +9,7 @@ import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
@@ -79,8 +80,14 @@ public class SnmpTrapSender implements AutoCloseable{
new TimeTicks(System.currentTimeMillis()/1000)));
pdu.add(new VariableBinding(SnmpConstants.snmpTrapOID,
new OID(enterpriseOid + ".1.1")));
byte[] msg = new byte[10];
try {
msg = message.getBytes("GBK");
} catch (Exception e) {
System.out.println("编码错误");
}
pdu.add(new VariableBinding(new OID(enterpriseOid + ".2.1"),
new OctetString(message)));
new OctetString(msg)));
pdu.add(new VariableBinding(new OID(enterpriseOid + ".2.2"),
new Integer32(severity)));
}
@@ -1,11 +1,7 @@
package com.sunyard.task;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class AsyncTaskExecutor {
@@ -20,24 +16,7 @@ public class AsyncTaskExecutor {
// 默认配置构造函数
public AsyncTaskExecutor() {
this(4, 2);
}
public List<TaskOutcome<String>> execTasks(List<TaskContext<String>> taskContexts) {
List<CompletableFuture<String>> futures =
taskContexts.stream().map(this::retryWithDelay).collect(Collectors.toList());
List<TaskOutcome<String>> taskOutcomes = new ArrayList<>();
taskContexts.forEach(task ->
retryWithDelay(task).whenCompleteAsync((result, ex) -> {
if (ex != null) {
// 提取原始异常
Throwable rootCause = ex instanceof CompletionException ? ex.getCause() : ex;
taskOutcomes.add(new TaskOutcome<>(task, rootCause));
} else {
taskOutcomes.add(new TaskOutcome<>(task, result));
}
}));
return taskOutcomes;
this(2, 2);
}
public List<TaskOutcome<String>> execBatchTasks(List<TaskContext<String>> taskContexts) {
@@ -94,12 +73,12 @@ public class AsyncTaskExecutor {
private <T> CompletableFuture<T> handleRetry(TaskContext<T> context, Throwable ex) {
if (context.canRetry()) {
context.decreaseRetry();
System.out.printf("[重试调度] ID: %s| IP:%s | 延迟: %ss | 剩余重试: %d | 错误: %s%n",
context.getTaskId(),
context.getAddress(),
context.getRetryDelay().getSeconds(),
context.getRetriesLeft(),
ex.getCause().getMessage());
// System.out.printf("[重试调度] ID: %s| IP:%s | 延迟: %ss | 剩余重试: %d | 错误: %s%n",
// context.getTaskId(),
// context.getAddress(),
// context.getRetryDelay().getSeconds(),
// context.getRetriesLeft(),
// ex.getCause().getMessage());
// 创建延迟重试的Future
CompletableFuture<T> delayedRetry = new CompletableFuture<>();
@@ -0,0 +1,107 @@
package com.sunyard.util;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* IP锁工具类 - 高并发安全实现
*
* 为每个IP地址提供唯一的可重入锁,确保同一IP的操作顺序执行
*/
public class IpLockUtils {
private static final ConcurrentMap<String, ReentrantLock> LOCK_MAP = new ConcurrentHashMap<>();
private static final Object CREATION_LOCK = new Object();
/**
* 获取IP对应的锁(保证唯一性)
* @param ip IP地址
* @return 可重入锁实例
*/
public static ReentrantLock getLockForIp(String ip) {
ReentrantLock lock = LOCK_MAP.get(ip);
if (lock != null) {
return lock;
}
synchronized (CREATION_LOCK) {
// 双重检查
lock = LOCK_MAP.get(ip);
if (lock == null) {
lock = new ReentrantLock(true); // 公平锁
LOCK_MAP.put(ip, lock);
}
return lock;
}
}
/**
* 安全加锁(带超时机制)
* @param ip IP地址
* @param timeout 超时时间
* @param unit 时间单位
* @return 是否成功获取锁
* @throws InterruptedException
*/
public static boolean lockWithTimeout(String ip, long timeout, TimeUnit unit)
throws InterruptedException {
ReentrantLock lock = getLockForIp(ip);
return lock.tryLock(timeout, unit);
}
/**
* 解锁指定IP的锁
* @param ip IP地址
*/
public static void unlockForIp(String ip) {
ReentrantLock lock = LOCK_MAP.get(ip);
if (lock != null && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
/**
* 清理未使用的锁(可选)
*/
public static void cleanupUnusedLocks() {
LOCK_MAP.entrySet().removeIf(entry ->
!entry.getValue().isLocked() &&
entry.getValue().getQueueLength() == 0
);
}
/**
* 获取当前锁数量
*/
public static int getLockCount() {
return LOCK_MAP.size();
}
/**
* 获取IP锁状态
* @param ip IP地址
* @return 锁状态字符串
*/
public static String getLockStatus(String ip) {
ReentrantLock lock = LOCK_MAP.get(ip);
if (lock == null) {
return "No lock exists";
}
return String.format("Locked: %s, Queue: %d, HoldCount: %d",
lock.isLocked(), lock.getQueueLength(), lock.getHoldCount());
}
/**
* 重置所有锁
*/
public static void reset() {
synchronized (CREATION_LOCK) {
LOCK_MAP.clear();
}
}
}