IE8 jquery Ajax 导致Session 丢失问题解决办法

本文探讨了在使用IE8浏览器处理JSON数据时遇到的Session丢失问题,并提供了解决方案——卸载特定补丁。该问题仅出现在IE8及其内核上,其他非IE内核浏览器则不受影响。

IE8 是个不错的东西,

今日,在做json数据返回处理的时候,发现每次返回数据之后session就丢失了

开始以为是程序问题,检查了很多次 都没发现程序有任何问题,最后在google浏览器的帮助下发现了问题,

IE8或者基于IE8内核 session会丢失,而非IE内核的都正常

 

解决办法:

卸载 KB976662 补丁

 

提高 Internet Explorer 8 的 JSON 互操作性。

<think>我们面临的核心问题是:在生产环境中,只有数据库视图的查询权限(没有写权限,不能创建触发器或使用CDC等高级功能),且前端使用JSP和jQuery,需要实现数据的实时更新。 根据之前的讨论,我们排除了需要数据库写权限的方案(如触发器、CDC)。因此,可行的方案有: 1. **WebSocket**:需要后端能够主动推送数据,但后端如何知道数据库变化?由于没有数据库的写权限,我们无法使用数据库的推送机制(如PostgreSQL的NOTIFY、MySQL的binlog订阅),因此后端只能通过轮询数据库来检测变化。 2. **Server-Sent Events (SSE)**:同样需要后端轮询数据库。 3. **长轮询(Long Polling)**:是轮询的改进版,但本质上还是轮询。 因此,无论选择哪种实时通信技术,后端都需要定期轮询数据库(因为我们只有查询权限)。但是,我们可以通过优化轮询策略来减少对数据库的压力。 ### 优化思路 1. **增量查询**:利用视图中的时间戳或自增ID字段,每次只查询上次轮询之后的变化。 - 例如:`SELECT * FROM your_view WHERE update_time > last_poll_time` - 需要确保视图中有这样的字段。 2. **轮询频率调整**:根据业务需求调整轮询间隔(例如,非高峰时段降低频率)。 3. **后端缓存**:在后端缓存上一次查询的结果,只获取新增或修改的数据。 ### 具体实现方案 由于前端是JSP和jQuery,我们可以选择以下两种方式: #### 方案1:使用WebSocket(后端轮询数据库) 步骤: 1. 前端通过WebSocket连接后端。 2. 后端启动一个定时任务,定期查询数据库视图的变化(使用增量查询)。 3. 当检测到变化时,通过WebSocket将新数据推送给前端。 优点:实时性高,连接持久化,减少不必要的请求头开销。 前端代码(JSP页面内): ```jsp <script> // 确保在支持WebSocket的浏览器中运行 var socket = new WebSocket("ws://your-server:port/db-update-ws"); socket.onmessage = function(event) { var newData = JSON.parse(event.data); // 更新页面 $("#data-container").html(generateTable(newData)); }; function generateTable(data) { // 根据数据生成表格的HTML // 这里假设data是数组 var html = '<table>'; // 表头 html += '<tr>'; for (var key in data[0]) { html += '<th>'+key+'</th>'; } html += '</tr>'; // 数据行 for (var i=0; i<data.length; i++) { html += '<tr>'; for (var key in data[i]) { html += '<td>'+data[i][key]+'</td>'; } html += '</tr>'; } html += '</table>'; return html; } </script> ``` 后端(Java)使用Spring Boot的WebSocket(如果后端是Spring Boot): 1. 添加依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 配置WebSocket: ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new DbUpdateWebSocketHandler(), "/db-update-ws"); } } ``` 3. 编写WebSocket处理器和定时轮询任务: ```java public class DbUpdateWebSocketHandler extends TextWebSocketHandler { private static final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>(); private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private volatile long lastPollTime = System.currentTimeMillis(); @PostConstruct public void init() { // 启动定时任务,每5秒轮询一次 scheduler.scheduleAtFixedRate(this::pollDatabase, 0, 5, TimeUnit.SECONDS); } private void pollDatabase() { // 查询数据库:使用视图,并只查询上次轮询之后的变化 String sql = "SELECT * FROM your_view WHERE update_time > ?"; // 假设使用JdbcTemplate List<Map<String, Object>> changes = jdbcTemplate.queryForList(sql, new Timestamp(lastPollTime)); if (!changes.isEmpty()) { // 更新最后轮询时间为当前时间(注意:这里使用获取到变化的时间点,避免丢失数据) lastPollTime = System.currentTimeMillis(); // 将变化发送给所有客户端 String json = new ObjectMapper().writeValueAsString(changes); for (WebSocketSession session : sessions) { if (session.isOpen()) { session.sendMessage(new TextMessage(json)); } } } } @Override public void afterConnectionEstablished(WebSocketSession session) { sessions.add(session); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { sessions.remove(session); } } ``` 注意:这里为了简化,使用了内存存储会话和轮询任务。实际应用中需要考虑分布式环境(多个后端实例)下的同步问题。 #### 方案2:使用SSE(Server-Sent Events) 如果不想使用WebSocket,或者需要兼容旧浏览器(SSE在除IE外的现代浏览器都支持),可以使用SSE。 前端代码(jQuery): ```jsp <script> $(document).ready(function() { var eventSource = new EventSource("/db-update-sse"); eventSource.onmessage = function(event) { var newData = JSON.parse(event.data); // 更新页面 $("#data-container").html(generateTable(newData)); }; // 错误处理 eventSource.onerror = function() { console.log("SSE error."); }; }); </script> ``` 后端(Servlet): ```java @WebServlet("/db-update-sse") public class SSEServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/event-stream"); response.setCharacterEncoding("UTF-8"); // 设置连接特性 response.setHeader("Cache-Control", "no-cache"); response.setHeader("Connection", "keep-alive"); PrintWriter writer = response.getWriter(); // 记录上一次轮询时间(可以存储在Servlet的成员变量中,但注意线程安全) long lastPollTime = System.currentTimeMillis(); // 模拟长连接,不断轮询 while (true) { try { // 每次轮询间隔5秒 Thread.sleep(5000); // 查询数据库 String sql = "SELECT * FROM your_view WHERE update_time > ?"; List<Map<String, Object>> changes = jdbcTemplate.queryForList(sql, new Timestamp(lastPollTime)); if (!changes.isEmpty()) { lastPollTime = System.currentTimeMillis(); // 更新轮询时间 String json = new ObjectMapper().writeValueAsString(changes); // 发送事件 writer.write("data: " + json + "\n\n"); writer.flush(); } } catch (Exception e) { // 发生异常(比如客户端断开)则退出循环 break; } } } } ``` 注意:由于SSE是单向的,且一个连接会持续很长时间,因此需要设置响应头并保持连接不关闭。同时,在客户端关闭时,服务器端需要捕获异常并退出循环。 #### 方案3:长轮询(Long Polling) 如果环境不支持WebSocket和SSE,可以使用长轮询(本质上是AJAX,但服务器在有数据更新时才返回响应)。 前端jQuery代码: ```javascript function longPolling() { $.ajax({ url: '/long-polling', method: 'GET', dataType: 'json', data: { lastUpdate: lastUpdateTime }, // 发送上一次获取数据的时间 success: function(data) { if (data.changes) { // 更新页面 updateUI(data.changes); // 更新lastUpdateTime为当前时间(或数据中的最新时间) lastUpdateTime = data.lastUpdateTime; } // 无论有没有数据,都再次发起长轮询 longPolling(); }, error: function() { // 错误处理,稍后重试 setTimeout(longPolling, 5000); } }); } // 启动 $(document).ready(longPolling); ``` 后端Servlet: ```java @WebServlet("/long-polling") public class LongPollingServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long lastUpdateTime = Long.parseLong(request.getParameter("lastUpdate")); // 设置超时时间(比如30秒) long startTime = System.currentTimeMillis(); long timeout = 30000; // 30秒 while (System.currentTimeMillis() - startTime < timeout) { // 查询数据库 String sql = "SELECT * FROM your_view WHERE update_time > ?"; List<Map<String, Object>> changes = jdbcTemplate.queryForList(sql, new Timestamp(lastUpdateTime)); if (!changes.isEmpty()) { // 有变化,返回数据 response.setContentType("application/json"); PrintWriter out = response.getWriter(); // 构建响应,包含变化和最新的时间戳(取变化中最大的时间戳) long maxTime = ...; // 从changes中获取最大的update_time Map<String, Object> result = new HashMap<>(); result.put("changes", changes); result.put("lastUpdateTime", maxTime); out.print(new ObjectMapper().writeValueAsString(result)); out.flush(); return; } // 等待一段时间再查 Thread.sleep(2000); } // 超时,返回空 response.setContentType("application/json"); PrintWriter out = response.getWriter(); out.print("{}"); // 返回空对象 out.flush(); } } ``` ### 总结 在仅有视图查询权限的情况下,我们无法避免轮询数据库,但可以通过以下方式优化: 1. **增量查询**:只查询上次轮询之后的变化,减少数据库压力。 2. **选择合适的通信方式**:WebSocket(推荐)、SSE或长轮询,避免频繁的AJAX轮询(短轮询)。 3. **调整轮询频率**:根据业务需求,在实时性和数据库压力之间取得平衡。 注意:以上方案都需要确保视图中有一个可靠的时间戳字段(或自增ID)用于增量查询。如果没有这样的字段,可能需要与数据库管理员协商添加。 ### 相关问题 1. 如何在没有时间戳字段的视图上实现增量查询? 2. 在分布式环境下,如何避免多个后端实例重复轮询数据库? 3. 如何防止WebSocket或SSE连接数过多导致服务器资源耗尽? 4. 长轮询和短轮询在性能上有什么具体差异?</think>### 解决方案:基于视图查询权限的实时数据更新(JSP/jQuery实现) 在仅有生产库视图查询权限的情况下,我们可以通过以下方案实现前端数据的实时更新,核心思路是**优化轮询机制+增量数据捕获**: #### 一、优化轮询策略(推荐方案) ```jsp <!-- JSP页面实现 --> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script> // 智能轮询控制器 let pollInterval = 3000; // 初始3秒 let lastVersion = 0; // 最后获取的数据版本号 function smartPoll() { $.ajax({ url: 'DataServlet', type: 'GET', data: { action: 'getChanges', lastVersion: lastVersion }, success: function(response) { if (response.updated) { // 更新页面数据 updateUI(response.data); // 更新版本号 lastVersion = response.newVersion; // 成功时加快轮询(1秒) pollInterval = 1000; } else { // 无更新时减慢轮询(5秒) pollInterval = 5000; } // 设置下次轮询 setTimeout(smartPoll, pollInterval); }, error: function() { // 错误时重试间隔(10秒) setTimeout(smartPoll, 10000); } }); } // 启动轮询 $(document).ready(function() { smartPoll(); }); </script> ``` **后端实现 (Java Servlet)**: ```java @WebServlet("/DataServlet") public class DataServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action = request.getParameter("action"); long lastVersion = Long.parseLong(request.getParameter("lastVersion")); if ("getChanges".equals(action)) { // 使用版本号增量查询 String sql = "SELECT * FROM production_view WHERE version > ? ORDER BY version ASC"; try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setLong(1, lastVersion); ResultSet rs = stmt.executeQuery(); JSONObject result = new JSONObject(); if (rs.next()) { // 获取最新数据 JSONArray data = new JSONArray(); do { JSONObject item = new JSONObject(); item.put("id", rs.getInt("id")); item.put("value", rs.getString("value")); item.put("version", rs.getLong("version")); data.put(item); } while (rs.next()); result.put("updated", true); result.put("data", data); result.put("newVersion", data.getJSONObject(data.length()-1).getLong("version")); } else { result.put("updated", false); } response.setContentType("application/json"); response.getWriter().write(result.toString()); } } } } ``` #### 二、关键技术优化点 1. **增量数据捕获**: ```sql -- 在视图中添加版本号字段 CREATE VIEW production_view AS SELECT *, UNIX_TIMESTAMP(update_time)*1000 AS version FROM production_table; ``` 2. **智能轮询算法**: - 动态调整轮询间隔(1-5秒) - 无更新时自动降低频率 - 有更新时立即加速 - 错误时指数退避重试 3. **数据压缩传输**: ```java // 启用GZIP压缩 if (request.getHeader("Accept-Encoding").contains("gzip")) { response.setHeader("Content-Encoding", "gzip"); try (GZIPOutputStream gzip = new GZIPOutputStream(response.getOutputStream())) { gzip.write(json.getBytes(StandardCharsets.UTF_8)); } } ``` #### 三、性能优化策略 1. **数据库查询优化**: ```sql -- 添加覆盖索引 CREATE INDEX idx_version ON production_table (update_time); ``` 2. **前端批处理更新**: ```javascript function updateUI(data) { // 使用文档片段减少DOM操作 const fragment = document.createDocumentFragment(); data.forEach(item => { const row = document.createElement('tr'); row.innerHTML = `<td>${item.id}</td><td>${item.value}</td>`; fragment.appendChild(row); }); $('#data-table').append(fragment); } ``` 3. **连接池配置**: ```java // 使用HikariCP连接池 HikariConfig config = new HikariConfig(); config.setJdbcUrl(DB_URL); config.setUsername(USER); config.setPassword(PASS); config.setMaximumPoolSize(10); HikariDataSource ds = new HikariDataSource(config); ``` #### 四、备选方案:时间窗口轮询 当无法添加版本号字段时: ```java // 基于时间戳的增量查询 long lastTimestamp = Long.parseLong(request.getParameter("lastTimestamp")); String sql = "SELECT * FROM production_view WHERE update_time > ?"; stmt.setTimestamp(1, new Timestamp(lastTimestamp)); ``` 前端调整: ```javascript let lastTimestamp = Date.now(); // 初始化为当前时间 // 请求时传递时间戳 data: { action: 'getChanges', lastTimestamp: lastTimestamp } // 成功时更新时间戳 lastTimestamp = response.newTimestamp; ``` #### 五、方案对比 | **方案** | 实时性 | 数据库压力 | 实现复杂度 | 适用场景 | |------------------|--------|------------|------------|----------| | 智能版本轮询 | ★★★★ | ★★ | ★★★ | 推荐方案 | | 固定间隔轮询 | ★★ | ★★★★ | ★ | 简单场景 | | 时间窗口轮询 | ★★★ | ★★★ | ★★ | 无版本字段 | | WebSocket | ★★★★★ | ★★ | ★★★★ | 需后端改造 | ### 实施建议 1. **优先添加版本字段**: - 与DBA协商在视图中添加`BIGINT`类型的版本号字段 - 使用触发器自动更新版本号(若权限允许) 2. **监控与调优**: ```sql -- 监控慢查询 SHOW FULL PROCESSLIST; EXPLAIN SELECT * FROM production_view WHERE version > ?; ``` 3. **前端降级策略**: ```javascript // 检测浏览器性能自动调整 const isSlowDevice = navigator.hardwareConcurrency < 4; if (isSlowDevice) pollInterval = Math.max(pollInterval, 5000); ``` 4. **安全加固**: ```java // 防止SQL注入 stmt.setLong(1, lastVersion); // 使用参数化查询 // 添加速率限制 if (requestCount > 1000) { response.setStatus(429); return; } ``` > 参考实现:[^1]前端AJAX交互优化 [^2]Spring Boot后端实现 [^3]MySQL查询优化 [^4]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值