fix:主备切换测试
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user