Skip to content

Terminal 断线重连问题 #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 0 additions & 52 deletions app/backendAPI/websocketClient.js

This file was deleted.

147 changes: 147 additions & 0 deletions app/backendAPI/websocketClients.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { Stomp } from 'stompjs/lib/stomp'
import SockJS from 'sockjs-client'
import getBackoff from 'utils/getBackoff'
import config from 'config'
import { autorun, runInAction } from 'mobx'

const log = console.log || (x => x)
const warn = console.warn || (x => x)

const io = require(__RUN_MODE__ ? 'socket.io-client/dist/socket.io.min.js' : 'socket.io-client-legacy/dist/socket.io.min.js')

class FsSocketClient {
constructor () {
if (FsSocketClient.$$singleton) return FsSocketClient.$$singleton
const url = config.isPlatform ?
`${config.wsURL}/sockjs/${config.spaceKey}`
: `${config.baseURL}/sockjs/`
// SockJS auto connects at initiation
this.sockJSConfigs = [url, {}, { server: `${config.spaceKey}`, transports: 'websocket' }]
this.backoff = getBackoff({
delayMin: 50,
delayMax: 5000,
})
this.maxAttempts = 7
FsSocketClient.$$singleton = this
}

connect (connectCallback, errorCallback) {
const self = this
if (!this.socket || !this.stompClient) {
this.socket = new SockJS(...this.sockJSConfigs)
this.stompClient = Stomp.over(this.socket)
this.stompClient.debug = false // stop logging PING/PONG
}
self.stompClient.connect({}, function success () {
runInAction(() => config.fsSocketConnected = true)
self.backoff.reset()
connectCallback.call(this)
}, function error () {
log('fsSocket error')
switch (self.socket.readyState) {
case SockJS.CLOSING:
case SockJS.CLOSED:
runInAction(() => config.fsSocketConnected = false)
self.reconnect(connectCallback, errorCallback)
break
case SockJS.OPEN:
log('FRAME ERROR', arguments[0])
break
default:
}
errorCallback(arguments)
})
}

reconnect (connectCallback, errorCallback) {
if (config.fsSocketConnected) return
log(`try reconnect fsSocket ${this.backoff.attempts}`)
// unset this.socket
this.socket = undefined
if (this.backoff.attempts <= this.maxAttempts) {
const retryDelay = this.backoff.duration()
log(`Retry after ${retryDelay}ms`)
const timer = setTimeout(
this.connect.bind(this, connectCallback, errorCallback)
, retryDelay)
} else {
this.backoff.reset()
warn('Sock connected failed, something may be broken, reload page and try again')
}
}
}


class TtySocketClient {
constructor () {
if (TtySocketClient.$$singleton) return TtySocketClient.$$singleton
if (config.isPlatform) {
const wsUrl = config.wsURL
const firstSlashIdx = wsUrl.indexOf('/', 8)
let [host, path] = firstSlashIdx === -1 ? [wsUrl, ''] : [wsUrl.substring(0, firstSlashIdx), wsUrl.substring(firstSlashIdx)]
this.socket = io.connect(host, {
forceNew: true,
reconnection: false,
autoConnect: false, // <- will manually handle all connect/reconnect behavior
reconnectionDelay: 1500,
reconnectionDelayMax: 10000,
reconnectionAttempts: 5,
path: `${path}/tty/${config.shardingGroup}/${config.spaceKey}/connect`,
transports: ['websocket']
})
} else {
this.socket = io.connect(config.baseURL, { 'resource': 'coding-ide-tty1' })
}

this.backoff = getBackoff({
delayMin: 1500,
delayMax: 10000,
})
this.maxAttempts = 5

TtySocketClient.$$singleton = this
return this
}

// manually handle all connect/reconnect behavior
connectingPromise = undefined
connect () {
// Need to make sure EVERY ATTEMPT to connect has ensured `fsSocketConnected == true`
if (this.socket.connected || this.connectingPromise) return this.connectingPromise
let resolve, reject
this.connectingPromise = new Promise((rsv, rjt) => { resolve = rsv; reject = rjt })
const dispose = autorun(() => { if (config.fsSocketConnected) resolve(true) })
this.connectingPromise.then(() => {
dispose()
this.connectingPromise = undefined

// below is the actual working part of `connect()` method,
// all logic above is just for ensuring `fsSocketConnected == true`
this.socket.io.connect(err => {
if (err) {
runInAction(() => config.ttySocketConnected = false)
return this.reconnect()
}
// success!
runInAction(() => config.ttySocketConnected = true)
this.backoff.reset()
})
this.socket.connect()
})
}

reconnect () {
log(`try reconnect ttySocket ${this.backoff.attempts}`)
if (this.backoff.attempts <= this.maxAttempts && !this.socket.connected) {
const timer = setTimeout(() => {
this.connect()
}, this.backoff.duration())
} else {
warn(`TTY reconnection fail after ${this.backoff.attempts} attempts`)
this.backoff.reset()
}
}

}

export { FsSocketClient, TtySocketClient }
8 changes: 5 additions & 3 deletions app/backendAPI/workspaceAPI.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* @flow weak */
import config from '../config'
import { request } from '../utils'
import Client from './websocketClient'
import { FsSocketClient } from './websocketClients'

var connectedResolve
export const websocketConnectedPromise = new Promise((rs, rj) => connectedResolve = rs)
export const fsSocketConnectedPromise = new Promise((rs, rj) => connectedResolve = rs)

export function isWorkspaceExist () {
return request.get(`/workspaces/${config.spaceKey}`).catch(() => false).then(() => true)
Expand All @@ -22,9 +22,11 @@ export function createWorkspace (options) {

export function connectWebsocketClient () {
return new Promise(function (resolve, reject) {
Client.connect(function () {
const fsSocketClient = new FsSocketClient()
fsSocketClient.connect(function () {
connectedResolve(this)
resolve(true)
}, function (err) {
})
})
}
Expand Down
20 changes: 14 additions & 6 deletions app/components/FileTree/subscribeToFileChange.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
/* @flow weak */
import config from '../../config'
import api from '../../backendAPI'
import store, { getState, dispatch } from '../../store'
import mobxStore from '../../mobxStore'
import config from 'config'
import api from 'backendAPI'
import { autorun } from 'mobx'
import { FsSocketClient } from 'backendAPI/websocketClients'
import store, { getState, dispatch } from 'store'
import mobxStore from 'mobxStore'
import * as TabActions from 'commons/Tab/actions'
import * as FileTreeActions from './actions'
import * as GitActions from '../Git/actions'
import * as TabActions from 'commons/Tab/actions'

function handleGitFiles (node) {
const path = node.path
Expand Down Expand Up @@ -46,7 +48,13 @@ function handleGitFiles (node) {
}

export default function subscribeToFileChange () {
return api.websocketConnectedPromise.then(client => {
autorun(() => {
if (!config.fsSocketConnected) return
const client = FsSocketClient.$$singleton.stompClient
client.subscribe('CONNECTED', (frame) => {
console.log('FS CONNECTED', frame);
})

client.subscribe(`/topic/ws/${config.spaceKey}/change`, (frame) => {
const data = JSON.parse(frame.body)
const node = data.fileInfo
Expand Down
2 changes: 1 addition & 1 deletion app/components/Modal/FilePalette/component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import api from '../../../backendAPI'
import store, { dispatch as $d } from '../../../store'
import * as TabActions from 'commons/Tab/actions'
import cx from 'classnames'
import { dispatchCommand } from '../../../commands/lib/keymapper'
import dispatchCommand from 'commands/dispatchCommand'

const debounced = _.debounce(function (func) { func() }, 1000)

Expand Down
15 changes: 9 additions & 6 deletions app/components/Terminal/Terminal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import Terminal from 'sh.js';
import _ from 'lodash';
import { emitter, E } from 'utils'

import terms from './terminal-client';
import TerminalManager from './terminal-client';
import * as TabActions from 'commons/Tab/actions';
terms.setActions(TabActions);


class Term extends Component {
constructor(props) {
Expand All @@ -17,26 +17,29 @@ class Term extends Component {

componentDidMount() {
var _this = this;
var terminalManager = new TerminalManager()
var terminal = this.terminal = new Terminal({
theme: 'terminal_basic',
cols: 80,
rows:24
});

terminalManager.setActions(TabActions);

terminal.tabId = this.props.tab.id;
terminal.open(this.termDOM);
terminal.name = this.sessionId = _.uniqueId('term_');
terminal.id = this.sessionId = _.uniqueId('term_');

terminal.on('resize', (cols, rows) => {
terms.resize(terminal, cols, rows);
terminalManager.resize(terminal, cols, rows);
});
setTimeout(() => terminal.sizeToFit(), 0)
emitter.on(E.PANEL_RESIZED, this.onResize.bind(this))
emitter.on(E.THEME_CHANGED, this.onTheme.bind(this))

terms.add(terminal);
terminalManager.add(terminal);
terminal.on('data', data => {
terms.getSocket().emit('term.input', {id: terminal.name, input: data})
terminalManager.getSocket().emit('term.input', {id: terminal.id, input: data})
});
terminal.on('title', _.debounce(title => {
_this.props.tab.title = title
Expand Down
Loading