遇到一个网站打开慢怎么排查
ping域名
free/top命令查看服务器内存和CPU使用情况,iftop等工具查看带宽
chrome的debug->network查看响应慢的
排查响应慢的接口代码,看php,mysql,redis等的日志看错误信息(mysql的慢查询日志功能php-fpm慢日志功能,需要配置开启)
如何提高网站性能和并发能力
-
HTML静态化
-
图片等大文件分开存储
-
nginx负载均衡
-
数据库集群(mysql主从),大数据量hash分表
-
缓存
-
CDN加速
-
PHP代码层的优化(多用静态函数,减少循环嵌套,少用正则等)
-
数据库优化
Mysql查询优化
列越小越快
枚举类型替代varchar
避免null值
固定长度的表比动态的快(避免text等不定长字段)
垂直分表(降低表的复杂度,不常用的字段分离出来单独存储)
合理设置索引
分表,分布式(主从)
Mysql常用引擎和区别
| 功能 | InnoDB | MyISAM |
| 事务 | 支持 | 不支持 |
| 锁 | 行级锁 | 表级锁 |
| 外键 | 支持 | 不支持 |
| 全文索引 | 不支持 | 支持 |
| 行数 | 不记录 | 记录 |
PHP设计模式
单例模式,策略模式,适配器模式,工厂模式,观察者模式
PHP运行原理
FastCGI
与cgi区别:cgi是请求过来再初始化进程并执行代码,FastCGI预先初始化一些进程,请求过来可以直接分发处理,并且进程重复使用,实现了一个进程池。
与php-fpm关系:php-fpm是一个被纳入php核心的FastCGI进程管理程序(FastCGI Process Manager),FastCGI模式的关键部分是对fastcgi进程的有效管理。
与nginx通讯:nginx通过tcp socket或者unix socket与fastcgi进程通讯。
fpm模式下的生命周期:
1.MINIT (初始化fpm)
在这步(包括之前)php引擎会初始化一些公用配置,读取ini文件,加载zend引擎,执行所以模块的MINIT模块,然后就长驻在fpm进程中,然后就等待处理请求
(2-4步为每个请求的处理过程)
2.RINIT(初始化模块)
在每个请求过来之后,会调用所有模块的RINIT进行一些请求内数据的初始化,比如一些超全局变量,一些模块数据初始化等
3.执行php(编译PHP语言为opcode,暂存结果)
然后在这加载php文件,进行词法,语法分析,生成opcode代码,交由zend vm执行, 暂存执行结果
4.RSHUTDOWN(模块数据回收,暂存结果输出)
在把结果返回给fpm之前,会调用所有模块的RSHUTDOWN模块进行一些数据的回收,zend vm也会关闭打开的数据流,进行内存释放等操作,然后把暂存的执行结果flush输出
MSHUTDOWN(结束fpm)
这一阶段在重启fpm时发生,会调用所有模块的MSHUTDOWN,关闭zend引擎等操作
swoole
与fastcgi区别:swoole接管了nginx和php-fpm这部分的功能,由类似的进程结构实现。swoole从第3步开始接管PHP,避免了fastcgi每次请求初始化后所有文件都需要重新加载。
进程结构:一个master(相当于nginx角色),一个manager(相当于fpm角色),若干worker(相当于fastcgi进程),若干taskWorker(用于异步处理长时间的任务)。
异步事件模型,并实现了一些异步IO库。
TCP三次握手四次挥手
握手:
客户端请求服务端,发送连接请求标示和一串顺序码(X)
服务端收到请求,回复确认标示和顺序码(X)+1的确认码和另一个顺序码(Y)
客户端验证(X)+1确认码,通过后发送确认标示和(Y)+1的顺序码给服务端,服务端验证通过后建立连接
挥手:
第一次握手:TCP发送一个FIN(结束),用来关闭客户到服务端的连接。
第二次握手:服务端收到这个FIN,他发回一个ACK(确认),确认收到序号为收到序号+1。
第三次握手:服务端发送一个FIN(结束)到客户端,服务端关闭客户端的连接。
第四次握手:客户端发送ACK(确认)报文确认,并将确认的序号+1,这样关闭完成
为什么是四次不是三次?
server端收到结束请求后,需要等待数据传输完毕,所以只能先发送一个收到请求的确认信息给客户端,等数据传输完毕后再发送结束报文。
HTTPS通讯过程
-
客户端与服务端建立连接
-
互相Hello(包含支持的版本、算法;加上随机数)
-
服务端发送公钥
-
客户端发送公钥(双向验证才需要,单向跳过)
-
服务端验证客户端公钥(双向验证才需要,单向跳过)
-
客户端验证服务端公钥
-
交换DH参数(如果用DH密钥交换算法)
-
客户端生成PreMaster Secret,并发送给服务端(DH根据随机数和参数直接算)
-
服务端解密PreMaster Secret,得到对称密钥(DH根据随机数和参数直接算)
-
使用对称密钥通讯
项目中遇到的难点
这个很难回答,说没遇到说明项目太简单,说遇到的可能说出来的面试官觉得不难,很尴尬的问题
如何防止数据重复提交,重复写入
csrf验证
乐观锁/悲观锁
队列
常见网络攻击和防范
CSRF攻击(跨站请求伪造)
验证referer
请求参数中添加token
SQL注入
验证输入参数
使用PDO方式连接数据库
不要使用有管理员权限的账户链接数据库
header攻击
过滤所有header,去掉非法字符
cookie攻击
设置httpOnly
重定向攻击
验证重定向url的合法性,添加域名白名单
token验证
上传文件攻击
过滤文件后缀
PHP命名空间
5.3版本以后加入,用来组织和重用代码。目的是解决了自己编写的类/函数/常量/或者不同类库中的类/函数/函数名称重复的问题。
PSR-4规范支持autoload
Include/Require的区别
include的文件不存在会报warning,require的文件不存在会报Fatal error
一致性hash算法
-
先准备0~2^32的圆盘
-
将存储数据key的hash值映射到圆盘上
-
将服务器标识的hash值也映射到圆盘上
-
顺时针查找,将数据存储到第一个找到的服务器上
echo,print,print_r,var_dump区别
echo和print
共同点:只能打印int、float、string类型的数据
区别:echo可以连续输出多个,而print只能一次输出一个变量;
print打印的值能赋值给变量,而echo不行(echo没有像函数的行为)
echo比print稍快
print_r和var_dump
共同点:都可以打印array、object等复合型变量
区别:var_dump更详细,还能打印资源类型的变量,调试时用var_dump多。
字符串翻转
strrev($str)
@符的作用
阻止警告输出。慎用。
快速获取url中的信息
parse_url($url) 该函数解析url、返回其组成部分;
返回关联数组
scheme 方案;如 http
host 域名
port
user 用户
pass
path 路径
query 在问号?之后
fragment 在散列符号#之后
pathinfo($path)解析文件路径,返回其组成部分;
返回关联数组
dirname 文件路径
basename 文件名+扩展名
extension 最后一个扩展名
filename 文件名
eg: print_r( pathinfo('/ab/cd/e.php') );
Array(
[dirname] => /ab/cd
[basename] => e.php
[extension] => php
[filename] => e
)
PHP安全模式开启后影响的函数
mkdir,unlink,chown,fopen等文件系统函数
exec,shell_exec,system,popen等系统命令函数
PHP垃圾收集机制
通过引用计数的方式认定一个垃圾内存。引用数为0时清除内存。
如果有内存泄漏,再通过一个回收周期算法来处理。
多进程同时读写一个文件
$pid = pcntl_fork();
$fp = fopen("test.txt", "a");
if (flock($fp, LOCK_EX)){
fwrite($fp, "content");
fflush($fp);
flock($fp, LOCK_UN);
}else{
echo "此文件正在被其他进程占用";
}
常用正则
数字:/d
字母:[A-Za-z]
数字+字母:[A-za-z0-9]
中文:/[^\x{00}-\x{ff}]/u
抓取网页并保存到本地
$content = file_get_contents("[http://www.baidu.com");](http://www.baidu.com%22%29%3B/)
$fileObject = fopen('content.txt',w);
fwrite($fileObject,$content);
fcolse();
LEFT JOIN ,RITHG JOIN和INNER JOIN的区别
left join返回包括左表中的所有记录和右表中联结字段相等的记录
right join返回包括右表中的所有记录和左表中联结字段相等的记录
Inner join只返回两个表中联结字段相等的行
SSO单点登陆
在多个系统中,只需要登陆/注销一次,就可以完成所有系统中的用户登陆验证。
-
同一个域名下的子域名各个系统可以使用设置顶级域名的cookie存储登陆信息来实现;
-
拦截登陆请求给用户中心发放令牌到子系统,子系统校验令牌后局部登陆,注销时用户中心发送请求到各个子系统注销局部登陆;
-
jwt,各个子系统使用相同的secret,相同的token可以在不同系统中使用;
以上方式的前提都要有统一的用户信息存储中心。
PHP配置文件
session.cookie_lifetime = 0 #cookie存活时间(0为直至浏览器重启,单位秒)
upload_max_filesize = 8m
post_max_size = 8m
Redis和memcache
两种常用的内存数据库(缓存)
作用:
存储临时数据,缓解并发压力
区别:
Memcache适用于k/v存储和文件的存储,Redis有k/v,list,set,hash等多种存储格式
持久化方面Redis优势,有磁盘持久化和AOF持久化(记录每一条执行过的命令)两种方式
SOCKET编程
服务端:
<?php
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
//本地IP
$address = 'localhost';
//设置用111端口进行通信
$port = 111;
//创建SOCKET
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) {
echo "socket创建失败原因 " . socket_strerror($sock) . "\n";
}
if (($ret = socket_bind($sock, $address, $port)) < 0) {
echo "创建套接字失败原因 " . socket_strerror($ret) . "\n";
}
//监听
if (($ret = socket_listen($sock, 5)) < 0) {
echo "监听失败原因 " . socket_strerror($ret) . "\n";
}
do {
//接收命令
if (($msgsock = @socket_accept($sock)) < 0) {
echo "命令接收失败原因: " . socket_strerror($msgsock) . "\n";
break;
}
$msg = "\nPHP Test Server. \n" ."用quit,shutdown,sun...等命令测试.\n";
@socket_write($msgsock, $msg, strlen($msg));
do {
if (false === ($buf = @socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
echo "socket_read() failed: reason: " . socket_strerror($ret) . "\n";
break 2;
}
if (!$buf = trim($buf)) {
continue;
}
if ($buf == 'quit') {
break;
}
if ($buf == 'shutdown') {
socket_close($msgsock);
break 2;
}
if ($buf == 'sun') {
echo'what are you doing?';
}
$talkback = "Backinformation : '$buf'.\n";
socket_write($msgsock, $talkback, strlen($talkback));
echo "$buf\n";
} while (true);
socket_close($msgsock);
} while (true);
socket_close($sock);
?>
客户端:
<?php
error_reporting(E_ALL);
//端口111
$service_port = 111;
//本地
$address = 'localhost';
//创建 TCP/IP socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket < 0) {
echo "socket创建失败原因: " . socket_strerror($socket) . "\n";
} else {
echo "OK,HE HE.\n";
}
$result = socket_connect($socket, $address, $service_port);
if ($result < 0) {
echo "SOCKET连接失败原因: ($result) " . socket_strerror($result) . "\n";
} else {
echo "OK.\n";
}
//发送命令
$in = "HEAD / HTTP/1.1\r\n";
$in .= "Connection: Close\r\n\r\n";
$out = '';
echo "Send Command..........";
$in = "sun\n";
socket_write($socket, $in, strlen($in));
echo "OK.\n";
echo "Reading Backinformatin:\n\n";
while ($out = socket_read($socket, 2048)) {
echo $out;
}
echo "Close socket........";
socket_close($socket);
echo "OK,He He.\n\n";
?>
网友评论