Skip to content
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

[RFC] egg-socket.io #269

Closed
atian25 opened this issue Jan 17, 2017 · 68 comments
Closed

[RFC] egg-socket.io #269

atian25 opened this issue Jan 17, 2017 · 68 comments

Comments

@atian25
Copy link
Member

atian25 commented Jan 17, 2017

Directory Structure

app
├── io
│   ├── controller
│   │   └── chat.js
│   └── middleware
│       ├── auth.js
│       ├── filter.js
├── router.js
config
 ├── config.default.js
 └── plugin.js

Configuration

// {app_root}/config/config.default.js
exports.io = {
  namespace: {
    '/': {
      connectionMiddleware: [],
      packetMiddleware: [],
    },
  },
  redis: {
    host: '127.0.0.1',
    port: 6379
  }
};

Middleware

middleware are functions which every connection or packet will be processed by.

Connection Middleware

  • Write your connection middleware
    app/io/middleware/auth.js
module.exports = app => {
    return function* (next) {
        this.socket.emit('res', 'connected!');
        yield* next;
        // execute when disconnect.
        console.log('disconnection!');
    };
};
  • then config this middleware to make it works.

config/config.default.js

exports.io = {
  namespace: {
    '/': {
      connectionMiddleware: ['auth'],
    },
  },
};

pay attention to the namespace, the config will only work for a specific namespace.

Packet Middleware

  • Write your packet middleware
    app/io/middleware/filter.js
module.exports = app => {
    return function* (next) {
        this.socket.emit('res', 'packet received!');
        console.log('packet:', this.packet);
        yield* next;
    };
};
  • then config this middleware to make it works.

config/config.default.js

exports.io = {
  namespace: {
    '/': {
      packetMiddleware: ['filter'],
    },
  },
};

pay attention to the namespace, the config will only work for a specific namespace.

Controller

controller is designed to handle the emit event from the client.

example:

app/io/controller/chat.js

module.exports = app => {
  return function* () {
    const message = this.args[0];
    console.log(message);
    this.socket.emit('res', `Hi! I've got your message: ${message}`);
  };
};

next, config the router at app/router.js

module.exports = app => {
  // or app.io.of('/')
  app.io.route('chat', app.io.controllers.chat);
};
@ngot
Copy link
Member

ngot commented Jan 23, 2017

基于cluster-client,实现client没问题,但是server还是不可以的。 一旦启动 cluster 模式,websocket就会握手失败。我感觉,要和egg有机结合起来,并利用多核能力,需要设计一番。

@atian25
Copy link
Member Author

atian25 commented Jan 23, 2017

嗯, websocket / socket.io 算是比较重要的一个 showcase 的插件.

cc @fengmk2 @popomore @dead-horse

@popomore
Copy link
Member

主要是集群的不好做

@denghongcai
Copy link
Contributor

denghongcai commented Feb 6, 2017

egg-cluster 有支持 sticky session 的开关,并且插件可以有办法在框架启动之前改变 cluster 的配置,有办法吗

@fengmk2
Copy link
Member

fengmk2 commented Feb 6, 2017

依赖 socket.io ,基于标准的 redis 来做 cluster server。跟 cluster-client 没有半毛关系的。

@denghongcai
Copy link
Contributor

@fengmk2 现在的 cluster 逻辑会导致 websocket 握手失败

@gxcsoccer
Copy link
Contributor

没明白为啥要用 cluster-client 实现?

@atian25
Copy link
Member Author

atian25 commented Feb 7, 2017

当我没说 cluster-client ~

反正就是需要一个完善的 websocket 插件~

@fengmk2
Copy link
Member

fengmk2 commented Feb 7, 2017

@denghongcai 所以要基于 redis 来维持 websocket 的 session,走 socket.io 标准的模式。

@fengmk2
Copy link
Member

fengmk2 commented Feb 7, 2017

目前最快的是 https://github.com/uWebSockets/uWebSockets

但是最通用和最易用的是 socket.io https://www.npmjs.com/package/socket.io

@fengmk2
Copy link
Member

fengmk2 commented Feb 7, 2017

https://www.npmjs.com/package/socket.io-redis

By running socket.io with the socket.io-redis adapter you can run multiple socket.io instances in different processes or servers that can all broadcast and emit events to and from each other.

@arden
Copy link

arden commented Feb 8, 2017

@fengmk2 socket.io可以直接使用uWebsockets

@arden
Copy link

arden commented Feb 8, 2017

websocket和socket.io都是非常重要的一个模块,使用的人太多了,所以egg得在这个上面封装一套支持集群模式的方案。

@atian25
Copy link
Member Author

atian25 commented Feb 8, 2017

将会在 https://github.com/eggjs/egg-websocket 做.

@fouber

@arden
Copy link

arden commented Feb 8, 2017

@atian25 会有 socket.io 吗?

@atian25
Copy link
Member Author

atian25 commented Feb 8, 2017

@arden 这个库有可能内部实现用 socket.io 吧, 看具体写的时候.

@gxcsoccer @ngot 你们谁上?

@ngot
Copy link
Member

ngot commented Feb 8, 2017

我来搞

@fengmk2
Copy link
Member

fengmk2 commented Feb 8, 2017

@ngot 👍 ,可以在正文 issue 写一下方案

@arden
Copy link

arden commented Feb 9, 2017

@fengmk2 @ngot @atian25
目前我们也使用了socket.io,使用uWebsockets做为engine。本来想用pm2来做集群方案,可是在pm2的cluster运行模式下,socket.io有总有问题,只能使用pm2的fork运行模式,直接在代码里通过socket.io-redis实现socket.io的cluster模式。但光socket.io-redis还不够,session这块还需要处理。所以整个项目依赖了以下几个模块:
集群和session处理模块
https://github.com/uqee/sticky-cluster
https://github.com/socketio/socket.io-redis
高效率websocket引擎
https://github.com/uWebSockets/uWebSockets
https://github.com/Unitech/pm2

另外据说socket.io 2.0默认用uWebsockets做为ws引擎。

@ngot
Copy link
Member

ngot commented Feb 9, 2017

eggjs/egg-cluster#14

@ngot
Copy link
Member

ngot commented Feb 9, 2017

eggjs/egg-bin#32

@ngot
Copy link
Member

ngot commented Feb 10, 2017

我觉得如果基于socket.io做的话,就叫 egg-socket.io 吧,egg-websocket 要做的话,就只对uWebSockets封装

@popomore
Copy link
Member

要不要把某领域的解决方案放这里 #287

@fengmk2 fengmk2 changed the title egg-websocket egg-socket.io Feb 10, 2017
@fengmk2
Copy link
Member

fengmk2 commented Feb 10, 2017

@ngot 改名了

@ngot
Copy link
Member

ngot commented Feb 10, 2017

https://github.com/eggjs/egg-socket.io 先占坑

@atian25
Copy link
Member Author

atian25 commented Feb 10, 2017 via email

@dead-horse
Copy link
Member

先不用考虑这么多,实践中摸索吧,egg-socketio 也只是我们提供的一个插件和一个扩展的解决方案,先不侵入到 egg 的核心中。

@arden
Copy link

arden commented Feb 14, 2017

@ngot
首先,不支持 http 服务和 socket 服务混布。主要考虑到,目前的 sticky 负载不是适合于 http 服务,同时,两套约定规范融合于同一个项目也是非常的复杂和痛苦。从应用维护角度看来,也是长连接服务单独部署比较合理。
其实我还是比较认可这种方案,现在有很多http/socket混合在一起的方案,但实际上都很少使用,感觉实用价值并不大,并且容易复杂化。

@dead-horse
Copy link
Member

@arden 上面说了,如果混部,那根本不需要考虑 egg 集成的事情了。

@ngot
Copy link
Member

ngot commented Feb 17, 2017

Directory Structure

├── app
│   ├── io 
│   │   ├── controller
│   │   │   └── chat.js
│   │   └── middleware
│   │       ├── auth.js
│   │       └── filter.js
│   └── router.js
├── config
│   ├── config.default.js
│   └── plugin.js
└──  package.json

router.js

module.exports = app => {
  app.get('/', 'home'); // http router

  app.io.on('connection', socket => {
    socket.on('chat', app.io.controller.chat);
  });
};

config.js

exports.io = {
    cmiddleware: [ 'auth' ], // connection middleware
    pmiddleware: [ 'filter' ], // packet middleware
};

app/io/middleware/auth.js

module.exports = app => {
    return (socket, next) => {
        console.log('auth middleware!');
        next();
    };
};

app/io/middleware/filter.js

module.exports = app => {
    return (packet, next) => {
        console.log('filter middleware!');
        next();
    };
};

app/io/controller/chat.js

module.exports = app => {
    return msg => {
        console.log('chat :', msg + process.pid);
        setTimeout(function () {
            socket.emit('res', 'Hello World !');
        }, 500);
    }
};

最终就这样吧

@atian25
Copy link
Member Author

atian25 commented Feb 17, 2017

midware typo ?

@ngot
Copy link
Member

ngot commented Feb 17, 2017

@atian25 fixed

@atian25
Copy link
Member Author

atian25 commented Feb 17, 2017

exports.io = {
   middleware: {
      connection:  [ 'auth' ], // connection middleware
      packet: [ 'filter' ], // packet middleware
   }
};

这样怎么样?

另外, 这里会不会存在数组配置合并的问题?

@dead-horse
Copy link
Member

这个只有应用会配,问题不大的

@atian25
Copy link
Member Author

atian25 commented Feb 17, 2017

cmiddleware 这种命名有点让人疑惑

@dead-horse
Copy link
Member

connectionMiddlewarepacketMiddleware 吧,这种不要简写

@atian25
Copy link
Member Author

atian25 commented Feb 17, 2017

@ngot 把两者方案选型的考虑, 以及最终方案, 更新到顶楼吧.

@ngot
Copy link
Member

ngot commented Feb 17, 2017

嗯。我还在继续看,上下文,session那块,等完整了,更新到正文。

@atian25
Copy link
Member Author

atian25 commented Feb 20, 2017

@ngot 先更新一版吧,需要补全的地方,注明下。

等会我要发 egg-feed 了

@luicfer
Copy link
Contributor

luicfer commented Feb 21, 2017

我觉得可以参考下sailsjs。那上面的websocket,我看着炒鸡爽

@atian25
Copy link
Member Author

atian25 commented Feb 21, 2017

@luicfer 提炼下写下提案?

@ngot
Copy link
Member

ngot commented Feb 23, 2017

我还在国外考驾照…下周发个版

@ngot
Copy link
Member

ngot commented Mar 1, 2017

eggjs/egg-socket.io#1

@atian25
Copy link
Member Author

atian25 commented Mar 1, 2017

@ngot 提案也更新到顶楼吧

@ngot
Copy link
Member

ngot commented Mar 1, 2017

@atian25 done

@ngot
Copy link
Member

ngot commented Mar 3, 2017

发了 + egg-socket.io@1.0.0-beta.1 了。求试用建议@_@

@atian25
Copy link
Member Author

atian25 commented Mar 3, 2017

有没有写 examples/socket.io ? 这样试用方便点

@ngot
Copy link
Member

ngot commented Mar 3, 2017

@atian25
Copy link
Member Author

atian25 commented Mar 3, 2017

好, 回头可以放到 https://github.com/eggjs/examples

@ngot
Copy link
Member

ngot commented Mar 10, 2017

已经发布 1.0.0

@ngot ngot closed this as completed Mar 10, 2017
@atian25 atian25 changed the title egg-socket.io [RFC] egg-socket.io Sep 4, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests