欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于WebSocket的项目构建工具实现

程序员文章站 2024-03-14 15:04:04
...

目录

一、背景

二、技术方案

三、实现

四、效果展示

五、问题与扩展

Reference


一、背景

类似Jenkins的构建工具在项目维护以及部署上为开发人员节约了大量时间,但是构建工具本身也会消耗一部分服务器资源,对于服务器配置较低的小项目或个人项目,往往很难分配出相应资源运载这个"大家伙"。针对这类场景,一套简单的构建系统:能实现基本的构建任务;能实时查看构建进度及构建信息;最好能实现构建的配置化、管理化,其实就完全可以满足需求。

二、技术方案

引入node ssh2模块 实现远程ssh连接服务器并执行相关shell命令

基于WebSocket实现远程shell命令的实时数据传输,查看构建进度

通过建立构建配置表,支持多项目构建的管理

三、实现

1、基于ThinkJs+MongoDB搭建基本后端服务,并根据官方文档配置需要的websocket服务

adapter.js - ThinkJs

/**
 * socketio config
 * 配置websocket
 */
exports.websocket = {
 type: 'socketio',
 common: {
  // common config
 },
 socketio: {
  handle: socketio,
  // allowOrigin: 'https://127.0.0.1', // 默认所有的域名都允许访问
  path: '/socket.io', // 默认 '/socket.io'
  adapter: null, // 默认无 adapter
  messages: {
   open: '/websocket/open', // 建立连接时处理对应到 websocket Controller 下的 open Action
   close: '/websocket/close', // 关闭连接时处理的 Action
   structure: '/websocket/structure', // 构建事件处理的 Action
  }
 }
}

2、实现一个连接ssh的Service并实现相关Controller对于的业务

service/ssh.js - ThinkJs

var Client = require('ssh2').Client;
module.exports = class extends think.Service {
 constructor({ ip, username, password, command }) {//实例化时初始化参数
  super();
  this.ip = ip;
  this.username = username;
  this.password = password;
  this.command = command;
 }
 //执行命令并传递回调函数(用于输出shell控制台信息)
 connect({
  output
 }) {
  let self = this;
  var conn = new Client();
  conn.on('ready', function() {
   // console.log('Client :: ready');
   conn.shell(function(err, stream) {
    if (err) throw err;
    stream.on('end', function() {
     output('Stream :: end');
     // console.log('Stream :: end')
     conn.end();
    }).on('close', function() {
     output('Stream :: close');
     // console.log('Stream :: close')
     conn.end();
    }).on('data', function(data) {
     // console.log(data.toString());
     output(data.toString('utf-8')) //实时输出控制台信息
    }).stderr.on('data', function(data) {
     output('STDERR: ' + data);
    });
    stream.end(self.command);//设置执行的命令
   });
  }).connect({//ssh传递配置
   host: self.ip,
   port: 22,
   username: self.username,
   password: self.password,
  });
 }
}

controller/websocket.js - ThinkJs

module.exports = class extends think.Controller {
 constructor(...arg) {
  super(...arg);
 }
 openAction() {
  this.emit('opend', 'websocket connect successfully!')
 }
 closeAction() {
  console.log('websocket close!')
 }
 structureAction() {
  let self = this;
  var ctx = this.ctx;
  var websocketData = ctx.req.websocketData;
  const ssh = think.service('ssh', websocketData);//实例化ssh service
  ssh.connect({
   output: function(data) {
    self.emit('ssh-output', data) // websocket提交信息到前端
   }
  });
 }
}

3、页面建立websocket连接并根据返回信息完成数据展示(这里只展示具体构建相关的前端代码)

/frame/utils/structure.vue - Nuxt

//...
  // 打开构建面板
  startStructure(row) {
   let connectSuccess = false;
   let self = this;
   self.structureActionVisible = true;
   self.socket = io(location.origin);
   self.socket.on('opend', function(data) {
    self.$message.success('websocket连接成功!');
    self.connectSuccess = true;
    self.structureData = row;
   });
   self.socket.on('connect', () => {});
   //监听服务器返回
   self.socket.on('ssh-output', (data) => {
    //********信息打印到浏览器之前的一些过滤处理 START
    if (data.match(/^\s+$/)) {
     return;
    }
    console.log(data);
    if ((data.indexOf('Client') != -1 && data.indexOf('building') != -1) || (data.indexOf('Server') != -1 && data.indexOf('building') != -1) || (data.indexOf('') != -1) || (data.indexOf('[email protected]') != -1)) {
     if (data.indexOf('[PM2]') != -1) {} else {
      self.console += "|";
     }
    //********信息打印到浏览器之前的一些过滤处理 END
    } else {
     self.console += data + '<br>'
    }
    self.$nextTick(_ => {
     $('.console').scrollTop(999999999999999999);
    })
   });
   self.socket.on('disconnect', () => {
    self.$message.info('websocket已经断开!');
    self.connectSuccess = false;
   });
   self.socket.on('connect_error', () => {
    self.$message.error('websocket连接失败!');
    self.connectSuccess = false;
   });
  },
  //进行构建
  structureAction() {
   let self = this;
   let socket = self.socket;
   if (!self.connectSuccess) {
    self.$message.info('websocket并未连接!');
    return;
   }
   socket.emit('structure', self.structureData)
  },
//...

 

四、效果展示

暂无

五、问题与扩展

1、由于服务器输出流存在16进制转码以及色彩信息,在浏览器页面(控制台无问题)部分内容展示会出现16进制乱码问题

2、扩展实现简单的构建次数或构建历史统计

3、多项目同时构建

Reference

1、ThinkJs WebSocket官方文档

https://thinkjs.org/zh-cn/doc/3.0/websocket.html

2、ssh2 官方文档

https://github.com/mscdex/ssh2