概述
好久没有更新博客了,因为疫情的原因被集中隔离在集中隔离点,今天想整理一下最近学习的知识点,做一个小的节点总结,对工作和职业有控制力,就会多一点安全感。
我的职业目标以后向 性能优化 和 侧重于解决问题的系统方案架构师。
编程语言
首先我先优化了一下自己的编程语言结构,编程语言更像是一个实现方案的工具,PHP擅长解决Web服务,Java属于编程语言中的坦克,偏向解决各种服务,以稳定著称,公司内部在研究Rust,我眼前也在研究Python。
下面是我准备学习的方向:
php -> 自研swoole框架 -> php扩展 -> 进而延伸到生产环境
rust -> 实现Brainfuck解释器 -> JIT编译器 -> Web开发(极客时间)
python -> 零基础学python(极客时间) -> 办公自动化(极客时间)
java -> 零基础学java -> socket编程(慕课网)
go -> 网关实战(慕课网)
上面都是我现阶段要学习的编程语言的学习目标,掌握了更多的工具,做起架构方案会更得心应手,做完的实践代码,存放在github里,做未来路上的基石。
现在市场上的架构师的开发语言主要集中在Java,Python,Go三大类,PHP里的Swoole的生态市场没有别打通,想想就有些遗憾,其实Swoole是一个很不错的扩展,协程,链接池,异步应有尽有。
编程语言是一个剑客的招式,学会了剑招,领略了真谛,最后才能成为一代宗师,无招胜有招。
编程语言的分类与执行过程
对于编译型语言,编译结果已经是针对当前CPU体系的指令;而解释型语言,需要先编译成中间代码,
再经由该解释型语言的特定虚拟机,翻译成特定CPU体系的指令被执行。
编译型语言的执行过程(以Rust为例):
1.Rust编写代码(cargo new)
2.将程序编译后转换成机器语言二进制文件(cargo build)
3.程序运行时,生成二进制文件副本
4.Cpu解释并执行内容
解释语言的执行过程(依赖于虚拟机,以PHP为例):
1.php代码
2.Zend虚拟机
2.1 解释层(词法分析、语法分析)->Ast(抽象语法树)->编译器
2.2 中间数据层(执行栈、opline指令、符号表)
2.3 执行层(执行引擎)
3.Cpu解释并执行内容
数据类型
类型是对二进制数据的一种约束行为。类型比起二进制数据,有许多优势:
- 减少开发者心智负担
- 安全、容易优化
常见的类型分类:
- 静态类型:在编译器对类型进行检查
- 动态类型:在运行期对类型进行检查
- 强类型:不允许隐式类型转换
- 弱类型:允许隐式类型转换
高性能
高性能的两个重要的点:链接数量和请求数量 ,服务器的处理能力(TPS)。
听到的并发量高就会瑟瑟发抖,其实大可不必,比如一小时大概100w的链接数,那么一秒的链接数:
1000000 / 3600s = 277/tps
一个用户在 App中的各种操作如果需要请求15个接口,但并不是每一个用户都会完整地走完这 15 个接口。
277 * 15 = 18.46666666
经过计算,每秒的活跃用户为18个人,一个小时的活跃人数大概6w人左右,是不是就没有那么瞎人了,所有的高并发处理都是分而治之,分成小块处理的。
并发用户 = 最大TPS/单用户TPS
在线用户 = 并发用户/并发度
用户的高并发,归根结底的本质是对容量可承载大小的计算。
高性能的实践
读写分离:开源数据库中间件方案:
- MySQL Proxy
- MySQL Router
- Atlas (奇虎 360)
MySQL Router的主要功能有读写分离、故障自动切换、负载均衡、连接池等。
Atlas 是一个位于应用程序与 MySQL 之间中间件。在后端 DB 看来,Atlas 相当于连接它的客户端,在前端应用看来,Atlas 相当于一个 DB。Atlas 作为服务端与应用程序通信,它实现了 MySQL 的客户端和服务端协议,同时作为客户端与 MySQL 通信。它对应用程序屏蔽了 DB 的细节,同时为了降低 MySQL 负担,它还维护了连接池。
单服务器高性能模式:Reactor与Proactor
实现方案:
1.单 Reactor 单进程的方案:redis
需要注意的是,C 语言编写系统的一般使用单 Reactor 单进程,因为没有必要在进程中再创建线程;而 Java 语言编写的一般使用单 Reactor 单线程,因为 Java 虚拟机是一个进程,虚拟机中有很多线程,业务线程只是其中的一个线程而已。
- 单 Reactor 多线程
没有列出“单 Reactor 多进程”方案,这是什么原因呢?主要原因在于如果采用多进程,子进程完成业务处理后,将结果返回给父进程,并通知父进程发送给哪个 client,这是很麻烦的事情。因为父进程只是通过 Reactor 监听各个连接上的事件然后进行分配,子进程与父进程通信时并不是一个连接。如果要将父进程和子进程之间的通信模拟为一个连接,并加入 Reactor 进行监听,则是比较复杂的。而采用多线程时,因为多线程是共享数据的,因此线程间通信是非常方便的。虽然要额外考虑线程间共享数据时的同步问题,但这个复杂度比进程间通信的复杂度要低很多。
- 多 Reactor 多进程 / 线程
目前著名的开源系统 Nginx 采用的是多 Reactor 多进程,采用多 Reactor 多线程的实现有 Memcache 和 Netty。
Nginx 采用的是多 Reactor 多进程的模式,但方案与标准的多 Reactor 多进程有差异。具体差异表现为主进程中仅仅创建了监听端口,并没有创建 mainReactor 来“accept”连接,而是由子进程的 Reactor 来“accept”连接,通过锁来控制一次只有一个子进程进行“accept”,子进程“accept”新连接后就放到自己的 Reactor 进行处理,不会再分配给其他子进程。