feat(SMB): 重构SMB文件服务支持多服务器配置和连接池优化
重构SmbFileService以支持多服务器配置,引入连接池和会话池管理机制。主要变更包括: 1. 实现基于主机的多服务器认证配置 2. 新增连接池和会话池管理,提高连接复用率 3. 添加定时清理空闲连接和会话的功能 4. 优化异常处理和重试机制 5. 改进日志记录和资源释放 同时更新相关配置文件和应用属性以支持新功能: 1. 修改application.properties支持多服务器SMB配置 2. 增强SmbConfig类以管理多服务器配置 3. 添加任务映射到tasker_mapper.json 4. 新增客户端和服务端任务规则文档
This commit is contained in:
@@ -1,21 +1,5 @@
|
||||
package com.ecep.contract.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.ecep.contract.SpringApp;
|
||||
import com.ecep.contract.config.SmbConfig;
|
||||
import com.hierynomus.msdtyp.AccessMask;
|
||||
@@ -26,13 +10,26 @@ import com.hierynomus.mssmb2.SMB2CreateDisposition;
|
||||
import com.hierynomus.mssmb2.SMB2CreateOptions;
|
||||
import com.hierynomus.mssmb2.SMB2ShareAccess;
|
||||
import com.hierynomus.mssmb2.SMBApiException;
|
||||
import com.hierynomus.protocol.transport.TransportException;
|
||||
import com.hierynomus.smbj.SMBClient;
|
||||
import com.hierynomus.smbj.auth.AuthenticationContext;
|
||||
import com.hierynomus.smbj.common.SMBRuntimeException;
|
||||
import com.hierynomus.smbj.common.SmbPath;
|
||||
import com.hierynomus.smbj.event.SessionLoggedOff;
|
||||
import com.hierynomus.smbj.share.DiskShare;
|
||||
import com.hierynomus.smbj.share.File;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* SMB文件服务类,提供SMB/CIFS协议的文件操作功能
|
||||
@@ -41,21 +38,59 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Service
|
||||
public class SmbFileService implements DisposableBean {
|
||||
private final SMBClient client;
|
||||
private AuthenticationContext authContext;
|
||||
private final SmbConfig smbConfig;
|
||||
private final ReentrantLock authLock = new ReentrantLock();
|
||||
|
||||
// 连接空闲超时时间:3分钟
|
||||
private static final long CONNECTION_IDLE_TIMEOUT_MS = 3 * 60 * 1000;
|
||||
|
||||
// 连接信息内部类,用于存储连接和最后使用时间
|
||||
// Session信息内部类,用于存储session和最后使用时间
|
||||
private static class SessionInfo implements AutoCloseable {
|
||||
private final com.hierynomus.smbj.session.Session session;
|
||||
private final String username; // 添加用户名字段
|
||||
private volatile long lastUsedTimestamp;
|
||||
|
||||
public SessionInfo(com.hierynomus.smbj.session.Session session, String username) {
|
||||
this.session = session;
|
||||
this.username = username;
|
||||
this.lastUsedTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public com.hierynomus.smbj.session.Session getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public long getLastUsedTimestamp() {
|
||||
return lastUsedTimestamp;
|
||||
}
|
||||
|
||||
public void updateLastUsedTimestamp() {
|
||||
this.lastUsedTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public boolean isIdle(long timeoutMs) {
|
||||
return (System.currentTimeMillis() - lastUsedTimestamp) > timeoutMs;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 连接信息内部类,用于存储连接和session连接池
|
||||
private static class ConnectionInfo {
|
||||
private final com.hierynomus.smbj.connection.Connection connection;
|
||||
private volatile long lastUsedTimestamp;
|
||||
// Session连接池,使用List存储
|
||||
private final List<SessionInfo> sessionPool;
|
||||
|
||||
public ConnectionInfo(com.hierynomus.smbj.connection.Connection connection) {
|
||||
this.connection = connection;
|
||||
this.lastUsedTimestamp = System.currentTimeMillis();
|
||||
this.sessionPool = Collections.synchronizedList(new ArrayList<>());
|
||||
}
|
||||
|
||||
public com.hierynomus.smbj.connection.Connection getConnection() {
|
||||
@@ -73,6 +108,81 @@ public class SmbFileService implements DisposableBean {
|
||||
public boolean isIdle(long timeoutMs) {
|
||||
return (System.currentTimeMillis() - lastUsedTimestamp) > timeoutMs;
|
||||
}
|
||||
|
||||
// 清理空闲的session
|
||||
public int cleanupIdleSessions(long timeoutMs) {
|
||||
List<SessionInfo> idleSessions = new java.util.ArrayList<>();
|
||||
|
||||
// 查找所有空闲session
|
||||
for (SessionInfo sessionInfo : sessionPool) {
|
||||
if (sessionInfo != null && sessionInfo.isIdle(timeoutMs)) {
|
||||
idleSessions.add(sessionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭并移除空闲session
|
||||
synchronized (sessionPool) {
|
||||
for (SessionInfo sessionInfo : idleSessions) {
|
||||
if (sessionInfo != null && sessionPool.contains(sessionInfo)) {
|
||||
try {
|
||||
sessionInfo.close();
|
||||
} catch (IOException e) {
|
||||
log.error("Error closing idle session for username: {}", sessionInfo.getUsername(), e);
|
||||
}
|
||||
sessionPool.remove(sessionInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return idleSessions.size();
|
||||
}
|
||||
|
||||
// 从session池中获取任意一个有效的session
|
||||
public SessionInfo peekSession() {
|
||||
if (sessionPool.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return sessionPool.removeFirst();
|
||||
}
|
||||
|
||||
// 创建新session并添加到池中
|
||||
public SessionInfo createSession(AuthenticationContext authContext) throws IOException {
|
||||
String username = authContext.getUsername();
|
||||
// 创建新session
|
||||
com.hierynomus.smbj.session.Session session = connection.authenticate(authContext);
|
||||
SessionInfo newSession = new SessionInfo(session, username);
|
||||
return newSession;
|
||||
}
|
||||
|
||||
// 更新session的最后使用时间
|
||||
public void returnSession(SessionInfo sessionInfo) {
|
||||
// 重新添加到池中
|
||||
sessionPool.addLast(sessionInfo);
|
||||
sessionInfo.updateLastUsedTimestamp();
|
||||
}
|
||||
|
||||
// 关闭所有session
|
||||
public void closeAllSessions() {
|
||||
// 创建副本以避免并发修改异常
|
||||
List<SessionInfo> sessionsCopy = new ArrayList<>();
|
||||
|
||||
// 先获取副本并清空池
|
||||
synchronized (sessionPool) {
|
||||
sessionsCopy.addAll(sessionPool);
|
||||
sessionPool.clear();
|
||||
}
|
||||
|
||||
// 关闭所有session
|
||||
for (SessionInfo sessionInfo : sessionsCopy) {
|
||||
try {
|
||||
if (sessionInfo != null) {
|
||||
sessionInfo.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Error closing session for username: {}", sessionInfo.getUsername(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 连接池,使用ConcurrentHashMap确保线程安全
|
||||
@@ -91,123 +201,148 @@ public class SmbFileService implements DisposableBean {
|
||||
this.client = smbClient;
|
||||
this.smbConfig = SpringApp.getBean(SmbConfig.class);
|
||||
|
||||
this.smbConfig.subscribe(this);
|
||||
|
||||
// 初始化定时清理任务,每30秒运行一次
|
||||
this.cleanupScheduler = executor;
|
||||
|
||||
// 启动定时清理任务,延迟1分钟后开始,每30秒执行一次
|
||||
this.cleanupScheduler.scheduleAtFixedRate(this::cleanupIdleConnections, 1, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
//
|
||||
// @net.engio.mbassy.listener.Handler
|
||||
// private void onSessionLoggedOff(SessionLoggedOff sessionLoggedOffEvent) {
|
||||
//
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取认证上下文,线程安全实现
|
||||
* 获取认证上下文,根据主机名获取对应的认证信息
|
||||
*
|
||||
* @param host 主机名
|
||||
* @return 认证上下文
|
||||
* @throws IOException 如果找不到对应的服务器配置
|
||||
*/
|
||||
private AuthenticationContext getAuthenticationContext(String host) {
|
||||
// 双重检查锁定模式,确保线程安全
|
||||
if (authContext == null) {
|
||||
authLock.lock();
|
||||
try {
|
||||
if (authContext == null) {
|
||||
log.debug("Creating new AuthenticationContext for host: {}", host);
|
||||
authContext = new AuthenticationContext(
|
||||
smbConfig.getUsername(),
|
||||
smbConfig.getPassword().toCharArray(),
|
||||
"");
|
||||
}
|
||||
} finally {
|
||||
authLock.unlock();
|
||||
}
|
||||
private AuthenticationContext getAuthenticationContext(String host) throws IOException {
|
||||
log.debug("Creating AuthenticationContext for host: {}", host);
|
||||
|
||||
// 获取该主机对应的配置信息
|
||||
SmbConfig.ServerConfig serverConfig = smbConfig.getServerConfig(host);
|
||||
|
||||
// 检查是否找到配置
|
||||
if (serverConfig == null) {
|
||||
String errorMsg = String.format("No SMB configuration found for host: %s", host);
|
||||
log.error(errorMsg);
|
||||
throw new IOException(errorMsg);
|
||||
}
|
||||
return authContext;
|
||||
|
||||
// 检查配置是否完整
|
||||
if (serverConfig.getUsername() == null || serverConfig.getPassword() == null) {
|
||||
String errorMsg = String.format("Incomplete SMB configuration for host: %s, username or password missing",
|
||||
host);
|
||||
log.error(errorMsg);
|
||||
throw new IOException(errorMsg);
|
||||
}
|
||||
|
||||
return new AuthenticationContext(
|
||||
serverConfig.getUsername(),
|
||||
serverConfig.getPassword().toCharArray(),
|
||||
"");
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行SMB操作的通用方法,简化连接和会话的创建
|
||||
*
|
||||
* @param smbPath SMB路径
|
||||
* @param operation 要执行的操作
|
||||
* @param <T> 操作返回类型
|
||||
* @return 操作结果
|
||||
* @throws IOException 如果操作失败
|
||||
*/
|
||||
/**
|
||||
* 从连接池获取或创建连接
|
||||
*
|
||||
*
|
||||
* @param hostname 主机名
|
||||
* @return SMB连接
|
||||
* @throws IOException 如果创建连接失败
|
||||
*/
|
||||
private ConnectionInfo getConnectionInfo(String hostname) throws IOException {
|
||||
// 首先检查连接池是否已有该主机的连接
|
||||
com.hierynomus.smbj.connection.Connection connection = null;
|
||||
int maxTrys = 3;
|
||||
while (maxTrys-- > 0) {
|
||||
|
||||
ConnectionInfo connectionInfo = connectionPool.get(hostname);
|
||||
|
||||
// 如果连接存在且有效,则更新最后使用时间并返回
|
||||
if (connectionInfo != null) {
|
||||
connection = connectionInfo.getConnection();
|
||||
if (connection != null && connection.isConnected()) {
|
||||
// 更新连接的最后使用时间
|
||||
connectionInfo.updateLastUsedTimestamp();
|
||||
log.debug("Reusing SMB connection for host: {}", hostname);
|
||||
return connectionInfo;
|
||||
}
|
||||
log.debug("Closing invalid SMB connection for host: {}", hostname);
|
||||
connectionInfo.closeAllSessions();
|
||||
}
|
||||
|
||||
// 如果连接不存在或已关闭,则创建新连接
|
||||
connectionPoolLock.lock();
|
||||
try {
|
||||
// 创建新连接
|
||||
log.debug("Creating new SMB connection for host: {}", hostname);
|
||||
connection = client.connect(hostname);
|
||||
connectionInfo = new ConnectionInfo(connection);
|
||||
connectionPool.put(hostname, connectionInfo);
|
||||
return connectionInfo;
|
||||
} finally {
|
||||
connectionPoolLock.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从连接池获取或创建连接
|
||||
*
|
||||
* 从连接池获取或创建连接(兼容旧方法签名)
|
||||
*
|
||||
* @param hostname 主机名
|
||||
* @return SMB连接
|
||||
* @throws IOException 如果创建连接失败
|
||||
*/
|
||||
private com.hierynomus.smbj.connection.Connection getConnection(String hostname) throws IOException {
|
||||
// 首先检查连接池是否已有该主机的连接
|
||||
ConnectionInfo connectionInfo = connectionPool.get(hostname);
|
||||
com.hierynomus.smbj.connection.Connection connection = null;
|
||||
|
||||
// 如果连接存在且有效,则更新最后使用时间并返回
|
||||
if (connectionInfo != null) {
|
||||
connection = connectionInfo.getConnection();
|
||||
if (connection != null && connection.isConnected()) {
|
||||
// 更新连接的最后使用时间
|
||||
connectionInfo.updateLastUsedTimestamp();
|
||||
log.debug("Reusing SMB connection for host: {}", hostname);
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果连接不存在或已关闭,则创建新连接
|
||||
connectionPoolLock.lock();
|
||||
try {
|
||||
// 双重检查锁定模式
|
||||
connectionInfo = connectionPool.get(hostname);
|
||||
if (connectionInfo != null) {
|
||||
connection = connectionInfo.getConnection();
|
||||
if (connection != null && connection.isConnected()) {
|
||||
connectionInfo.updateLastUsedTimestamp();
|
||||
log.debug("Reusing SMB connection for host: {}", hostname);
|
||||
return connection;
|
||||
}
|
||||
// 如果连接已失效,从池中移除
|
||||
connectionPool.remove(hostname);
|
||||
}
|
||||
|
||||
// 创建新连接
|
||||
log.debug("Creating new SMB connection for host: {}", hostname);
|
||||
connection = client.connect(hostname);
|
||||
connectionInfo = new ConnectionInfo(connection);
|
||||
connectionPool.put(hostname, connectionInfo);
|
||||
} finally {
|
||||
connectionPoolLock.unlock();
|
||||
}
|
||||
|
||||
return connection;
|
||||
return getConnectionInfo(hostname).getConnection();
|
||||
}
|
||||
|
||||
// Session空闲超时时间:2分钟(比连接超时时间短)
|
||||
private static final long SESSION_IDLE_TIMEOUT_MS = 2 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* 清理空闲连接的定时任务
|
||||
* 清理空闲连接和session的定时任务
|
||||
* 1. 检查并关闭所有超时的session
|
||||
* 2. 检查并关闭所有超时的连接
|
||||
*/
|
||||
private void cleanupIdleConnections() {
|
||||
log.debug("Running idle connections cleanup task");
|
||||
log.debug(
|
||||
"Running idle connections and sessions cleanup task with session timeout: {}ms, connection timeout: {}ms",
|
||||
SESSION_IDLE_TIMEOUT_MS, CONNECTION_IDLE_TIMEOUT_MS);
|
||||
|
||||
// 创建要移除的连接列表,避免在迭代时修改Map
|
||||
List<String> idleHostnames = new java.util.ArrayList<>();
|
||||
int totalClosedSessions = 0;
|
||||
|
||||
// 查找所有空闲连接
|
||||
// 首先清理每个连接中的空闲session
|
||||
for (Map.Entry<String, ConnectionInfo> entry : connectionPool.entrySet()) {
|
||||
String hostname = entry.getKey();
|
||||
ConnectionInfo connectionInfo = entry.getValue();
|
||||
|
||||
// 检查连接是否空闲超时
|
||||
if (connectionInfo != null && connectionInfo.isIdle(CONNECTION_IDLE_TIMEOUT_MS)) {
|
||||
idleHostnames.add(hostname);
|
||||
log.debug("Found idle connection for host: {}, will be closed", hostname);
|
||||
if (connectionInfo != null) {
|
||||
// 清理该连接下的空闲session - 检查session是否超时,超时则关闭
|
||||
log.debug("Checking for idle sessions on host: {}", hostname);
|
||||
|
||||
int closedSessions = connectionInfo.cleanupIdleSessions(SESSION_IDLE_TIMEOUT_MS);
|
||||
totalClosedSessions += closedSessions;
|
||||
|
||||
if (closedSessions > 0) {
|
||||
log.debug("Closed {} idle/expired sessions for host: {}", closedSessions, hostname);
|
||||
}
|
||||
|
||||
// 然后检查连接是否空闲超时
|
||||
if (connectionInfo.isIdle(CONNECTION_IDLE_TIMEOUT_MS)) {
|
||||
idleHostnames.add(hostname);
|
||||
log.debug("Found idle connection for host: {}, will be closed", hostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,12 +354,18 @@ public class SmbFileService implements DisposableBean {
|
||||
ConnectionInfo connectionInfo = connectionPool.get(hostname);
|
||||
if (connectionInfo != null) {
|
||||
try {
|
||||
// 先关闭所有session
|
||||
connectionInfo.closeAllSessions();
|
||||
log.debug("Closed all remaining sessions for host: {}", hostname);
|
||||
// 再关闭连接
|
||||
log.debug("Closing idle connection for host: {}", hostname);
|
||||
connectionInfo.getConnection().close();
|
||||
} catch (IOException e) {
|
||||
log.error("Error closing idle connection for host: {}", hostname, e);
|
||||
} finally {
|
||||
connectionPool.remove(hostname);
|
||||
log.debug("Removed connection from pool for host: {}", hostname);
|
||||
}
|
||||
connectionPool.remove(hostname);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -232,12 +373,14 @@ public class SmbFileService implements DisposableBean {
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Idle connections cleanup completed, closed {} connections", idleHostnames.size());
|
||||
log.debug(
|
||||
"Idle connections and sessions cleanup completed successfully. Results: closed {} connections and {} expired sessions",
|
||||
idleHostnames.size(), totalClosedSessions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行SMB操作的通用方法,使用连接池
|
||||
*
|
||||
* 执行SMB操作的通用方法,使用连接池和session池
|
||||
*
|
||||
* @param smbPath SMB路径
|
||||
* @param operation 要执行的操作
|
||||
* @param <T> 操作返回类型
|
||||
@@ -246,26 +389,69 @@ public class SmbFileService implements DisposableBean {
|
||||
*/
|
||||
private <T> T executeSmbOperation(SmbPath smbPath, SmbOperation<T> operation) throws IOException {
|
||||
String hostname = smbPath.getHostname();
|
||||
com.hierynomus.smbj.connection.Connection connection = null;
|
||||
ConnectionInfo connectionInfo = null;
|
||||
SessionInfo sessionInfo = null;
|
||||
T re = null;
|
||||
|
||||
try {
|
||||
// 从连接池获取连接
|
||||
connection = getConnection(hostname);
|
||||
// 尝试执行获取连接执行操作,当发生 TransportException 时 尝试重试,最多3次
|
||||
int maxTrys = 3;
|
||||
while (maxTrys-- > 0) {
|
||||
try {
|
||||
// 获取连接
|
||||
connectionInfo = getConnectionInfo(hostname);
|
||||
|
||||
// 使用获取的连接进行身份验证
|
||||
var session = connection.authenticate(getAuthenticationContext(hostname));
|
||||
// 从session池获取session
|
||||
sessionInfo = connectionInfo.peekSession();
|
||||
// 如果session不存在
|
||||
if (sessionInfo == null) {
|
||||
// 获取认证上下文
|
||||
AuthenticationContext authContext = getAuthenticationContext(hostname);
|
||||
// 创建新session并添加到池中
|
||||
sessionInfo = connectionInfo.createSession(authContext);
|
||||
log.debug("Created new SMB session for host: {}", hostname);
|
||||
} else {
|
||||
log.debug("Reusing SMB session for host: {}", hostname);
|
||||
}
|
||||
|
||||
try (var share = (DiskShare) session.connectShare(smbPath.getShareName())) {
|
||||
return operation.execute(share, smbPath.getPath());
|
||||
// 连接共享
|
||||
try (var share = (DiskShare) sessionInfo.getSession().connectShare(smbPath.getShareName())) {
|
||||
re = operation.execute(share, smbPath.getPath());
|
||||
// 操作完成后更新session的最后使用时间,将session放回池中
|
||||
connectionInfo.returnSession(sessionInfo);
|
||||
log.debug("Returned SMB session to pool for host: {}", hostname);
|
||||
|
||||
} catch (SMBRuntimeException e) {
|
||||
sessionInfo.close();
|
||||
throw e;
|
||||
} finally {
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
} catch (TransportException e) {
|
||||
log.warn("TransportException occurred while trying to connect to host: {}. Retrying...", hostname);
|
||||
// 延迟1秒
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
continue;
|
||||
} catch (IOException e) {
|
||||
// 如果操作失败且连接信息存在,检查连接状态
|
||||
if (connectionInfo != null) {
|
||||
com.hierynomus.smbj.connection.Connection connection = connectionInfo.getConnection();
|
||||
if (connection != null && !connection.isConnected()) {
|
||||
// 从连接池移除失效的连接,并关闭所有session
|
||||
connectionInfo.closeAllSessions();
|
||||
connectionPool.remove(hostname);
|
||||
log.debug("Removed disconnected SMB connection from pool for host: {}", hostname);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// 如果操作失败且连接存在,检查连接状态
|
||||
if (connection != null && !connection.isConnected()) {
|
||||
// 从连接池移除失效的连接
|
||||
connectionPool.remove(hostname);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return re;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,10 +633,10 @@ public class SmbFileService implements DisposableBean {
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭并清理所有连接资源
|
||||
* 关闭并清理所有连接和session资源
|
||||
*/
|
||||
public void shutdown() {
|
||||
log.debug("Shutting down SMB connection pool");
|
||||
log.debug("Shutting down SMB connection pool and sessions");
|
||||
|
||||
// 关闭定时清理任务
|
||||
try {
|
||||
@@ -463,15 +649,20 @@ public class SmbFileService implements DisposableBean {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
// 关闭所有连接
|
||||
// 关闭所有连接和session
|
||||
connectionPoolLock.lock();
|
||||
try {
|
||||
for (Map.Entry<String, ConnectionInfo> entry : connectionPool.entrySet()) {
|
||||
String hostname = entry.getKey();
|
||||
ConnectionInfo connectionInfo = entry.getValue();
|
||||
try {
|
||||
log.debug("Closing connection to host: {}", entry.getKey());
|
||||
entry.getValue().getConnection().close();
|
||||
// 先关闭所有session
|
||||
connectionInfo.closeAllSessions();
|
||||
// 再关闭连接
|
||||
log.debug("Closing connection to host: {}", hostname);
|
||||
connectionInfo.getConnection().close();
|
||||
} catch (IOException e) {
|
||||
log.error("Error closing connection to host: {}", entry.getKey(), e);
|
||||
log.error("Error closing connection or sessions to host: {}", hostname, e);
|
||||
}
|
||||
}
|
||||
connectionPool.clear();
|
||||
|
||||
@@ -8,7 +8,20 @@ import com.ecep.contract.Message;
|
||||
import com.ecep.contract.handler.SessionInfo;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
/**
|
||||
* WebSocket服务器任务接口
|
||||
* 定义了所有通过WebSocket与客户端通信的任务的通用方法
|
||||
* 包括任务名称、初始化参数、设置会话、更新消息、更新标题、更新进度等操作
|
||||
*
|
||||
* 所有通过WebSocket与客户端通信的任务类都应实现此接口, 文档参考 .trace/rules/server_task_rules.md
|
||||
* tips:检查是否在 tasker_mapper.json 中注册
|
||||
*/
|
||||
public interface WebSocketServerTasker extends Callable<Object> {
|
||||
/**
|
||||
* 初始化任务参数
|
||||
*
|
||||
* @param argsNode 任务参数的JSON节点
|
||||
*/
|
||||
void init(JsonNode argsNode);
|
||||
|
||||
default void setSession(SessionInfo session) {
|
||||
@@ -19,10 +32,19 @@ public interface WebSocketServerTasker extends Callable<Object> {
|
||||
*/
|
||||
void setMessageHandler(Predicate<Message> messageHandler);
|
||||
|
||||
/**
|
||||
* 设置标题处理函数
|
||||
*/
|
||||
void setTitleHandler(Predicate<String> titleHandler);
|
||||
|
||||
/**
|
||||
* 设置属性处理函数
|
||||
*/
|
||||
void setPropertyHandler(BiConsumer<String, Object> propertyHandler);
|
||||
|
||||
/**
|
||||
* 设置进度处理函数
|
||||
*/
|
||||
void setProgressHandler(BiConsumer<Long, Long> progressHandler);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user