基于GatewayWorker+Vue所写的聊天室

回复

搬砖屌丝 发起了问题 • 1 人关注 • 0 个回复 • 39 次浏览 • 3 天前 • 来自相关话题

workerman全套,tp,laravel等分享专注PHP中高级进阶

PHP中高级进阶 发表了文章 • 1 个评论 • 124 次浏览 • 2018-11-29 15:51 • 来自相关话题

 

10.22_.png


10.23_.png


QQ截图20181116143935.png



QQ截图20181129154048.png


网盘资料分享.png

 

Workerman MySQL 连接池

stephen 发表了文章 • 0 个评论 • 92 次浏览 • 2018-11-22 16:54 • 来自相关话题

由于 workerman 的mysql组件在操作事物的时候需要在一个独立的db连接里面处理,如果相同的连接处理一个事物可能会造成异常,所以开发了一个db连接池,代码如下,如果有问题,欢迎交流指教   <?php ...查看全部
由于 workerman 的mysql组件在操作事物的时候需要在一个独立的db连接里面处理,如果相同的连接处理一个事物可能会造成异常,所以开发了一个db连接池,代码如下,如果有问题,欢迎交流指教
 
<?php

use Workerman\MySQL\Connection as DbConn;

class DbPool
{

/**
* db connection pool
* @var resource
*/
public static $_db = null;

/**
* db connection pool
* @var array
*/
public static $_dbs = [];

/**
* db mapping to pool
*/
public static $_dbsPool = [];

/**
* 闲置分配和设置锁
* @var boolean
*/
public static $_idleLock = false;

/**
* 连接池默认数量
* @var int
*/
public static $_defaultDbConnPoolNum = 100;

/**
* make a db connection
* @
*/
public static function initDbConn()
{
if (self::$_db === null) {
self::$_db = self::createDbConn();
}
return self::$_db;
}

/**
* Create a new db connection
* @return db instance
*/
public static function createDbConn()
{
$config = $GLOBALS['app_conf']['db'];
return new DbConn(
$config['host'],
$config['port'],
$config['username'],
$config['password'],
$config['db_name']
);
}

/**
* Get a DB connection instance
* @param mixed $useTrans Defaults to false
* @return object The db connection instance
*/
public static function getDB($useTrans = false)
{
if ($useTrans === false) {
return self::initDbConn();
}

if (!isset(self::$_dbsPool[$useTrans])) {
$index = self::getIdle($useTrans); // 获取置的连接,如果有,用闲置
if ($index === false || !isset(self::$_dbs[$index])) {
$index = 'dbConn_' . md5(microtime(true) . count(self::$_dbs));
self::$_dbs[$index] = self::createDbConn();
}
self::$_dbsPool[$useTrans] = $index;
} else {
$index = self::$_dbsPool[$useTrans];
}
return self::$_dbs[$index];
}

/**
* close db conn
* @param mixed $useTrans defaults to false
*/
public static function closeDB($useTrans = false)
{
if ($useTrans !== false and isset(self::$_dbsPool[$useTrans])) {
if (count(self::$_dbs) > self::$_defaultDbConnPoolNum) {
$index = self::$_dbsPool[$useTrans];
self::$_dbs[$index]->closeConnection();
self::$_dbs[$index] = null;
unset(self::$_dbs[$index]);
unset(self::$_dbsPool[$useTrans]);
} else {
self::setIdle($useTrans); // 将连接设置为闲置
}
}
if ($useTrans === false) {
self::$_db = null;
}
}

/**
* 从pool获取一个闲置的连接, 并赋值为指定的连接transToken
* @return mixed 找到闲置则返回连接索引,否则返回false
*/
private static function getIdle($transToken)
{
if (self::$_idleLock === true) {
return;
}
self::$_idleLock = true;
foreach (self::$_dbsPool as $key => $item) {
if (strpos($key, 'idle_') === 0) {
self::$_dbsPool[$transToken] = self::$_dbsPool[$key];
unset(self::$_dbsPool[$key]);
self::$_idleLock = false;
return self::$_dbsPool[$transToken];
}
}
self::$_idleLock = false;
return false;
}

/**
* 将一个连接设置为闲置
*/
private static function setIdle($transToken)
{
if (self::$_idleLock === true) {
return;
}
self::$_idleLock = true;
if (isset(self::$_dbsPool[$transToken])) {
$key = 'idle_' . md5(microtime(true));
$tmp = self::$_dbsPool[$transToken];
unset(self::$_dbsPool[$transToken]);
self::$_dbsPool[$key] = $tmp;
}
self::$_idleLock = false;
}
}

windows phpstudy配置环境变量

回复

lalala 发起了问题 • 1 人关注 • 0 个回复 • 81 次浏览 • 2018-11-12 09:49 • 来自相关话题

感觉在runAll()之前实例化mysql或者redis是可以的,因为是copy on write

walkor 回复了问题 • 2 人关注 • 2 个回复 • 121 次浏览 • 2018-11-11 10:53 • 来自相关话题

多卓科技——物联智能手环

回复

智骏科技 发起了问题 • 1 人关注 • 0 个回复 • 88 次浏览 • 2018-11-08 18:10 • 来自相关话题

centos php7.1环境安装event扩展问题

walkor 回复了问题 • 2 人关注 • 1 个回复 • 169 次浏览 • 2018-10-15 15:42 • 来自相关话题

amh 安装workerman PHP-CLI 命令行模式下 扩展支持pcntl

walkor 回复了问题 • 3 人关注 • 2 个回复 • 128 次浏览 • 2018-10-15 15:42 • 来自相关话题

封装composer包,实现thinkphp、larverl、yii框架中使用(使用框架实现回调方法)

879042886 发表了文章 • 1 个评论 • 410 次浏览 • 2018-08-08 17:23 • 来自相关话题

内有demo thinkphp、larverl、yii框架使用worker-socket 可以查看我写的即时通讯案例 佳萌驿站 然后点击QQ登录 完美解决在 event 里面 ,onmessage 里面如何 ...查看全部
内有demo thinkphp、larverl、yii框架使用worker-socket

可以查看我写的即时通讯案例 佳萌驿站 然后点击QQ登录



完美解决在 event 里面 ,onmessage 里面如何 嵌入 thinkphp,使用框架实现回调方法


1.安装
composer require tinymeng/worker-socket dev-master

2.业务逻辑
在项目 /application/index/controller 下创建文件Events.php
首先创建控制器类并继承 tinymeng\worker\Server,然后设置属性和添加回调方法

2.1 ThinkPhp框架示例如下:
<?php
/**
* Name: Events.php.
* Author: JiaMeng <666@majiameng.com>
* Description: websocket callback
*/
namespace app\index\controller;

use tinymeng\worker\Server;
use GatewayWorker\Lib\Gateway;

class Events extends Server{
/**
* @var string Socket connect address
*/
protected $socket = 'websocket://0.0.0.0:1314';
/**
* @var string The current class of namespace
*/
protected $eventHandler = 'app\index\controller\Events';

/**
* Description: 当客户端连接时时触发
* Author: JiaMeng <666@majiameng.com>
* @param $client_id
*/
public static function onConnect($client_id){
echo 'client_id : '.$client_id. ', connect ' .PHP_EOL;
}

/**
* Description: 当客户端发来消息时触发
* Author: JiaMeng <666@majiameng.com>
* @param int $client_id 连接id
* @param string $data 具体消息
*/
public static function onMessage($client_id, $data) {
echo 'client : '.$client_id. ',message data :'.$data .PHP_EOL;
}

/**
* Description: 当客户端断开连接时触发
* Author: JiaMeng <666@majiameng.com>
* @param int $client_id 连接id
*/
public static function onClose($client_id) {
echo 'client_id : '.$client_id .' close '.PHP_EOL;
}

}


3.配置thinkphp的启动文件然后来启动 workman 这样回调就会主动回调回写的控制中了,可以试一试哦
4.


支持workerman所有的回调方法定义(回调方法必须是public static类型)

php7安装event的两个问题

lrjxgl 发表了文章 • 1 个评论 • 289 次浏览 • 2018-07-26 17:16 • 来自相关话题

按照官方的event安装教程,发现两个问题。 1.按照 echo extension=event.so > /etc/php.d/30-event.ini 这个引入模块会出错 改成 echo extension=event.so ...查看全部
按照官方的event安装教程,发现两个问题。
1.按照 echo extension=event.so > /etc/php.d/30-event.ini 这个引入模块会出错
改成 echo extension=event.so > /etc/php.d/zevent.ini

2.安装的时候 有项namespace 要改为no ,不然wokerman会提示找不到eventbase;

WebSocket客户端连接不上和掉线的问题以及解决方案

jonychen1 发表了文章 • 0 个评论 • 510 次浏览 • 2018-07-16 18:26 • 来自相关话题

看到最近有很多人在blog上问关于WebSocket客户端连接和掉线的问题,有一部分我以前也遇到过,在这里我罗列了一些我以前遇到过的问题和其他简单的WebSocket服务。 Q1 客户端连接不上的问题 这个问题我遇到的基本 ...查看全部
看到最近有很多人在blog上问关于WebSocket客户端连接和掉线的问题,有一部分我以前也遇到过,在这里我罗列了一些我以前遇到过的问题和其他简单的WebSocket服务。

Q1 客户端连接不上的问题
这个问题我遇到的基本集中在后端,遇到的话需要收集日志进行分析,首先要查看是因为什么原因出现问题的,不过这种情况下多半会出现Exception,从Exception着手分析问题即可。

Q2 1客户端掉线问题
这里所指的客户端掉线的问题是指客户端非正常掉线。有的时候会出现Exception,通过处理Exception即可关闭连接,有的时候像客户端突然网络离线的情况下,这个时候后台没有收到客户端发来的close frame,会认为这个客户端还是处在在线的情况。那这个问题可以通过心跳包的问题解决。也可以通过客户端向服务器发心跳包,也可以服务器发送心跳包至客户端。我们最终采用的方案是客户端向服务端发送心跳包,服务端收到心跳包后会检测当前websocket session队列中是否存在同属性的websocket session,有的话关闭以前的session即可。我个人认为还是服务端像客户端发送心跳包好一些,但这样做要注意性能问题。

Q3 websocket连接后1分钟自动断开关闭
这是因为websocket长连接有默认的超时时间(1分钟,由proxy_read_timeout决定),就是超过一定的时间没有发送任何消息,连接会自动断开。解决办法就是让浏览器每隔一定时间(要小于超时时间)发送一个心跳。代码如下:
window.setInterval(function(){ //每隔5秒钟发送一次心跳,避免websocket连接因超时而自动断开
var ping = {"type":"ping"};
ws.send(JSON.stringify(ping));
},5000);

Q4 能收到客户端消息,后台也显示发送,但是客户端收不到
这个问题经我个人观察和猜测,应该是websocketsession没有成功关闭或者成功关闭但是和一个websocketsession对应多个session有关,因为我把上面的问题解决了之后,这个问题也没出现过了。
希望我遇到的问题以及解决方案对你们有所帮助。

客户端连接WebSocket服务器时连接失败会出现的一些问题及解决方案

jonychen1 发表了文章 • 0 个评论 • 248 次浏览 • 2018-07-16 18:26 • 来自相关话题

前段时间自己在本地写的一个WebSocket程序部署到服务器出现了几个错误,我自己也做了记录和分析,最后总结出来给大家看一下,希望能帮助大家规避掉这些问题。同时给现在正在做WebSocket和正准备想做WebSocket的程序猿多指出一条较为效率的路。 ...查看全部
前段时间自己在本地写的一个WebSocket程序部署到服务器出现了几个错误,我自己也做了记录和分析,最后总结出来给大家看一下,希望能帮助大家规避掉这些问题。同时给现在正在做WebSocket和正准备想做WebSocket的程序猿多指出一条较为效率的路。

Q1 客户端链接WebSocket服务器时连接失败,报404错误。
排查了地址错误和配置相关后,是因为JAR冲突,因为Tomcat7及以上,在Tomcat根目录的lib里已经有了websocket-api.jar,部署项目的时候如果项目的wen-inf的lib下也有websocket-api-jar会冲突,导致连接不上。

Q2 本地运行正常,连接到服务器上时出错
本地环境:JDK1.8(32位)Tomcat8.0
服务器环境:JDK1.8(64位)Tomcat8.0
本地运行是正常的,在服务器部署运行时可以打开页面,并执行WebSocket连接失败并关闭的代码。
在Tomcat8部署的项目不要导入catalina.jar和WebSocket-api.jar这两个包,因为Tomcat8自带有这两个包。自己再导入的话会有冲突,还有就是IP写服务器的IP地址,不要写localhost,在调试远程服务器的Websocket程序时,要关闭本地的Tomcat服务器。

Q3 客户端链接WebSocket服务器成功后,测试消息推送获取session失败。(java WebSocket)
这时一定要注意,客户端连接服务器时的地址如果使用了localhost:8080/,那么浏览器也一定要使用localhost:8080/,不然两个请求不是同一个session,所以会获取不到。

Q4 WebSocket链接服务器报302错误
链接重定向,可以检查一下是不是后台访问机制的问题,还有可以看一下是不是URL路径不对。

这是我写的WebSocket程序部署到服务器出现的几个错误以及解决方法,如果我下次还会用到WebSocket这块我可能会选择第三方的……不想在这块花太多时间。
希望这次的解答对你们有所帮助。

WebSocket配置中会遇到的一些问题

jonychen1 发表了文章 • 0 个评论 • 358 次浏览 • 2018-07-16 18:25 • 来自相关话题

今天来整理一些我在工作中配置上遇到的一些问题。 Q1使用nginx代理后不能访问。报错WARNING:tornado.access:400 GET /ws (127.0.0.1) 0.79ms 或者连接失败后会反复发起连接请求。 ...查看全部
今天来整理一些我在工作中配置上遇到的一些问题。

Q1使用nginx代理后不能访问。报错WARNING:tornado.access:400 GET /ws (127.0.0.1) 0.79ms 或者连接失败后会反复发起连接请求。
需要在nginx的location中新增如下配置
# websocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Q2 跨域问题(spring websocket)
经查阅官方文档springwebsocket 4.1.5版本前默认支持跨域访问,之后的版本默认不支持跨域,需要设置
.setAllowedOrigins(“*”)
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myWebSocketHandler(), "/echo.do").setAllowedOrigins("*").addInterceptors(new HandshakeInterceptor());

registry.addHandler(myWebSocketHandler(), "/echo").setAllowedOrigins("*").addInterceptors(new HandshakeInterceptor())
.withSockJS();
}

Q3 *.do配置导致sockjs失效问题(spring websocket)
web.xml中添加如下配置
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/sockjs/*</url-pattern>
</servlet-mapping>
websocket配置类中路径中不加sockjs
registry.addHandler(myWebSocketHandler(), "/echo").setAllowedOrigins("*").addInterceptors(new HandshakeInterceptor())
.withSockJS();
前端调用时应加上sockjs
websocket = new SockJS("http://127.0.0.1:8080/WebSocketTest001/sockjs/echo");

Q4 一个websocketsession对应多个session的情况
这是由于尽管每次都能保证有一个websocketsession存在,但客户端没连接一次tomcat都建立一个链接,除非你在请求头里添加cookie:JSESSIONID=xxxxxx

Q5 能收到客户端消息,后台也显示发送,但是客户端收不到
这个问题经我个人观察和猜测,应该是websocketsession没有成功关闭或者成功关闭但是和一个websocketsession对应多个session有关,因为我把上面的问题解决了之后,这个问题也没出现过了。

GatewayClient 无法使用的问题

alex0048 发表了文章 • 1 个评论 • 241 次浏览 • 2018-07-09 17:32 • 来自相关话题

有时候会出现 调用GatewayClient中的sendToClient 方法不能给客户端发送指令的问题,请注意查看 数据库存储的client_id 是否为当前进程的client_id。 注意表结构和表字段的类型,如果有错误会造成写入 ...查看全部
有时候会出现 调用GatewayClient中的sendToClient 方法不能给客户端发送指令的问题,请注意查看 数据库存储的client_id 是否为当前进程的client_id。

注意表结构和表字段的类型,如果有错误会造成写入的client_id与实际进程不相符 导致无法发送指令。


QQ截图20180709172952.png

workerman实战教程,利用websocket完成即时通讯,在线客服

hehe 发表了文章 • 0 个评论 • 916 次浏览 • 2018-06-28 18:16 • 来自相关话题

工作需要使用了workerman的getwayworker实现了长连接的点对点在线客服系统,真心感觉到workerman的强大,以及getwayworker对来实现长连接是多吗容易上手,在tp5上实现的,过程踩了不少坑,特意出了一个视频教程,有需要的可以看一下 ...查看全部
工作需要使用了workerman的getwayworker实现了长连接的点对点在线客服系统,真心感觉到workerman的强大,以及getwayworker对来实现长连接是多吗容易上手,在tp5上实现的,过程踩了不少坑,特意出了一个视频教程,有需要的可以看一下。

workerman实战之php在线客服









HTTP协议下的IO阻塞任务,如何保证请求均匀分配到进程

whyme 发表了文章 • 2 个评论 • 450 次浏览 • 2018-05-31 16:38 • 来自相关话题

比如以HTTP协议创建了5个进程,然后每个进程里的业务执行时间是2秒。 然后并发4个用户,16次请求ab -c 4 -n 16 'http://127.0.0.1:8080/' 结果请求并没有均匀分配到进程,某个进程对某个 ...查看全部
比如以HTTP协议创建了5个进程,然后每个进程里的业务执行时间是2秒。

然后并发4个用户,16次请求
ab -c 4 -n 16 'http://127.0.0.1:8080/'

结果请求并没有均匀分配到进程,某个进程对某个请求进行了阻塞
2018-05-31 15:48:22.6796 service_log 'executeTime:2.0680978298187'
2018-05-31 15:48:22.6797 service_log 'executeTime:2.0688819885254'
2018-05-31 15:48:22.6799 service_log 'executeTime:2.069078207016'
2018-05-31 15:48:22.6801 service_log 'executeTime:2.0692131519318'

2018-05-31 15:48:24.6901 service_log 'executeTime:2.0080099105835'
2018-05-31 15:48:24.6901 service_log 'executeTime:2.0079879760742'

2018-05-31 15:48:26.6998 service_log 'executeTime:2.0084669589996'
2018-05-31 15:48:26.6998 service_log 'executeTime:2.0076689720154'
2018-05-31 15:48:26.6998 service_log 'executeTime:2.0074841976166'
2018-05-31 15:48:26.6999 service_log 'executeTime:2.0084929466248'

2018-05-31 15:48:28.7072 service_log 'executeTime:2.0056090354919'
2018-05-31 15:48:28.7073 service_log 'executeTime:2.0057229995728'
2018-05-31 15:48:28.7073 service_log 'executeTime:2.0057458877563'

2018-05-31 15:48:30.7168 service_log 'executeTime:2.0076150894165'
2018-05-31 15:48:30.7171 service_log 'executeTime:2.0088679790497'
2018-05-31 15:48:30.7173 service_log 'executeTime:2.0084898471832'


可以通过在建立连接后暂停请求,结束后关闭请求的方式,避免多个请求阻塞到一个进程中执行:
$worker->onConnect = function(TcpConnection $connection)use($worker) {
$worker->pauseAccept(); // 暂停接收请求

// 接收消息回调
$connection->onMessage = function (TcpConnection $connection, $data) {
// 耗时代码
$connection->close('success‘); // 关闭连接
};
};
$worker->onClose = function(TcpConnection $connection)use($worker) {
$worker->resumeAccept(); // 恢复接收请求
};


最后就得到了如下结果:
2018-05-31 16:34:22.2053 service_log 'executeTime:2.058121919632'
2018-05-31 16:34:22.2053 service_log 'executeTime:2.0567870140076'
2018-05-31 16:34:22.3065 service_log 'executeTime:2.1597728729248'
2018-05-31 16:34:22.3065 service_log 'executeTime:2.1576390266418'

2018-05-31 16:34:24.3266 service_log 'executeTime:2.1183500289917'
2018-05-31 16:34:24.3267 service_log 'executeTime:2.1188020706177'
2018-05-31 16:34:24.4269 service_log 'executeTime:2.1188299655914'
2018-05-31 16:34:24.4272 service_log 'executeTime:2.118989944458'

2018-05-31 16:34:26.4402 service_log 'executeTime:2.1116380691528'
2018-05-31 16:34:26.4402 service_log 'executeTime:2.1118400096893'
2018-05-31 16:34:26.5402 service_log 'executeTime:2.1115860939026'
2018-05-31 16:34:26.5402 service_log 'executeTime:2.1114778518677'

2018-05-31 16:34:28.4544 service_log 'executeTime:2.0095458030701'
2018-05-31 16:34:28.5551 service_log 'executeTime:2.1137058734894'
2018-05-31 16:34:28.6556 service_log 'executeTime:2.1138899326324'
2018-05-31 16:34:28.6556 service_log 'executeTime:2.1138758659363'

以上是单一的HTTP协议的worker模型,之后我试过用HTTP协议的worker接收请求,然后异步转发给TCP协议的worker进行处理,发现如果一次性发起多个请求时,TCP协议的worker也会出现类似上面的情况,然后通过上面的方法,也很完美的解决了这个问题。

最后我觉得pauseAccept()、resumeAccept()应用场景很多啊,比如生存消费者模型中,消费者IO阻塞等待任务,又比如对时间点要求较高的爬虫任务,能尽量的均匀分配请求到进程,进而减少时间差。

官方文档目前还没有相应描述,我还是好不容易找了一篇文章中的关键字pauseAccept,然后搜索源码发现的。。。这真是两个神奇的方法,希望官方能在后面补充一下,对于我这类的小白就帮助大了,哈哈哈

小程序与GatewayWorker建立连接及 apache 配置 https 转发

默然 发表了文章 • 1 个评论 • 1045 次浏览 • 2018-03-08 09:30 • 来自相关话题

1、apache 加载 proxy_module ,proxy_wstunnel_module 模块 2、apache 配置 ssl # Proxy Config SSLProxyEngine on #监听的路径和转发的路径 ...查看全部
1、apache 加载 proxy_module ,proxy_wstunnel_module 模块
2、apache 配置 ssl 
# Proxy Config
SSLProxyEngine on
#监听的路径和转发的路径,需要输入https://和最后面的/
ProxyRequests Off
ProxyPass /wss ws://0.0.0.0:8585
ProxyPassReverse /wss ws://0.0.0.0:8585

3、小程序代码
var that = this;
wx.connectSocket({
url: "wss://www.xxx.com/wss"
});

4、GatewayWorker 代码
$gateway = new Gateway('websocket://0.0.0.0:8585', $context);

原文链接:]微信小程序 - websocket wss[/url]

写了一个基于 workerman 的小型 HTTP 框架 WorkerA

wazsmwazsm 发表了文章 • 15 个评论 • 1652 次浏览 • 2017-09-27 10:17 • 来自相关话题

2018-07-29 更新 最小版本升级 1.1.0 ==> 1.1.1 完善 IOC 容器,解决 IOC 容器只能依赖注入一层的问题。 修复平滑启动不能响应配置修改的问题。 ...查看全部
2018-07-29 更新

最小版本升级 1.1.0 ==> 1.1.1

完善 IOC 容器,解决 IOC 容器只能依赖注入一层的问题。

修复平滑启动不能响应配置修改的问题。

修复动态路由缓存内存泄漏导致进程重启的问题,使用 LRU 算法控制了缓存的大小。


2018-07-15 更新

WorkerA 发布了 1.1 版本,新增了中间件、动态路由、异常 Handler 等功能。

修复若干 bug,改善了很多不足。

编写了框架的文档:文档地址

2017-10-18 更新
添加了 redis 支持

=============

接触 workerman 一个多月,让我重新认识了 PHP。很佩服 workerman 的性能和稳定性。

之前有用 laravel \ lumen 做一个接口的项目,虽然 laravel 的开发很方便,但是由于服务器配置和传统 wnmp wamp 模型的限制,QPS 小的可怜。于是用 workerman 写一个小型的 HTTP 框架的想法就出来了。

WorkerA 目前只写了一个月不到,第一版发布,实现了一些基础功能。
使用自动依赖注入
提供 mysql 驱动、支持断线重连,跑在进程单例模式中
提供单例模式,可以注册需要的类为单例
支持 gizp deflate 压缩传输

个人喜欢 laravel 的查询构造器和路由的风格,于是将路由和查询构造器都写成了 laravel 风,你可以像 laravel 一样的方式写路由和查数据库。

WorkerA 没有 view 层,更适合做接口的项目,当然目前框架没有提供 JWT 等验证功能,要完成一个标准的 webapi ,你需要自己写一些东西。不过有自动依赖注入的功能,扩展也不算难题。

在此谢谢 workerman 的作者,虽然只是用了 workerman 功能的一小部分,但是依然解决了我自己的很多业务上的难题。

Github 地址 :https://github.com/wazsmwazsm/WorkerA
框架核心部分地址 :https://github.com/wazsmwazsm/WorkerF

实现http与workerman的交互demo

woeoio 发表了文章 • 1 个评论 • 1989 次浏览 • 2017-08-15 12:55 • 来自相关话题

这里主要用到wm的聊天室demo做了一个示例。<?php /** * This file is part of workerman. * * Licensed under The MIT License ...查看全部
这里主要用到wm的聊天室demo做了一个示例。
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;
use \Workerman\Connection\AsyncTcpConnection;
// 自动加载类
require_once __DIR__ . '/../../Workerman/Autoloader.php';
Autoloader::setRootPath(__DIR__);

// 这个55150端口不解析web内容,主要用于post和get,
//在web——onMessage事件取出$_GET和$_POST的数据,相当于仅作API接口
$web = new Worker("http://0.0.0.0:55150");
// WebServer进程数量固定为1
$web->count = 1;

//开启一个AsyncTcpConnection转发器
// 设置访问对方主机的本地ip及端口(每个socket连接都会占用一个本地端口)
$context_option = array(
'socket' => array(
// ip必须是本机网卡ip,并且能访问对方主机,否则无效
'bindto' => '127.0.0.1:2333',
),
);

$con = new AsyncTcpConnection('ws://127.0.0.1:7272', $context_option);
//用ATC做中转来和Http的数据做收发交互
//主要WEB启动的时候做ATC转发器的事件初始化
$web->onWorkerStart = function($web)
{

file_get_contents('http://a-vi.com/api/send2qw/?WorkerMan_Web回调:服务启动');
// $web->send('receive success');
// 这个网址是我的消息接收器,

GLOBAL $con;
// 中转器链接到workerman的时候自动以一个客户端身份登录,并保持在线
$con->onConnect = function($con) {
$con->send('{"type":"login","client_name":"邓伟(企业微信小秘书)","room_id":"1"}');
};
// 中转器收到workerman消息的时候做转发处理
$con->onMessage = function($con, $dat) {
echo "\r\n GC收到来自GM的消息:$dat \r\n \r\n";
$data=json_decode($dat);
switch($data->type){
// 服务端ping客户端
case 'ping':
$con->send('{"type":"pong"}');
break;;
// 登录 更新用户列表
case 'login':
//{"type":"login","client_id":xxx,"client_name":"xxx","client_list":"[...]","time":"xxx"}
file_get_contents('http://a-vi.com/api/send2qw/?电子黑板回发【登录】'.$data->client_name);
break;
// 客户端发言 message: {type:say, to_client_id:xx, content:xx}
case 'say':
//{"type":"say","act":"talk_text","from_client_id":xxx,"to_client_id":"all/client_id","content":"xxx","time":"xxx"}
if($data->act=='talk_text'){
$newmsg=$data->content;
}else{
$newmsg='<其他消息>:'.substr($dat,100);
}
file_get_contents('http://a-vi.com/api/send2qw/?电子黑板回发【消息】('.$data->from_client_name.'说:)'.$newmsg);
break;
// 登录 更新用户列表
case 'logout':
//{"type":"logout","from_client_id":xxx,"from_client_name":"xxx","time":"xxx"}
file_get_contents('http://a-vi.com/api/send2qw/?电子黑板回发【关闭】'.$data->from_client_name);
break;
}
};
// 开始登录
$con->connect();
};
// web worker 收到来自http数据的时候取出来通过ATC转发给workerman
$web->onMessage = function($conn, $data)
{
if(isset($data['get']['msg'])){ //注意 ico 请求过滤
GLOBAL $con;
$con->send('{"type":"say","act":"talk_text","to_client_id":"all","to_client_name":"所有人","content":"'.$data['get']['msg'].'"}');
//ws.send('{"type":"say","act":"talk_text","to_client_id":"all","to_client_name":"所有人","content":"【图灵回答】:'+data.text+'"}');
// file_get_contents('http://a-vi.com/api/send2qw/?WorkerMan_Web回执:'.$data['get']['msg']);
// $web->send('receive success');
print_r($data['get']['msg']);
}
$conn->close("hello\n");
};

// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
Worker::runAll();
}


其他就看附件了吧。


--------------------------------补充,源本发了完整demo附件,被人说有广告嫌疑,已经删除。不再分享

workerman源码分析

church 发表了文章 • 0 个评论 • 976 次浏览 • 2017-03-27 09:56 • 来自相关话题

# workerman workerman源码分析 前置知识事件驱动Libevent信号守护进程Socket编程 ...查看全部
# workerman
workerman源码分析

pcntl_alarm定时闹钟信号详解

haotian 发表了文章 • 7 个评论 • 2619 次浏览 • 2017-02-28 11:23 • 来自相关话题

目的:主要测试pcntl_alarm的功能 测试代码一<?php declare(ticks = 1); function signal_handler($signal) { prin ...查看全部
目的:主要测试pcntl_alarm的功能
测试代码一
<?php
declare(ticks = 1);

function signal_handler($signal) {
print "catch you ";
pcntl_alarm(5);
}

pcntl_signal(SIGALRM, "signal_handler", true);
pcntl_alarm(5);

while(1) {
}
?>

正确代码,证实每次pcntl_alarm预定一个计时器,然后当计时器到时间的时候,就会给当前进程触发SIGALRM信号

测试代码二
<?php
declare(ticks = 1);

function signal_handler($signal) {
print "catch you ";
// pcntl_alarm(5);
}

pcntl_signal(SIGALRM, "signal_handler", true);
pcntl_alarm(5);

while(1) {
}
?>

注释掉触发函数中的信号计时器,我们发现信号只触发了一次,这就说明pcntl_alarm只会一次触发

测试代码三
<?php
declare(ticks = 1);

function signal_handler($signal) {
print "catch you ";
// pcntl_alarm(5);
}

pcntl_signal(SIGALRM, "signal_handler", true);
pcntl_alarm(5);

sleep(100);

将while循环换成sleep,发现在5秒的时候信号触发,程序结束,这是为什么呢,对应手册中提到的注意点,pcntl_alarm信号会提前结束掉sleep()函数

测试代码四
之前发过文章说过declare和pcntl_signal_dispatch两个函数的区别,那么这里用后面一个函数实现呢?
<?php
function signal_handler($signal) {
print "Caught SIGALRM ";
pcntl_alarm(5);
}

pcntl_signal(SIGALRM, "signal_handler", true);
pcntl_alarm(5);

for(;;) {
sleep(100);
pcntl_signal_dispatch();
}
?>

这里我用到了一个特性 第一个特性就是sleep和SIGALRM信号之间的关系
其实你看可以去掉sleep,但是这样dispatch函数调用就太频繁,会对系统造成压力

关于declare和posix_signal_dispatch()的验证和说明

haotian 发表了文章 • 3 个评论 • 598 次浏览 • 2017-02-26 16:17 • 来自相关话题

下面都是自己程序验证的,如果有说的不对的, 请指出 验证原因:之前自己看workerman源码,写相关的信号,进程程序,模仿网上开始写,碰到declare(ticks=1),查资料说是让进程自己检测信号,数字是隔多少时间检测多少,今天碰巧看到wor ...查看全部
下面都是自己程序验证的,如果有说的不对的, 请指出
验证原因:之前自己看workerman源码,写相关的信号,进程程序,模仿网上开始写,碰到declare(ticks=1),查资料说是让进程自己检测信号,数字是隔多少时间检测多少,今天碰巧看到workerman信号分发那段代码,发现使用了一个函数posix_signal_dispatch(),资料说该函数是:调用等待信号的处理器,也摸不着头绪这个函数到底是干嘛的
测试代码一:
echo "安装信号处理器...\n";
echo "父进程: ".posix_getpid()."\n";
pcntl_signal(SIGHUP, function($signo) {
echo posix_getpid()." 信号处理器被调用\n";
});

$pid = pcntl_fork();
if($pid){
//parent
echo "这里是父进程".posix_getpid()."\n";
echo "刚刚新建了子进程".$pid."\n";
posix_kill($pid, SIGHUP);
}else{
echo "我是子进程".posix_getpid()."\n";
}
//pcntl_signal_dispatch();
echo "完成\n";

这段代码里面我没有使用declare和pcntl_signal_dispatch,可以发现处理信号函数并没有被触发

测试代码二
declare(ticks=1);
echo "安装信号处理器...\n";
echo "父进程: ".posix_getpid()."\n";
pcntl_signal(SIGHUP, function($signo) {
echo posix_getpid()." 信号处理器被调用\n";
});

$pid = pcntl_fork();
if($pid){
//parent
echo "这里是父进程".posix_getpid()."\n";
echo "刚刚新建了子进程".$pid."\n";
posix_kill($pid, SIGHUP);
}else{
echo "我是子进程".posix_getpid()."\n";
}
echo "完成\n";

发现信号检测函数被触发了,这就说明declare(ticks=1);确实可以帮助进程检测接收到的信号
但是这个函数ticks后面的数值代表着基础代码执行了多少次后会被检测一次,基础代码是什么,如何检测,喜欢这块的可以自己查查
那么如果我们将ticks后面的数值调到1000,你就会发现信号又接收不到了,为什么,留给自己思考

测试代码三
echo "安装信号处理器...\n";
echo "父进程: ".posix_getpid()."\n";
pcntl_signal(SIGHUP, function($signo) {
echo posix_getpid()." 信号处理器被调用\n";
});

$pid = pcntl_fork();
if($pid){
//parent
echo "这里是父进程".posix_getpid()."\n";
echo "刚刚新建了子进程".$pid."\n";
posix_kill($pid, SIGHUP);
}else{
echo "我是子进程".posix_getpid()."\n";
}
pcntl_signal_dispatch();
echo "完成\n";

这里发现信号触发函数也被触发了,因为这个函数的作用就是检测当前进程有没有接收到信号,如果有,就按照信号处理函数之前已经绑定的处理方法进行处理

测试代码
echo "安装信号处理器...\n";
echo "父进程: ".posix_getpid()."\n";
pcntl_signal(SIGHUP, function($signo) {
echo posix_getpid()." 信号处理器被调用\n";
});

$pid = pcntl_fork();
if($pid){
//parent
echo "这里是父进程".posix_getpid()."\n";
echo "刚刚新建了子进程".$pid."\n";
sleep(1);
posix_kill($pid, SIGHUP);
}else{
echo "我是子进程".posix_getpid()."\n";
}
pcntl_signal_dispatch();
sleep(2);
echo "完成\n";

这个稍微有个小改动就是让主进程睡了一秒,(听了@walkor的意见)但是在接受消息之后子进程和父进程都睡两秒,这样就排除了子进程已经结束,父进程发送信号没有接受者
然后再发送信息,这样做主进程就在子进程调用dispatch函数之后才发送了信息,我做这个主要就是为了验证,dispatch是一个让进程一直持有的状态,还是只执行一个动作,结果就是子进程没有接收到信号

[分享]解决C++ client socket send to Workerman 中文乱码问题

egirlasm 发表了文章 • 1 个评论 • 712 次浏览 • 2016-11-24 17:03 • 来自相关话题

在windows上 用正常的socket send(s,"你好世界",4,0); 这样workerman服务器收到的始终是乱码.这是因为workerman里使用的都是 utf-8 因此发送之 ...查看全部
在windows上 用正常的socket
send(s,"你好世界",4,0);

这样workerman服务器收到的始终是乱码.这是因为workerman里使用的都是 utf-8

因此发送之前,一定要。
string UnicodeToUTF8(const wstring& str)
{
char* pElementText;
int iTextLen;
// wide char to multi char
iTextLen = WideCharToMultiByte(936,
0,
str.c_str(),
-1,
NULL,
0,
NULL,
NULL);
pElementText = new char[iTextLen + 1];
memset((void[i])pElementText, 0, sizeof(char) [/i] (iTextLen + 1));
::WideCharToMultiByte(CP_UTF8,
0,
str.c_str(),
-1,
pElementText,
iTextLen,
NULL,
NULL);
string strText;
strText = pElementText;
delete pElementText;
return strText;
}

wstring str = L"你好世界";
string utf8str = UnicodeToUTF8(str);
send(s,utf8str.cstr(),utf8str.length(),0);
上面的会被截断一部分文字,故把字节buffer弄大了一点,就没问题了。
修正如下,
string UnicodeToUTF8(const wstring& str)
{
char* pElementText;
int iTextLen;
// wide char to multi char
iTextLen = WideCharToMultiByte(936,
0,
str.c_str(),
-1,
NULL,
0,
NULL,
NULL);
pElementText = new char[iTextLen*2 + 1];
memset((void[i])pElementText, 0, sizeof(char) [/i] (iTextLen*2 + 1));
::WideCharToMultiByte(CP_UTF8,
0,
str.c_str(),
-1,
pElementText,
iTextLen*2,
NULL,
NULL);
string strText;
strText = pElementText;
delete pElementText;
return strText;
}

[分享]解决 Workerman Tcp 丢包,粘包问题。

egirlasm 发表了文章 • 2 个评论 • 1448 次浏览 • 2016-11-17 15:42 • 来自相关话题

1.Workerman 是不会丢包的。 Workerman 的 TCP 协议会粘包。 然后我简单的写了一个 粘包处理,希望大家参考。 嗯嗯嗯,我分享这东西,就是为自己以后方便搜索。 下面是代码的一部 ...查看全部
1.Workerman 是不会丢包的。
  1. Workerman 的 TCP 协议会粘包。


然后我简单的写了一个 粘包处理,希望大家参考。
嗯嗯嗯,我分享这东西,就是为自己以后方便搜索。
下面是代码的一部分。测试时我在C++客户端加了index,收到的包很完整,顺序也是准确。
$tcp_worker->onMessage = function($tcp_connection, $data){

global $sender_io,$global;

//如果需要,可以在这里加 包头判断 if(substr($data,0,1) != "{") 包头不是括号时,才进行组包。这样可能更strong一点。
if(!empty($global->IncompletePacket)){
echo "HAVE SUB PACKET,PACKET MERGE!\n";
echo "RECEIVED DATA\n";
var_dump($data);
echo "LASTED DATA\n";
var_dump($global->IncompletePacket);
echo "===PACKET MERGE START!===\n";
$data = $global->IncompletePacket . $data;
var_dump($data);
echo "===PACKET MERGE END!===\n";
}

//var_dump($data);
$json_data = json_decode($data,true);
if($json_data == NULL){
echo "INCOMPLETE PACKET\n";
var_dump($data);
$global->IncompletePacket = $data;
return;
}

$global->IncompletePacket = NULL;


switch ($json_data['msgtype']){
case 'USAGE_INFO':

二级路由器下服务器的映射

edgexie 发表了文章 • 2 个评论 • 1100 次浏览 • 2016-08-30 18:00 • 来自相关话题

2016/08/30 15:58 最近在学习workerman框架,虽然也在看手册,但找到些好玩儿的会增强自己学习的主动性。所以就寻思着如何将自己制作的东西分享给朋友,也就是怎么把本地搭建的服务映射到外网。所以今天做下笔记,以便在成长路上留下一抹印 ...查看全部
2016/08/30 15:58
最近在学习workerman框架,虽然也在看手册,但找到些好玩儿的会增强自己学习的主动性。所以就寻思着如何将自己制作的东西分享给朋友,也就是怎么把本地搭建的服务映射到外网。所以今天做下笔记,以便在成长路上留下一抹印记。
我的家还算大,书房跟 客厅路由器的位置比较远,所以采用两级路由器的方式组网,用文本表示一下组网的线路结构吧。
光猫LAN口->路由器A WAN口 | 路由器A LAN口->路由器B WAN口 | 路由器B LAN口->centos服务器。其中路由器A负责拨号上网。
先来想想如果想在有internet的地方访问到本地centos服务器的访问链:

  • 通过公网地址访问到路由器A的WAN口。所以你必须保证自己家的网络运营商给你分配的是一个公网地址,随着上网用户的增减,IPv4的IP地址紧张,现在运营商的普遍做法是在用户没要求的时候给用户分配一个内网地址,如10.XXX.XXX.XXX 这样的地址。你会发现百度‘IP地址’所显示的地址和路由器A的WAN口地址不一样。这时候你需要跟运营商沟通,说你需要公网地址。
  • 如果运营商给你改成了公网地址,事情就可以进行下去了。把路由器A给路由器B分配的IP地址映射出去,插一句:路由器B的上网方式你要设置成静态获取一个路由器的IP地址。操作路由器A在管理页面里,找到功能如“虚拟服务器”或者“端口段映射”(具体看所属品牌路由器上有什么对应的功能名称),把路由器B的WAN口地址做映射,端口填写你服务器对外提供服务的端口,我开的是一个apache(端口80)和一个worker(端口2345)。路由器(tplink)映射设置如下图:
tp.png
  • 这样通过公网就可以访问到路由器B的WAN口了。接下来把路由器B下挂的服务器地址映射出去。如下图(腾达):
td.png
  • 这样就可以在浏览器中通过公网地址访问到你的服务器了。
[list=1]
[*]浏览器输入公网地址如:60.223.XX.XX ,插一句:有的用户80端口会被服务商屏蔽,如果被屏蔽请修改为一个10000以上的端口去把服务器的apache映射出去,当然这里也需要在服务器中改apache配置文件里的服务端口。[/*]
[*]在服务器上开启wokerman,start.php如下:
[/*]
[/list]<?php
use Workerman\Worker;
require_once './Workerman/Autoloader.php';
// 创建一个Worker监听2345端口,使用websocket协议通讯
$ws_worker = new Worker("websocket://0.0.0.0:2345");
$ws_worker->onConnect = function($connection)use($ws_worker){
echo $connection->id."\n";
};
function send($ws_worker,$data){
foreach($ws_worker->connections as $connection)
{
$connection->send($data);
}
}
$ws_worker->onMessage = function($connection,$data)use($ws_worker)
{
send($ws_worker,$data);
};
// 运行worker
Worker::runAll();

3.在服务器的apache服务中(默认路径:/var/www/html)上传一个index.html网页,一定要注意填写正确的地址即你的公网地址,我源码中用60.223.XX.XX表示了。源码如下:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
<style>
body{ font-size:16px; line-height:1.8; width:900px; margin:auto;padding: 60px 0}
*{ margin:0; padding:0;}
#show{
width: 200px;border: 1px solid black;margin: 10px;display: inline-block;height: 29px;
}
input{
height: 29px;line-height: 29px;padding-left: 10px;width: 200px;
}
</style>
</head>
<body>
<div>输入:<input type="text" id="content" /><button onclick="sendText()" type="submit">测试</button></div>
<div>记录:<span id="show"></span></div>
<script type="text/javascript">
//使用websocket协议
ws = new WebSocket("ws://60.223.XX.XX:2345");
ws.onopen = function(){
ws.send('即时聊天@xie');
};
ws.onmessage = function(e){
document.getElementById('show').innerHTML = e.data;
};
ws.onclose = function(e) {
alert('WebSocketClosed!');
};

//测试按钮发送文本框内容
function sendText(){
var content = document.getElementById('content').value;
if(content!=''){
ws.send(document.getElementById('content').value);
document.getElementById('content').value='';
}
};
</script>
</body>
</html>

4.打开浏览器输入公网地址,截图如下:
test1.png


test2.png


test3.png


题外话:
我为什么要这样折腾?
前几天从朋友家拿了个他一脚踹开的电脑,配置一般。我就寻思的在家自己搭建个服务器学习。就顺过来了,拿回来第一件事就是用U盘装centos无图形界面,安装还算顺利,唯一要注意的是引导程序别安装在U盘上,不然,拔了U盘centos启动不了。
有花生壳的朋友还可以在路由器A上开启DDNS,这样通过访问花生壳提供的免费域名就可以了,不用再输入那个公网地址,具体我想大家应该会操作吧,只是简单的登录一下花生壳就行。
一定要注意centos的防火墙设置,把2345和80端口开了。

我这样折腾以后,只要开着那个服务器和路由器就相当于我有了一个随时可以远程玩儿弄的服务器。

我是新手,老司机们不要笑啊。

基于 Workerman 的简单聊天室

pader 发表了文章 • 0 个评论 • 573 次浏览 • 2016-08-26 14:33 • 来自相关话题

DEMO://do[dot]vgot[dot]net:2345/xchat 现在就一个聊天房间,有各种进入离开提醒,有在线人列表功能。 服务端目前就一个 websocket 服务进程,未来再用 Gateway ...查看全部
DEMO://do[dot]vgot[dot]net:2345/xchat

现在就一个聊天房间,有各种进入离开提醒,有在线人列表功能。

服务端目前就一个 websocket 服务进程,未来再用 GatewayWorker。

服务端代码很少://github[dot]com/xpader/Navigation/tree/master/Applications/XChat
主要是 JS://github[dot]com/xpader/Navigation/blob/master/static/xchat.js

代码不多,做着玩玩实现个小思路,供大家参考。

为什么不能发链接啊,真是醉了。