大神的见解:https://www.cnblogs.com/52fhy/p/9293015.html
socket
第一章 socket基础编码
1.1 服务端代码
<?php
header("content-type:text/html;charset=UTF-8");
//确保在连接客户端时不会超时
set_time_limit(0);
//设置IP和端口号
$address = "127.0.0.1";
$port = 1083; //调试的时候,可以多换端口来测试程序!
/**
* 创建一个SOCKET
* AF_INET=是ipv4 如果用ipv6,则参数为 AF_INET6
* SOCK_STREAM为socket的tcp类型,如果是UDP则使用SOCK_DGRAM
*/
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() 失败的原因是:" . convertToUtf8( socket_strerror(socket_last_error()) ) . "/n");
//阻塞模式
socket_set_block($sock) or die("socket_set_block() 失败的原因是:" .convertToUtf8( socket_strerror(socket_last_error()) ) . "/n");
//绑定到socket端口
$result = socket_bind($sock, $address, $port) or die("socket_bind() 失败的原因是:" . convertToUtf8( socket_strerror(socket_last_error()) ) . "/n");
//开始监听
$result = socket_listen($sock, 4) or die("socket_listen() 失败的原因是:" . convertToUtf8( socket_strerror(socket_last_error()) ) . "/n");
echo "OK\nBinding the socket on $address:$port ... ";
echo "OK\nNow ready to accept connections.\nListening on the socket ... \n";
do { // never stop the daemon
//它接收连接请求并调用一个子连接Socket来处理客户端和服务器间的信息
$msgsock = socket_accept($sock) or die("socket_accept() failed: reason: " . convertToUtf8( socket_strerror(socket_last_error()) ) . "/n");
//读取客户端数据
echo "Read client data \n";
//socket_read函数会一直读取客户端数据,直到遇见\n,\t或者\0字符.PHP脚本把这写字符看做是输入的结束符.
$buf = socket_read($msgsock, 8192);
echo "Received msg: $buf \n";
//数据传送 向客户端写入返回结果
$msg = "welcome 服务器\n ";
socket_write($msgsock, $msg, strlen($msg)) or die("socket_write() failed: reason: " . convertToUtf8( socket_strerror(socket_last_error()) ) ."/n");
//一旦输出被返回到客户端,父/子socket都应通过socket_close($msgsock)函数来终止
socket_close($msgsock);
} while (true);
socket_close($sock);
function convertToUtf8($str) {
$encode = mb_detect_encoding($str, array("ASCII",'UTF-8',"GB2312","GBK",'BIG5')); #"EUC-CN" 属于GB2312中的一种 https://blog.csdn.net/qq_37788558/article/details/78241748
if ($encode != 'UTF-8') {
$str = mb_convert_encoding($str, 'UTF-8', $encode);
}
return $str;
}
1.2 客户端代码
<?php
error_reporting(E_ALL);
set_time_limit(0);
echo "<h2>TCP/IP Connection</h2>\n";
$port = 1083;
$ip = "127.0.0.1";
/*
+-------------------------------
* @socket连接整个过程
+-------------------------------
* @socket_create
* @socket_connect
* @socket_write
* @socket_read
* @socket_close
+--------------------------------
*/
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket < 0) {
echo "client:socket_create() failed: reason: " . socket_strerror($socket) . "\n";
}else {
echo "OK.\n";
}
echo "<br>client:试图连接 '$ip' 端口 '$port'...\n";
$result = socket_connect($socket, $ip, $port);
if ($result < 0) {
echo "<br>client:socket_connect() failed.\nReason: ($result) " . socket_strerror($result) . "\n";
}else {
echo "<br>client:连接OK\n";
}
$in = "Ho\r\n";
$in .= "first blood\r\n";
$out = '';
if(!socket_write($socket, $in, strlen($in))) {
echo "<br>client:socket_write() failed: reason: " . socket_strerror($socket) . "\n";
}else {
echo "<br>client:发送到服务器信息成功!\n";
echo "<br>client:发送的内容为:<font color='red'>$in</font> <br>";
}
while($out = socket_read($socket, 8192)) {
echo "<br>client:接收服务器回传信息成功!\n";
echo "<br>client:接受的内容为:",$out;
}
echo "<br>client:关闭SOCKET...\n";
socket_close($socket);
echo "<br>client:关闭OK\n";
?>
1.3 socket通信流程 | tcp握手
大佬地址:Nginx backlog配置概述
参考文章:百度百科
1.3.1 nginx 配置
1.3.2 php-fpm配置
- 根据php-fpm的QPS来决定backlog的大小。计算方式最好为QPS=backlog。
1.4 如何查看accept queue溢出
- netstat –s | grep LISTEN返回*** SYNS to LISTEN sockets ignored
第二章 RPC
RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
2.1 RPC作用
- 屏蔽远程调用跟本地调用的区别,让我们感觉就是调用项目内的方法;
隐藏底层网络通信的复杂性,让我们更专注于业务逻辑。 - 利用 RPC 我们不仅可以很方便地将应用架构从“单体”演进成“微服务化”,而且还能解决实际开发过程中的效率低下、系统耦合等问题,这样可以使得我们的系统架构整体清晰、健壮,应用可运维度增强。
2.2 应用场景
- 当然 RPC 不仅可以用来解决通信问题,它还被用在了很多其他场景,比如:发 MQ、分布式缓存、数据库等。下图是我之前开发的一个应用架构图:
2.3 调用过程
- 调用方持续地把请求参数序列化成二进制后,经过 TCP 传输给了服务提供方。服务提供方从 TCP 通道里面收到二进制数据。
- 所以 RPC 请求在发送到网络中之前,他需要把方法调用的请求参数转成二进制;转成二进制后,写入本地 Socket 中,然后被网卡发送到网络设备中。
2.4 协议
我们把数据格式的约定内容叫做“协议”,大多数的协议会分成两部分,分别是数据头和消息体。数据头一般用于身份识别,包括协议标识、数据大小、请求类型、序列化类型等信息;消息体主要是请求的业务参数信息和扩展属性等。
2.4.1 如何设计协议
初级版本
升级版本,可拓展性好
选用序列化格式,
但用 JSON 进行序列化有这样两个问题,你需要格外注意:
- JSON 进行序列化的额外空间开销比较大,对于大数据量服务这意味着需要巨大的内存和磁盘开销;
- JSON 没有类型,但像 Java 这种强类型语言,需要通过反射统一解决。
- 所以性能不会太好。所以如果 RPC 框架选用 JSON 序列化,服务提供者与服务调用者之间传输的数据量要相对较小,否则将严重影响性能。
2.4.2 性能考虑优先级
2.5 RPC 框架在使用时要注意哪些问题
序列化时,对象不要过于复杂、庞大、不要有复杂的继承关系
- 对象要尽量简单,没有太多的依赖关系,属性不要太多,尽量高内聚;
- 入参对象与返回值对象体积不要太大,更不要传太大的集合;
- 尽量使用简单的、常用的、开发语言原生的对象,尤其是集合类;
- 对象不要有复杂的继承关系,最好不要有父子类的情况。