fooking文档(不定期更新)
为什么80%的码农都做不了架构师?>>>
简介
fooking是一个分布式网关,其主要目的是用于承载客户端长连接,然后将接受的客户端数据以fastcgi协议转发给后端业务逻辑处理服务器,让后端服务真正独立的同时还无需关心扩展的问题,简单配置即可。
fooking服务包含两部分,一部分是gateway主要用于承载客户端链接、转发请求;另一部分是router,主要用于各gateway之间的消息传递、数据统计等。gateway可以开多个进程,并且可以配置多台服务器来提升连接数。
优势
1、非侵入式,无需安装扩展,php版本随你挑,后端错误逻辑不会影响网关正常服务
2、开发简单,做为后端php可以像写web一样,轻松echo即可
3、动态网关,方便线上应急扩容
4、session维持,每个客户端分配唯一ID,无需关心fd
5、单播/组播,指定一人或者多人或者群组发送消息
6、服务器状态监控,监控信息细化到每个gateway的进程
7、客户端事件通知,连接打开与关闭
8、无语言限制,后端只需遵循fastcgi协议即可
9、自定义协议,可用lua自定义协议处理
10、负载均衡,配合分配策略,可让每台服务器,甚至每个进程的连接数量基本相同
getting started
既然你已经点到这里了,那么我相信你应该了解fooking的基本架构了,那么开始我们的fooking初体验。
fooking使用c++开发,安装g++、make等基础开发工具,那么接下来的事就简单了(windows下可以使用cygwin编译为exe)。
git clone http://git.oschina.net/scgywx/fooking.git
make
第一招,启动router。在启动之前我得再BB两句,fooking的配置文件和脚本都是使用lua,所以建议了解一下lua的基本知识(只是建议,不是必须)。
router的配置文件位于src/router.lua,配置文件里面的参数都有详细的解释,这里就不多说了,接下来只需要按这个姿势执行命令就能启动router了。
cd src
./fooking ../router.lua
第二招,启动gateway。配置文件位于src/config.lua,配置参数相必阁下见其名便能知其意,只是其中几个参数需要说明,1是ROUTER_HOST与ROUTER_PORT,这里配置刚刚启动router的host与port,主要与router连通;2是BACKEND_SERVER,列出php-fpm的host与port即可(如果是unix domain socket,要在前面加unix://前缀),3是FASTCGI_ROOT与FASTCGI_FILE,分别是php脚本路径与脚本文件名(仅测试可指到example/chat目录下)。启动命令如下
./fooking ../config.lua
第三招,启动fpm;作为php后端服务端,php-fpm肯定少不了。
service php-fpm start
第四招,启动nginx,其实这一步,可有可无的,主要就是用来访问example/chat下面的静态资源,把目录指到example/chat目录下即可(当然你也可以不用装nginx,直接用浏览器打开example/chat/index.html也行)。
service nginx start
第五招,测试chat,打开你的浏览器(需要支持websocket协议的浏览器),直接localhost,应该就能聊天了。这个聊天的例子使用了websocket协议,协议部分的实现位于scripts/WebSocket.lua,并且这个文件也配置在config.lua的SCRIPT_FILE参数中。
协议说明
前端协议
前端协议是指gateway与客户端的通信协议,默认是使用4字节数据长度(大端)+数据,当然你可以使用lua来自定义协议(需要配置SCRIPT_FILE),在lua自定义协议的脚本里有4个事件onConnect,onClose,onInput,onOutput分别对应连接、关闭、输入(解包)、输出(打包),具体更详细的用法可以参见scripts/WebSocket.lua,里面实现了websocket协议的打包与解包。
后端协议
后端协议是指gateway与后端服务的通信协议,使用fastcgi与php-fpm通信,这个对于phper来说应该不陌生,并且使用很简单;由于客户端的请求已经在前端协议中处理了,所以发给php的都是完整的数据包,所以在php里面要获取数据包内容就很简单了,只需要这样:
file_get_contents("php://input");
另外自定义参数还可以通过配置文件的FASTCGI_PARAMS来随意添加,在php里面只需要使用$_SERVER就能获取参数值了,需要注意的是SESSIONID、EVENT、REMOTE_ADDR、REMOTE_PORT已经被fooking使用,请不需添加在自定义参数列表内,具体说明如下:
$sessionid = $_SERVER['SESSIONID'];//客户端唯一ID
$event = $_SERVER['EVENT'];//事件类型(0-表示消息事件,1-表示连接事件,2-表示关闭事件)
$ip = $_SERVER['REMOTE_ADDR'];//客户端ip
$port = $_SERVER['REMOTE_PORT'];//客户端端口
$myparam = $_SERVER['MY_PARAM'];//自定义参数
那么php需要返回消息给客户端怎么办?首先你要告诉fooking,你要返回多少数据给客户端,因此你需要设置header("Content-Length: 字节数"), 接下来只需要echo数据就好了,是不是很简单?
header("Content-Length: 11");
echo "hello world";
如果你还想指定数据数据的偏移位置,那么你可以使用Content-Offset,比如:
header("Content-Length: 5");
header("Content-Offset: 6");//也可以使用-5,效果一样。
echo "hello world";//客户端会收到world
什么时候会用到Content-Offset呢?比如你有debug、warning等调试信息在消息前面,这时候就可以使用Content-Offset做负值偏移,同时还能在fooking的日志中完整的看到debug、warning等信息。
那为什么要把debug、warning信息放在消息前面?因为正文消息可能是加密或者其它二进制流信息,如果带有\0的字符,那么日志就会被截断,所以把消息放在最后确保日志能完整输出。
单播/组播
fooking提供的组播类似于redis的pub/sub,当你对某个组感兴趣,可以加入到该组,而该组有消息则会通知你。同时每个client的信息和组信息在gateway上有存储,并且会同步到router上,所以如果你想要给某个组或者单人发消息,只需要发到router即可,那么对应php的后端,只需要包含api.php即可。
include 'api.php';
$router = new RouterClient('router host', 'router port');
$router->sendMsg('session id', 'hi');//指定单人发消息
$router->sendAllMsg('hi');//给所有人发消息
$router->kickUser('session id');//关闭指定连接
$router->addChannel('channel name', 'session id');//加入到指定组
$router->delChannel('channel name', 'session id');//从指定组移除
$router->publish('channel name', 'msg');//向指定组发送消息
$router->info();//获取当前router信息
服务器分配策略
通过使用info接口拉取到各服务器监控信息,可以很轻松实现服务器均匀分配,info接口返回如下:
Array([clients] => 8 //当前总连接数[channels] => 0 //当前channel总数[gateways] => Array(//服务器列表[1] => Array([0] => Array([serverid] => 1,//服务器id(这个就是config.lua里面的SERVER_ID)[workerid] => 0,//进程编号[clients] => 4,//当前进程负责连接数[channels] => 0,//当前进程负责channel数)[1] => Array([serverid] => 1,[workerid] => 1,[clients] => 4,[channels] => 0,) ))
)
知道info接口数据之后,我们再根据每台服务器ID当前连接数进行处理,分配当前连接数最少的服务器给客户端,代码如下:
$router = new RouterClient();
$info = $router->info();
$gateways = array();
foreach($info[‘gateway’] as $serverid => $workers){foreach($workers as $worker){$gateways[$serverid]+= $worker[‘clients’];}
}asort($gateways);
$serverid = key($gateways);
转载于:https://my.oschina.net/scgywx/blog/465186
fooking文档(不定期更新)相关推荐
- oracle查看jdk文档_Oracle JDK 9 Early Access文档已更新
oracle查看jdk文档 Raymond Gallardo于2017年4月4日发布的针对Oracle JDK 9的抢先 访问文档已更新,今天宣布对Oracle JDK9文档的抢先访问页面进行了更新. ...
- Oracle JDK 9 Early Access文档已更新
Raymond Gallardo在2017年4月4日发布的针对Oracle JDK 9的抢先体验文档 已更新,今天宣布对Oracle JDK9文档的抢先体验页面进行了更新. Gallardo重点介绍了 ...
- word、excel文档内容更新技术方案
需求背景 惯例先说下背景. 生产.研发业务上往往使用大量word和excel文档来作为资料载体,如操作规程.控制手册.卡片--,这些文档会反复使用到一些设备.工艺等参数数据.参数属性主要是名称.编码. ...
- jQuery基础文档(持续更新)
文章目录 jQuery基础文档(持续更新) 1 jQuery入门仪式: jQuery基础文档(持续更新) 1 jQuery入门仪式: 还是先上一段代码吧,对照这看: <!DOCTYPE html ...
- Elastic Search创建文档和更新文档
Elastic Search创建文档和更新文档(Put && Update) 在平时的es中需要对文档创建或者更新,通常情况下有2种api实现,举例如下: 第1种:PUT /testi ...
- mongodb php代码实例,MongoDB文档的更新(php代码实例)
MongoDB更新文档分为两大类: 文档替换,使用新文档完全替换掉旧文档 修改器,修改部分文档 文档替换 使用文档替换非常的简单,下面来看演示: $collect->insertOne(['na ...
- mongodb php update,MongoDB文档的更新(php代码实例)
MongoDB更新文档分为两大类:文档替换,使用新文档完全替换掉旧文档 修改器,修改部分文档 文档替换 使用文档替换非常的简单,下面来看演示:$collect->insertOne(['name ...
- (电商API文档)更新电子面单号
想要及时更新电子面单号对应的面单信息,首先要对接好电商平台的电子面单接口,才能将平台变动后的收件信息及时更新到电子面单上,没有对接电商平台的ERP.WMS系统可以通过点三API接口来获取打印和更新电子 ...
- 魔坊APP项目-11-PyMongo、数据库连接,管理、集合管理、文档管理(添加文档、查询文档、删除文档、更新文档)
PyMongo 安装: pip install pymongo 一.数据库连接 数据库连接,无密码 from pymongo import MongoClient# 数据库链接,必须保证当前系统能正常 ...
- Web前端开发规范文档(更新于2013-01-13)
规范目的 为提高团队协作效率, 便于后台人员添加功能及前端后期优化维护, 输出高质量的文档, 特制订此文档. 本规范文档一经确认, 前端开发人员必须按本文档规范进行前台页面开发. 本文档如有不对或者不 ...
最新文章
- 20181113-2 每周例行报告
- 20170601xlVBA正则表达式提取体检数据
- 算法导论 第六章 堆排序 习题6.5-8 k路合并排序
- MySQL—表的完整性约束(外键约束)(二)
- [转]MySQL数据库优化总结
- hdfs中一个block块默认多大?
- mysql 判断等于空字符串_mysql 判断null 和 空字符串
- PHP 过滤字符串特殊符号
- 绝对路径、相对路径详解
- 智慧能源:浅谈新一代信息技术在智慧能源的应
- 6.STM32F407之HAL库——定时器中断
- 如何使用Apollo / Graphene管理GraphQL突变中的文件上传
- 数据结构和算法学习之路-----必要的数学知识
- Win10 设置系统还原点
- 苹果留给 iOS 开发者的时间不多了:30 天内必须更新旧版本!
- PDF文件提取单独页面
- 原生js实现table 横向纵向全选功能
- 小码农的代码(一)----------SpringJDBC的使用
- bios中 启动首选项 找不到固态硬盘
- App推广人员应该知道的事:一条热门微博背后的传播心理