首先需要在系统中安装swoole服务!
PHP的ws_server.php文件中的代码:
//创建WebSocket Server对象,监听0.0.0.0:9502端口 $ws = new Swoole\WebSocket\Server('0.0.0.0', 9502); //监听WebSocket连接打开事件 $ws->on('Open', function ($ws, $request) { $ws->push($request->fd, "hello, welcome\n"); }); //监听WebSocket消息事件 $ws->on('Message', function ($ws, $frame) { echo "Message: {$frame->data}\n"; $ws->push($frame->fd, "server: {$frame->data}"); }); //监听WebSocket连接关闭事件 $ws->on('Close', function ($ws, $fd) { echo "client-{$fd} is closed\n"; }); $ws->start();
客户端向服务器端发送信息时,服务器端触发 onMessage 事件回调
服务器端可以调用 $server->push() 向某个客户端(使用 $fd 标识符)发送消息
在Linux中运行程序
php ws_server.php
可以使用 Chrome 浏览器进行测试,JS 代码为:
var wsServer = 'ws://127.0.0.1:9502'; var websocket = new WebSocket(wsServer); websocket.onopen = function (evt) { console.log("Connected to WebSocket server."); }; websocket.onclose = function (evt) { console.log("Disconnected"); }; websocket.onmessage = function (evt) { console.log('Retrieved data from server: ' + evt.data); }; websocket.onerror = function (evt, e) { console.log('Error occured: ' + evt.data); };
其他相关说明请查看:Swoole官网
1、首先查看当前php命令运行的是哪个版本
ls /usr/bin/php -al
2、查看当前php运行目录
find / -name php
3、删除当前运行的目录
rm -rf /usr/bin/php
4.用ln -s 命令重新建立连接
ln -s /www/server/php/70/bin/php /usr/bin/php
前置:本人测试使用的系统为windows,MySQL版本为5.7+,以下结果可能会受到影响,这里提前说明下。
表结构:
CREATE TABLE `ifnull_test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `money1` decimal(10,2) DEFAULT NULL, `money2` decimal(10,2) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
表数据:
id | money1 | money2 |
1 | 10.00 | (Null) |
2 | (Null) | -100.00 |
3 | 150.00 | 10.00 |
假设我要查询money1大于money2的,并且要把差值计算出来。SQL如下:
SELECT ( money1 - money2 ) AS money,money1,money2 FROM ifnull_test WHERE money1 > money2
但当表中数据存在null的情况下,不管是比较还是计算都会出问题,上面SQL的运行结果为:
money | money1 | money2 |
140.00 | 150.00 | 10.00 |
比我们预想的要差很多,按照我们的思维,表中的三条数据都是应该出现在结果中,这就需要用到IFNULL这个SQL函数了:
-- IFNULL() 函数用于判断第一个表达式是否为 NULL,如果为 NULL 则返回第二个参数的值,如果不为 NULL 则返回第一个参数的值。 IFNULL(expression, alt_value)
这时我们就可以加上这个函数完善上面的查询语句:
SELECT ( IFNULL(money1,0) - IFNULL(money2,0) ) AS money,money1,money2 FROM ifnull_test WHERE IFNULL(money1,0) > IFNULL(money2,0)
查询结果:
money | money1 | money2 |
10.00 | 10.00 | (Null) |
100.00 | (Null) | -100.00 |
140.00 | 150.00 | 10.00 |
end!
前置:本人测试使用的系统为windows,MySQL版本为5.7+,以下结果可能会受到影响,这里提前说明下。
下载安装的是最新版的mysql5.7.x版本,默认是开启了 only_full_group_by 模式的,但开启这个模式后,原先的 group by 语句就报错,然后又把它移除了。
一旦开启 only_full_group_by ,感觉,group by 将变成和 distinct 一样,只能获取受到其影响的字段信息,无法和其他未受其影响的字段共存,这样,group by 的功能将变得十分狭窄了
only_full_group_by 模式开启比较好。
因为在 mysql 中有一个函数: any_value(field) 允许,非分组字段的出现(和关闭 only_full_group_by 模式有相同效果)。
所以解决办法大致有两种:
第一种:
1、查看sql_mode
select @@global.sql_mode;
查询出来的值一般为:
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
2、去掉ONLY_FULL_GROUP_BY,重新设置值。
set @@global.sql_mode ='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
3、上面是改变了全局sql_mode,对于新建的数据库有效。对于已存在的数据库,则需要在对应的数据下执行:
set sql_mode ='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
第二种:
在sql查询语句中不需要group by的字段上使用any_value()函数。
这种对于已经开发了不少功能的项目不太合适,毕竟要把原来的sql都给修改一遍
CAST(expression AS TYPE) 函数可以将任何类型的值转换为具有指定类型的值,利用该函数可以直接在数据库层处理部分因数据类型引起的问题。
以下为该函数支持的数据类型:
支持的 TYPE | 描述 |
BINARY | 二进制型 |
CHAR | 字符型 |
DATE | 日期,格式为 ‘YYYY-MM-DD’ |
DATETIME | 日期加具体的时间,格式为 ‘YYYY-MM-DD HH:MM:SS’ |
TIME | 时间,格式为 ‘HH:MM:SS’ |
DECIMAL | float 型 |
SIGNED | int 型 |
UNSIGNED | 无符号int |
示例:
固定字符串转为SIGNED int 型
SELECT CAST("12321" AS SIGNED ) AS result 运行结果:12321
前提:此命令在windows平台上不可用,且最好是在cli(命令行)下运行(ps:宝塔默认禁用pcntl_fork()函数)。
需要用到pcntl_fork()函数,此函数创建一个子进程,父进程和子进程都从fork的位置开始向下继续执行,不同的是父进程执行过程中,得到的fork返回值为子进程号,而子进程得到的是0。
const NEWLINE = "\n\n"; if (strtolower(php_sapi_name()) != 'cli') { die("请在cli模式下运行"); } echo "当前进程(执行fork前):" . getmypid() . NEWLINE; //fork出子进程 $pid = pcntl_fork(); //fork后父进程会走自己的逻辑,子进程从处开始走自己的逻辑,堆栈信息会完全复制给子进程内存空间,父子进程相互独立 if ($pid == -1) { // 创建错误,返回-1 die('进程fork失败'); } else if ($pid) { // $pid > 0, 如果fork成功,在子进程中pid为0,而父进程中pid则大于0。 // 父进程逻辑 $time = microtime(true); $thispid=getmypid(); echo "{$thispid}-{$pid}-我是父进程:{$time}".NEWLINE; } else { // $pid = 0 // 子进程逻辑 $time = microtime(true); $thispid=getmypid(); echo "{$thispid}-{$pid}-我是子进程-{$pid}:{$time}".NEWLINE; }
补充:getmypid()获取当前进程id。
一个单独的控制进程(父进程)负责产生子进程,这些子进程用于监听请求并作出应答。
Apache总是试图保持一些备用的 (spare)或是空闲的子进程用于迎接即将到来的请求。
这样客户端就无需在得到服务前等候子进程的产生。
最明显的例子就是在CGI模式下,如果修改了php.ini的配置文件,不用重启web服务便可生效(每次都会加载php.ini的配置,这也是导致性能能地下的一个原因)。而模块模式下需要重启web服务。
以mod_php模式运行PHP,意味着PHP是作为Apache的一个模块来启动的,因此只有在Apache启动的时候会读取php.ini配置文件并加载扩展模块,在Apache运行期间是不会再去读取和加载扩展模块的。
当一个服务web-service(nginx)分配过来请求的时候,通过匹配后缀是动态的php的请求。CGI就会去读取php.ini的基本配置信息,初始化环境,创建进程,返回数据,退出进程。每一次请求都是循环往复,所以有些繁琐,这是后面为什么会诞生fastcgi的原因。
这种形式是CGI的加强版本,CGI是单进程,FastCGI多线程的运行方式,程序执行完成之后就会销毁,所以每次都需要加载配置和环境变量fork-and-execute(创建-执行)。
而FastCGI则不同,FastCGI 像是一个常驻 (long-live) 型的 CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去 fork 一次。
FastCGI进程管理器自身初始化,启动多个CGI解释器进程 (在任务管理器中可见多个php-cgi.exe)并等待来自Web Server的连接。
命令行运行。
array_key_exists() 会检查键值的存在. 这个函数会返回TRUE,只要键值存在,即使值为NULL.
$data = array("one" => "1", "two" => "", "three" => null); var_dump(array_key_exists("one", $data));// true var_dump(array_key_exists("two", $data));// true var_dump(array_key_exists("three", $data));// true
和arrry_key_exitst()不同,isset()会同时检查键和值,只有当健存在,对应的变量不为NUll的时候才会返回TURE。
$data = array("one" => "1", "two" => "", "three" => null); var_dump(isset($data['one']));// true var_dump(isset($data['two']));// true var_dump(isset($data['three']));// false
因为有时候公司服务器的端口不会全部开放,所以安装好后的宝塔可能无法用默认的8888端口访问,所以查询了一下解决办法。
修改面板端口,如要改成38888(centos 6 系统)
echo '38888' > /www/server/panel/data/port.pl && /etc/init.d/bt restart iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 38888 -j ACCEPT service iptables save
修改面板端口,如要改成38888(centos 7 系统)
echo '38888' > /www/server/panel/data/port.pl && /etc/init.d/bt restart firewall-cmd --permanent --zone=public --add-port=38888/tcp firewall-cmd --reload
亲测有效!
现在我有一台服务器,这里叫服务器①,服务器①的80端口因为某些原因被封了,而原来指向这台服务器的域名全都失效了,只能通过服务器①的ip:端口号的方式进行访问,这样用户体验极差,所以找办法解决了一下,于是有了下面的方案。
需要两台服务器(服务器①和服务器②,服务器①是源码所在服务器),以及域名解析权限。
首先解析一个ip:端口号的访问地址,并确保能正常访问。
然后把想要恢复正常的网站解析到服务器②上,然后此服务器打开Nginx的配置,在配置的最后一行会有这么一句:
include /www/server/panel/vhost/nginx/*.conf;
我们去打开这个目录,因为他是用*进行加载的配置,所以这里建议每个网站建立一个conf文件,文件名随意,但建议和域名同步,防止混乱后期也便于维护,要配置的内容是:
upstream shx { server 服务器①的ip:端口号 weight=1; } server { listen 80; server_name 解析的域名地址; index index.html; location / { proxy_pass http://shx/; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location ~ ^/(WEB-INF)/ { deny all; } }
配置好后就可以通过域名正常访问,看上去是域名进行访问,但实际上访问的是ip:端口号。
因为此目录下所有配置是一起加载的,所以每个upstream后面的名字都要不一样,下面proxy_pass中的也要相应替换。
这个方法还有其它作用,例如某阿域名要求必须绑定自己公司的服务器,如果你绑定其他公司的服务器,会导致网站无法正常访问,也可以用这种方法,先解析到某阿自己的服务器,再指向到自己的服务器。