【面试】后端开发需要懂得奇奇怪怪的知识点 (1)

274 阅读20分钟

导致 TCP 性能问题的三个重要原因: TCP 的拥塞控制在发生丢包时会进行退让,减少能够发送的数据段数量,但是丢包并不一定意味着网络拥塞,更多的可能是网络状况较差; TCP 的三次握手带来了额外开销,这些开销不只包括需要传输更多的数据,还增加了首次传输数据的网络延迟; TCP 的重传机制在数据包丢失时可能会重新传输已经成功接收的数据段,造成带宽的浪费;

QUIC 与现有 TCP + TLS + HTTP/2 方案相比,有以下几点主要特征: 1)利用缓存,显著减少连接建立时间; 2)改善拥塞控制,拥塞控制从内核空间到用户空间; 3)没有 head of line 阻塞的多路复用; 4)前向纠错,减少重传; 5)连接平滑迁移,网络状态的变更不会影响连接断线。

QUIC 是 Quick UDP Internet Connections ,谷歌发明的新传输协议。

QUIC还面临以下挑战: 1)小地方,路由封杀UDP 443端口( 这正是QUIC 部署的端口); 2)UDP包过多,由于QS限定,会被服务商误认为是攻击,UDP包被丢弃; 3)无论是路由器还是防火墙目前对QUIC都还没有做好准备。

QUIC 底层通过 UDP 协议替代了 TCP,上层只需要一层用于和远程服务器交互的 HTTP/2 API。这是因为 QUIC 协议已经包含了多路复用和连接管理,HTTP API 只需要完成 HTTP 协议的解析即可。

SSL 是 “Secure Sockets Layer” ,“安全套接层” TLS,Transport Layer Security Protocol,传输层安全协议

使用 TLS 时,客户端和服务器之间的连接具有以下一个或多个属性: 连接私密性:使用对称加密算法用于加密数据的传输,例如 AES [AES], RC4 [SCH] 等 可以使用公钥加密来验证通信方的身份 连接可靠性:发送的每个消息都使用 MAC(消息认证码) 进行完整性检查

TLS 1.3 与之前的协议有较大差异,主要在于: 1.相比过去的的版本,引入了新的密钥协商机制 — PSK 2.支持 0-RTT 数据传输,在建立连接时节省了往返时间 3.废弃了 3DES、RC4、AES-CBC 等加密组件,废弃了 SHA1、MD5 等哈希算法 4.ServerHello 之后的所有握手消息采取了加密操作,可见明文大大减少 5.不再允许对加密报文进行压缩、不再允许双方发起重协商 6.DSA 证书不再允许在 TLS 1.3 中使用

TLS 1.3 在之前版本的基础上删除了那些不安全的加密算法,这些加密算法包括: RSA 密钥传输 —— 不支持前向安全性 CBC 模式密码 —— 易受 BEAST 和 Lucky 13 攻击 RC4 流密码 —— 在 HTTPS 中使用并不安全 SHA-1 哈希函数 —— 建议以 SHA-2 取而代之 任意 Diffie-Hellman 组—— CVE-2016-0701 漏洞 输出密码 —— 易受 FREAK 和 LogJam 攻击

QUIC 核心特性 1.连接建立延时低,传输层 0RTT 就能建立连接;加密层 0RTT 就能建立加密连接。 2.改进的拥塞控制,改进在 (1)可插拔,就是能够非常灵活地生效,变更和停止 (2)单调递增的 Packet Number (3)不允许 Reneging (4)更多的 Ack 块 (5)精确计算 Ack Delay 时间 3.基于 stream 和 connecton 级别的流量控制 4.没有队头阻塞的多路复用 5.加密认证的报文 6.连接迁移 7.其他:前向冗余纠错,证书压缩,针对包头进行验证

为什么需要QUIC 一方面是历史悠久使用广泛的古老协议,另外一方面用户的使用场景对传输性能的要求又越来越高。 如下几个问题和矛盾就变得越来越突出: 1.协议历史悠久导致中间设备僵化; 2.依赖于操作系统的实现导致协议本身僵化; 3.建立连接的握手延迟大; 4.队头阻塞。

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

Raft 算法

  • 每个节点有三种状态:Follower,Candidate,Leader,状态之间是互相转换的
  • 每个节点上都有一个倒计时器 (Election Timeout),时间随机在 150ms 到 300ms 之间。 有几种情况会重设 Timeout: 收到选举的请求 收到 Leader 的 Heartbeat

在 Raft 运行过程中,最主要进行两个活动: 选主 Leader Election 复制日志 Log Replication

只要有超过一半的节点投支持票了,Candidate 才会被选举为 Leader。

为了在任何异常情况下系统不出错,即满足safety属性,对leader election,log replication两个子问题有诸多约束 leader election约束: - 同一任期内最多只能投一票,先来先得 - 选举人必须比自己知道的更多(比较term,log index)

log replication约束: - 一个log被复制到大多数节点,就是committed,保证不会回滚 - leader一定包含最新的committed log,因此leader只会追加日志,不会删除覆盖日志 - 不同节点,某个位置上日志相同,那么这个位置之前的所有日志一定是相同的

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

Redis线程模型 redis 内部使用文件事件处理器 file event handler 这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。 它采用 IO 多路复用机制同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进行处理。

结构包含4个部分: 1.多个socket 2.IO多路复用程序 3.文件事件分派器 4.事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)

redis单线程模型也能效率这么高 1.纯内存操作 2.核心是基于非阻塞的IO多路复用机制 3.单线程反而避免了多线程的频繁上下文切换问题

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

分布式锁,一般有三种选择 1、redis 2、zookeeper 3、DB锁(悲观锁 乐观锁)

setnx + lua / setkey value px milliseconds

这种实现方式有3大要点 1.set命令要用 setkey value px milliseconds nx 2.value要具有唯一性 3.释放锁时要验证value值,不能误解锁

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

Redlock操作 1.获取当前Unix时间,以毫秒为单位。

2.依次尝试从5个实例,使用相同的key和具有唯一性的value(例如UUID)获取锁。 当向Redis请求获取锁时,客户端应该设置一个网络连接和响应超时时间, 这个超时时间应该小于锁的失效时间。例如你的锁自动失效时间为10秒, 则超时时间应该在5-50毫秒之间。这样可以避免服务器端Redis已经挂掉的情况下, 客户端还在死死地等待响应结果。如果服务器端没有在规定时间内响应, 客户端应该尽快尝试去另外一个Redis实例请求获取锁。

3.客户端使用当前时间减去开始获取锁时间(步骤1记录的时间)就得到获取锁使用的时间。 当且仅当从大多数(N/2+1,这里是3个节点)的Redis节点都取到锁, 并且使用的时间小于锁失效时间时,锁才算获取成功。

4.如果取到了锁,key的真正有效时间等于有效时间减去获取锁所使用的时间(步骤3计算的结果)

5.如果因为某些原因,获取锁失败(没有在至少N/2+1个Redis实例取到锁或者取锁时间已 经超过了有效时间),客户端应该在所有的Redis实例上进行解锁 (即便某些Redis实例根本就没有加锁成功,防止某些节点获取到锁但是客户端没有得 到响应而导致接下来的一段时间不能被重新获取锁)。

采用redis的lua脚本进行原子操作,即原子操作查找和删除

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

php-fpm的静态和动态执行方式比较 一种是直接开启指定数量的php-fpm进程,不再增加或者减少; 另一种则是开始的时候开启一定数量的php-fpm进程,当请求量变大的时候, 动态的增加php-fpm进程数到上限,当空闲的时候自动释放空闲的进程数到一个下限。 参数: pm static/dynamic pm.max_children:静态方式下开启的php-fpm进程数量。 pm.start_servers:动态方式下的起始php-fpm进程数量。 pm.min_spare_servers:动态方式下的最小php-fpm进程数量。 pm.max_spare_servers:动态方式下的最大php-fpm进程数量。 内存较少的服务器 : 动态方式因为会结束掉多余的进程,可以回收释放一些内存 比较大内存的服务器 : 设置为静态的话会提高效率。因为频繁开关php-fpm进程会有时滞,所以内存够大的情况下开静态效果会更好。

PHP在业务批量操作时如何保证原子性 1、建一个支持事务类型的db 2、删除时并非真正意义上的物理删除,只是设置一个标志位,比如status=1表示删除了,0表示正常。 3、记录删除日志,这些冗余信息有助于恢复记录。

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

惊群现象 惊群现象(thundering herd)就是当多个进程和线程在同时阻塞等待同一个事件时,如果这个 事件发生,会唤醒所有的进程,但最终只可能有一个进程/线程对该事件进行处理, 其他进程/线程会在失败后重新休眠,这种性能浪费就是惊群现象。

在Linux2.6版本以后,内核内核已经解决了accept()函数的“惊群”问题,大概的处理方式就是, 当内核接收到一个客户连接后,只会唤醒等待队列上的第一个进程或线程。所以,如果服务器 采用accept阻塞调用方式,在最新的Linux系统上,已经没有“惊群”的问题了。

对于实际工程中常见的服务器程序,大都使用select、poll或epoll机制,此时, 服务器不是阻塞在accept,而是阻塞在select、poll或epoll_wait, 这种情况下的“惊群”仍然需要考虑。

解决惊群问题 1.旧版本Nginx中使用mutex互斥锁解决这个问题,具体措施有使用全局互斥锁,每个子进程 在epoll_wait()之前先去申请锁,申请到则继续处理,获取不到则等待, 并设置了一个负载均衡的算法(当某一个子进程的任务量达到总设置量的7/8时,则不会再 尝试去申请锁)来均衡各个进程的任务量。

2.Linux内核的3.9版本带来了SO_REUSEPORT特性,该特性支持多个进程或者线程绑定到同一端口, 提高服务器程序的性能,允许多个套接字bind()以及listen()同一个TCP或UDP端口, 并且在内核层面实现负载均衡。

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

php-fpm master只是负责监听管理工作,并不是很多人认为的把客户端发来的请求分给worker进程处理, 而是由worker进程直接负责客户端的请求监听和处理。 master进程负责监听子进程的状态,子进程挂掉之后,会发信号给master进程, 然后master进程重新启一个新的worker进程。

nginx master负责将请求分配给手下的worker, worker接到分配的请求来做具体的事情, 所有的worker并不是轮询来处理master的请求, 而是采用"争抢"机制。 worker处理后Reponse原路返回。

区别:php-fpm的master不直接参与请求的分发和处理。nginx的请求必须经过master的中继。

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

集群环境下Redis 1、key批量操作支持有限:例如mget、mset必须在一个slot; 2、Key事务和Lua支持有限:操作的key必须在一个节点; 3、key是数据分区的最小粒度:不支持bigkey分区; 4、不支持多个数据库:集群模式下只有一个db0; 5、复制只支持一层:不支持树形复制结构。

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

mysql JOIN语句的优化原则 1.小表驱动大表(EXPLAIN的第一行是驱动表),WHERE 条件驱动表的筛选出尽量少的数 2.where里有筛选条件,而且可以使用索引,并对驱动表晒选出尽量少的行数 3.非驱动表连接join字段最好是主键索引,无法建立索引的时候,设置足够的Join Buffer Size 4.尽量避免联表数量,避免尽量少返回字段,避免返回字段有计算,越多嵌套循环算法越慢 5.join连接表的语句不能再用子查询,COUNT(1) 分页统计返回字段要尽量少

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

查找访问日志中访问量最大的ip

101.231.147.230 - - [27/Sep/2018:11:12:22 +0800] "GET / HTTP/1.1" 200 53318 101.231.147.230 - - [27/Sep/2018:11:12:23 +0800] "GET /psp2/css/reset.css HTTP/1.1" 304 - 101.231.147.230 - - [27/Sep/2018:11:12:23 +0800] "GET /psp2/css/index.css HTTP/1.1" 304 - 211.152.37.8 - - [27/Sep/2018:11:12:23 +0800] "GET / HTTP/1.1" 302 - 210.205.3.195 - - [27/Sep/2018:11:12:23 +0800] "POST /reloadProjectList.action?projectPage=10 HTTP/1.1" 200 608 211.152.37.8 - - [27/Sep/2018:11:12:23 +0800] "GET / HTTP/1.1" 200 53318 101.231.147.230 - - [27/Sep/2018:11:12:23 +0800] "GET /public/js/common.js HTTP/1.1" 304 - 101.231.147.230 - - [27/Sep/2018:11:12:23 +0800] "GET /psp2/js/jquery_min.js HTTP/1.1" 304 - 101.231.147.230 - - [27/Sep/2018:11:12:23 +0800] "GET /public/js/app/CaeeResources_zh_CN.js HTTP/1.1" 304 - 101.231.147.230 - - [27/Sep/2018:11:12:23 +0800] "GET /jwplayer/jwplayer.js HTTP/1.1" 304 - 101.231.147.230 - - [27/Sep/2018:11:12:23 +0800] "GET /psp2/image/share_01.png HTTP/1.1" 304 - 101.231.147.230 - - [27/Sep/2018:11:12:24 +0800] "GET /psp2/image/share_02.png HTTP/1.1" 304 - 101.231.147.230 - - [27/Sep/2018:11:12:24 +0800] "GET /psp2/image/share_03.png HTTP/1.1" 304 -

cat access_log-20190602 | awk -F" " '{print 1}' | sort | uniq -c | sort -nr | head -20 awk '{print 1}' access_log-20190602 | sort -nr | uniq -c | sort -nr | head -20 -- cat debug.log | sort | awk -F 'ip":|,"latency_time"' '{print $2}' | uniq -c | sort -n -r -k 1 -t ' ' | head -5

一条命令查出使用某个文件的进程id lsof | grep /usr/local/nginx/logs/error.log | awk -F" " '{print $2}'

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

MRR 是 MySQL 针对特定查询的一种优化手段。假设一个查询有二级索引可用,读完二级索引 后要回表才能查到那些不在当前二级索引上的列值,由于二级索引上引用的主键值不一定是有 序的,因此就有可能造成大量的随机 IO,如果回表前把主键值给它排一下序,那么在回表的 时候就可以用顺序 IO 取代原本的随机 IO。 MRR 要把主键排个序,这样之后对磁盘的操作就是由顺序读代替之前的随机读。从资源的使 用情况上来看就是让 CPU 和内存多做点事,来换磁盘的顺序读。

索引下推 索引条件下推优化(Index Condition Pushdown (ICP) )是MySQL5.6添加的,用于优化数据查询。 (1)不使用索引条件下推优化时存储引擎通过索引检索到数据,然后返回给MySQL服务器,服务器 然后判断数据是否符合条件。 (2)当使用索引条件下推优化时,如果存在某些被索引的列的判断条件时,MySQL服务器将 这一部分判断条件传递给存储引擎,然后由存储引擎通过判断索引是否符合MySQL服务器传递 的条件,只有当索引符合条件时才会将数据检索出来返回给MySQL服务器。 索引条件下推优化可以减少存储引擎查询基础表的次数,也可以减少MySQL服务器从存储 引擎接收数据的次数。

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

php守护脚本卡在那里,怎么监控和定位? php脚本oom问题如何定位? 接口和抽象类的区别,接口可以替换为抽象类吗? php错误为什么分成error和exception?哪些异常是可以被捕获到的? 如何设计一个秒杀系统? redis如何保证数据不丢失?磁盘坏了呢? redis高可用方案 有没有对比过memcached和redis的性能? mysql主从同步原理,如何解决同步延迟问题? - 1.内部部署,最好是同一个交换机下的 - 2.升级硬件配置,最好ssd,写入快 - 3.业务层逻辑分开,分库结构 - 4.禁用从库的binlog,sync_binlog设为0 - 5.前面加redis做缓存,减少从库(读库)压力

有没有自己搭建过高可用高性能的架构? docker k8s有没有用过? mysql join原理,如何优化? -- 3种join的算法 index/block/simple Nested-Loop join xss,csrf,sql inject原理?如何防范? https原理 一个http请求有几个ttl? dns原理 -- 递归查询,迭代查询,13组根域名服务器 php机器可用内存只有100m,怎么实现1GB大文件以csv格式下载到客户端? -- fputcsv 设计模式在实际业务场景的应用 怎么实现连接池? -- swoole 长连接使用场景和优缺点

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

最大传输单元(Maximum Transmission Unit,MTU)用来通知对方所能接受数据服务单元的 最大尺寸,说明发送方能够接受的有效载荷大小 由于历史的原因,互联网上物理链路的最小MTU = 576,基于UDP传输的DNS为了限 制报文不超过576,所以将DNS报文限制在512字节。

为什么DNS更适合用UDP ? 使用基于UDP的DNS协议只要一个请求、一个应答就好了 而使用基于TCP的DNS协议要三次握手、发送数据以及应答、四次挥手 明显基于TCP协议的DNS更浪费网络资源! 当然以上只是从数据包的数量以及占有网络资源的层面来进行的分析,那数据一致性层面呢? DNS数据包不是那种大数据包,所以使用UDP不需要考虑分包,如果丢包那么就是全部丢包, 如果收到了数据,那就是收到了全部数据!所以只需要考虑丢包的情况,那就算是丢包了, 重新请求一次就好了。而且DNS的报文允许填入序号字段,对于请求报文和其对应的应答报文, 这个字段是相同的,通过它可以区分DNS应答是对应的哪个请求

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

CSRF (Cross-site request forgery),跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。 产生过程: 1.登录受信任网站A,并在本地生成Cookie。 2.在不登出A的情况下,访问危险网站B。 3.在B网站的页面,发出请求发向A网站,带上在第一步中A生成的Cookie 4.产生危害攻击

最简单的例子 示例1: 银行网站A,它以GET请求来完成银行转账的操作,如:www.mybank.com/Transfer.ph… 危险网站B,它里面有一段HTML的代码如下: <img src=www.mybank.com/Transfer.ph… 首先,你登录了银行网站A,然后访问危险网站B,噢,这时你会发现你的银行账户少了1000块...... 为什么会这样呢?原因是银行网站A违反了HTTP规范,使用GET请求更新资源。在访问危险网站B的之前, 你已经登录了银行网站A,而B中的以GET的方式请求第三方资源(这里的第三方就是指银行网站了, 原本这是一个合法的请求,但这里被不法分子利用了),所以你的浏览器会带上你的银行网站A的Cookie发出Get请求, 去获取资源“www.mybank.com/Transfer.ph… 认为这是一个更新资源操作(转账操作),所以就立刻进行转账操作......

CSRF攻击是源于WEB的隐式身份验证机制!WEB的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器, 但却无法保证该请求是用户批准发送的!

如果减轻CSRF攻击? 1.只使用JSON api 2.禁用CORS 3.检验referrer头部 4.GET总是幂等的 5.避免使用POST而用PUT, PATCH, DELETE 6.不要复写方法 7.不要兼容旧浏览器 8.CSRF Tokens

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

XSS Cross Site Scripting,跨站脚本攻击 跨站脚本攻击是指恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码 会被执行,从而达到恶意攻击用户的目的。 xss漏洞通常是通过php的输出函数将javascript代码输出到html页面中,通过用户本地浏览器执行的,所以xss漏洞 关键就是寻找参数未过滤的输出函数。 常见的输出函数有: echo printf print print_r sprintf die var-dump var_export.

xss 分类:(三类) 1.反射型XSS:<非持久化> 攻击者事先制作好攻击链接, 需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没 有这样的页面和内容),一般容易出现在搜索页面。

2.存储型XSS:<持久化> 代码是存储在服务器中的,如在个人信息或发表文章等地方,加入代码,如果没有过滤或过滤 不严,那么这些代码将储存到服务器中,每当有用户访问该页面的时候都会触发代码执行,这种XSS非常危险,容易 造成蠕虫,大量盗窃cookie 例子:input输入框中填写 并提交,浏览器发生弹窗。

3.DOM型XSS:基于文档对象模型Document Objeet Model,DOM)的一种漏洞。DOM中有很多对象,其中一些是用户可以 操纵的,如uRI ,location,refelTer等。客户端的脚本程序可以通过DOM动态地检查和修改页面内容,它不依赖于 提交数据到服务器端,而从客户端获得DOM中的数据在本地执行,如果DOM中的数据没有经过严格确认,就会产生DOM XSS漏洞。

漏洞防范 A.PHP直接输出html的,用方法过滤: 1.htmlspecialchars 2.htmlentities 3.RemoveXss函数 B.PHP输出到JS代码中,需要前端在JS中进行过滤: 尽量使用innerText()和textContent(),jQuery的text() C.其它的通用的补充性防御手段 1.加上Content Security Policy的Http Header 2.设置Cookie时加上HttpOnly参数 3.检验请求的Referer参数 4.使用扫描工具自动检测XSS漏洞 5.输入内容长度控制 6.验证码

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

sql注入 过于相信前端所传参数所致,外部数据不可信任 解决方法: 1、检查变量数据类型和格式 2、过滤特殊符号 3、绑定变量使用预编译语句   4、调用存储过程

mysql类型 1.获取元数据 2.UNION查询 3.函数利用 4.显错式注入 5.宽字节注入 6.长字符截断 7.延时注入

声明:本文为本人在准备面试过程中,将学习知识点整理的汇总笔记,内容来源为各大博客网站相关技术文章,仅做技术分享用途。如有部分篇幅侵权,请联系告知,本人会立即删除。