虛擬機器vnc埠切換時如何讓guacamole切換埠重試連線
阿新 • • 發佈:2019-02-11
虛擬機器vnc預設埠是5901,如果虛擬機器不屬於正常關閉,檔案/tmp/.X11-unix/X1不會被刪除,當虛擬機器啟動的時候不會使用5901埠啟動vnc server服務,而是啟用5902埠(如果檔案/tmp/.X11-unix/X2也存在則使用5903埠,依次類推)。
建立到guacamole代理伺服器的tunnel時候需要帶上vnc的連線資訊,如果guacd連線目標虛擬機器出錯時不會丟擲Exception,所以我們的程式碼沒有辦法捕獲異常去換埠重試連線。
解決方案是過載GuacamoleHTTPTunnelServlet的doRead方法,通過讀取webserver發出connect請求之後的第一個流資料,分析裡面是否包含錯誤資訊,如果有則重新建立一個tunnel(要拿到舊tunnel的uuid,所有的tunnel都放到map裡面快取的,key就是uuid),然後註冊新的tunnel。
@Override protected void doRead(HttpServletRequest request, HttpServletResponse response, String tunnelUUID) throws GuacamoleException { // Get tunnel, ensure tunnel exists GuacamoleTunnel tunnel = getTunnel(tunnelUUID); // Ensure tunnel is open if (!tunnel.isOpen()) throw new GuacamoleResourceNotFoundException("Tunnel is closed."); // Obtain exclusive read access GuacamoleReader reader = tunnel.acquireReader(); try { // Note that although we are sending text, Webkit browsers will // buffer 1024 bytes before starting a normal stream if we use // anything but application/octet-stream. response.setContentType("application/octet-stream"); response.setHeader("Cache-Control", "no-cache"); // Get writer for response Writer out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), "UTF-8")); // Stream data to response, ensuring output stream is closed try { // Deregister tunnel and throw error if we reach EOF without // having ever sent any data char[] message = reader.read(); if (message == null) throw new GuacamoleConnectionClosedException("Tunnel reached end of stream."); // 這裡是我新增的程式碼--begin // guacd server error if (new String(message).contains("3.519")) { rebuildTunnel(request, response, tunnelUUID, tunnel); } // 這裡是我新增的程式碼--end // For all messages, until another stream is ready (we send at // least one message) do { // Get message output bytes out.write(message, 0, message.length); // Flush if we expect to wait if (!reader.available()) { out.flush(); response.flushBuffer(); } // No more messages another stream can take over if (tunnel.hasQueuedReaderThreads()) break; } while (tunnel.isOpen() && (message = reader.read()) != null); // Close tunnel immediately upon EOF if (message == null) { deregisterTunnel(tunnel); tunnel.close(); } // End-of-instructions marker out.write("0.;"); out.flush(); response.flushBuffer(); } // Send end-of-stream marker and close tunnel if connection is // closed catch (GuacamoleConnectionClosedException e) { // Deregister and close deregisterTunnel(tunnel); tunnel.close(); // End-of-instructions marker out.write("0.;"); out.flush(); response.flushBuffer(); } catch (GuacamoleException e) { // Deregister and close deregisterTunnel(tunnel); tunnel.close(); throw e; } // Always close output stream finally { out.close(); } } catch (IOException e) { // Log typically frequent I/O error if desired log.debug("Error writing to servlet output stream", e); // Deregister and close deregisterTunnel(tunnel); tunnel.close(); } finally { tunnel.releaseReader(); } }
private void rebuildTunnel(HttpServletRequest request, HttpServletResponse response, String tunnelUUID, GuacamoleTunnel tunnel) throws GuacamoleException { GuacamoleSocket socket = tunnel.getSocket(); GuacamoleConfiguration configuration = ((ConfiguredGuacamoleSocket) socket).getConfiguration(); if ("vnc".equalsIgnoreCase(configuration.getProtocol())) { int port = Integer.parseInt(configuration.getParameter("port")); log.warn(String.format("vnc service with [%s:%d] not work, increasing port and retry", configuration.getParameter("hostname"), port)); if (port++ < 5910) { configuration.setParameter("port", port + ""); deregisterTunnel(tunnel); tunnel.close(); // TODO get cloud providerId tunnel = new GuacamoleVncReconnectTunnel( new ConfiguredGuacamoleSocket(getSocket(""), configuration), tunnelUUID); registerTunnel(tunnel); doRead(request, response, tunnelUUID); } } }