session.flush()与session.clear()的区别

本文介绍了session.flush()和session.clear()两种方法对一级缓存的处理方式。session.flush()用于将缓存中的数据与数据库同步,而session.clear()则用于清除缓存数据。文章还详细解释了flush方法的工作原理及应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

session.flush()和session.clear()就针对session的一级缓存的处理。

简单的说,

1 session.flush()的作用就是将session的缓存中的数据与数据库同步。

2 session.clear()的作用就是清除session中的缓存数据(不管缓存与数据库的同步)。

 

执行完session.flush()时,并不意味着数据就肯定持久化到数据库中的,因为事务控制着数据库,如果事务提交失败了,缓存中的数据还是照样会被回滚的。

 

flush本意是冲刷,这个方法大概取自它引申义冲马桶的意思,马桶有个池子,你往里面扔东西,会暂时保存在池子里,只有你放水冲下去,东西才会进入下水道。
同理很多流都有一个这样的池子,专业术语叫缓冲区,当你print或者write的时候,会暂时保存在缓冲区,并没有发送出去,这是出于效率考虑的,因为数据不会自己发送过去,必须有其他机制,而且这个很消耗资源,就像马桶你需要很多水,才能冲走,你如果扔一点东西,就冲一次水,那你水费要爆表了,同样如果你写一行文字,或者一个字节,就要马上发送出去,那网络流量,CPU使用率等等都要爆表了,所以一般只有在你真正需要发送否则无法继续的时候,调用flush,将数据发送出去。

 

http://blog.youkuaiyun.com/leidengyan/article/details/7514484

session.flush()和session.clear()就针对session的一级缓存的处理。

转载于:https://www.cnblogs.com/hym-pcitc/p/6023362.html

package com.example.demo.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.UUID; import cn.hutool.core.thread.ThreadFactoryBuilder; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.demo.dao.entity.WExecuteHost; import com.example.demo.dao.entity.WPersonalHost; import com.example.demo.dao.mapper.WExecuteHostMapper; import com.example.demo.service.WExecuteHostService; import com.example.demo.service.WPersonalHostService; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import javax.annotation.PreDestroy; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @Service public class WExecuteHostServiceImpl extends ServiceImpl<WExecuteHostMapper, WExecuteHost> implements WExecuteHostService { @Autowired private WPersonalHostService wPersonalHostService; @Data private static class UserSession { private Process cmdProcess; private volatile boolean isProcessRunning; private String sessionId; private String logFilePath; private final Queue<String> logBuffer = new ConcurrentLinkedQueue<>(); private static final int BATCH_SIZE = 50; // 批量写入阈值 } private final Map<String, UserSession> userSessions = new ConcurrentHashMap<>(); private final SimpMessagingTemplate messagingTemplate; // 异步日志写入线程池 private static final ExecutorService LOG_WRITER_POOL = Executors.newCachedThreadPool( new ThreadFactoryBuilder().setNamePrefix("log-writer-").build()); // 日志刷新调度器 private static final ScheduledExecutorService LOG_FLUSH_SCHEDULER = Executors.newSingleThreadScheduledExecutor(); public WExecuteHostServiceImpl(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; startLogFlushService(); } // 初始化日志刷新服务 private void startLogFlushService() { LOG_FLUSH_SCHEDULER.scheduleAtFixedRate(() -> { userSessions.values().forEach(session -> { if (!session.getLogBuffer().isEmpty()) { List<String> batch = CollUtil.newArrayList(session.getLogBuffer()); session.getLogBuffer().clear(); asyncWriteLog(session.getLogFilePath(), String.join("\n", batch)); } }); }, 0, 1, TimeUnit.SECONDS); } @Override public String pipList() { // 示例实现:返回模拟的管道列表(实际需根据业务需求实现) return "[]"; } @Override public void executeCommand(String command, String executeHost, String userId) { // 1. 权限校验 if (!validateUserHost(userId, executeHost)) { sendError("无权访问该主机", userId); return; } // 2. 初始化日志系统 UserSession session = userSessions.computeIfAbsent(userId, key -> { if (userSessions.containsKey(userId)) { sendError("已有命令执行中,请等待完成", userId); return null; } return createNewSession(userId, executeHost); }); if (session == null) return; // 3. 命令处理 if ("ABORT".equalsIgnoreCase(command)) { handleAbort(userId); return; } // 写入命令到日志 session.getLogBuffer().offer( "\n———————— 执行时间 " + DateUtil.now() + " ————————\n[<<Input<<] " + command); // 发送命令到进程 IoUtil.write(session.getCmdProcess().getOutputStream(), "GBK", false, command + "\n"); } private boolean validateUserHost(String userId, String executeHost) { LambdaQueryWrapper<WPersonalHost> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(WPersonalHost::getP13, userId) .eq(WPersonalHost::getExecuteHost, executeHost) .eq(WPersonalHost::getState, "在线"); return wPersonalHostService.getOne(queryWrapper) != null; } private UserSession createNewSession(String userId, String executeHost) { try { UserSession session = new UserSession(); session.setSessionId(UUID.randomUUID().toString()); session.setLogFilePath(initLogFile(userId, executeHost)); // 启动CMD进程(带唯一标题) ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/k", "title " + generateUniqueTitle(userId)); session.setCmdProcess(pb.redirectErrorStream(true).start()); // 启动输出监听线程 startOutputThread(session, userId); return session; } catch (IOException e) { sendError("进程启动失败: " + e.getMessage(), userId); return null; } } private String initLogFile(String userId, String executeHost) { // 1. 构建基础路径(使用File.separator) String baseDir = FileUtil.normalize( System.getProperty("user.dir") + File.separator + "command-log"); // 2. 构建安全路径(统一使用File.separator) String safePath = FileUtil.normalize( baseDir + File.separator + userId + File.separator + executeHost + File.separator + "项目名称"); // 3. 安全校验(现在路径分隔符一致) if (!safePath.startsWith(baseDir)) { throw new SecurityException("非法日志路径: " + safePath); } // 4. 创建目录(自动处理路径分隔符) FileUtil.mkdir(safePath); // 5. 生成日志文件 String logFileName = DateUtil.today() + ".log"; return FileUtil.touch(safePath + File.separator + logFileName).getAbsolutePath(); } private void startOutputThread(UserSession session, String userId) { new Thread(() -> { try (BufferedReader reader = new BufferedReader( new InputStreamReader(session.getCmdProcess().getInputStream(), "GBK"))) { String line; while ((line = reader.readLine()) != null) { // WebSocket推送 messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, line); // 日志缓冲 session.getLogBuffer().offer(line); if (session.getLogBuffer().size() >= UserSession.BATCH_SIZE) { asyncWriteLog(session.getLogFilePath(), String.join("\n", session.getLogBuffer())); session.getLogBuffer().clear(); } } } catch (Exception e) { sendError("输出流异常: " + e.getMessage(), userId); } finally { cleanupSession(userId); } }).start(); } private void asyncWriteLog(String logFilePath, String content) { CompletableFuture.runAsync(() -> { try { FileUtil.appendString(content + System.lineSeparator(), logFilePath, "GBK"); } catch (Exception e) { System.err.println("日志写入失败: " + e.getMessage()); } }, LOG_WRITER_POOL); } private void cleanupSession(String userId) { UserSession session = userSessions.remove(userId); if (session != null) { try { if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } // 强制将剩余日志写入文件(新增代码) if (!session.getLogBuffer().isEmpty()) { asyncWriteLog(session.getLogFilePath(), String.join("\n", session.getLogBuffer())); session.getLogBuffer().clear(); } } catch (Exception ignored) {} } } @PreDestroy public void cleanup() { LOG_FLUSH_SCHEDULER.shutdown(); LOG_WRITER_POOL.shutdown(); userSessions.forEach((userId, session) -> { try { if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } } catch (Exception ignored) { } }); userSessions.clear(); } public void handleAbort(String userId) { UserSession session = userSessions.get(userId); if (session != null && session.isProcessRunning()) { session.getCmdProcess().destroyForcibly(); sendError("⏹ 用户命令已终止", userId); } else { sendError("⏹ 无执行中命令", userId); } } private void sendError(String message, String userId) { try { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "❌ " + message); } catch (Exception ignored) { } } private String generateUniqueTitle(String userId) { return "CMD_SESSION_" + userId + "_" + System.currentTimeMillis(); } }能不能在命令执行结束的时候,追加一行“命令执行结束”返回前端页面
最新发布
06-07
package com.example.demo.service.impl; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.demo.dao.entity.WExecuteHost; import com.example.demo.dao.entity.WPersonalHost; import com.example.demo.dao.mapper.WExecuteHostMapper; import com.example.demo.service.WExecuteHostService; import com.example.demo.service.WPersonalHostService; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import javax.annotation.PreDestroy; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.file.Files; import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; /** * @author fangpeiyuan * @description 针对表【w_execute_host(执行主机表)】的数据库操作Service实现 * @createDate 2025-06-06 09:32:45 */ @Service public class WExecuteHostServiceImpl extends ServiceImpl<WExecuteHostMapper, WExecuteHost> implements WExecuteHostService { @Autowired private WPersonalHostService wPersonalHostService; @Data private static class UserSession { private Process cmdProcess; private BufferedWriter commandWriter; private BufferedWriter logWriter; // 添加日志写入器 private volatile boolean isProcessRunning; private String sessionId; private String logFilePath; } // 替换全局变量为会话映射 private final Map<String, UserSession> userSessions = new ConcurrentHashMap<>(); private final SimpMessagingTemplate messagingTemplate; public WExecuteHostServiceImpl(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; } @Override public String pipList() { return null; } @Override public void executeCommand(String command, String executeHost, String userId) { // 判断这个host是否属于该用户并状态是活跃的 LambdaQueryWrapper<WPersonalHost> queryWrapper = new LambdaQueryWrapper<>(); //todo 后续动态 queryWrapper .eq(WPersonalHost::getP13, userId) .eq(WPersonalHost::getExecuteHost, executeHost) .eq(WPersonalHost::getState, "在线"); WPersonalHost wPersonalHost = wPersonalHostService.getOne(queryWrapper); if (ObjectUtil.isEmpty(wPersonalHost)) { System.out.println("该主机并不属于当前用户,请联系管理员处理。"); return; } // 初始化日志目录 // todo 后面动态 String logDir = System.getProperty("user.dir") + File.separator + "command-log" + File.separator + userId + File.separator + executeHost + File.separator + "项目名称"; // 创建目录(如果不存在) File logDirectory = new File(logDir); if (!logDirectory.exists()) { boolean created = logDirectory.mkdirs(); if (!created) { System.err.println("无法创建日志目录: " + logDir); return; // 或者抛出异常 } System.out.println("日志目录已创建: " + logDir); } // 按天生成日志文件名 String dateStr = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); String logFilePath = logDir + File.separator + dateStr + ".log"; try { // 确保日志目录存在 Files.createDirectories(Paths.get(logDir)); // ABORT 命令处理 if ("ABORT".equalsIgnoreCase(command)) { try (BufferedWriter logWriter = new BufferedWriter(new FileWriter(logFilePath, true))) { logWriter.write("\n———————— 执行时间 " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " ————————\n"); logWriter.write("[<<Input<<] ABORT command received\n"); } catch (IOException e) { // 错误处理 } handleAbort(userId); return; } // 获取或创建用户专属会话 UserSession session = userSessions.computeIfAbsent(userId, key -> { if (userSessions.containsKey(userId)) { sendError("已有命令执行中,请等待完成", userId); return null; } try { UserSession newSession = new UserSession(); newSession.setSessionId(UUID.randomUUID().toString()); // 初始化专属进程 (关键修改点) String uniqueTitle = "User_" + userId + "_" + System.currentTimeMillis(); newSession.setCmdProcess(new ProcessBuilder("cmd.exe", "/k", "title " + uniqueTitle) .redirectErrorStream(true).start()); newSession.setCommandWriter( new BufferedWriter(new OutputStreamWriter( newSession.getCmdProcess().getOutputStream(), "gbk")) ); // 创建并绑定日志写入器 newSession.setLogWriter(new BufferedWriter(new FileWriter(logFilePath, true))); newSession.getLogWriter().write("\n———————— 执行时间 " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " ————————\n"); // 启动输出线程(传递日志写入器) startOutputThread(newSession, userId); return newSession; } catch (IOException e) { sendError("进程启动失败: " + e.getMessage(), userId); return null; } }); // 写入输入命令 try { assert session != null; session.getLogWriter().write("[<<Input<<] " + command + "\n"); session.getLogWriter().flush(); session.getCommandWriter().write(command + "\n"); session.getCommandWriter().flush(); } catch (IOException e) { sendError("命令发送失败: " + e.getMessage(), userId); } } catch (IOException e) { e.printStackTrace(); } } // 独立的输出线程方法 private void startOutputThread(UserSession session, String userId) { new Thread(() -> { try (BufferedReader reader = new BufferedReader( new InputStreamReader(session.getCmdProcess().getInputStream(), "gbk"))) { String line; while ((line = reader.readLine()) != null) { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, line); // 直接使用会话的日志写入器 synchronized (session) { session.getLogWriter().write(line + "\n"); session.getLogWriter().flush(); } } } catch (Exception e) { sendError("输出流异常: " + e.getMessage(), userId); } finally { try { // 安全关闭日志写入器 if (session.getLogWriter() != null) { session.getLogWriter().close(); } } catch (IOException e) { // 错误处理 } session.setProcessRunning(false); userSessions.remove(userId); } }).start(); } public void handleAbort(String userId) { UserSession session = userSessions.get(userId); if (session != null && session.isProcessRunning()) { session.getCmdProcess().destroyForcibly(); sendError("⏹ 用户命令已终止", userId); } else { sendError("⏹ 无执行中命令", userId); } } private void sendError(String message, String userId) { try { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "❌ " + message); } catch (Exception ignored) { } } @PreDestroy public void cleanup() { userSessions.forEach((userId, session) -> { try { if (session.getLogWriter() != null) { session.getLogWriter().close(); } if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } } catch (IOException e) { System.err.println("清理会话资源时出错: " + e.getMessage()); } }); userSessions.clear(); } } 这里的日志写入是不是有什么依赖可以更好地实现?如果一个websocket长时间没有使用能否帮它断开
06-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值