├── .gitignore
├── README.md
├── img
├── MapReduce.png
├── bigdata-tool.png
├── blockchain-merkletree.png
├── cloud-duty.png
├── cloud-native.png
├── data-tech-relationship.png
├── flink.png
├── hbase.png
├── hystrix-flow.png
├── hystrix-sliding-window.png
├── language-classification.png
├── language-trend.png
├── server-tech-tree-model.png
├── server-tech-tree-model.xmind
├── spark-arch.png
├── spark.png
├── spring-framework-arch.png
├── springmvc-process.png
└── tiobe.png
├── 元问题主要技术
├── 伸缩性
│ ├── 伸缩性总概.md
│ ├── 应用层负载均衡.md
│ └── 接入层负载均衡.md
├── 可用性
│ ├── 单一应用可用性.md
│ ├── 数据层可用性.md
│ └── 整体应用架构可用性.md
├── 安全
│ ├── 分布式事务.md
│ ├── 区块链.md
│ ├── 安全监测.md
│ ├── 安全编程框架.md
│ ├── 应用层安全.md
│ ├── 接入层安全.md
│ └── 演练与应急.md
├── 开发效率
│ ├── ServiceMesh服务网格技术.md
│ ├── 共享技术与中台.md
│ ├── 开发框架与微服务
│ │ ├── SpringBoot.md
│ │ ├── SpringCloud.md
│ │ ├── SpringFramework.md
│ │ └── 开发框架与微服务总概.md
│ ├── 扩展性技术.md
│ ├── 无代码开发.md
│ └── 编程语言.md
├── 性能
│ ├── 垃圾回收.md
│ ├── 异步架构与消息队列.md
│ ├── 性能测试.md
│ ├── 硬件使用优化.md
│ ├── 缓存.md
│ ├── 边缘计算.md
│ └── 高性能模型.md
├── 数据
│ ├── 大数据之批计算.md
│ ├── 大数据之流批混合.md
│ ├── 大数据之流计算.md
│ ├── 大数据之资源管理.md
│ ├── 数据库之NewSQL.md
│ ├── 数据库之NoSQL.md
│ ├── 数据库之关系型数据库.md
│ ├── 数据库之链式数据库.md
│ ├── 数据应用:OLTP和OLAP.md
│ ├── 数据的一致性设计.md
│ ├── 数据的扩展性设计.md
│ ├── 数据预处理.md
│ ├── 文件系统.md
│ └── 概览.md
├── 智能
│ └── 智能.md
├── 质量
│ ├── 全阶段测试.md
│ ├── 测试驱动开发.md
│ ├── 白盒、黑盒、灰盒测试.md
│ └── 静态测试和动态测试.md
└── 资源管理能力
│ ├── DevOps与CICD.md
│ ├── Serverless无服务技术.md
│ ├── 云计算之IaaS与虚拟化技术.md
│ ├── 云计算之PaaS与容器.md
│ ├── 云计算之SaaS与云原生.md
│ └── 服务治理.md
├── 元问题定义和指标
└── 定义和指标.md
└── 特定领域技术
└── 金融.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.DS_Store
3 | /img/.DS_Store
4 | **/.DS_Store
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 服务端软件技术树
2 |
3 | 自20世纪30年代现代计算机诞生至今,服务端软件技术(本文简称服务端技术)已经发展成为IT领域一大重要分支。大到操作系统,小到仅一行代码的工具,数以亿计的服务端软件运行在无数服务器和各类硬件设备上,支撑着现代人类文明的运转。
4 |
5 | 为使一个服务端软件从开发到最终交付运行,技术人员除了要实现业务相关的功能外,还要解决一些业务无关的问题,比如性能、效率、质量等。进一步说,所有业务无关问题又都能归类到少数几个问题上,本文称之为“业务无关元问题”,简称`元问题`。举个例子,我们经常遇到的一个业务无关问题是开发成本过高,而成本过高的原因往往是开发效率低下,因此成本并非元问题,而是结果或指标,开发效率才是元问题。事实上,业内对元问题的划分和定义由来已久,参与者众多,包括权威机构(比如ISO/IEC 9126、25010软件质量模型标准)、各企业以及个人,但目前并未形成统一的事实标准。
6 |
7 | 本文还有一个演绎版本[《服务端技术全栈之九大门派》](https://www.jianshu.com/p/1303d81b7451),欢迎阅读。
8 |
9 | ## 服务端技术元问题模型
10 |
11 | 本文通过对业内主流思路的借鉴,提出一个独特的元问题模型,将现代服务端技术描述成是一棵由9大元问题领域组成的技术树。经过多年发展,每个元问题领域都已沉淀大量成熟软件或技术理论,在日常研发和技术选型前,技术人员应该先弄清楚自己要解决的元问题是什么,再利用或借鉴该元问题领域下的已有软件或技术理论去做研发。
12 |
13 |
14 |

15 |
16 |
17 |
18 | ## 元问题定义与指标
19 | * [定义和指标](元问题定义和指标/定义和指标.md)
20 |
21 | ## 元问题主要技术
22 | * 开发效率
23 | * [编程语言](元问题主要技术/开发效率/编程语言.md)
24 | * [开发框架与微服务](元问题主要技术/开发效率/开发框架与微服务)
25 | * [开发框架与微服务总概](元问题主要技术/开发效率/开发框架与微服务/开发框架与微服务总概.md)
26 | * [SpringFramework](元问题主要技术/开发效率/开发框架与微服务/SpringFramework.md)
27 | * [SpringBoot](元问题主要技术/开发效率/开发框架与微服务/SpringBoot.md)
28 | * [SpringCloud](元问题主要技术/开发效率/开发框架与微服务/SpringCloud.md)
29 | * ServiceMesh服务网格技术
30 | * 库&中台&开发平台
31 | * 扩展性技术
32 | * 无代码LCNC
33 | * 资源管理能力
34 | * 云计算之SaaS
35 | * 云计算之PaaS
36 | * 云计算之IaaS
37 | * Serverless无服务技术(FaaS)
38 | * 虚拟化&容器化&编排&弹性裸金属
39 | * 大数据之批计算
40 | * 大数据之流计算
41 | * 大数据之流批混合
42 | * 大数据之资源管理
43 | * 资源治理
44 | * DevOps与CICD
45 | * 可用性
46 | * 应用层可用性
47 | * [单一应用可用性](元问题主要技术/可用性/单一应用可用性.md)
48 | * 数据层可用性
49 | * 伸缩性
50 | * 伸缩性总概
51 | * 接入层负载均衡
52 | * 应用层负载均衡
53 | * 性能
54 | * 高性能模型
55 | * 异步架构与消息队列
56 | * 缓存
57 | * 垃圾回收
58 | * 边缘计算
59 | * 硬件使用优化
60 | * 性能测试
61 | * 安全
62 | * 区块链
63 | * 分布式事务
64 | * 零信任架构
65 | * 安全算法&语言
66 | * 监测&演练&应急
67 | * 质量
68 | * 全阶段测试
69 | * 白盒、黑盒、灰盒测试
70 | * 静态测试和动态测试
71 | * 测试驱动开发
72 | * 数据
73 | * 概览
74 | * 底层/文件存储:RAID、HDFS、NAS、块存储、对象存储
75 | * 数据库之关系型数据库
76 | * 数据库之NoSQL
77 | * 数据库之NewSQL
78 | * 架构设计&权衡:原则(CAP/BASE)、共识算法(Paxos|Raft|Gossip|PBFT|POW)
79 | * 数据的扩展性设计
80 | * 数据接入&预处理
81 | * 数据应用&分析:OLTP、OLAP、HTAP
82 | * 智能
83 |
84 | ## 特定领域技术
85 |
86 | 上文提过,元问题指的是业务无关的元问题。一旦我们把目光放到某个特定领域里,就会发现九大元问题仍然覆盖了系统大部分方面,而与此同时,也会有不少业务相关的问题需要另外归类和解决。
87 |
88 | * [金融](特定领域技术/金融.md)
89 |
--------------------------------------------------------------------------------
/img/MapReduce.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/MapReduce.png
--------------------------------------------------------------------------------
/img/bigdata-tool.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/bigdata-tool.png
--------------------------------------------------------------------------------
/img/blockchain-merkletree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/blockchain-merkletree.png
--------------------------------------------------------------------------------
/img/cloud-duty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/cloud-duty.png
--------------------------------------------------------------------------------
/img/cloud-native.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/cloud-native.png
--------------------------------------------------------------------------------
/img/data-tech-relationship.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/data-tech-relationship.png
--------------------------------------------------------------------------------
/img/flink.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/flink.png
--------------------------------------------------------------------------------
/img/hbase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/hbase.png
--------------------------------------------------------------------------------
/img/hystrix-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/hystrix-flow.png
--------------------------------------------------------------------------------
/img/hystrix-sliding-window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/hystrix-sliding-window.png
--------------------------------------------------------------------------------
/img/language-classification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/language-classification.png
--------------------------------------------------------------------------------
/img/language-trend.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/language-trend.png
--------------------------------------------------------------------------------
/img/server-tech-tree-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/server-tech-tree-model.png
--------------------------------------------------------------------------------
/img/server-tech-tree-model.xmind:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/server-tech-tree-model.xmind
--------------------------------------------------------------------------------
/img/spark-arch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/spark-arch.png
--------------------------------------------------------------------------------
/img/spark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/spark.png
--------------------------------------------------------------------------------
/img/spring-framework-arch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/spring-framework-arch.png
--------------------------------------------------------------------------------
/img/springmvc-process.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/springmvc-process.png
--------------------------------------------------------------------------------
/img/tiobe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star2478/server-tech-tree/2e948fe94136e9c71f7503714dda8cd4baab642d/img/tiobe.png
--------------------------------------------------------------------------------
/元问题主要技术/伸缩性/伸缩性总概.md:
--------------------------------------------------------------------------------
1 | 提升软件的伸缩性,要同时保证增加或减少服务器时整个系统生产率不会下降。大家往往着眼于扩容时系统跑得更快更稳,以此来衡量系统的伸缩性做得好不好,却忽略了缩容后系统的运行能力。未来资源弹性会变得越来越流行,需要多少资源就给多少资源,保证够用且不浪费,届时反复的扩容缩容会变成常态,扩容时变好就意味着缩容时变差,因此伸缩性要考虑两个方向的系统运行能力,不过一般来看,扩容做好了缩容就不会有什么问题。
2 |
3 | 在伸缩性问题上,大部分系统面临的是不能通过扩容使系统保持能力,最常见的是一些老的单体应用或本地缓存应用,分布式多节点部署的话会使应用不可用或数据不一致。提升伸缩性的常见手段是服务拆分和负载均衡。先看服务拆分,比如微服务,将整个系统拆成多个服务,每个服务都可以独立部署和横向扩容,热点服务可以部署多些节点,当然了,单体应用也可以做伸缩性,但在流量大业务复杂高的场景下,扩缩容成本和风险更高,生成率更差,意味着伸缩性更差。再来看看负载均衡,其作用是将上游流量均衡地分流到系统各个节点,极大影响到系统的伸缩性,举个例子,流量变大后系统扩容了一个节点,但负载均衡做得不好,导致新节点只分担走了少量流量,随着流量继续变大很快又扩容了新节点,反过来良好的负载均衡下系统短时间内无需二次扩容。
4 |
--------------------------------------------------------------------------------
/元问题主要技术/伸缩性/应用层负载均衡.md:
--------------------------------------------------------------------------------
1 | # 应用层负载均衡
2 |
3 | * Spring Cloud OpenFeign
4 | * 各类负载均衡算法:随机、轮询、权重、最空闲、一致性hash
5 |
--------------------------------------------------------------------------------
/元问题主要技术/伸缩性/接入层负载均衡.md:
--------------------------------------------------------------------------------
1 | # 接入层负载均衡
2 |
3 | ## Nginx、HAProxy:c10w
4 | 工作在网络第七层的反向代理软件,主要支持http协议
5 |
6 | ## LVS、F5:c100w
7 | 当Nginx达到瓶颈时,就该上LVS或F5了。LVS和F5是工作在网络第四层的负载均衡解决方案,其中LVS是软件,运行在操作系统内核态,可对TCP请求或更高层级的网络协议进行转发,因此支持的协议更丰富,并且性能也远高于Nginx,可假设单机的LVS可支持几十万个并发的请求转发;F5是一种负载均衡硬件,与LVS提供的能力类似,性能比LVS更高,但价格昂贵。由于LVS是单机版的软件,若LVS所在服务器宕机则会导致整个后端系统都无法访问,因此需要有备用节点。可使用keepalived软件模拟出虚拟VIP,然后把虚拟IP绑定到多台LVS服务器上,浏览器访问虚拟IP时,会被路由器重定向到真实的LVS服务器,当主LVS服务器宕机时,keepalived软件会自动更新路由器中的路由表,把虚拟IP重定向到另外一台正常的LVS服务器,从而达到LVS服务器高可用的效果
8 |
9 | ## DNS:c1kw
10 | 当LVS遇到瓶颈时,就该分机房上DNS了。在DNS服务器中可配置一个域名对应多个IP地址,每个IP地址对应到不同的机房里的虚拟IP。至此,系统可做到机房级别的水平扩展,千万级到亿级的并发量都可通过增加机房来解决
11 |
--------------------------------------------------------------------------------
/元问题主要技术/可用性/单一应用可用性.md:
--------------------------------------------------------------------------------
1 | # 单一应用可用性
2 |
3 |
10 |
11 |
12 | ## 简介
13 |
14 | 对一个应用系统来说,可用性100%永远都是一个梦,但永远不乏追梦者。
15 |
16 | 导致应用系统不可用的原因五花八门,包括但不限于:
17 |
18 | * 流量过载
19 | * 程序主动抛异常
20 | * 程序运行超时
21 | * 程序bug
22 | * 线程池满
23 | * 硬件故障
24 | * 硬件资源耗尽
25 | * 缓存穿透、击穿、雪崩
26 |
27 | 保证应用系统可用性的思路,一种是尽可能保证系统不出问题,另一种是系统出问题时尽可能保证最低要求的可用。第一种思路只能应对部分原因导致的不可用,比如Serverless技术在流量过载下通过弹性资源分配解决不可用,但不可能适用于任何场景。而第二种思路是更常规的做法,本文称之为应用容错,具体策略包括但不限于:
28 |
29 | * 超时重试
30 | * 熔断降级:当请求累计达到某些条件阈值后(比如请求错误率大于某个值),后续请求将不再走正常运行的流程,而是转而执行一个降级流程,从而避免后续请求被夯住导致整个系统雪崩
31 | * 资源隔离/舱壁模式:不同服务使用不同线程池,一个服务耗尽其线程资源后不会影响其他服务
32 | * 流量控制:控制进入系统的流量,将流量控制在应用可承受的范围内,从而避免应用被瞬时的流量高峰冲垮
33 |
34 | 当前,有很多技术提供了有效的应用容错策略,帮助开发者快速构建高可用的应用系统,Hystrix是其中的标杆。
35 |
36 |
37 | ## Hystrix
38 |
39 | Hystrix是Netflix公司开源的一款容错系统,能帮助使用者码出具备强大的容错能力和鲁棒性的程序。Hystrix是豪猪的意思,也是Hystrix的图腾,寓意身披尖刺保护程序获得高可用能力。Spring Cloud集成了很多Netflix项目,其中就包括了Hystrix。Hystrix始于2011年,2018年宣布不再更新,进入长期维护状态,官方推荐使用更强大的`Resilience4J`,尽管如此,Hystrix作为早期容错技术,提供了许多开创性高可用设计,值得我们学习。
40 |
41 | Hystrix开源代码库是[https://github.com/Netflix/Hystrix](https://github.com/Netflix/Hystrix)。
42 |
43 | ### 核心设计
44 |
45 | 我在[《Hystrix使用入门手册(中文)》](https://github.com/star2478/java-hystrix/wiki/Hystrix%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8%E6%89%8B%E5%86%8C%EF%BC%88%E4%B8%AD%E6%96%87%EF%BC%89)中列举了Hystrix的核心设计。
46 |
47 | * 熔断降级:当达到某些条件阈值后(具体见下面的熔断原理),后续请求将不再走正常运行的流程(定义在`run()/construct()`里),而是执行降级后的流程(定义在`getFallback()`里),从而避免后续请求被夯住导致整个系统雪崩
48 | * 资源隔离/限流:支持线程池隔离和信号量隔离,可以基于此来做简单的限流
49 | * 线程池隔离:一个服务拥有自己的线程池,用完自己线程也不会影响到其他服务。适用于大部分场景
50 | * 信号量隔离:一个服务拥有自己的信号量,实际就是一个计时器,每个过来的请求都要先获取信号量,取得才能访问,否则进入降级流程
51 | * 两者对比:线程池隔离模式下,对于每个请求,hystrix都会启动一个线程来执行`run()`;而信号量隔离模式下,hystrix只用一个线程来执行所有请求对`run()`的调用,而且只支持同步,即一个请求调用`run()`直到返回才会释放信号量。信号量隔离比较适合系统间内部访问的场景,因为内部访问响应时间短,同步请求不会导致成功率下降,而在高延迟的外部访问场景下,则可以发起异步访问的线程池隔离更合适
52 | * 结果cache:支持在同一个context下,自动将前一个请求的结果缓存,后面请求可以直接使用该缓存
53 | * 合并请求:支持将N个请求自动合并为一个请求,这个功能在有网络交互的场景下尤其有用,比如每个请求都要网络访问远程资源,如果把请求合并为一个,将使多次网络交互变成一次,极大节省开销。重要一点,两个请求能自动合并的前提是两者足够“近”,即两者启动执行的间隔时长要足够小,默认为10ms,即超过10ms将不自动合并
54 |
55 | ### 原理
56 |
57 | ```Java
58 |
59 | // 应用程序要使用Hystrix,需要继承HystrixCommand或HystrixObservableCommand
60 | public class HelloWorldHystrixCommand extends HystrixCommand {
61 | private final String name;
62 | public HelloWorldHystrixCommand(String name) {
63 | super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
64 | this.name = name;
65 | }
66 | // 正常运行的流程
67 | @Override
68 | protected String run() {
69 | return "Hello " + name;
70 | }
71 | // 熔断降级后的流程
72 | @Override
73 | protected String getFallback() {
74 | return "Fallback " + name;
75 | }
76 | }
77 |
78 | public class HelloWorldTest {
79 | // 构造函数执行
80 | HelloWorldHystrixCommand command = new HelloWorldHystrixCommand("HLX");
81 | // execute()执行
82 | String result = command.execute();
83 | System.out.println(result); // 打印出Hello HLX
84 | }
85 |
86 | ```
87 |
88 | 上面是使用Hystrix的典型代码示例,可以看到应用程序需要继承Hystrix的类HystrixCommand或HystrixObservableCommand,对此更多的介绍详见我写的[《Hystrix使用入门手册(中文)》](https://github.com/star2478/java-hystrix/wiki/Hystrix%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8%E6%89%8B%E5%86%8C%EF%BC%88%E4%B8%AD%E6%96%87%EF%BC%89)。接下来,我们结合`HelloWorldTest`和下图分析一下Hystrix的工作流程。
89 |
90 |
91 |
92 |

93 |
94 |
95 |
96 | * HelloWorldHystrixCommand构造函数执行
97 | * 初始化`Metrics`:包含`HealthCounts`对象,负责记录累积的请求数、各类错误数、错误率等。熔断执行阶段Hystrix会根据HealthCounts保存的数据,决定应用切换为哪个状态(进入熔断、半熔断、关闭熔断)
98 | * `execute()`执行
99 | * 如果本次请求可以从cache中获得上一次请求的结果,直接返回
100 | * 如果熔断器被打开,转去执行降级流程`getFallback()`
101 | * 如果线程池满或信号量满,转去执行降级流程`getFallback()`,同时HealthCounts增加线程池拒绝次数或信号量拒绝次数
102 | * 如果能走到这一步,就可以执行正常流程`run()`
103 | * 如果`run()`执行失败,同时HealthCounts增加失败次数
104 | * 如果`run()`执行超时,同时HealthCounts增加超时次数
105 | * 熔断执行
106 | * 上面execute()执行阶段,HealthCounts统计了请求数和错误数(失败次数+超时次数+线程池拒绝次数+信号量拒绝次数)。如果错误率(错误数/请求数)大于预设的熔断临界值,则熔断器被打开,后面请求执行execute()时将转去执行降级流程getFallback(),一段时间后熔断器自动转为半熔断状态,之后第一个请求将执行执行正常流程run(),如果返回成功则关闭熔断器,后面请求继续走正常流程run(),如果返回失败或超时则继续打开熔断器
107 | * 这里有一个Hystrix很重要的问题,那就是错误率是如何统计的?一个简单的做法就是设置一个固定的时间区间,比如5s,一个时间区间内发生的所有请求和错误的累加就能得到该区间内的错误率,但这种做法无法有效和及时地反映系统的流量趋势,容易导致熔断器误判,比如一波请求的错误率大于预设的熔断临界值,但不巧的是这波请求分散在一前一后两个时间区间里,这样可能会导致两个时间区间的错误率都小于熔断临界值,结果错过了熔断,使系统面临雪崩的风险。为此,Hystrix采用了滑动窗口算法,将一个时间区间切分成一个个更小粒度的窗口。如下图,时间区间是5s,窗口是500ms
108 | * 当第1个500ms过去时累计了1个请求(错误率为0/1),所有窗口的错误率都设置为0/1,再在后面新增一个窗口(新增窗口错误率都是0),最后弹出第1个窗口,如果弹出窗口的错误率大于预设的熔断临界值,则熔断器被打开
109 | * 当第2个500ms过去时累计了1个请求(错误率为1/1),所有窗口都在原来基础上加上1/1,可以看到前8个窗口都变成了1/2,第9个窗口是第1个500ms新增的窗口,由于上一次是0,本次加上1/1后得到1/1,再在后面新增一个窗口(新增窗口错误率都是0),最后弹出第1个窗口,如果弹出窗口的错误率大于预设的熔断临界值,则熔断器被打开
110 | * 后面的统计以此类推
111 |
112 |
113 |

114 |
115 |
116 |
117 |
118 | > Hystrix滑动窗口是基于`RxJava`技术实现的,好处是代码得到极大简化。实际上,Hystrix里大量使用了RxJava。RX(Reactive Extensions)是一种基于观察者模式的响应式编程模型技术,由Microsoft架构师Erik Meijer领导提出,现已推出多种语言库,包括RxJava,RxJS、Rx.NET、RxAndroid等。RxJava是RX的Java版本,尤其适合简化处理数据流的代码,数据流越长简化越明显,其局限是有一定的学习成本,调试比较麻烦。如果有兴趣,可以去[RxJava中文网](https://mcxiaoke.gitbooks.io/rxdocs/content/)进行学习。观察者模式下,会存在`背压`(Backpressure),即被观察者产生的请求过快过多,观察者无法及时处理。RxJava 1.x对背压问题解决得并不好,RxJava 2.x提供了更灵活处理背压的策略,目前已发展到3.x版本
119 |
120 | ## 其他技术
121 | Spring Cloud生态中,支持的容错技术包括Hystrix、Resilience4J、Sentinel,它们的区别可见[这里](https://github.com/alibaba/Sentinel/wiki/Guideline:-%E4%BB%8E-Hystrix-%E8%BF%81%E7%A7%BB%E5%88%B0-Sentinel)。Spring Cloud Circuit Breaker集成了以上技术组件,允许开发者自行选择适合自己的。
122 |
123 | 其中,[Sentinel](https://github.com/alibaba/Sentinel)是阿里巴巴研发的技术,有几点值得关注:
124 |
125 | * 插槽链(slot chain)设计:将整个工作流程设计成由一个个插槽组成,每个插槽都有不同的职责,用户也可以定制化自己的插槽进行扩展,提高了系统的扩展性和可读性
126 | * 自适应保护:结合系统负载load、CPU使用率、RT、QPS和并发线程数等指标进行流量控制,让保持应用可用前提下尽可能达到最大吞吐量。这个能力Hystrix和Resilience4J都没有
127 | * 滑动窗口:与Hystrix基于RxJava实现不同,Sentinel使用独创的`LeapArray`实现来滑动窗口。LeapArray算法最主要是在一个窗口数组里算出本次统计应该落到哪个窗口:先用当前时间算出将要落到的窗口的下标,如果窗口数组中该下标对应的窗口为空,则创建一个窗口返回;如果窗口不为空且窗口开始时间等于当前时间所算的开始时间,则直接返回该窗口;如果窗口开始时间小于当前时间所算的开始时间,说明窗口已过期,于是更新窗口开始时间并返回该窗口;其他情况不存在。更加详细的解读可参见[这篇博文](https://www.jianshu.com/p/05677381e155)。可以看到,从一个时间窗口数组中找到所需窗口是通过对当前时间进行除余得到的,而除余操作抽象点看很像在一个环里打转,这可能也是`LeapArray`名字的由来吧。
128 |
--------------------------------------------------------------------------------
/元问题主要技术/可用性/数据层可用性.md:
--------------------------------------------------------------------------------
1 | # 数据层可用性
2 |
3 | * 主备:备节点正常情况不接流量,一旦主节点挂了,切到备节点进行服务。数据层的备节点要做好与主节点的同步,应用层是无状态的,无需同步。热备份是实时备份,冷备份则是周期性备份,发生主备切换时,备节点数据不是最新的
4 | * 一主一从一灾备:这是常见数据引擎的架构,主从在同城,灾备在异地,同步延迟时间视情况而定
5 | * 双活:两个节点集群都接流量,但按指定路由规则接流量,比如按用户ID分接流量。一旦某个节点集群挂了,其流量无缝切换到另一个节点集群。双活3个要求:1、流量按路由分派到不同节点集群;2、一个请求在一个节点集群闭环,不能跨到另一个节点集群去;3、数据能够分片访问。这样一来,双活能保证一个节点集群只写属于它的数据分片,单向同步另一个节点集群的数据分片,避免两个节点集群会相互更新彼此数据分片,数据一致性的技术成本将大增
6 | * MySQL:MHA、PXC
7 | * 多活:节点集群数>2的双活升级版
8 | * 物理隔离:同城 vs 异地。主备、双活、多活等都可以基于同城或异地
9 | * 两地三中心:同城双活+异地多活
10 | * 云计算
11 | * 私有云、公有云
12 | * 混合云:私有云和公有云混搭
13 | * 多云:部署在多个厂商的公有云或多个厂商的私有云,避免锁死在一个云厂商,需要实现统一管理
14 | * 定期演练
15 |
16 |
17 | ## 集群容错机制
18 |
19 | * failover:失效转移
20 | Fail-Over的含义为“失效转移”,是一种备份操作模式,当主要组件异常时,其功能转移到备份组件。其要点在于有主有备,且主故障时备可启用,并设置为主。如Mysql的双Master模式,当正在使用的Master出现故障时,可以拿备Master做主使用
21 |
22 | * failback:失效自动恢复
23 | Fail-over之后的自动恢复,在簇网络系统(有两台或多台服务器互联的网络)中,由于要某台服务器进行维修,需要网络资源和服务暂时重定向到备用系统。在此之后将网络资源和服务器恢复为由原始主机提供的过程,称为自动恢复
24 |
25 | * failfast:快速失败
26 | 从字面含义看就是“快速失败”,尽可能的发现系统中的错误,使系统能够按照事先设定好的错误的流程执行,对应的方式是“fault-tolerant(错误容忍)”。以JAVA集合(Collection)的快速失败为例,当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件,例如当某线程A通过iterator去遍历非线程安全的集合过程中,该集合的内容被其他线程改变,那么线程A访问集合时,就会抛出ConcurrentModificationException异常(发现错误执行设定好的错误的流程),产生fail-fast事件
27 |
28 | * failsafe:失效安全
29 | Fail-Safe的含义为“失效安全”,如果出现故障可以忽略,保证系统仍然可用。fail-safe里的故障需要是在可接受范围内。仍然以Java为例,使用线程安全的集合(比如ConcurrentHashMap)进行iterator遍历和删除,不会抛出异常,但要付出的代价是需要额外空间来拷贝数据,而且不保证遍历得到的结果是最新的(这算是一种故障)
30 |
--------------------------------------------------------------------------------
/元问题主要技术/可用性/整体应用架构可用性.md:
--------------------------------------------------------------------------------
1 | # 整体应用架构可用性
2 |
3 | 微服务架构
4 |
5 | ## 注册中心
6 | 服务发现&注册
7 | * 代表:Spring Cloud Eureka、Spring Cloud Consul、zookeeper
8 |
--------------------------------------------------------------------------------
/元问题主要技术/安全/分布式事务.md:
--------------------------------------------------------------------------------
1 | # 分布式事务
2 | * 代表:LCN
--------------------------------------------------------------------------------
/元问题主要技术/安全/区块链.md:
--------------------------------------------------------------------------------
1 | # 区块链
2 | * 特性:去中心化、不可篡改、可追溯性、开放性、隐私性
3 | * 可追溯:无余额概念,转出都是在之前的转入基础上取出部分或全部,取出部分情况下剩余部分转出到自己,达到每笔资金可追溯。银行的权责发生制,要求每笔交易都记录,不一定非得发生实际的资金流转(比如每天计提都要记录,而非等到结息时才记录)。这种实际上对交易流向的严格记录,那么区块链的可回溯机制是否可以取代之呢,实际上可回溯机制仅在实际流转时进行记录,似乎是不能取代的
4 | * 去中心化、防篡改、可追溯,都是如何做到的,都分别对应哪个领域问题
5 | * 节点
6 | * 普通节点和挖矿节点
7 | * 创世块的付款人是空,后续任何一笔交易都有付款人、收款人和金额
8 | * 也就是多个合法的账本。而因为挖矿的成本太高,导致要同时跟进多个账本是不可能的,所以矿工们只能赌跟其中一个。大多数人所选择的那一个分支的链就会越来越多,于是另外一边也就无人问津,从而作废了
9 |
10 | * 关键技术:智能合约(交易脚本)、算法和共识机制、P2P网络通信
11 | * 公有链不可能三角问题:区块链系统通常难以在可扩展性、去中心化、安全性三方面同时达到商用要求。
12 | * 联盟链应用场景
13 | * 金融类、溯源类、存证类等
14 | * 金融领域:贸易融资、合同存证、跨境交易清算、供应链金融、征信、存管结算、abs等
15 |
16 | * 私有链、公有链、联盟链
17 | * 联盟链是金融领域的选择
18 | * 联盟链并非去中心化,而是弱中心化
19 | * 联盟链代表:hyperledger
20 |
--------------------------------------------------------------------------------
/元问题主要技术/安全/安全监测.md:
--------------------------------------------------------------------------------
1 | # 安全监测
2 |
3 | ## 静态代码扫描
4 | 商用checkmarx静态代码检测、开源sonarxxxx、漏洞扫描工具等
5 |
--------------------------------------------------------------------------------
/元问题主要技术/安全/安全编程框架.md:
--------------------------------------------------------------------------------
1 | # 安全编程框架
2 | * Spring Cloud Security
3 |
--------------------------------------------------------------------------------
/元问题主要技术/安全/应用层安全.md:
--------------------------------------------------------------------------------
1 | # 应用层安全
2 |
3 | ## 风控
4 |
5 | ## 反垃圾
6 |
--------------------------------------------------------------------------------
/元问题主要技术/安全/接入层安全.md:
--------------------------------------------------------------------------------
1 | # 接入层安全
2 | ## 防火墙
3 | ## 防DDoS
4 | * iOA腾讯新一代企业网:帮助员工在不受信环境下可以安全访问企业系统,顺利工作。不论来自职场内外的访问要求,只要访问企业资源都需进入iOA的零信任安全平台评估,对身份、终端、链路进行持续动态的安全检测,确保访问来自可信的用户、设备、进程,并分配至智能网关访问相匹配的企业资源
5 |
--------------------------------------------------------------------------------
/元问题主要技术/安全/演练与应急.md:
--------------------------------------------------------------------------------
1 | # 演练与应急
2 |
3 | ## 演练
4 | 腾讯蓝军多次展开内部攻防演练
5 |
6 | ## 应急响应中心
7 | 形成了及时有效的应急响应机制
8 |
9 |
--------------------------------------------------------------------------------
/元问题主要技术/开发效率/ServiceMesh服务网格技术.md:
--------------------------------------------------------------------------------
1 | # ServiceMesh服务网格技术
2 |
3 | ## Istio
4 |
--------------------------------------------------------------------------------
/元问题主要技术/开发效率/共享技术与中台.md:
--------------------------------------------------------------------------------
1 | # 共享技术与中台
2 | ```
3 | 这类技术的设计思路是将自己以共享方式发布,提供某些通用能力,其他应用通过调用它们后,避免重复开发这些通用能力,仅需专注在自有业务能力的开发上,从而提升研发效率
4 | ```
5 | ## 中台
6 | * 业务中台
7 | * 数据中台
8 | ## 开放平台
9 | ## 公共库
10 |
--------------------------------------------------------------------------------
/元问题主要技术/开发效率/开发框架与微服务/SpringBoot.md:
--------------------------------------------------------------------------------
1 | # Spring Boot
2 |
3 |
9 |
10 |
11 | ## 简介
12 |
13 | `Spring Boot`是Spring家族2014年推出的一款开发框架,也是市场份额最大的Spring框架产品。Spring Boot主要基于Spring Framework/Spring MVC,大部分核心都依赖了Spring Framework/Spring MVC库。
14 |
15 | Spring Boot官网是[https://spring.io/projects/spring-boot](https://spring.io/projects/spring-boot),源码托管在[https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot),目前已发展到2.x版本。
16 |
17 | ## 核心设计
18 |
19 | [Spring Boot官网](https://spring.io/projects/spring-boot)中feature一栏,罗列了Spring Boot的核心设计,大部分是对Spring Framework/Spring MVC的封装和改进,通过简化配置和依赖,提供`开箱即用`的开发体验。
20 |
21 | * 可创建独立的Spring应用:Spring Boot应用本质上就是Spring应用,最终就是一堆Spring Bean托管在IoC容器中,这个特性没什么可说的,是最低标准
22 | * 内嵌web容器:支持内嵌Tomcat(默认)、Jetty、Undertow,具体讲就是可以将应用打包成一个jar包直接运行,不需要独立安装web容器部署war包再运行
23 | * 提供starter简化依赖:原来开发者使用Spring Framework开发一个功能,要在maven或gradle中加很多依赖,而Spring Boot将常用的依赖集合整合成一个个独立依赖,一般以`spring-boot-starter-{name}`命名,这样能极大简化依赖配置和开发者工作量,比如spring-boot-starter-web整合了Spring web依赖库和web容器依赖库,开发者只要依赖一个spring-boot-starter-web就能开发web应用。Spring Boot为不同常用功能都提供了对应的starter依赖库,比如web、jdbc、log等。另外,如果一个应用依赖多个starter库,但这些库不同版本可能无法兼容,那开发者可以依赖一个spring-boot-starter-parent,只需为这个parent库指定版本,它可以将其他starter库自动配置至相互兼容的版本,这样开发者就无需自己指定其他starter库版本了。开发者还可以自定义自己的starter库,非官方starter一般建议命名为`{name}-spring-boot-starter`,Spring Boot在启动时会解析starter包中`META-INF/spring.factories`文件,将文件里配置的AutoConfigure类自动加载成Bean注入容器
24 | * 尽可能自动配置Spring和第三方库:利用starter和各类技术(部分详见下面的启动原理),推测容器应需bean并完成自动配置
25 | * 提供诸多辅助功能,包括度量、健康监测、外部配置,这些对工业级生产环境应用至关重要
26 | * 无XML配置:XML配置出了名的难维护已经被开发者吐槽多年,Spring借Spring Boot之手,终于可以做到零XML配置。熟悉Spring MVC的开发者都知道,使用Spring MVC要配置几个XML,比如web.xml、springmvc.xml等,但实际上现在Spring MVC已经支持无XML启动,通过继承WebApplicationInitializer,完成DispatcherServlet的指定和初始化等一些原本在XML配置里指定的行为,例子详见[Spring MVC官网](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html)。无论如何,Spring Boot较之前辈们倡导`约定大于配置`原则,不光可以无XML配置,甚至可以不需要任何配置,一切配置项都有默认的约定值,但在实际应用中,我们一般都需要为少量配置项指定值,比如端口号、数据库信息等,这些配置可以放在application.properties里。虽然XML格式表现力更好,但properties的kv格式更简单更友好
27 |
28 | ## 启动原理
29 | Spring应用的运行期阶段,主要就是运用IoC容器里各种bean,而在启动阶段却有不少学问,所以本文重点讨论Spring Boot的启动原理。下面代码片段是使用Spring Boot的典型入口程序,通过这段代码来看Spring Boot启动过程,一共两步,分别是SpringApplication实例化和run()执行。
30 |
31 | ```Java
32 | // @SpringBootApplication是一个复合注解,由多个注解组成,主要有@Configuration、@EnableAutoConfiguration、@ComponentScan
33 | @SpringBootApplication
34 | public class HelloWorld {
35 | public static void main(String[] args) {
36 | SpringApplication.run(HelloWorld.class, args);
37 | }
38 | }
39 | ```
40 |
41 | SpringApplication实例化原理如下:
42 |
43 | * 推测应用类型:在classpath寻找特征类,决定本次应用的类型,是Web应用(SERVLET)、响应式Web应用(REACTIVE)、还是默认非Web应用(default)
44 | * 加载初始化器和监听器:借助SpringFactoriesLoader,查找、实例化(Class.forName)、排序所有初始化器ApplicationContextInitializer和监听器ApplicationListener
45 | > 初始化器和监听器实际都是Spring Framework相关接口,这里用在了Spring Boot启动阶段。为了在Spring Boot启动时完成自动加载,初始化器和监听器的所有实现类都要配置在classpath下的`META-INF/spring.factories`里,不仅如此,配置在该文件里的所有类,都可以在Spring Boot启动时完成自动加载和执行,这是因为在Spring Boot启动时,会在各个环节调用`SpringFactoriesLoader`去读取该文件加载和执行相关类,同时会将读取结果缓存到cache里,避免重复读取文件。开发者也可以自己实现相关接口并配置到spring.factories里,在Spring Boot启动时实现定制化的操作,所以严格意义上来说,能配置到spring.factories里的类都可以看作Spring Boot的扩展点
46 | * 推断主类:找到main()所在类,并赋值到属性mainApplicationClass
47 |
48 | run()的工作原理如下:
49 |
50 | * 启动时间监控:初始化StopWatch,可用于监控启动阶段每个任务的总耗时
51 | * 启动事件监听:事件监听器SpringApplicationRunListener主要用于run()后面各个子环节中产生各类事件,将事件广播给对应的监听器处理。监听器在SpringApplication实例化时已完成加载和实例化
52 | > SpringApplicationRunListener也配置在META-INF/spring.factories中,也是Spring Boot的一个扩展点
53 | * 设置环境变量,包括application.properties等配置
54 | * 创建IoC容器:根据SpringApplication实例化时的应用类型推测,决定创建什么类型的ApplicationContext容器。ApplicationContext容器的介绍详见[Spring Framework](SpringFramework.md)
55 | * 创建错误分析器,用于处理启动过程中发生的异常
56 | * 执行初始化器:执行所有初始化器的initialize()。初始化器在SpringApplication实例化时已完成加载和实例化
57 | * 初始化IoC容器:实际会执行到Spring Framework中的refresh(),完成IoC容器初始化和bean初始化,详见[Spring Framework](SpringFramework.md)。除此之外,此环节还完成了以下工作:
58 | * 完成内嵌web容器的端口设置和初始化工作,默认是Tomcat,还可以支持Jetty和Undertow
59 | * 充分利用`@SpringBootApplication`注解,将许多关键Spring bean自动注册到IoC容器。@SpringBootApplication是Spring Boot最重要的注解,是一个复合注解,主要集成了`@Configuration`、`@ComponentScan`、`@EnableAutoConfiguration`三个运行期注解,前两个是Spring Framework的注解,后一个是Spring Boot的注解。@Configuration将所修饰的类变成配置类,配置类里可以指定任意类为Spring bean并注册到IoC容器。@ComponentScan扫描指定路径并找出所有Spring bean,实例化注册到IoC容器中。@EnableAutoConfiguration借助SpringFactoriesLoader,从spring.factories中找到所有key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的类,通过反射实例化成bean注册到IoC容器中
60 | * 执行CommandLineRunner:这是Spring Boot一个扩展点,如果开发者想在启动快结束时做点什么,可以通过实现CommandLineRunner接口来实现。开发者可以实现多个CommandLineRunner,并能指定相应的Order来进行排序执行
61 | * 关闭事件监听
62 | * 关闭时间监控
63 |
64 | ## 结语
65 |
66 | Spring Boot比Spring Framework/Spring MVC进一步提高了开发效率,而且非常适合微服务开发。但Spring Boot只能开发单个微服务,而现实中应用大多由多个微服务组成,服务间需要大量的相互关联,Spring Boot对此无能为力。于是,[Spring Cloud](SpringCloud.md)应运而生。
67 |
--------------------------------------------------------------------------------
/元问题主要技术/开发效率/开发框架与微服务/SpringCloud.md:
--------------------------------------------------------------------------------
1 | # Spring Cloud
2 |
3 |
7 |
8 |
9 | ## 简介
10 |
11 | `Spring Cloud`于2015年推出,是Spring真正能完成微服务应用开发的框架产品,其内核是Spring Boot。Spring Cloud框架由很多子项目组成,这些子项目的贡献者除了Spring还有其他机构。子项目的概念是从官网直译过来的,可能不易理解,实际上可以等同于组件或依赖库,开发一个微服务应用,可能要依赖多个不同子项目/组件/依赖库。
12 |
13 | Spring Cloud官网是[https://spring.io/projects/spring-cloud](https://spring.io/projects/spring-cloud),中文网是[https://www.springcloud.cc](https://www.springcloud.cc),源码托管在[https://github.com/spring-projects/spring-cloud](https://github.com/spring-projects/spring-cloud),目前发展到Hoxton版本,对应的Spring Boot版本是2.2。
14 |
15 | > Spring Cloud版本号以伦敦地铁命名,而非惯用的数字
16 |
17 | 绝大部分的Spring Cloud模块,本质上还是一个Spring Boot/Spring Framework模块,不过一个微服务应用除了一般的应用模块外,还包括了一些特殊职能的模块,比如注册中心、网关等,这些模块可能依赖了非Spring机构的子项目/组件/依赖库,所以这些模块很可能不是Spring风格的。下面代码片段是Spring Cloud的典型入口程序,可以看到与Spring Boot唯一区别是增加了一个`@EnableDiscoveryClient`注解(该注解的作用是将本模块注册到注册中心),这样看来,Spring Cloud启动也没什么好说的。对于Spring Cloud,本文侧重于介绍其名下一些关键的子项目,比如注册中心、网关等,而这些子项目的主业大多不在开发效率元问题上,而是在可用性等其他元问题上。
18 |
19 | ```Java
20 |
21 | @SpringBootApplication
22 | @EnableDiscoveryClient
23 | public class HelloWorld {
24 | public static void main(String[] args) {
25 | SpringApplication.run(HelloWorld.class, args);
26 | }
27 | }
28 | ```
29 |
30 | ## 核心设计
31 |
32 | 要打造一个微服务应用,除了开发一个个模块外,还要考虑模块间通讯、路由、可用性等等,这正是Spring Cloud的核心设计所在。[Spring Cloud官网](https://spring.io/projects/spring-cloud)中feature一栏,罗列了Spring Cloud的核心设计,每条设计的背后都有一个或多个子项目提供的解决方案,这些子项目在本文其他元问题上进行分析介绍。
33 |
34 | * 分布式/版本化配置:对应的主要子项目包括Spring Cloud Config
35 | * 服务注册与发现:对应的主要子项目包括Spring Cloud Netflix Eureka、Spring Cloud Zookeeper
36 | * 路由:对应的主要子项目包括Spring Cloud Gateway、Spring Cloud Netflix Zuul
37 | * 服务间通讯:对应的主要子项目包括Spring Cloud OpenFeign
38 | * 负载均衡:对应的主要子项目包括Spring Cloud OpenFeign、Spring Cloud Netflix Ribbon
39 | * 熔断器:对应的主要子项目包括[Spring Cloud Netflix Hystrix](../../可用性/应用容错.md)
40 | * 全局锁:对应的主要子项目包括Spring Cloud Cluster,借助Zookeeper、Redis实现
41 | * 领导选举和集群状态:对应的主要子项目包括Spring Cloud Cluster,借助Zookeeper、Hazelcast、Etcd实现
42 | * 分布式消息传递:对应的主要子项目包括Spring Cloud Bus
43 |
44 | 值得关注的是Spring Cloud Alibaba,这是Spring集成了阿里巴巴若干技术的项目,这是阿里在Java微服务领域技术实力的重要体现。除此之外,Spring Cloud还有其他重要的子项目,比如Spring Cloud Sleuth等。
--------------------------------------------------------------------------------
/元问题主要技术/开发效率/开发框架与微服务/SpringFramework.md:
--------------------------------------------------------------------------------
1 | # Spring Framework
2 |
3 |
16 |
17 |
18 | ## 简介
19 |
20 | Spring,一个Java领域最重要的开发工具集合,由Java语言编写而成。通过官网[spring.io](https://spring.io)可以看到,Spring已经发展成一个大家族,在这个家族里,最元老级的项目当属`Spring Framework`。Spring Framework包含了Spring最重要的设计哲学,也是几乎所有Spring项目的基石。我们常说的Spring 1到5版本实际就是Spring Framework 1~5。
21 |
22 | 1998年,Java原母公司sun和IBM推出J2EE `EJB(Enterprise Java Bean)`,但该框架很快由于极其难用被开发者诟病,这给了一个叫Rod Johnson的码农人生逆袭的机会。针对EJB,他在2001年出版`《Expert One-on-One J2EE Design and Development》`,提出更简单优美的开发思路,其中就包括了奠定Spring的部分核心设计,比如依赖注入。紧接着,2004年祭出神作`《Expert One-on-One J2EE Development without EJB》`,同年发布Spring 1.0版本,从此开启统治Java开发框架之路。在Rod带领下,2007年成立了SpringSource公司,经过多年收购和被收购,最终Spring项目被收入Pivotal公司名下,自Spring 4起,到最新的Spring 5,均由Pivotal推出。
23 |
24 | Spring Framework官网是[https://spring.io/projects/spring-framework](https://spring.io/projects/spring-framework),源码托管在[https://github.com/spring-projects/spring-framework](https://github.com/spring-projects/spring-framework),目前已发展到5.x版本。
25 |
26 | ## 核心设计
27 |
28 | Spring Framework一开始以其简洁周全的设计受到追捧,等到拥有一定市场份额后,才不断增加新的功能,所以其最初的核心设计值得我们来了解。
29 |
30 | * 非侵入:它一开始就设计成尽可能减少对应用的侵入。使用Spring核心功能的应用无需继承任何Spring类,通过外置的xml文件就可以使用,而应用一旦不想再使用Spring了,无需修改任何代码。而如果应用使用的是Spring非核心功能,比如JDBC,则还是会有侵入。不过,时至今日Spring已经成为业内的事实标准,它开始鼓励使用核心功能的应用也依赖框架本身,这相当于应用被侵入了,比如开始鼓励使用内置的JavaConfig(纯Java代码或注解)替代外置的XML配置,相反如果不依赖则会导致代码变得复杂难以维护
31 | * `Spring bean`:不管是简单的Java类如`POJO(Plain Old Java Object)`、还是遵循一定规则的Java类如JavaBean,Spring可以将任何形式的Java类转化成可以受到Spring容器管理的实体,这种实体就是Spring bean(后面简称bean)。正是Spring能对Java类实施管理和控制,它才能提供各种原生Java代码无法提供的功能,包括下面的IoC和AOP
32 | * `IoC(Inversion of Control)`:IoC即控制反转,是Spring最著名的设计,指Java对象不由应用本身来创建和控制,而是交给Spring来控制,简单理解就是用一个类时不需要直接new出来,而是通过注解或配置预设好一个类的创建/销毁行为,当Spring启动或运行时自动将类实例化成bean,然后将bean放入一个称为IoC容器的内存空间里进行管理。几乎所有应用都是由多个类相互依赖和协作实现的,IoC的设计可以让相互协作的类保持松耦合,相反,如果一个类要自己控制其所依赖的其他类的创建,软件将变得难以维护和测试,比如要写一个单测,程序里居然要将依赖链路上的所有Java类实例化才能跑起来,可想而知多复杂。应用里的类越多,依赖关系越复杂,IoC实现的松耦合越显威力。严格来说,IoC有两种常见实现方式:
33 | * `DI(Dependency Injection,依赖注入)`:指类对象被动接受IoC容器将其依赖的对象注入,比如bean初始化过程就用到了DI
34 | * `DL(Dependency Lookup,依赖查找)`:指类对象主动去IoC容器寻找其依赖对象,比如通过`getBean()`获取依赖对象
35 | * `AOP(Aspect Oriented Programming)`:AOP即面向切面编程,是一种高级的编程模式。AOP并非Spring的专利,有个AOP联盟,Spring是其中的成员。AOP的作用是帮忙开发者更容易将可重用的组件抽离出来,实现组件高内聚,常用的例子就是我们会为每个接口请求前和请求后分别加上监控log,AOP可以通过一个类去定义哪些接口的哪些位置需要加什么样的逻辑,这样一来我们就不需要在每个接口前后都写上两条差不多一样的log了
36 | > IoC实现松耦合,AOP实现高内聚,两者结合就能帮助开发者写出我们常推崇的高内聚低耦合的应用
37 | * 扩展性:Spring提供了很多扩展点,开发者通过实现相关接口就能在整个生命周期的某些阶段进行定制化,比如给bean某个属性设置默认值。虽然大多数应用都不会对开发框架进行扩展改造,而且扩展性导致对应用的侵入,但好处在于不改框架源码就能实现对框架的增强,仍然是一个优秀开发框架应该具备的重要特性。Spring相关扩展点详见下面介绍
38 | * 贴近实际生产的诸多能力:一个开发框架能够成功,前提必须是能直接帮助到实际的生产开发。除了上面几点,Spring还提供了企业开发中诸多常用的功能,包括web、数据库访问等
39 |
40 | ## 整体架构
41 |
42 |
43 |

44 |
45 |
46 | 如上图所示,Spring Framework由若干个模块组成。
47 |
48 | * 测试:集成Junit等工具库,便于开发者单元测试
49 | * Spring核心容器:包含了所有核心类库,提供两种类型IoC容器`BeanFactory`和`ApplicationContext`,实现对`bean`的管理。还提供了包括`SpEl(Spring Expression Language)`等功能
50 | * 面向切面编程:包括了Spring自己的`AOP`和基于AspectJ框架实现的`AOP`
51 | * Instrumentation:提供对JVM和Web容器的检测
52 | * 数据访问与集成:集成了`JDBC`、`ORM`(如Mybatis,Hibernate)、`事务`、OXM、消息、JMS等
53 | * Web与远程调用:包括了`Spring MVC`、WebSocket等
54 |
55 | 下面,我们从整体架构里抽出几个重要模块介绍其原理。
56 |
57 | ## IoC原理
58 | 这里介绍的主要是Spring核心容器(或称IoC容器)原理,即Spring从启动到销毁的过程。Spring容器有两大类,分别是`BeanFactory`和`ApplicationContext`,前者过于简单,所以一般都用后者。
59 |
60 | 下面代码片段是使用ApplicationContext的典型入口程序,通过这段代码来看Spring容器的整个过程,分别是容器初始化、bean初始化、应用运行期、销毁。
61 |
62 | ```Java
63 |
64 | public static void main(String[] args) {
65 | // 容器初始化、bean初始化:源码里refresh()是关键入口函数
66 | ApplicationContext context = new ClassPathXmlApplicationContext("helloworld.xml");
67 | // 应用运行期:通过getBean从Spring容器获得一个bean(对应HelloWorld类),这种属于DL依赖查找
68 | Demo obj = context.getBean(HelloWorld.class);
69 | }
70 | ```
71 |
72 | #### 容器初始化
73 | 容器初始化阶段的核心流程如下:
74 |
75 | * bean定位:从指定路径(classpath、文件系统、web容器、远程等)扫描包,找出定义了bean的资源,比如xml文件,存入Resource对象中,不同Resource实现类可以存放不同位置的bean资源
76 | * bean加载:根据Resource读取出所有类,检查类对应的class是否存在,基于ASM(Java字节码操控框架)解析class并转成IoC内部数据结构`BeanDefinition`对象
77 | * BeanDefinition记录了一个类的所有bean信息,包括class、scope、lazyinit、beanName等bean属性
78 | * bean注册
79 | * 将BeanDefinition对象放入BeanDefinitionMap里,key是beanName。如果key重复,则判断是否设置了override
80 | * 这个map和后面的singletonObjects单例池都是全局数据结构,大部分操作都得使用synchronized防并发
81 | * 按排序顺序执行所有`BeanFactoryPostProcessor`:BeanFactoryPostProcessor是一个接口,实际执行的是该接口的所有实现类相关方法
82 | * 注册和初始化所有`BeanPostProcessor`:BeanPostProcessor也是一个接口,但这里只是注册和初始化它的实现类,真正执行是在下一个阶段——bean初始化
83 | > BeanFactoryPostProcessor和BeanPostProcessor都是Spring的扩展点。前者在容器初始化阶段执行,可以对BeanDefinition进行扩展改造(比如修改bean的scope属性),后者在bean初始化阶段执行,可以对bean实例化过程进行扩展改造(比如修改bean的属性值)。除了这两种扩展点,Spring还提供了一个Aware扩展点,详见下文
84 |
85 | #### bean初始化
86 | bean初始化阶段也常称为bean生命周期。一个bean的初始化,不仅仅只是实例化一个Java类那么简单。
87 |
88 | 如果用的是BeanFactory容器,则所有bean都在应用运行期第一次使用时被初始化。如果使用ApplicationContext容器,情况相对复杂些,要根据bean的scope(作用域)属性和懒加载属性来判断,这里以最常用的scope为单例模式(singleton)且非懒加载为例来说明整个初始化过程。
89 |
90 | * 从BeanDefinitionMap依次取出所有bean,循环执行下列操作
91 | * Java类实例化
92 | * 如果scope为singleton、非懒加载、不是抽象bean,则基于反射对bean对应的Java类进行实例化,执行构造代码块和构造函数
93 | * 如果bean设置为懒加载(init-lazy),则在运行期被使用时才初始化
94 | * Java类实例化后,bean只是半成品,只有成为成品后,bean才可被应用所用
95 | * 属性设置/属性注入
96 | * 如果bean的某个属性是另一个bean,则跳到另一个bean的初始化过程
97 | * 如果有A和B两个bean,A有B属性,B有A属性,则形成循环依赖。Spring采用三级缓存来实现循环依赖,即使用三个HashMap来分别存放半成品bean、bean工厂对象、成品bean,整个过程相对复杂,这里不展开讲了
98 | * Aware扩展点执行:与BeanFactoryPostProcessor和BeanPostProcessor一样,Aware也是扩展点。一个bean可以实现了以下三个Aware接口得到相关信息,进而实现相关扩展
99 | * 如果实现了BeanNameAware,则调用setBeanName()获取beanName
100 | * 如果实现了BeanFactoryAware,则调用setBeanFactory()获取当前工厂实例的引用
101 | * 如果实现了ApplicationContextAware,则调用setApplicationContext()获取ApplicationContext容器上下文
102 | * 按排序顺序执行所有BeanPostProcessor实现类的`BeforeInitialization()`
103 | * 承接上文,我们说过在容器初始化阶段对BeanPostProcessor只是注册和初始化,这里终于执行了
104 | * BeanPostProcessor很重要,@Autowired和AOP都是BeanPostProcessor各种实现类来完成的
105 | * 执行@PostConstruct注解修饰的方法
106 | * afterPropertiesSet:只要bean实现了InitializingBean接口,就需要实现afterPropertiesSet(),此处就是执行该方法
107 | * init-method:执行init-method属性指定的方法。和afterPropertiesSet不同,afterPropertiesSet要求bean实现Spring接口,这样Java类和Spring发生了强绑定,但init-method通过外部配置来指定初始方法,Spring没有对Java类发生侵入
108 | * 按排序顺序执行所有BeanPostProcessor实现类的`AfterInitialization()`
109 | * 完成bean,从半成品变成产品,最后放入容器,即放入一个叫singletonObjects的HashMap,这个HashMap就是上文提到的三级缓存中的一级缓存,也叫`单例池`
110 | * 循环进入下一个bean实例化过程
111 |
112 | #### 应用运行期
113 | 这个阶段,应用运行起来,通过直接或间接调用`getBean()`获取相应bean。如果要取的bean是singleton模式,意味着该bean已经在bean初始化阶段完成了初始化,直接从单例池里拿出即可。如果要取的bean是prototype或其他模式、或者设置为懒加载,则走一遍bean初始化流程,实时得到bean。
114 |
115 | #### 销毁
116 | * bean销毁
117 | * 如果bean实现了DisposableBean接口的destroy方法,执行该方法
118 | * 如果设置了destory-method,执行该方法
119 | * 容器销毁
120 |
121 | ## AOP原理
122 |
123 | 下面代码片段展示了使用Spring AOP编写一个切面类。
124 |
125 | ```Java
126 |
127 | @Aspect
128 | @Component
129 | public class HelloworldAspect {
130 | // 设置连接点:@Pointcut指定包路径xx.yy下所有*Controller类,这些类任何方法调用时就会触发这个切面
131 | @Pointcut("execution(* xx.yy..*Controller.*(..))")
132 | public void helloworld() {}
133 |
134 | // 设置advice:*Controller类任何方法调用前执行本方法
135 | @Before(value="helloworld()")
136 | public void doBefore() {
137 | // do something
138 | }
139 |
140 | // 设置advice:*Controller类任何方法调用后执行本方法
141 | @After(value="helloworld()")
142 | public void doAfter() {
143 | // do something
144 | }
145 | }
146 | ```
147 |
148 | 如果一个bean被织入了切面逻辑,即位于切面类中`@Pointcut`指定包路径下,以上面代码为例就是某个xxController类,那在bean初始化时会对这个bean做进一步增强,最终变成一个代理bean,代理bean的每个方法前后都会加上doBefore和doAfter相关逻辑,这个过程发生在BeanPostProcessor执行阶段。代理bean的生成是基于Java两种动态代理技术实现:`JDK动态代理`和`CGLIB(Code Generation Library)动态代理`。默认情况下,如果bean实现了接口则使用JDK动态代理(也可以指定使用CGLIB生成),否则使用CGLIB生成。JDK动态代理是基于Java反射技术实现的,其优势在于内置在JDK中,不需要依赖任何第三方技术,但要求bean要实现接口。CGLIB不要求bean必须实现接口,但生成的代理bean继承了bean,所以要求bean不能是final class,此外,CGLIB使用FastClass技术调用方法,比JDK动态代理的反射方式性能更好。
149 |
150 | 除了JDK动态代理和CGLIB,Java领域还有其他动态代理技术,比如ASM和Javassist。CGLIB是高级字节码操作库,要依赖ASM操作字节码,而ASM是低级字节码操作库,可直接操作字节码,性能最好,但对开发和维护的要求很高。
151 |
152 | 与动态代理相对应的是静态代理。AspectJ就是一种静态代理技术,因为它通过专门编译器在编译期生成代理,而动态代理技术是在运行期生成代理。
153 |
154 | 总而言之,Java动态代理技术是实现Spring AOP的关键技术。
155 |
156 | > 题外话:如上面所述,Spring是AOP联盟成员,加入AOP联盟后,Spring曾推出过自己的AOP语法,但使用很不友好,被开发者纷纷吐槽,后来便借鉴AspectJ改进了其AOP表达式,还引用了AspectJ包来提供注解驱动的AOP。
157 |
158 | ## Spring MVC原理
159 | 通过Spring Framework,我们可以写出各种类型的应用程序(web、数据、安全、消息等),其中web应用是最重要的应用之一,比如我们想实现在浏览器上输入`http://127.0.0.1:80/helloworld`时,浏览器上显示出HelloWorld,这就是一个简单的http web应用,而Spring MVC正是Spring Framework提供的web开发框架。Spring MVC与Spring Boot、Spring Cloud,是Spring家族里开发者使用最多的框架。
160 |
161 | > 除了Spring MVC,Spring Framework 5还包含一款新web框架`Spring WebFlux`。两者最大区别在于同步与异步,Spring MVC是同步堵塞I/O模型,为每个请求分配一个线程,而Spring WebFlux是异步非堵塞I/O模型,更能利用多核处理高并发的场景。虽然Spring WebFlux能处理更高并发,但其不支持JDBC,同时响应式编程需要一定学习成本,因此两者没有高下之分,开发者可以自行选择
162 |
163 | Spring MVC最常见的使用方式是开发者按照Spring MVC规范编写好一个web程序,打成war包,放到web容器(比如Tomcat)指定路径下启动运行。
164 |
165 | 顾名思义,Spring MVC以著名的`MVC(Model-View-Controller)`模型来组织代码,将代码分为3层,每一层解耦,使代码更清晰易维护,其中Controller层是第一层。下面代码片段展示了使用Spring MVC编写一个web接口的典型入口程序。
166 |
167 |
168 | ```Java
169 | @Controller
170 | public class HelloWorldController {
171 |
172 | // 在浏览器上输入http://{ip}:{port}/helloworld时,浏览器上显示出HelloWorld
173 | @RequestMapping(value="/helloworld", method="GET")
174 | public String helloworld() {
175 | return "HelloWorld";
176 | }
177 | }
178 | ```
179 |
180 |
181 |

182 |
183 |
184 | 结合上图,我们来看看Spring MVC一个http web接口的请求流程。
185 |
186 | * web容器(比如Tomcat)接收到http web接口请求,如果web.xml里能找到本次请求url匹配的DispatchServlet,则为该请求启动一个线程交给Spring MVC,同时设置ThreadLocal保存请求的context和RequestAttr
187 | * Spring MVC维护一个单例DispatchServlet接收请求。虽然DispatchServlet是单例,但线程安全,因为它没有临界区操作
188 | * DispatchServlet根据url、请求方式(get/post等)、header、cookie等,找到处理器映射HandlerMapping
189 | * DispatchServlet执行对应的Handler
190 | * 执行拦截器preHandler
191 | * 执行控制器Controller(即上面的示例代码),返回相应model和view逻辑名给DispatchServlet。Controller由@Controller修饰,默认也是单例的,非线程安全
192 | * 执行拦截器postHandler
193 | * DispatchServlet根据view逻辑名找到view解析器
194 | * view解析器(比如jsp解析器)整合model数据到view,将完整view返回给前端
195 |
196 | ## 结语
197 |
198 | 虽然对比起原生Java,Spring Framework/Spring MVC已经为开发者提供了相当便捷的手段,但仍然不够,比如不能独立运行需要依赖web容器(Tomcat)、存在配置地狱问题等等。为了更进一步降低学习曲线、提升开发效率,Spring推出了更先进的[Spring Boot](SpringBoot.md)。
199 |
--------------------------------------------------------------------------------
/元问题主要技术/开发效率/开发框架与微服务/开发框架与微服务总概.md:
--------------------------------------------------------------------------------
1 | # 开发框架与微服务总概
2 |
3 |
8 |
9 |
10 | ## 开发框架
11 | 众所周知,开发框架是提升开发效率的强大利器。
12 |
13 | 再强大的编程语言,为了保持设计上的全面性和兼容性,都不会为了提升某些场景的开发效率而做出及时改动。为了弥补语言上的缺失,开发框架将广大开发者的最佳实践进行封装,相当于对语言进行增强,形成一套独特的开发标准,有助于提升特定场景的开发效率。
14 |
15 | > 在开发效率上的设计,编程语言需要考虑全面的使用场景(包括web、数据分析等),而一个开发框架往往只需考虑一种特定场景(比如web)
16 |
17 | 开发框架一般由多个公共库组成。为了方便开发者使用,成熟的开发框架还需要提供代码管理(比如maven)、脚手架、使用手册,甚至内置到IDE(比如Spring内置到Idea)。各大语言都有相应的主流开发框架,包括Java的`Spring`、Python的`Django`、Go的`Beego`等。
18 |
19 | 当前,在众多开发框架中,要属微服务框架最火。
20 |
21 | ## 微服务
22 | 微服务是一种软件设计思想,源自`SOA(Service-Oriented Architecture,面向服务的体系结构)`。
23 |
24 | 在服务端技术尚未成熟的年代,开发者倾向于将所有代码都编写成一个模块运行,这种模式称为`单体应用`。当系统处于小规模时,单体应用显示出简单易部署的优势。随着互联网兴起,系统规模不断变大,单体应用出现了难以协作开发、启动慢、不能隔离部署等问题。
25 |
26 | 1996年,Gartner提出`SOA`,将系统拆分成多个服务模块,服务间通过约定好的接口格式和协议进行连接互访。相较于单体应用,作者认为SOA最关键的是简化了开发协作,使大规模的团队开发成为现实,同时还解决了启动慢、分布式部署等问题。
27 |
28 | 虽然`Microservices(微服务)`这个概念很早就有人提出,但一般认为,是由Martin Fowler在2014年发表的[《Microservices》](https://martinfowler.com/articles/microservices.html)正式奠定。虽然《Microservices》中提到微服务和SOA不是一个风格的东西,但作者认为微服务仍然在SOA的范围内,因为相对于SOA,微服务是进一步提供了明确可落地的思路,比如围绕业务的组织、容错设计、推荐http和多语言等,但核心思想还是面向服务的架构。
29 |
30 | 值得一提的是,SOA的一个变种`ESB(Enterprise Service Bus,企业服务总线)`,使用一个中心化的总线模块连接所有服务,这个总线模块提供了服务发现注册、适配协议、数据格式、参数转换、负载等功能。可以认为,微服务和ESB都是SOA思想的进一步完善,相较而言,前者偏去中心化,后者偏中心化。
31 |
32 | ## 微服务开发框架
33 | 微服务是软件设计思想,将这一思想变成现实的是微服务开发框架。主流微服务开发框架包括`Spring Cloud`、`Google gRPC`、`Facebook Thrift`、`阿里Dubbo/HSF`等。
34 |
35 | 本文通过Spring Cloud一窥微服务开发框架的究竟。要了解Spring Cloud,得从[Spring Framework](SpringFramework.md)开始介绍,因为Spring Framework是Spring家族一系列技术的基石。
36 |
--------------------------------------------------------------------------------
/元问题主要技术/开发效率/扩展性技术.md:
--------------------------------------------------------------------------------
1 | # 扩展性技术
2 | ```
3 | 这类技术的设计思路是面向未来的,即当未来遇到需求或环境的增长和变化时,能够高效率完成功能升级和环境适配
4 | 这里仅指软件扩展性,数据的扩展性技术属于数据元问题
5 |
6 | AKF扩展立方模型
7 | * xyz轴
8 | * x(加机器,通过克隆扩展,也称横向复制,属于伸缩性)
9 | * y(拆不同的东西,soa)
10 | * z(拆相近的东西,数据分区分类型分维度,服务隔离)
11 | * 可移植性:分厂商移植
12 | ```
13 |
14 | ## 事件驱动架构(Event Driven Architecture)
15 | * 消息中间件
16 | * Kafka、ActiveMQ
17 |
18 | ## 模块化/组件化架构
19 | * Java9 Jigsaw
20 | * k8s的CRI、CNI、CSI、CRD、Aggregated APIServer、Initializer、Device Plugin 等各个层级的可扩展能力
21 | * k8s默认调度器会被设计成可插拔形式,即queue、predicate、priority各个环节都可以定制化组件化
22 |
23 | ## 调用扩展
24 | * RPC框架
25 | * 数据库适配:MyBatis
26 |
27 | ## 网关
28 | Zuul、Spring Cloud Gateway
29 |
30 | ## 业务能力扩展
31 | ### 规则引擎
32 | groovy&drools,设计思路和目标??
33 | ### 工作流
34 | activity
35 | 工作流可以变成有向有环图,和spark的dag不一样
36 | ### 通用业务
37 | * jeecg等平台都是把通用能力开放出来,比如权限角色机构用户审批流等
38 |
--------------------------------------------------------------------------------
/元问题主要技术/开发效率/无代码开发.md:
--------------------------------------------------------------------------------
1 | # 无代码开发
2 | [腾讯课堂·无代码软件开发入门: https://m.ke.qq.com/course/390888?tuin=5a20b1a&_bid=167&_wv=1]
3 | * 可视化或无代码编程:tersus、清流。看市面上目前的可视化或无代码编程主要还是集中在面向企业内部的系统,比如erp和crm,这类系统的特点是有成熟规范的一套业内流程
4 | * 可视化编程:blockly、scratch:代码语句级别的可视化编程,多用于教育
5 | * 通过定义有不同生命周期的全局变量,实现不同模块间的数据传输。
6 | * 设计先行
7 | * 构建好设计图,然后按图开发(每个模块对应一个接口),事后自动化检查
8 |
--------------------------------------------------------------------------------
/元问题主要技术/开发效率/编程语言.md:
--------------------------------------------------------------------------------
1 | # 编程语言
2 |
3 |
17 |
18 |
19 | ## 编程语言与开发效率
20 |
21 | 编程语言是影响开发效率最直接的因素,选择适当的语言可以使开发工作事半功倍。编程语言是计算机科学里一门综合的技术,要解决开发效率、性能、安全等一系列元问题,其中开发效率是绝大多数语言追求的核心指标之一。语言的发展呈现出越来越远离晦涩的硬件的趋势,那些对人类更友好简单、能更快开发出功能的高级语言成为主流,一门学习曲线陡峭的语言注定只能是小众的。所以,我把编程语言归入开发效率元问题里进行讨论。
22 |
23 | 当然,天底之下没有银弹,每种语言都有时代的局限性,也可能为了某些领域而牺牲其他领域的开发效率,比如Rust在安全上的投入,开发阶段的成本会上去,但实际上却显著减少运行阶段由于内存安全引发的修复成本,最终反而提升了整个软件生命周期的开发效率。
24 |
25 | ## 编程语言历史与现状
26 |
27 | 最早的编程语言(形式上是一种编码)思想可追溯到[19世纪](https://zh.wikipedia.org/wiki/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80%E6%AD%B7%E5%8F%B2),比现代计算机还早。自20世纪30年代现代计算机诞生之日起,为了驱动机器,真正意义上的现代编程语言开始被发明出来。
28 |
29 | 编程语言几十年一路发展过来,从最初极其晦涩的0/1机器码,到更容易被人类记忆但不具备跨机器移植性的汇编,再到以BASIC为代表的逐步具备跨机器移植性的高级语言雏形,接着到以C为代表的主要面向UNIX等系统级软件的高级语言,然后到以Java为代表的面向互联网和企业应用开发并追求安全性和开发效率的工业级语言,(现阶段)最后到以Go为代表的面向多核CPU和云原生的新生代语言。现阶段,非官方统计世界上的编程语言数目在700 ~ 1000种左右,主流的约15 ~ 20种,对它们进行分类是困难的,下图是wikipedia上的编程语言分类表,仅供参考。
30 |
31 |
32 |

33 |
34 |
35 |
36 | 编程语言的设计理念突飞猛进,变化飞快,几乎可以每10~15年一个台阶来划分,但所有语言基本上都做到了图灵完备,即理论上可以写出任何算法,因此并没有任何语言是不可取代的。
37 |
38 | 什么语言最受欢迎?[TIOBE](https://www.tiobe.com/tiobe-index/)给了一些标准,2020年7月榜单见下图,可以看到上世纪90年代是高级语言的黄金时代,89年Python、94年PHP、95年Java和JavaScript、00年C#,这些90后语言占据了当今计算机语言绝对统治地位,70后C语言、80后C++、00后代表Go短时间内根本无法撼动他们。
39 |
40 |
41 |

42 |
43 |
44 |
45 | 受欢迎程度仅代表现在,未来谁是王者要看历史机遇和语言自身发展。下图是主流语言现阶段的使用趋势,位于创新者或早期采用者区域的语言,很可能会在未来变到大众区域成为业内最主流,其中Go和Rust值得关注。
46 |
47 |
48 |

49 |
50 |
51 |
52 | ## 现代编程语言高级特性
53 |
54 | 为了适应当前环境、更有效利用现代硬件,各种新语言新高级特性层出不穷,不断为开发者提供更高的开发效率。下面,我们来看看几个关键常见的语言高级特性。
55 |
56 | #### 变量
57 | 不同语言对变量命名规则要求不同,有些松有些严,比如Java可以随意命名、Ruby规定类名常量名以大写字母开头、PHP用$修饰变量等。松要求可降低新手学习成本,对他们开发效率有帮助,而严要求则增加可读性,有利于控制系统的升级和维护成本,提高后续的开发效率。
58 |
59 | #### 代码块
60 | 代码块的划分规则会轻微影响到开发效率,使用不当容易导致难以发现的bug。
61 | * 用`{}`圈定一个代码块,以C为代表,是大多数现代语言的选择。该规则往往不强制单语句使用,这经常容易留下bug
62 | * 用缩进来表示,以Python为代表
63 | * 以`end`表示一段代码块的结束,以Ruby为代表
64 |
65 | #### 编程模式
66 | 编程模式也称为编程范式,可以理解为一种种编程风格,不同编程模式适合不同场景,用对了可大幅提升软件开发效率。大多数语言都支持多模式,有些设计之初就引入多模式,比如Rust一开始就支持面向对象和函数式编程,有些则在不断更新中加入新模式,比如Java 8才引入lambda实现函数式编程。语言支持某种模式,并不意味着开发者能设计出符合该模式的正确程序,比如初学者经常抽象出不合理的对象,导致表面上是面向对象的程序实际退化成了面向过程。
67 |
68 | 编程模式分为三大类(参见上面的编程语言分类图):`指令式/命令式`、`声明式`、`元编程`。指令式要求开发者自己编写完成问题的每一步。声明式要求开发者仅编写问题的目标而让语言内部自行完成问题的各个步骤。元编程是用代码生成代码的编程模式,如果所用语言不支持某些喜欢的语法,但该语言支持元编程,开发者就可以造出这些语法,元编程常见的实现手段包括宏、反射等。我们最常用的模式包括面向过程(指令式)、面向对象(指令式)、函数式(声明式)、SQL(声明式)等,下面具体介绍一下。
69 |
70 | * 面向过程编程`(Procedure Oriented Programming, POP)`:POP思想是将问题拆解成一个个步骤,每个步骤可以是一段代码、n个函数、n个类,**用步骤来组织程序**。以造房子为例,POP就是依次实现打地基、盖楼、装修等步骤。POP是非常直观的编程模式,大多数语言都支持POP,很多时候我们用其他编程模式设计出POP系统。POP在简单场景下开发效率最高,但在复杂场景下难以设计出扩展性良好和简洁的系统,开发维护成本将变大
71 |
72 | * 面向对象编程`(Object Oriented Programming, OOP)`:OOP思想是将问题抽象出所有参与对象,将每个对象所有属性和行为封装在一起,**用对象来组织程序**。仍以造房子为例,我们很容易就能抽象出参与对象是房子,房子对象由属性(门、窗、材料等变量)和行为(打地基、盖楼、装修等方法)组成,初看之下OOP比POP更高级了,但似乎更费心智,岂不是会把开发效率降下来?如果我们把场景弄得复杂一点,假设系统要求适用不同类型的房子,虽然不同房子基本属性和行为是差不多的,但难点在于要低成本适配不同的地方,比如城市一般的商品房需要增加土地购买行为、茅草屋不需要装修、不同房子材料不同等,如果用POP我们只能针对不同房子编写不同程序,而OOP通过继承、多态等设计极容易将程序组织成扩展性非常高的形态,无论增加多少种房子对程序的改造都很小甚至零改造,极大提升开发效率
73 |
74 | * OOP两大实现方式:基于类`(class-based)`和基于原型`(prototype-based)`
75 |
76 | * 基于类型方式以Java为代表,基于原型方式以JS为代表。创建对象时,基于类方式要先定义一个类,再对这个类进行实例化,而基于原型方式是直接创建对象。继承对象时,基于类方式需要另起一个新类继承父类,而基于原型方式直接以某个对象a为原型创建出另一个对象b,实现b继承a。更详细的用例见[这里](https://www.ibm.com/developerworks/cn/web/wa-protoop/index.html)。两种方式各有长短,两个阵营的人要切换到对方都需要换思路,并不容易,基于类方式抽象度更高,学习曲线更陡峭,代码量也可能更多,而基于原型方式更直观更接近人类自然语言,对新手可能更友好
77 |
78 | * OOP三大特征:封装、继承、多态
79 |
80 | * 封装:将一个对象所有属性和行为封装在一起,比如Java的class或者Go的struct。封装实现程序的高内聚低耦合,难点在于是否合理抽象出所有参与对象以及对象属性和行为,这需要开发者有足够的训练量和熟悉业务。接口也是非常重要的封装形式,很多语言采用显式实现接口的风格,比如Java,但也有一些语言采用隐式实现,比如Go中只要实现了某个接口的所有方法就被认为实现了该接口。显式实现接口方式直观易懂,但接口修改后所有实现对象需要随之修改,而隐式实现接口方式没有这个问题,但这种不直观的风格会使阅读和梳理代码时花费更多精力
81 |
82 | * 继承:如果一个对象无法完成所有事情,我们可以实现一个子对象来继承该对象,然后在子对象上进行扩展,这实现了代码的高度复用。继承主要分为单一继承和多重继承。单一继承仅允许继承一个父对象,以Java为代表,这种方式简单不易错但无法复用多个对象代码。多重继承允许继承多个父对象,以C++为代表,可以复用多个对象代码,但多个父对象存在相同方法时会造成二义性,而且实现起来复杂得多,多重继承把所有对象变成有向无环图`(Directed Acyclic Graph, DAG)`,一般用广度优先`(Breadth-First-Search, BFS)`或深度优先`(Depth-First-Search, DFS)`算法搜索,也有某些语言(比如Python等)用`C3`搜索方法。为了弥补单一或多重继承的短板,各语言会做适当的设计,比如Java允许多接口以实现与多重继承相同的效果、Python在多重继承基础上引入[Mix-in机制](https://zhuanlan.zhihu.com/p/138046102)。Go提供了另一种代码复用风格,被复用的对象以匿名成员的方式(无需给成员命名)集成进目标对象,这实际是一种组合,取代了传统的继承实现复用,是践行`组合优于继承`的一种设计
83 |
84 | * 多态:如果一个对象拥有多种形态,利用多态设计将多种形态统一关联起来,不需要为每种形态单独编写对象。多态分为动态多态和静态多态,也称为运行期多态和编译期多态,区别在于是在运行期还是编译期确定使用对象的哪个形态。多态的常见运用是方法重载和重写,以Java为例,重载方法和static同名方法的调用在编译期可以确定使用哪个类和方法,属于静态多态,而非static重写方法的调用要到类加载期或运行期才能确定,属于动态多态。还有一种很重要的多态运用,将参数的类型泛化实现多态,称为`泛型`,举个例子,定义一个泛型方法`func( a)`,T表示可以赋予不同类型,我们可以这么使用该方法`func(123)`或`func("abc")`。一般泛型属于静态多态,以Java为例,能在编译期使用类型擦除技术将``替换成具体的类型
85 |
86 | * 函数式编程`(Functional Programming, FP)`:FP的理论基础是λ演算`(lambda calculus)`,主张将问题看成是一段段数据的转化过程(想想图灵机),把每个转化过程封装成一个个函数,**用函数来组织程序**。其他模式也能提供函数,那FP有何不同?我总结起来核心有两点:函数数据化、数据不可变性。这个归纳可能与一般的观点不太一样,后面再详说。理论上FP可以用在任何应用场景,不过数学计算场景更直观,所以这里举个数学计算例子,即输入一个整型数组,对该数组每个元素乘以2进行输出,非FP语言一般会用循环,而FP代码可以写成`numbers.map(x=>x*2)`,用非常简洁的的匿名函数 `x=>x*2`就表达了同样的语义。该例子里,FP代码无需关注遍历顺序、是否并发等问题,而非FP代码需要,这说明用FP能写出完全专注于做什么的代码(即乘以2),而不用关心怎么做(比如使用循环还是其他方式遍历数组),这种风格更接近自然语言,对编程新手更友好,这也正是FP优势之一:可写出更简洁、更灵活、更紧凑风格的代码,大大提升软件的开发维护效率
87 |
88 | * 函数数据化:函数被当成数据的一种,可以赋给变量、可作为入参传递、可作为出参返回。该思想衍生出以下设计
89 |
90 | * 支持高阶函数:参数为函数或返回值为函数的函数
91 |
92 | * [函数的合并和柯里化](https://ruanyifeng.com/blog/2017/02/fp-tutorial.html):支持将多个函数合并以及多参数转单参数
93 |
94 | * 闭包:这是一种可以使用其他函数数据的函数。假设一个函数a定义在另一个函数b的作用域中,a称为内部函数,b称为外部函数,如果a引用了b的局部变量,那么a就是一个闭包。当b返回后,a依然可以使用b的变量
95 |
96 | * 数据不可变性:数据允许传递但不允许修改,加上引用透明,FP可以做到高度冥等性,即相同输入总会产生相同的输出。这个设计看起来违反了冯诺依曼体系架构倡导的通过修改存储器里的值去影响后续计算,而实际上FP的底层理论λ演算在上世纪30年代就已早于冯诺依曼体系提出,为了在当前普遍采用该体系的计算机上运行,FP语言都会被编译成符合冯诺依曼机器的指令。该思想衍生出以下设计
97 |
98 | * 引用透明:函数只依赖入参不依赖于任何外部变量。非FP语言存在全局变量,无法实现引用透明
99 |
100 | * 尾递归:指递归调用是整个函数最后执行的语句。由于数据的不可变性,所以FP不使用for/while,因为循环语句会依赖可变状态变量来控制循环,FP只能选择递归,但频繁的递归会使栈内存使用大增。尾递归可以优化这种情况,由于尾递归的最后执行语句也是方法本身,整个过程只需要一个栈桢存储即可,最底层方法执行前,栈桢保存的是最底层方法的参数和返回值,每执行一层,该栈桢就是该层方法的参数和返回值,这种只用一个栈桢来保存整个递归过程也叫栈重用,极大节省了栈的内存开销
101 |
102 | * 严格遵循数据不可变性往往显得“异类”,导致学习曲线变陡(但实际更接近自然语言),只有少量纯FP语言(比如Haskell)存在,大多数支持FP的语言都会做权衡,比如Java的lambda里可以使用循环
103 |
104 | * 数据不可变性避免了共享数据的所有并发问题,没有锁还可有效利用多核,性能上会有很大提升
105 |
106 | > 函数响应式`(Functional Reactive Programming, FRP)`:可以看成FP的一种演化版,同样是简化数据的转化过程,更进一步加入了观察者模式,将观察数据和被观察数据绑定在一起,被观察数据一旦改变,观察数据可随之改变。FRP代表有Java的RxJava框架。下面的伪码直观体现了FRP的用途
107 |
108 | ```
109 | // a = b + c
110 | a = func(i, j) {return i + j};
111 | b = 1;
112 | c = 2;
113 | a.bindTo(b, c); // 将a和b、c绑定,a是观察者,b和c是被观察者
114 | a(); // 输出3
115 | b = 3;
116 | c = 4;
117 | a(); // 输出7
118 | ```
119 |
120 | #### 内存管理
121 | 内存是软件不可或缺的资源,用好语言限定范围下的内存是影响开发效率的重要因素。语言对内存管理分为非托管和托管。非托管型语言以C/C++为代表,允许应用自行创建、操作、销毁任意可用范围内的内存,这种设计授予开发者充分灵活性的同时也带来各种使用内存不当问题。托管型语言以Java/Python为代表,应用对内存的使用受到语言内部(比如JVM)的严格监管,这种设计保证了内存安全的同时也可能带来性能开销。不过很多语言并非严格地遵循非托管或托管,比如Java提供了unsafe接口放开对内存的操作。在此基础上,内存管理衍生出了许多设计。
122 |
123 | * 指针:指针一开始是为避免低级语言直接访问物理内存而诞生的,它可以指向一个变量、对象或函数的地址,通过它可以更安全更方便地管理内存。指针对开发者要求较高,一不小心就容易写出带有内存重叠、溢出等问题的代码。正因如此,后来很多语言直接放弃了指针(比如Java),或者对指针的使用做了较多限制(比如Go禁止对指针类型数据做加减运算)
124 |
125 | * 垃圾回收`(Garbage Collection, GC)`:GC是语言中自动回收无用内存和整理有用内存的技术,一般来说只有托管型语言才有。GC分为垃圾采集和清除两个步骤
126 |
127 | * 垃圾采集:主要有引用计数和跟踪采集两种算法。引用计数记录了所有对象被引用的数目,当引用数为0时意味着该对象不再被使用,后续应该被回收,但此算法无法解决循环引用问题,比如a和b相互引用,那么它们的引用次数就永远不会为0,将永远无法被回收。跟踪采集规定了一些根对象,比如静态变量或常量等,这些根对象不会被回收,如果一个对象被根对象直接或间接引用,视为可达,不会被回收,否则将被回收。引用计数的采用者包括Objective-C、Rust(仅智能指针)等,跟踪采集的采用者包括Java、Go等
128 |
129 | * 垃圾清除:主要算法包括清除、压缩、复制。清除算法将死亡对象所在内存直接标记为空闲,并维护一个空闲链表,优点是快,缺点是内存碎片。压缩算法将所有存活对象压缩到一起,优点是没有内存碎片节省空间,缺点是开销较大。复制算法将存活对象copy到另一个空间,并压缩放一起,优点是没有内存碎片,缺点是需要一个额外等大空间,相当于可用空间减半。各大语言采用不同算法,Java不同GC器用了不同算法,而Go采用清除算法
130 |
131 | * 内存安全:2019年初在以色列举行的BlueHat会议上,微软安全工程师指出过去12年里,微软70%安全漏洞来自于内存安全。从一个侧面可以推断,内存的安全使用深远影响到软件的开发维护成本,进而关系到软件的开发维护效率。上面提到的指针解决了低级语言直接操作物理内存引发的安全问题,而指针存在的安全隐患又导致不少语言抛弃了它,但这仍不够,就像我们在用Java编写程序时仍然遇到内存泄漏和线程安全问题。Rust提供了全新的内存安全思路,在语法层面设计了许多强制的安全限制(比如所有权和借用),并在编译器上加入了严格的安全检查。举一个例子,先初始化一个变量x,接着将x赋值给y,这会导致x的所有权交给y,如果发生delete x会在编译期报错,这样的好处很明显,绝对不会发生因为删了x而导致y被误删,严格地保证了内存安全。可以认为,Rust用新人学习成本和编译性能换取了安全和代码质量,进而无形中减少了维护成本提高开发效率,很可能是一笔划算的买卖
132 |
133 | #### 并发
134 | 并发主要影响到性能元问题,但也关系到开发效率元问题。随着电路密集度和热密度接近极限,单个CPU核心性能已无法获得提升,而使用多个CPU核心就成了当前主流也是唯一的提升机器性能的方法。硬件层面的改变会影响到软件设计,为了尽最大可能利用所有核心,软件就要并行启动多个分身去分别使用不同核心。“分身”可以通过多进程、多线程、多协程等方式实现。如果语言提供了简洁安全的“分身”语法,开发者就能在充分驱动多核获取高性能和避免并发问题的同时保证开发效率,反之则开发者要牺牲开发效率来保证性能和避免并发问题。
135 |
136 | * 多进程:多进程的最大优点是隔离性好,一个进程崩溃不会影响其他进程,但其缺点是开销大,包括创建、内存占用、进程间数据共享等方面都有不小的开销。几乎所有高级语言都支持多进程的创建和管理,只是友好程度不一
137 |
138 | * 多线程:线程相较进程来说各方面开销小很多。如果你喜欢多线程,可以使用以Java为代表的推崇多线程的语言。一直以来,语言层面提供的多线程能力不能避免原子性、锁等并发问题,完全要依赖开发者的能力,而Rust强制安全的设计保证了默认线程安全,似乎会成为趋势
139 |
140 | * 多协程:协程比线程更进一步轻量。单个协程初始内存(2KB)小于线程(1MB)。协程的最大数量不受OS限制,而进程和线程都有数量上限,意味着在极端高并发场景下协程更容易接收和维护更多请求。协程调度器在应用态,而进程和线程的调度切换要陷入OS`(Operating System)`内核态,调度开销更小,这一点在我的文章[《浅谈Go语言》](https://github.com/star2478/golang-wiki/wiki/%E6%B5%85%E8%B0%88Go%E8%AF%AD%E8%A8%80)中有所介绍。值得一提的是,多进程多线程语言中经常采用共享内存方式来传递数据,为了保证共享内存的并发访问往往要增加锁开销,而Go采用了基于CSP`(Communicating Sequential Process)`模型的消息通道来进行数据传递和共享——当一个协程完成某个共享数据的访问后将异步唤醒另一个协程去访问,可避免锁开销
141 |
142 | #### 语言处理器
143 | 软件要能运行,必须要转化成CPU平台能够识别的机器码(0/1组成),根据转成机器码和运行机器码的方式和时机将语言分为`编译型`和`解释型`。编译型语言以C为代表,先将源码通过编译器转成机器码再运行,转码和运行两步分开,其优点是由于事先已转成机器码所以运行速度快,缺点是运行前需要对整个源码进行编译转码,会导致开发调试的效率变低。解释型语言以Python为代表,通过解释器对源码逐条解释成机器码并直接运行,转码和运行合并在一起,其优点是运行时是逐条源码解释,能很快看到代码的运行结果,开发调试效率高,缺点是运行速度相对较慢。
144 |
145 | > 更进一步,编译还分为静态编译和动态编译,前者在编译时将依赖库链接到机器码文件中,可以使运行速度更快,而后者编译时不链接依赖库,可以使编译速度更快和机器码文件更小。不同语言对编译类型的默认选择是不同的,像C这种较老的语言诞生在内存紧缺的时代,所以它选择默认是动态编译,这样编译时可以尽可能用更少的内存,而Go诞生时内存已足够大,所以选择默认是静态编译
146 |
147 | 除了编译型和解释型,还有一种重要的类型,即以Java为代表的`混合型`或`半编译型`,先用编译器转成特殊的中间码(比如Java称之为字节码),再用虚拟机对中间码逐条解释运行。这种专属于语言的虚拟机有别于传统系统级虚拟机,语言虚拟机也称为进程虚拟机。虚拟机一般基于栈或寄存器实现,两种方案优劣并无定论,不同语言会采用不同方案,甚至同一个语言采用不同方案,比如Java Hotspot虚拟机(用于服务端)是基于栈的,而Java Dalvik虚拟机(用于Android)是基于寄存器的。中间码指令长度也可能采用不同方案,一般是8位和32位,前者内存使用率更高,后者性能更高(CPU 32位对齐所以取指令快)。从开发效率角度来看,混合型语言的优点是同一份编译好的中间码可以移植到任意CPU平台运行(当然前提是有该平台的虚拟机且安装了),降低了移植成本,缺点是存在编译环节,开发调试的效率相对较慢。
148 |
149 | 事实上,各个语言的类型界限早已模糊。比如,Java等大批语言引入了JIT`(Just In Time)`技术,将中间码中的热点代码直接编译成机器码缓存起来以加速后续的运行速度,相当于在“混合型”上杂糅了“编译型”的设计。又比如,Python可以直接解释源代码运行,也可以编译为中间码通过虚拟机运行。
150 |
151 | #### 类型系统
152 | 类型系统是编程语言的基础,其决定了程序中所有数据的类型,比如整数、浮点数还是字符。开发中处处受到类型系统的约束,对开发效率有重要影响,尤其对于新手,为了某些特性主动或被动选择了一门语言,但往往忽略其类型系统,很容易在实践中踩到坑。
153 |
154 | 程序错误分为`trapped error`和`untrapped error`,前者发生时会使程序终止运行,比如除零、数组越界等,后者发生时不会使程序终止运行。
155 |
156 | 按照类型相关untrapped error的检查时机,将语言分为`静态型`和`动态型`。静态型语言指在编译时可检查出类型相关untrapped error的语言,代表包括C/C++/Java/Go等,比如Java中定义`String a="1"`和`int b=1`,则`a/b`在编译时会报操作数类型错误。动态型语言指在运行时才检查类型相关untrapped error的语言,代表包括Python/PHP/JS等。可以想见,静态型在编译期做了检查,运行时会更快,而动态型给予开发者更灵活的空间,比如同一个变量可以适配不同类型,减少代码量,码写得飞快,但对老系统进行升级重构时却要谨慎地推断各种变量上所用类型,一不小心就会踩坑,正所谓“动态一时爽,重构火葬场”。
157 |
158 | > 经常有两个误解:一是将静态型、动态型分别与编译型、解释型等价起来,实际并非如此,虽然常用语言中确实大部分编译型语言是静态型的,同时大部分解释型语言是动态型的;二是将是否显式指定变量类型来划分静态型和动态型,但实际上动态型语言在运行时才进行类型推导,所以它们不需要显式指定,但静态型语言也可以不指定,编译器可以做推导——静态型可分为`显式静态`和`隐式静态`,前者需要指定变量类型而后者不需要,像C或Java定义变量时要加上类型(例如int a),属于显式静态,而Go或Haskell无需加类型(例如var a),属于隐式静态
159 |
160 | 此外,还可按照运行时是否存在类型相关untrapped error,将语言分为`强类型`和`弱类型`。强类型语言指在运行时不会发生类型相关untrapped error的语言,代表包括Java/Python/Go等,比如Python中`'123'/123`会在运行时报TypeError错误。弱类型语言指在运行时会发生类型相关untrapped error的语言,代表包括C/C++/JS等,比如C中越界数组访问不会报错。
161 |
162 | 一门语言在不断发展过程中,出于兼容性和用户习惯等原因,类型系统在大方向上一般不会做改变,但小优化还是会随着时代的需要而发生。比如Python 2中字符串是一组字节,因为其诞生在需要大量脚本开发的系统级软件喷发的时代,此时要适应以字节为单位的操作系统、管道、网络socket等场景。而到了Python 3,字符串变成了一组Unicode码,因为其诞生在Web时代,网络交互普遍以Unicode码为基础。
163 |
164 | #### 异常处理
165 | 程序发生异常后的处理机制是每个高级语言的一项重要设计,简洁的处理机制能让处理异常的代码减少,提升软件整体的开发效率。
166 |
167 | 一种异常处理机制是返回值中带上错误对象,调用者通过错误对象判断是否异常。对于这种机制,大多数语言都需要开发者自行设计错误对象。Go提供了内置的Error对象,且支持多参数返回,函数的最后一个参数可以是Error对象,这样就从语言层面支持这种异常处理机制了,无需开发者自行设计。
168 |
169 | 另一种普遍的异常处理机制是异常捕获,在程序发生异常处抛出异常,在某处捕获异常并进行处理,比如Java的`try-catch-finally`。这种机制会改变程序的控制流,使用不当容易出错,有些语言选择了谨慎使用,比如Go也提供了类似的`defer-panic-recover`,一般是由系统自动抛出的严重异常,当然,用户也可以使用panic(“xxxx”)主动抛出。这种机制放开了对异常的定义,要求开发者要尽可能对所有可能发生异常的地方加上捕获,这无疑加大了开发者的开发成本,为此Java将异常分为受检和非受检,当开发者使用了可能会引发受检异常的语句时(比如进行I/O),会强制要求加上捕获,否则编译不通过,从语言层面提醒开发者做异常处理(释放I/O),减少开发成本。
170 |
171 | 资源使用完未释放经常是造成系统异常的一大因素,为此各个语言想方设法让开发者尽可能记住释放资源。`finally`是普遍的做法,在`finally()`实现资源释放,当函数正常或异常返回时`finally()`会被自动执行,保证了资源的释放。但`finally`会让代码量增加,在`finally()`里资源释放失败也得捕获。Java 7提供的`try-with-resource`机制值得学习,资源类事先实现`AutoClosable`接口重写资源释放方法`close()`,资源使用时在`try()`中写上所有资源open语句,这个机制只需要写`try-catch`,不需要写`finally`,因为编译器会自动编译出`finally()`,里面调用了`try()`里定义的所有资源的`close()`。此外,Go提供的`defer`,不像`finally()`那样要放在函数后面,`defer()`可以放在资源打开后的任意地方,比如open下一行马上就加defer close,这样既容易记住要close,又能极大避免把close写得到处都是导致重复close。
172 |
173 | #### 其他
174 |
175 | * 语言交互接口`(Foreign Function Interface, FFI)`:这是一门语言能与其它语言实现的接口进行交互的能力。大多数语言都有FFI,比如Java和Go都可以直接调用C代码
176 |
177 | * 参数:多数语言都有函数参数的扩展性设计,比如Java支持可变长入参(用`...`表示),Go支持函数返回多个出参(用逗号隔开)
178 |
--------------------------------------------------------------------------------
/元问题主要技术/性能/垃圾回收.md:
--------------------------------------------------------------------------------
1 | # 垃圾回收
2 | * java gc技术
3 |
--------------------------------------------------------------------------------
/元问题主要技术/性能/异步架构与消息队列.md:
--------------------------------------------------------------------------------
1 | # 异步架构与消息队列
2 |
3 | ## 分布式消息队列
4 | * kafka
5 |
6 | ## 异步编程框架
7 | * 代表:Netty
8 | * Spring WebFlux
--------------------------------------------------------------------------------
/元问题主要技术/性能/性能测试.md:
--------------------------------------------------------------------------------
1 | # 性能测试
2 | * 代表:Jmeter
3 |
--------------------------------------------------------------------------------
/元问题主要技术/性能/硬件使用优化.md:
--------------------------------------------------------------------------------
1 | # 硬件使用优化
2 |
3 | ## CPU亲和力
4 | * spark可以配置每个应用可使用的总集群和单机executor进程最大数量,即每个应用最多可以使用几个进程来分片执行。当然,进程数最好别配置超过cpu核数
5 | * k8s:cpuset功能,即cpu亲和力,将容器分配到固定CPU减少切换,nginx也有,这是性能优化双刃剑,要求进程少于核数
6 |
7 | ## 局部性原理
8 | * 临近正在被访问数据的数据很有可能接下来被访问
9 | * 重复引用同一变量的程序有较好的时间局部性。
10 | * 对于具有步长为k的引用模式的程序,步长越小,空间局部性就越好。具有步长为1的引用模式的程序有很好的空间局部性。在存储器中以大步跳来跳去的程序的空间局部性就很差。
11 | * 对于取指令来说,循环有很好的空间和时间局部性。循环体越小,循环迭代次数越多,局部性越好。
12 |
13 |
--------------------------------------------------------------------------------
/元问题主要技术/性能/缓存.md:
--------------------------------------------------------------------------------
1 | # 缓存
2 | * Redis
3 | * 数据包控制在1000字节以下,不开启pipeline吞吐量约为6w/s,使用pipeline约为10w/s。1000字节是redis性能的分水岭
4 | * 穿透、击穿、雪崩
5 | * CDN
--------------------------------------------------------------------------------
/元问题主要技术/性能/边缘计算.md:
--------------------------------------------------------------------------------
1 | # 边缘计算
--------------------------------------------------------------------------------
/元问题主要技术/性能/高性能模型.md:
--------------------------------------------------------------------------------
1 | # 高性能模型
2 |
3 | ## 应用系统
4 | 根据不同量级、场景、资源,使用不同性能优化方案组合。
5 |
6 | * 量级
7 | * <= k级QPS:cache+DB
8 | * >= w级QPS:mq+cache+DB
9 | * >= 10wQPS:mq+多级cache+分库分表DB+过载保护+热点隔离
10 |
11 | * 场景
12 | * rt优先:cache、算法优化
13 | * tps优先:多线程、请求合并、传输数据压缩、过载保护(熔断/限流)
14 | * 成功率优先:mq
15 | * 连接多活跃少(I/O型):I/O多路复用
16 | * 读多写少:cache、SQL优化
17 | * 写多读少:mq、LSM树(HBase)
18 |
19 | * 资源
20 | * CPU
21 | * 应用层:算法优化、多线程
22 | * 系统层:CPU nice设置、cgroup
23 | * 内存
24 | * 应用层:资源池化、gc
25 | * 系统层:OOM、内存score设置、cgroup
26 | * 磁盘
27 | * 应用层:AIO、数据压缩、随机写改为顺序写
28 | * 系统层:ionice设置、cgroup、换成SSD
29 | * 网络
30 | * 应用层:NIO、cache、传输数据压缩、短连接改为长连接
31 | * 系统层:TCP缓冲区调整、增加time-wait时长
32 |
33 | ## 资源池化
34 | * java静态指定线程池
35 | * 连接池
36 |
37 | ## 数据传递/共享模型
38 | * 从共享内存,逐渐发展到Actor,Go routine(CSP)
39 |
40 | ## 工作线程
41 | * golang动态分配M(工作线程)
42 |
43 | ## ppc和tpc
44 | ## reactor和proactor
45 | ## IO复用
46 |
--------------------------------------------------------------------------------
/元问题主要技术/数据/大数据之批计算.md:
--------------------------------------------------------------------------------
1 | # 大数据之批计算
2 | * 另一方面,你看一下 Hadoop 几个主要产品的架构设计,就会发现它们都有相似性,都是一主多从的架构方案。HDFS,一个 NameNode,多个 DataNode;MapReduce 1,一个 JobTracker,多个 TaskTracker;Yarn,一个 ResourceManager,多个 NodeManager。
3 | * 事实上,很多大数据产品都是这样的架构方案:Storm,一个 Nimbus,多个 Supervisor;Spark,一个 Master,多个 Slave。
4 | * 大数据因为要对数据和计算任务进行统一管理,所以和互联网在线应用不同,需要一个全局管理者。而在线应用因为每个用户请求都是独立的,而且为了高性能和便于集群伸缩,会尽量避免有全局管理者。
5 |
6 | ## MapReduce
7 | 
8 | * 关键能力:代码到数据所在地执行(边缘计算)、map输出的同一个key结果会shuffle到同一个reduce进程节点处理(对key做hash%reduce数,就可保证到同一个ID的reduce上,但当同一个key数据大且reduce数不够时,可能会压垮reduce)
9 |
10 | ## Spark
11 | 
12 | * 注:上图,application master可能和MapReduce作业(图中容器)不在同一个datanode
13 | * spark:RDD 弹性数据集(Resilient Distributed Datasets),Spark 分布式计算的数据分片、任务调度都是以 RDD 为单位展开的,每个 RDD 分片都会分配到一个执行进程去处理。
14 | * RDD 上定义的函数分两种,一种是转换(transformation)函数,这种函数的返回值还是 RDD;另一种是执行(action)函数,这种函数不再返回 RDD。
15 | * RDD 定义了很多转换操作函数(很像rxjava),比如有计算map(func)、过滤filter(func)、合并数据集union(otherDataset)、根据 Key 聚合reduceByKey(func, [numPartitions])、连接数据集join(otherDataset, [numPartitions])、分组groupByKey([numPartitions]) 等十几个函数。
16 | * 相对RM的map和reduce,spark一个作业可以支持更多函数的计算,速度更快。spark也有关键的shuffle过程(一个数据集中的多个数据分片需要进行分区传输,写入到另一个数据集的不同分片中),shuffle会把上一次计算结果按key进行数据分片,不同分片传到不同的下一个计算节点
17 | * spark会将一个作业所有函数转化成dag(有向无环图,见上图),rdd是一个个数据集,如图上,一个rdd包含了多个小块,每个小块代表一个数据分片,相当于rdd是逻辑上的数据集,里面的一个分片对应一个物理位置
18 | * spark会将一个作业所有函数转化成dag(有向无环图,见上图),rdd是一个个数据集,如图上,一个rdd包含了多个小块,每个小块代表一个数据分片,相当于rdd是逻辑上的数据集,里面的一个分片对应一个物理位置。
19 | * 有些函数不会shuffle,比如上图map,c和d两个rdd数据量都一样。有些需要shuffle,比如上图groupby,rdd a转成一个数据量完全不同的rdd b,a结果要shuffle出去形成rdd b
20 | * Spark 有三个主要特性:RDD 的编程模型更简单,DAG 切分的多阶段计算过程更快速,使用内存存储中间计算结果更高效。这三个特性使得 Spark 相对 Hadoop MapReduce 可以有更快的执行速度,以及更简单的编程实现。
21 | * 首先,Spark 在 2012 年左右开始流行,那时内存的容量提升和成本降低已经比 MapReduce 出现的十年前强了一个数量级,Spark 优先使用内存的条件已经成熟;其次,使用大数据进行机器学习的需求越来越强烈,不再是早先年那种数据分析的简单计算需求。而机器学习的算法大多需要很多轮迭代,Spark 的 stage 划分相比 Map 和 Reduce 的简单划分,有更加友好的编程体验和更高效的执行效率
22 | ### 工作原理
23 | 
24 |
25 | * 首先,Spark 应用程序启动在自己的 JVM 进程里,即 Driver 进程,启动后调用 SparkContext 初始化执行配置和输入数据。SparkContext 启动 DAGScheduler 构造执行的 DAG 图,切分成最小的执行单位也就是计算任务。
26 | * 然后 Driver 向 Cluster Manager 请求计算资源,用于 DAG 的分布式计算。Cluster Manager 收到请求以后,将 Driver 的主机地址等信息通知给集群的所有计算节点 Worker。
27 | * Worker 收到信息以后,根据 Driver 的主机地址,跟 Driver 通信并注册,然后根据自己的空闲资源向 Driver 通报自己可以领用的任务数。Driver 根据 DAG 图开始向注册的 Worker 分配任务。
28 | * Worker 收到任务后,启动 Executor 进程开始执行任务。Executor 先检查自己是否有 Driver 的执行代码,如果没有,从 Driver 下载执行代码,通过 Java 反射加载后开始执行。
--------------------------------------------------------------------------------
/元问题主要技术/数据/大数据之流批混合.md:
--------------------------------------------------------------------------------
1 | # 大数据之流批混合
2 | ## 流批混合框架Lambda架构
3 | * 目前影响最深刻的大数据处理架构,它的核心思想是将不可变的数据以追加的方式并行写到批和流处理系统内,随后将相同的计算逻辑分别在流和批系统中实现,并且在查询阶段合并流和批的计算视图并展示给用户
4 | * Lambda的提出者 Nathan Marz 还假定了批处理相对简单不易出现错误,而流处理相对不太可靠,因此流处理器可以使用近似算法,快速产生对视图的近似更新,而批处理系统会采用较慢的精确算法,产生相同视图的校正版本
5 |
6 | ## Flink
7 | 
8 | * 不管流处理和批处理,对于flink来说就是数据源不同,用的是同一套执行引擎
9 | * 支持对数据进行各种transformation(类似spark)
10 | * 将数据按一个个时间window来切分处理
11 | * Flink 的架构和 Hadoop 1 或者 Yarn 看起来也很像,JobManager 是 Flink 集群的管理者,Flink 程序提交给 JobManager 后,JobManager 检查集群中所有 TaskManager 的资源利用状况,如果有空闲 TaskSlot(任务槽),就将计算任务分配给它执行
12 |
--------------------------------------------------------------------------------
/元问题主要技术/数据/大数据之流计算.md:
--------------------------------------------------------------------------------
1 | # 大数据之流计算
2 | ## Storm
3 | * nimbus 是集群的 Master,负责集群管理、任务分配等。supervisor 是 Slave,是真正完成计算的地方,每个 supervisor 启动多个 worker 进程,每个 worker 上运行多个 task,而 task 就是 spout 或者 bolt。storm将spout和bolt组成一个topology进行任务分发和关联。supervisor 和 nimbus 通过 ZooKeeper 完成任务分配、心跳检测等操作。
4 | * Hadoop、Storm 的设计理念,其实是一样的,就是把和具体业务逻辑无关的东西抽离出来,形成一个框架,比如大数据的分片处理、数据的流转、任务的部署与执行等,开发者只需要按照框架的约束,开发业务逻辑代码(spout和bolt),提交给框架执行就可以了。
--------------------------------------------------------------------------------
/元问题主要技术/数据/大数据之资源管理.md:
--------------------------------------------------------------------------------
1 | # 大数据之资源管理
2 | ## yarn(对标mesos和docker)
3 | * 独立部署一个resource manager节点集群,代码提交给它,它通过与nodemanager通讯,将某个MapReduce作业对应的application master程序分派到一个datanode的一个容器中执行,application master向resource manager汇报每个datanode的资源使用情况,resource manager通过fair或capacity算法分配map或reduce任务给不同datanode。resource manager;
4 | * 每个datanode进程节点上部署一个nodemanager进程,将节点分成一个个容器,每个容器都拥有自己的cpu和内存等资源。
5 | ## mesos
6 |
7 | > 注1:ApplicationMaster 向资源管理器申请计算资源时可以指定目标节点(数据分片所在节点),而如果系统资源能够满足,就会把mapreduce计算任务分发到指定的服务器上。如果资源不允许,比如目标节点非常繁忙,这时部分mapreduce计算任务可能会分配另外的服务器(数据分片不在本地,即做不到边缘计算,数据需要传到map和reduce程序所在节点)
8 |
9 | > 注2:为何专门起一个application master来给resource manager汇报和为mr作业申请资源,而非利用node manager(这样一个datanode很可能就有datanode、nodemanager、application master三个进程了)?这是因为一是节点管理(nodemanager)和作业监控/资源申请(application master)进行解耦,可以兼容各种计算框架(MapReduce、spark等),计算框架只要遵循yarn规范写applicationmaster即可实现自己的资源调度策略,二是每个MapReduce作业都对应一个application master,即每个作业可以定制化自己的资源调度策略,也有默认策略(fair或capacity)。可以说,application master是面向某个map和reduce应用的,而node manager是面向某个datanode的
--------------------------------------------------------------------------------
/元问题主要技术/数据/数据库之NewSQL.md:
--------------------------------------------------------------------------------
1 | # 数据库之NewSQL
2 |
--------------------------------------------------------------------------------
/元问题主要技术/数据/数据库之NoSQL.md:
--------------------------------------------------------------------------------
1 | # 数据库之NoSQL
2 | * [看图轻松理解数据结构与算法系列(NoSQL存储-LSM树): https://mv.mbd.baidu.com/sm7cwm7?f=wf&u=c5e4660b3a7db8a8]
3 | * lsm树也是大部分nosql的核心结构,可以避免btree在rmdb上的不足,因为btree无法避免逻辑上相近的数据在物理上很远,导致经常放在一起查的数据比较耗时
4 |
5 | ## nosql分类
6 | * 键值型:redis、levelDB
7 | * 文档型:MongoDB、couchDB
8 | * 搜索引擎:Elasticsearch、splunk、solr
9 | * 列式:HBase、Cassandra、ClickHouse(由俄罗斯Yandex 公司开源的面向 OLAP 的分布式列式数据库,能够使用 SQL 查询生成实时数据报告。有几倍于 GreenPlum 等引擎的性能优势。在大数据领域没有走 Hadoop 生态,而是采用 Local attached storage 作为存储,这样整个 IO 可能就没有 Hadoop 那一套的局限。)
10 | * 图形数据库:Neo4j、ArangoDB、Titan
11 |
12 | ## 列式存储vs行式存储
13 | * 国内:阿里Doris、小米Pegasus、头条abase(貌似未开源)
14 | * Doris不关注底层存储(基于Berkeley DB),主要关注分布式方面的改进,关键技术点包括:分区路由(数据一致性)、失效转移(可用性)、集群伸缩(伸缩性)
15 |
16 | ## Redis
17 | * 数据包控制在1000字节以下,不开启pipeline吞吐量约为6w/s,使用pipeline约为10w/s。1000字节是redis性能的分水岭
18 |
19 | ## HBase
20 | 
21 |
22 | * hbase仍然是主从模式,master记录元信息并注册到zk,client从zk获取数据key所在hregion然后去访问。hbase是根据数据的key来实现数据分片的,一个hregion就是一个分片,存储了某个范围key的数据
23 | * 一个hfile是一个hdfs文件,自动做好分片备份,无需hbase自己做
24 | * HBase其它特性
25 | * HBASE伸缩性:扩容容易,缩容难?hbase单个hregion存入数据量太大会自动拆成多个并自动迁到其他节点,如果数据变少了呢,hbase有考虑数据缩容情况吗,如果节点数缩容是不是也意味着数据要做缩容?实际上一般数据也不会变少,所以一般不会考虑数据的缩容,一般只考虑应用的缩容
26 | * HBASE扩展性:nosql都一样,可以扩展无数个字段
27 | * HBASE性能:通过 LSM 树,使数据可以通过连续写磁盘的方式保存数据,提高数据写入性能。lsm树原理见上面链接,lsm不是一种具体数据结构,而是可以使用不同数据结构组成的设计思想,比如c0 tree可以使用avl树来实现,其他级别tree使用b 树来实现,c0 tree在内存,其他级别tree在磁盘。先写log(顺序写磁盘很快)再写c0 tree,这样一来断电后虽然c0 tree会消失,但可以根据log恢复。
--------------------------------------------------------------------------------
/元问题主要技术/数据/数据库之关系型数据库.md:
--------------------------------------------------------------------------------
1 | # 数据库之关系型数据库
2 | * 真正的分布式数据库,与传统的mysql有何不同
3 | * tdingine:IoT大数据平台,super table创新模型
4 | * 国内数据库:华为GaussDB、阿里OceanBase、阿里cobar->tddl->drds
5 | * Mycat:数据库中间件,可通过它来组织数据库的分离读写和分库分表
6 | * MPP(大规模并行处理)数据库:开源中比较流行的有Greenplum、TiDB、Postgresql XC、HAWQ等,商用的如南大通用的GBase、睿帆科技的雪球DB、华为的LibrA等等,不同的MPP数据库的侧重点也不一样,如TiDB更侧重于分布式OLTP场景,Greenplum更侧重于分布式OLAP场景,这些MPP数据库基本都提供了类似Postgresql、Oracle、MySQL那样的SQL标准支持能力,能把一个查询解析为分布式的执行计划分发到每台机器上并行执行,最终由数据库本身汇总数据进行返回,也提供了诸如权限管理、分库分表、事务、数据副本等能力,并且大多能够支持100个节点以上的集群,大大降低了数据库运维的成本,并且使数据库也能够实现水平扩展。
--------------------------------------------------------------------------------
/元问题主要技术/数据/数据库之链式数据库.md:
--------------------------------------------------------------------------------
1 | # 数据库之链式数据库
2 | * 贴吧
3 | * 头条的拉链存储ByteGraph,计划以后开源。
4 |
--------------------------------------------------------------------------------
/元问题主要技术/数据/数据应用:OLTP和OLAP.md:
--------------------------------------------------------------------------------
1 | # 数据应用:OLTP和OLAP
2 |
3 | ## 数据仓库
4 | ### Hive
5 |
6 | ## BI
7 | * cboard开源
8 | * tableau商业
9 |
10 | ## 日志数据
11 | * ELK(Elasticsearch、Logstash、Kibana)。log的设计思路和目标??
12 |
13 | ## 配置数据
14 | * 代表:Archaius、Spring Cloud Config
15 | * 关键技术
16 |
17 |
18 | ## OLAP例子:[淘宝数据魔方技术架构解析(2011)](https://link.juejin.im/?target=http%3A%2F%2Fhistory.programmer.com.cn%2F7578%2F)
19 | * 数据魔方已下线,转为市场行情专业版
20 | 
21 | * 其业务属于OLAP,针对数据非实时写入场景,但读支持实时和非实时
22 | * 计算层:融合了批处理(云梯)和流计算(银河)。主要是离线批处理,但仍然有些数据要求时效性,例如针对搜索词的统计数据,引入了流计算,存入存储层NoSQL中
23 | * 存储层:融合关系型数据库(MyFOX)和NoSQL(Prom)。数据中间层glider相当于存储层对外的网关,对外屏蔽MyFOX和Prom的差异,提供统一的基于HTTP协议restful格式的数据查询接口
24 | * MyFOX是一个分布式MySQL集群的查询代理层,使得分区对客户端完全透明。进行了冷热数据分离,热数据使用15k转/m SAS接口磁盘(应该是SSD)存储,冷数据使用7.5k转/m SATA接口磁盘存储,前者成本是后者的三倍
25 | * Porm弥补多属性场景的查询,基于HBase。主要用在以各种商品属性作为筛选条件查询交易数据,所以用商品属性+属性值作为row-key,用交易列表和交易详情相关字段作为column-family
26 | * 存储层使用了多级缓存(分别位于glider、MyFOX、Prom不同层)以提高查询效率,并使用缓存所有数据(包括空值)减少缓存穿透
27 |
--------------------------------------------------------------------------------
/元问题主要技术/数据/数据的一致性设计.md:
--------------------------------------------------------------------------------
1 | # 数据的一致性设计
2 | ## CAP
3 | * CAP 定理关注的是对数据的读写操作,而不是分布式系统的所有功能,它要求分布式系统节点间是互相连接且有数据共享的,例如 Memcache 的集群中节点相互间没有连接和数据共享,因此不是 CAP 定理讨论的对象,同理 ZooKeeper 的选举机制也不是 CAP 探讨的对象。
4 | * cap:一致性属于数据;可用性;网络分区容错属于?
5 | ## BASE
6 | ## 锁
7 | * 追求性能->多线程并发->数据一致性问题->锁和分布式锁
8 |
9 | ## 一致性算法
10 | 
11 | * 常用的一致性算法:raft、paxos、pow等
12 | * pow解决了共识问题(数据可信同步),还解决了双花问题?
13 | * 区块链SVP什么场景下用到?拜占庭将军问题如何解决,为何raft和paxio不能解决?
14 | * ZooKeeper 通过 zab(Paxos简化版) 选举算法实现数据强一致性(zk是多主,即所有节点都存全量数据),这样一来zk本身就避免了脑裂,就可以充当其他系统主从的裁判了 (即zk保存谁是主谁是从,主失去心跳则从可以换成主),如果zk这个角色无法避免脑裂,整个系统(无论zk还是主从系统)都会有脑裂问题,元数据不一致,元数据出错会引发各种严重问题,比如下游数据不一致、数据错误、应用执行失败
15 | * 脑裂问题
16 |
17 | * 默克尔树
18 | * 数据完整性或一致性校验的方法
19 | * btc有一个,etc有三个,见极客
20 | 
21 |
22 | * CAP
23 | * “在网络发生阻断(partition)时,你只能选择数据的一致性(consistency)或可用性(availability),无法两者兼得”。论点比较直观:如果网络因阻断而分隔为二,在其中一边我送出一笔交易:“将我的十元给 A”;在另一半我送出另一笔交易:“将我的十元给 B ”。此时系统要么是,a)无可用性,即这两笔交易至少会有一笔交易不会被接受;要么就是,b)无一致性,一半看到的是 A 多了十元而另一半则看到 B 多了十元。要注意的是,CAP 理论和扩展性(scalability)是无关的,在分片(sharded)或非分片的系统皆适用
--------------------------------------------------------------------------------
/元问题主要技术/数据/数据的扩展性设计.md:
--------------------------------------------------------------------------------
1 | # 数据的扩展性设计
2 | ## SQL-数据处理
3 | ## 分库(拆不同)分表(拆相近)
4 | ## 数据格式与协议
5 | * json
6 | * hession
7 |
--------------------------------------------------------------------------------
/元问题主要技术/数据/数据预处理.md:
--------------------------------------------------------------------------------
1 | # 数据预处理
2 | ## 采集
3 | ### 爬虫
4 | * scrapy
5 |
6 | ## ETL
7 | * ETL,Extraction-Transformation-Loading的缩写,中文名称为数据抽取、清洗转换、加载
8 | * 抽取:kafka、flume、sync
9 | * 清洗转换:hive/tez、pig/tez、storm、spark
10 |
11 | ### 数据抽取:在线系统数据如何导给大数据系统
12 | * sqoop:数据库与hdfs的批量数据同步(导入导出)
13 | * canal:数据库与hdfs的实时数据同步(导入导出)
14 | * flume:日志导入导出hdfs,flume还可以实现级联,即一个flume输出可以作为另一个输入
15 | * kafka:埋点或爬虫采集的数据经过格式化转换后通过 Kafka 等消息队列导入给hdfs
--------------------------------------------------------------------------------
/元问题主要技术/数据/文件系统.md:
--------------------------------------------------------------------------------
1 | # 文件系统
2 |
3 | ## 文件系统的伸缩性
4 | * RAID 可以看作是一种垂直伸缩,在一台计算机集成更多的磁盘实现大数据存储,hdfs则是横向伸缩
5 |
6 | ## HDFS
7 | * namenode和datanode
8 | * hdfs的可用性
9 | * 磁盘冗余阵列rain,rain5使用奇偶校验(即异或运算)来恢复数据,失败节点太多就无法恢复了->跨机房备份,不同网络延迟多少->一个节点(物理机/虚机)装不下,采用分片分区,Redis、kafka、es的不同见[这里](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665516450&idx=1&sn=a6c7ec299a14a5fc85ea33ac9bc9cd6f&chksm=80d675e1b7a1fcf7f367a0186ec8caf2cac6ea0ef57ba9ed2c2d5a15ff227c7907b4a397f854&xtrack=1&scene=90&subscene=93&sessionid=1562289393&clicktime=1562289400&ascene=56&devicetype=android-27&version=2700043c&nettype=3gnet&abtest_cookie=BQABAAoACwASABMAFQAFACOXHgBWmR4AzpkeAPKZHgALmh4AAAA%3D&lang=zh_CN&pass_ticket=jj7cA9U%2BcUV5%2Bacw3nuvYMJ8smsVcqYHF%2BHqTfQuq%2FwNG%2BoSYibCq6cUqOcEpqpq&wx_header=1)
10 |
11 | ## 对象存储
12 | ## 块存储
13 |
--------------------------------------------------------------------------------
/元问题主要技术/数据/概览.md:
--------------------------------------------------------------------------------
1 | # 概览
2 |
3 | ## 数据技术全景图
4 | 
5 |
6 |
7 | ### 大数据相关技术
8 | 
9 |
10 | ```
11 | 大数据生态圈
12 | · HDFS =====> 解决存储问题
13 | · MapReduce =====> 批处理框架
14 | · Spark =====> 混合框架,能处理批处理和流计算
15 | · Flink =====> 混合框架,能处理批处理和流计算
16 | · Storm =====> 流计算框架
17 | · Yarn =====> 资源协调者
18 | · Zookeeper =====> 分布式应用程序协调服务
19 | · Flume =====> 日志收集系统
20 | · Hive =====> 基于Hadoop的数仓工具
21 | · HBase =====> 分布式、面向列的开源数据库
22 | · Sqoop =====> 数据传递工具
23 | · Scala =====> 多范式编程语言、面向对象和函数式编程的特性
24 | · Elasticsearch =====> 大数据分布式弹性搜索引擎
25 | ```
26 | * 在由很多台服务器组成的服务器集群中,某台服务器可能运行着 HDFS 的 DataNode 进程,负责 HDFS 的数据存储;同时也运行着 Yarn 的 NodeManager,负责计算资源的调度管理;而 MapReduce、Spark、Storm、Flink 这些批处理或者流处理大数据计算引擎则通过 Yarn 的调度,运行在 NodeManager 的容器(container)里面。至于 Hive、Spark SQL 这些运行在 MapReduce 或者 Spark 基础上的大数据仓库引擎,在经过自身的执行引擎将 SQL 语句解析成 MapReduce 或者 Spark 的执行计划以后,一样提交给 Yarn 去调度执行
27 | * 大数据技术方向(待改正):数据收集处理、推荐、实时计算、离线计算(基于数仓)、数据分析挖掘
28 | * 大数据lambda架构:大数据平台常规架构,各大公司使用各种大数据工具搭建了lambda架构的大数据平台后,还要考虑实现自己的作业调度系统,实现所有大数据任务的排队、执行时间管理、全局任务监控等
29 |
30 | ## 存储引擎的速度分级
31 | * L1 cache reference:0.5ns
32 | * Branch mispredict:5ns
33 | * L2 cache reference:7ns
34 | * Mutex lock/unlock:25ns
35 | * Main memory reference:100ns
36 | * Compress 1K bytes with Zippy:3 000ns
37 | * Send 2K bytes over 1Gbps network:20 000ns
38 | * Read 1MB sequentially from Memory:250 000ns
39 | * Round trip within same datacenter:500 000ns(0.5ms)
40 | * Disk seek:10 000 000ns(10ms)
41 | * Read 1MB sequentially from disk:20 000 000ns(20ms)
42 | * Send package CA->Netherlands->CA:150 000 000ns(150ms)
43 | * 对于15000转/分钟的磁盘,寻道时间大概在2-8毫秒之间。而旋转延时平均在2毫米左右
44 | * 我们知道千兆网卡的最大传输速率是每秒 125MB,这样的速率和 CPU 内存固然没法比,而虽然比单个磁盘快一些,但是服务器磁盘是 8 块磁盘组成的阵列,总的磁盘吞吐量依然碾压千兆网卡,因此网卡传输速率的瓶颈就成为整个系统的性能瓶颈
45 |
46 |
--------------------------------------------------------------------------------
/元问题主要技术/智能/智能.md:
--------------------------------------------------------------------------------
1 | # AI方向
2 | * 清华智班专业课课表
3 | * 机器学习
4 | * 深度学习
5 | * 计算机视觉
6 | * 数据挖掘
7 | * 自然语言处理
8 |
9 | # AI框架
10 | * Tensorflow
11 | * Theano
12 | * PaddlePaddle
13 | * Mahout:推荐
14 |
15 | # RPA
16 | * rpa-机器人流程自动化 Robotic Process Automation。模拟人类在计算机上的行为,主要将日常办公中的人工行为用机器自动化代替,这些行为可能横跨多个不同软件或网站,但rpa只是模拟人类操作,比如点击按钮、截屏等,无需对操作的软件或网站做任何改造。但并非所有工作都可以rpa,因为rpa也需要成本,所以一般选择重复性高且有固定规则的行为自动化。
17 | * 运用到的技术包括:键盘和鼠标的模拟操作技术,屏幕信息获取和定位的抓屏技术,流程控制处理的工作流引擎技术,以及自动化任务调动控制的管理台技术等
18 | * 业内龙头:AA、bp、uipath
19 | * 作用:模拟人类操作,比如点击鼠标;定时任务管理
20 | * 可视化拖拽编程
21 |
--------------------------------------------------------------------------------
/元问题主要技术/质量/全阶段测试.md:
--------------------------------------------------------------------------------
1 | # 全阶段测试
2 | ## 单元测试(unit testing)
3 | 指对软件中的最小可测试单元进行检查和验证。
4 |
5 | ## 集成测试(integration testing)
6 | 是单元测试的下一阶段,是指将通过测试的单元模块组装成系统或子系统,再进行测试,重点测试不同模块的接口部门。
7 |
8 | ## 冒烟测试(smoke testing)
9 | 指在对一个新版本进行大规模的测试之前,先验证一下软件的基本功能是否实现,是否具备可测性。
10 |
11 | ## 系统测试(system testing)
12 | 指的是将整个软件系统看做一个整体进行测试,包括对功能、性能、接口,以及软件所运行的软硬件环境进行测试。
13 |
14 | ## 验收测试(acceptance testing)
15 | 指的是在系统测试的后期,以用户测试为主,或有测试人员等质量保障人员共同参与的测试,它也是软件正式交给用户使用的最后一道工序。
16 |
17 | ## 随机测试(random testing)
18 | 指测试中所有的输入数据都是随机生成的,其目的是模拟用户的真实操作,并发现一些边缘性的错误。
--------------------------------------------------------------------------------
/元问题主要技术/质量/测试驱动开发.md:
--------------------------------------------------------------------------------
1 | # 测试驱动开发
2 | TDD、ATDD、BDD、RBE
3 |
--------------------------------------------------------------------------------
/元问题主要技术/质量/白盒、黑盒、灰盒测试.md:
--------------------------------------------------------------------------------
1 | # 白盒、黑盒、灰盒测试
2 | 按是否查看程序内部结构分类。
3 |
4 | ## 黑盒测试(black-box testing)
5 | 只关心输入和输出的结果。
6 |
7 | ## 白盒测试(white-box testing)
8 | 去研究里面的源代码和程序结构。
9 |
10 | ## 灰盒测试
11 | 是介于白盒测试与黑盒测试之间的一种测试,灰盒测试多用于集成测试阶段,不仅关注输出、输入的正确性,同时也关注程序内部的情况。灰盒测试不像白盒那样详细、完整,但又比黑盒测试更关注程序的内部逻辑,常常是通过一些表征性的现象、事件、标志来判断内部的运行状态。
--------------------------------------------------------------------------------
/元问题主要技术/质量/静态测试和动态测试.md:
--------------------------------------------------------------------------------
1 | # 静态测试和动态测试
2 | 按是否执行代码来分类。
3 |
4 | ## 静态测试(static testing)
5 | 指不实际运行被测软件,而只是静态地检查程序代码、界面或文档可能存在的错误的过程。
6 |
7 | ### 静态代码扫描
8 | * 代表:开源sonar、商业checkmarx(包括安全)
9 |
10 | ### 代码审计
11 | * codereview代表:Phabricator
12 |
13 | ## 动态测试(dynamic testing)
14 | 指实际运行被测程序,输入相应的测试数据,检查输出结果和预期结果是否相符的过程。
--------------------------------------------------------------------------------
/元问题主要技术/资源管理能力/DevOps与CICD.md:
--------------------------------------------------------------------------------
1 | # DevOps与CICD
2 |
3 | ## CICD
4 | * 持续集成:指在和向远程仓库 push 代码后,在这次提交合并入主分支前进行一系列测试,构建等流程。假设现在有个应用的代码存储在 gitlab 上,每天开发者都 push 很多次提交,针对每次 push,你可以创建一系列脚本进行自动测试,降低往应用里引入错误的概率。这就是持续集成,它可应用在包括开发分支在内的多个分支上。
5 | * 持续部署:在持续集成的基础上更进一步,指将推送指仓库默认分支的部署至产品环境。如果这部分需要手动触发,这就是一个持续交付(Continuous Delivery)环节。
6 | * 灰度发布(也称金丝雀发布,先将新版发布到少量节点,验证ok再全量发布全节点)、蓝绿发布(先把蓝流量断掉,只绿接流量,蓝发新版,测试验证OK切流量到蓝,绿断掉流量并发版,绿验证OK后重接流量,至此蓝绿都以新版本接流量)、AB test(发布不同版本到不同集群,以分析不同版本的效果)
7 | * 代表:Jenkins、容器
8 |
9 |
--------------------------------------------------------------------------------
/元问题主要技术/资源管理能力/Serverless无服务技术.md:
--------------------------------------------------------------------------------
1 | # Serverless无服务技术
2 |
3 | ## serverless
4 | * AWS lambda、knative、OpenWhisk、CSE
5 | * 研发效率 & 资源管理能力
6 | * 适用场景:
7 | * 应用负载有显著的波峰波谷
8 | * 基于事件的数据处理。数据中台是否可以用serverless来etl数据呢?
9 | 当前IaaS付费粒度还是时间维度的,serverless服务可以实现IaaS层的弹性扩容缩容,按按时间或调用次数计费等等。AWS lambda、[阿里CSE](https://yq.aliyun.com/articles/704496?spm=a2c4e.11155472.0.0.58b46ebe2Y17b8)
10 | * CSE可以在存量系统上实现serverless,无需重构。内部调用流程是A调用B,如果B资源不足(线程池满或触发限流阈值)则要求总控系统快速新建实例B',然后总控系统将B‘地址发给A,A去调用B’。如果是外部调用发现服务资源不足呢?
11 | * serverless关注服务启动时间,CSE将服务分为3级L1~L3,L1采用预先启动,L2磁盘快照,L3磁盘镜像冷启动
12 |
--------------------------------------------------------------------------------
/元问题主要技术/资源管理能力/云计算之IaaS与虚拟化技术.md:
--------------------------------------------------------------------------------
1 | # 云计算之IaaS与虚拟化技术
2 |
3 | ## 云计算基础设施框架
4 | * OpenStack、CloudStack、OpenNebula
5 |
6 | ## 虚拟化技术
7 | * Xen、VMWare、KVM
8 |
9 |
--------------------------------------------------------------------------------
/元问题主要技术/资源管理能力/云计算之PaaS与容器.md:
--------------------------------------------------------------------------------
1 | # 云计算之PaaS与容器
2 | 将容器技术划到PaaS并不十分准确,实际上,容器技术给IaaS与PaaS搭了一个桥,使两者能够相互打通??
3 |
4 | ## 容器
5 | ### Docker
6 | 包括了伸缩性、可用性
7 | * docker核心能力:打包(将应用及其依赖的整个OS文件系统打包,应用在容器内与本地真正做到一致,开发人员无需处理任何环境迁移问题
8 |
9 | ## 容器编排
10 | ### Kubernetes
11 | 包括了伸缩性、可用性
12 | * k8s资源管理能力
13 | * 资源分类:分为可压缩和不可压缩
14 | * 资源使用设置和限制:limit和request区别,设置每个容器的资源限制
15 | * 资源优先级:用以在资源紧张时谁被优先回收,QoS和pod里的等级将资源分了等级
16 | * 资源使用的读取:通过cgroup
17 | * k8s默认调度器
18 | * queue阶段:每个资源请求pod将排队到一个统一queue里,调度器循环并发取出pod
19 | * predicate阶段:从schedule cache里取出所有可用node信息
20 | * priority阶段:给node打分,最适合pod的node会被打最高分
21 | * bind阶段:pod被部署到最高分node里启动运行
--------------------------------------------------------------------------------
/元问题主要技术/资源管理能力/云计算之SaaS与云原生.md:
--------------------------------------------------------------------------------
1 | # 云计算之SaaS与云原生
2 |
3 | ## 云原生
4 | * CNCF(云原生计算基金会)认为云原生系统需包含的属性:
5 | * 容器化封装:以容器为基础,提高整体开发水平,形成代码和组件重用,简化云原生应用程序的维护。在容器中运行应用程序和进程,并作为应用程序部署的独立单元,实现高水平资源隔离
6 | * 自动化管理:统一调度和管理中心,从根本上提高系统和资源利用率,同时降低运维成本
7 | * 面向微服务:通过松耦合方式,提升应用程序的整体敏捷性和可维护性。
8 | * pivotal对云原生的最新定义,包括四要素:持续交付、DevOps、微服务、容器
9 | 
10 |
11 |
--------------------------------------------------------------------------------
/元问题主要技术/资源管理能力/服务治理.md:
--------------------------------------------------------------------------------
1 | # 服务治理
2 |
3 | ## 对模块的管理:服务治理框架
4 | * dubbo框架
5 | * 服务监视:依赖链路、错误率、时延、容量、APM、故障定位
6 | * 服务控制:上下架服务、故障处理、服务路由、限流降级、授权、发布
7 |
8 | ## 对任务的管理:分布式调度中心
9 | * 代表:quartz、xxl-job
10 | * 关键技术:数据分片、分布式处理、任务编排、监控管理
11 |
12 | ## 服务监控&APM(应用性能管理)
13 | * 美团点评CAT、zabbix。监控的设计思路和目标??
14 | * Spring Cloud Sleuth、zipkin(还有可用性)
15 | * ganlia
16 | * pingpoint vs skywalking
17 |
18 |
--------------------------------------------------------------------------------
/元问题定义和指标/定义和指标.md:
--------------------------------------------------------------------------------
1 | # 定义和指标
2 |
3 | 下面介绍各个元问题的定义和指标。指标非常重要,而且最好是可量化的,它既能辅助我们更好理解元问题含义,又能帮助我们判断系统是否存在某些元问题以及这些元问题是否得到了解决。但注意一点,指标中所标识的越大越好或越小越好,仅指同一个指标下的相对较好,不考虑其他关联因素,比如CPU利用率并非越小越好,过小反倒导致资源浪费。因此,每个元问题不能只通过单一指标来评估,而应该结合尽可能多的指标来评估。
4 |
5 | - [开发效率](#开发效率)
6 | - [资源管理能力](#资源管理能力)
7 | - [可用性](#可用性)
8 | - [伸缩性](#伸缩性)
9 | - [性能](#性能)
10 | - [安全](#安全)
11 | - [质量](#质量)
12 | - [数据](#数据)
13 | - [智能](#智能)
14 |
15 | ## 开发效率
16 | #### 定义
17 | 开发效率指开发完成需求的速度。我们常见的软件复用性、可移植性、可理解性等问题,归根结底都是要解决开发效率问题。
18 | #### 指标
19 | * 交付频率:单位时间内可完成开发交付的需求数量。可对老系统和新系统分开统计。该指标越大越好。
20 | * 交付周期:单个需求完成开发交付的平均时长。可对老系统和新系统分开统计。该指标越小越好。
21 |
22 | > 注:一个需求可以是一个接口、一个页面、一个脚本等
23 |
24 | ## 资源管理能力
25 | #### 定义
26 | 资源管理能力指对资源(软件及其使用的硬件)进行部署、监控、调配、备份、采集、存取、展示、故障处理等管理的能力。解决此问题要靠提升管理能力,具体就是减少管理成本和提升管理全面性,详见指标。云计算、运维人员、DevOps尤其关注此问题。
27 | #### 指标
28 | * 管理成本:实施管理的平均成本/资源规模。该指标越小越好。
29 | * 管理全面性:可管理的资源种类数、可支持的管理类型数。该指标越大越好。
30 |
31 | > 注:成本包括人力、软硬件、电力、带宽、培训等
32 |
33 | ## 可用性
34 | #### 定义
35 | 可用性也称稳定性,是指系统开始运行之后,不发生故障(能正常提供服务)的概率和能力。故障包括但不限于:
36 | * 在指定时间内客户端未收到服务端的返回,可能是由网络抖动、服务器宕掉等原因导致
37 | * HTTP状态码为非200
38 | * 服务端返回值里自定义的错误码为异常状态
39 | #### 指标
40 | * 可用性:`MTBF/(MTBF+MTTR)`。该指标一般表现为百分之N个9,一般系统达到5个9(99.999%)可算是HA(High Availability),达到6个9相当难,意味着一年内系统仅30秒不可用。该指标越大越好。
41 | * 故障平均间隔时间(MTBF-Mean Time Between Failure):相邻两次故障之间的平均时长,可看成系统可用的平均时长。
42 | * 故障平均修复时间(MTBF-Mean Time To Repair):故障出现到修复的平均时长,可看成系统不可用的平均时长。
43 |
44 | ## 伸缩性
45 | #### 定义
46 | 伸缩性指系统(尤其分布式系统)在增加或减少硬件资源时能够继续服务的能力。
47 | #### 指标
48 | * 生产变化率:`φ(k1,k2)=F(k1)/F(k2)`,表示系统规模从k1变为k2后生产率的变化。如果φ(k1,k2)<1(或>1),则φ(k2,k1)>1(或<1),这意味着系统在规模扩容变大(或缩容变小)后可以使生产率变高,但规模变小(或变大)后生产率将变低。现实中规模忽大忽小是常态,一个高伸缩性系统应该在任意规模下保持稳定的生产率,因此,该指标越接近1越好。
49 | * 生产率`F(k)=吞吐量*单任务平均处理的业务量/成本`,表示系统规模为k时单位时间内系统处理请求的投产比。
50 | * 系统规模k指节点数、代码量、流量、数据量、系统数量等。
51 |
52 | > 注:成本包括人力、软硬件、电力、带宽、培训等
53 |
54 | ## 性能
55 | #### 定义
56 | 性能指系统的运行速度和消耗资源量。
57 | #### 指标
58 | * 响应时间(RT-Response Time):也可以称为延迟,指执行一次任务平均花费的时长。该指标越小越好。
59 | * 吞吐量:单位时间可以完成的任务数,包括了每秒事务数(TPS)、每秒HTTP请求数(HPS)、每秒查询数(QPS)。该指标越大越好。
60 | * 并发数:系统在同一时间内能够处理的任务数。有时并发数指单位时间内总请求数,所以并发数并非公认的性能指标。该指标越大越好。
61 | * 性能计数器:以下子指标大多标识了越小越好,但过小有时意味着资源浪费,需要根据经验估算一个平衡值。
62 | * 系统负载(System Load):当前正在或等待被CPU执行的进程总数,反映系统忙闲程度。该指标越接近CPU核数越好。
63 | * 线程数:该指标越小越好。
64 | * 进程数:该指标越小越好。
65 | * 句柄数:该指标越小越好。
66 | * CPU使用率:按经验来算,最好在请求高峰期时小于75%,系统算是比较健康。该指标越小越好。
67 | * 内存使用率:该指标越小越好。
68 | * 网络IO:该指标越小越好。
69 | * 磁盘IO:该指标越小越好。
70 |
71 | > 注:一个任务可以是一次网络请求、一次接口调用、一个事务、一次定时跑批等
72 |
73 | > 注:响应时间和吞吐量经常被误解是直接关系,这是因为我们经常观察到,响应时间越小,吞吐量越大。然而,两者并非直接的正反关系。实际上,随着并发请求数不断增加,吞吐量刚开始呈现线性增加,到达某个峰值后随之下降直到系统完全不可用,而响应时间一开始会保持不变,然后慢慢升高直到系统完全不可用。在并发请求数不高时,我们可以通过增加线程数提高吞吐量,而响应时间可能不变,也可以通过优化系统降低响应时间,同时吞吐量不变。有个形象的[高速公路例子](https://blog.csdn.net/dahuzix/article/details/78636370)可以帮助我们更好地理解
74 |
75 | ## 安全
76 | #### 定义
77 | 安全指系统的漏洞问题,由漏洞密度、合规密度等若干指标组成。漏洞指的是软件中存在能被攻击者利用来实施窃取信息、瘫痪系统等攻击的地方。合规指的是软件在国家法律或业内规范允许范围内运行和处理数据,比如未经用户允许不得采集用户敏感数据、对敏感数据加密存储等。
78 | #### 指标
79 | * 漏洞密度:漏洞数量/系统规模。该指标越小越好。
80 | * 合规密度:合规系统/系统规模,或者合规数据/数据规模。该指标越大越好。
81 | * 漏洞频率
82 | * 单位时间内新增、现存的漏洞数。该指标越小越好。
83 | * 单位时间内修复的漏洞数。该指标越大越好。
84 | * 漏洞周期/暴露窗口期:漏洞从发现、诊断到修复的平均时长。该指标越小越好。
85 | * 各等级漏洞分布:高危等级漏洞占比越小越好。
86 | * 监测成本:完成单个系统安全监测的平均时长。该指标越小越好。
87 | * 覆盖率:经过安全监测的系统数/总系统数。该指标越大越好。
88 | * 攻击持续时间:攻击者驻留在系统所在网络中的平均时长。该指标越小越好。
89 |
90 | > 注:系统规模指节点数、代码量、流量、数据量、系统数量等
91 |
92 | ## 质量
93 | #### 定义
94 | 质量指系统的缺陷问题,由缺陷密度、缺陷频率等若干指标组成。缺陷和漏洞很容易混淆,缺陷是软件功能未实现、实现不完整、实现不正确的地方。测试人员尤其关注此问题。
95 | #### 指标
96 | * 缺陷密度:缺陷数量/需求规模。该指标越小越好。
97 | * 缺陷频率
98 | * 单位时间内新增、现存的缺陷数。该指标越小越好。
99 | * 单位时间内修复的缺陷数。该指标越大越好。
100 | * 缺陷周期:缺陷从创建、诊断到修复的平均时长,反映了系统的可维护性。该指标越小越好。
101 | * 各等级缺陷分布:严重等级缺陷占比越小越好。
102 | * 测试成本:完成单个需求测试的平均时长。该指标越小越好。
103 | * 覆盖率:经过测试的需求数/总需求数。该指标越大越好。
104 |
105 | > 注:一个需求可以是一个接口、一个页面、一个脚本等
106 |
107 | ## 数据
108 | #### 定义
109 | 数据指数据在存取和分析过程中的效率、管理能力、可用性&一致性、伸缩性、性能、安全、质量问题。
110 | #### 指标
111 | * 效率
112 | * 接入效率:程序接入数据引擎的时长。该指标越小越好。
113 | * 扩展性:在一条数据或一个数据集上增加或删除某个字段数据的平均时长。该指标越小越好。
114 | * 管理能力
115 | * 管理成本:对数据进行管理的成本/数据规模。管理包括部署、监控、调配、备份、采集、存取、展示、故障处理等。该指标越小越好。
116 | * 数据全面性:可管理的数据种类数。该指标越大越好。
117 | * 可用性&一致性
118 | * 可用性:数据可在规定时间内进行读写的总时长/系统运行总时长。该指标越大越好。
119 | * 一致性:读操作可以读到更新操作提交的数据的平均时长。该指标越小越好。
120 | * 伸缩性
121 | * 生产变化率:`φ(k1,k2)=F(k1)/F(k2)`,表示数据规模由k1变为k2后生产率的变化。该指标越接近1越好。
122 | * 生产率`F(k)=单位时间内数据最大处理量/成本`,表示系统规模为k时单位时间内系统处理请求的投产比。
123 | * 性能
124 | * QPS:每秒能够完成的查询数据量。该指标越大越好。
125 | * TPS:每秒能够完成的增删改数据量。该指标越大越好。
126 | * 连接数:单位时间内所有线程数,包括活跃线程数和非活跃线程数。该指标越小越好。
127 | * 慢查询:单位时间内执行时间超过阈值(比如10s)的所有语句的平均执行时长或数目。该指标越小越好。
128 | * 资源负载:CPU、内存、网络IO、磁盘IO等硬件资源使用率。该指标越小越好。
129 | * 安全
130 | * 数据泄露率:单位时间内被泄露的数据量/全量数据。该指标越小越好。
131 | * 监测成本:完成单条数据安全监测的平均时长。该指标越小越好。
132 | * 覆盖率:经过安全监测的数据量/全量数据。该指标越大越好。
133 | * 质量
134 | * 数据损失率:单位时间内产生的问题数据与全量数据的比例。该指标越小越好。
135 | * 测试成本:完成单条数据正确性测试的平均时长。该指标越小越好。
136 | * 覆盖率:经过正确性测试的数据量/全量数据。该指标越大越好。
137 |
138 | > 注:成本包括人力、软硬件、电力、带宽、培训等
139 |
140 | ## 智能
141 | #### 定义
142 | 智能特指当前软件人工智能问题,其指标主要对标机器学习的模型评估标准。当前机器学习主要关注在分类、回归、聚类三个子问题上,所以指标也以这三个问题来划分。
143 | #### 指标
144 | * 分类
145 | * 混淆矩阵:T(True)代表预测正确,F(False)代表预测失败,P(Positive)代表预测为正样本,N(Negative)代表预测为负样本
146 | * TP:实际为正样本预测也为正样本的概率。该指标越大越好。
147 | * FP:实际为负样本但预测为正样本的概率,属于误报。该指标越小越好。
148 | * FN:实际为正样本但预测为负样本的概率,属于漏报。该指标越小越好。
149 | * TN:实际为负样本预测也为负样本的概率。该指标越大越好。
150 | * 准确率/正确率(Accuracy):所有预测正确的样本/所有样本=`(TP+TN)/(TP+FP+FN+TN)`。该指标越大越好。
151 | * 精确率/查准率(Precision):将正样本预测正确的样本/所有预测为正样本的样本 = `TP/(TP+FP)`。该指标越大越好。
152 | * 召回率/查全率(Recall):所有将正预测正确的样本/所有为正的样本 = `TP/(TP+FN)`。该指标越大越好。
153 | * F1值(F1-measure):`2*精确率*召回率/(正确率+召回率)`,等于精确率和召回率的调和平均值。该指标越大越好。
154 | * ROC(Receiver Operating Characteristic) + AUC(Area Under Curve)
155 | * TPR=所有将正预测正确的样本/所有为正的样本=`TP/(TP+FN)`,与召回率一样。该指标越大越好。
156 | * FPR=所有将负预测为正的样本/所有为负的样本=`FP/(FP+TN)`。该指标越小越好。
157 | * ROC曲线:二值分类器中根据一个阈值来划分样本是正还是负,选不同阈值可得到不同(TPR,FPR)点对,以TPR作为y轴,以FPR作为x轴,两者都取值在`[0,1]`,(TPR,FPR)点对组成的曲线即为ROC曲线。TPR越大越好,FPR越小越好,所以该指标越陡峭越好。
158 | * AUC:ROC曲线下的面积。由于TPR和FPR均取值在`[0,1]`,所以AUC取值在`[0.5,1]`,因为如果小于0.5,则全部取反就会大于0.5。该指标越大越好。
159 | * 其他
160 | * 对数损失/逻辑回归损失(Logistic regression loss)/交叉熵损失(Cross-entropy loss)
161 | * 铰链损失
162 | * kappa系数
163 | * 海明距离
164 | * 杰卡德相似系数
165 |
166 | * 回归(拟合)
167 | * 平均误差:如果异常点仅当成受损数据,则选择MAE;如果异常点很重要,必须被检测出来,则选择MSE;RMSE是在MSE基础上,让误差结果和真实值/预测值处于同一个数量级,使模型效果的描述性更好。
168 | * MAE(Mean Absolute Error,平均绝对误差)/L1损失:∑(|真实值-预测值|)/样本量。该指标越小越好。
169 | * MSE(Mean Squared Error,均方误差)/L2损失:∑((真实值-预测值)^2)/样本量。该指标越小越好。
170 | * RMSE(Root Mean Squared Error,均方根误差):MSE的二次平方根=√MSE。该指标越小越好。
171 | * 拟合优度(Goodness of Fit)/决定系数/R-square(R^2):`1-SSE/SST`。取值在`[0,1]`,越接近1说明回归直线对真实值的拟合程度越好,当预测值等于真实值,则完全拟合,SSE等于0,R^2=1。该指标越大越好
172 | * SSE(Error Sum of Squares,残差平方和):`∑((真实值-预测值)^2)`。
173 | * SSR(Regression Sum of Squares,回归平方和):`∑((预测值-真实值平均值)^2)`。
174 | * SST(Total Sum of Squares,总平方和):SSE+SSR=`∑((真实值-真实值平均值)^2)`。
175 | * 其他
176 | * 解释变异(Explained variance)
177 |
178 | * 聚类
179 | * 兰德指数(RI-Rand Index):`a+d/a+b+c+d`,其中,假设U和V分别是预测和真实的聚类分布空间,a表示在U和V中都属于同一分类的样本对数,b表示在U和V中都不属于同一分类的样本对数,c表示在U中属于同一分类但在V中不属于同一分类的样本对数,d表示在U中不属于同一分类但在V中属于同一分类的样本对数。取值在`[0,1]`。该指标越大越好。
180 | * 调整兰德系数(ARI-Adjusted Rand index):对于任意随机算法,RI无法保证趋近于0,即任意一种算法都可能比聚类模型得到更好的评估结果,ARI可解决此问题。
181 | * 轮廓系数(Silhouette coefficient):`∑(S(i)^2)/样本量`。该指标越大越好。
182 | * `S(i)=(b−a)/max(a,b)`,表示样本i的轮廓系数,其中a是该样本与其同分类中其他样本的平均距离,b是该样本与其距离最近的其他分类中样本的平均距离。S(i)取值在`[−1,1]`,同分类样本越距离相近且不同分类样本距离越远(即a越小b越大),分数越高。
183 | * 其他
184 | * 互信息(Mutual Information)
185 |
--------------------------------------------------------------------------------
/特定领域技术/金融.md:
--------------------------------------------------------------------------------
1 | # 金融
2 |
3 | 金融业包括了银行、保险、证券、信托、基金等几大分支,下面主要以银行为主来介绍。
4 |
5 | ## 领域系统特点
6 |
* 金融业务相关
7 | * 金融业务繁杂:由于金融业务门槛相对高,对技术人员的业务要求也远高于其他领域。以银行为例,主要包括了资产业务(贷款)、负债业务(存款)、中间业务(银行卡、外币结算、代理等)、客户(ECIF)、支付清算(人行大小额/超网/网联)、前置等,账务遵循会计原则(会计日、科目分录、借贷复式记账、权责发生制等),还要理解联机交易和跑批交易区别,等等
8 | * 交易检查:一笔交易往往要经过多道金融属性检查,包括渠道、交易类型、限额、账号状态、黑白名单、账户类型、币种等
9 | * 交易确定周期长:一笔交易要经过多个不同系统,数据在这些系统中得到保存和交叉检验,这个周期较长,如果采取同步方式则极易引发超时,所以往往采取异步方式处理,并通过轮询接口来查证,交易失败启动补偿机制,最后通过对账最终确定
* 交易可追溯:每笔交易产生的影响都要记录,不能仅记录最终结果,比如对于账户余额,除了记录各个账户当前余额外,还要用余额历史表记录每一次余额的变化。如果要避免交易记录丢失,还可以通过消息队列来缓存。如果存入区块链,则天然具备可追溯性
* 流程控制:对数据的准确性、合规性、安全可控有更高要求,为此需要设计大量的管理后台操作流程,方便对银行各类资源进行管理。管理基于权限实现,而权限一般基于`RBAC(Role-Based Access Control)`来设计
* 新技术接受度:由于对系统稳定性的高要求,加上业务形态变化并不快,所以金融领域对新技术的接受度谨慎且缓慢。但同时看到,得益于数据量爆发和新一代金融科技公司的崛起,区块链等新技术纷纷在金融领域率先落地
* 开发效率
10 | * 高扩展性:主要针对IT技术服务提供商和金融科技公司,他们往往为多个银行提供一套比较通用的解决方案,所以要寻找不同银行在技术要求上的共性和差异,为共性打造复用空间,为差异预留扩展空间
* 支持SaaS或本地化部署
* 支持单机构独立部署或多机构混布
11 | * 接入和接出不同关联方:这部分工作繁琐耗时,往往占到整个开发周期的30%以上,为了提高效率,要研发一套适配系统,实现对不同关联方协议、参数转化、数据格式的适配
12 | * 适配不同主流数据库
13 | * 资源管理能力
14 | * 定时任务多:由于交易确定周期长,需要加入不少异步设计,往往相应地引入定时任务。一般需要通过分布式调度中心实现对大规模定时任务的管理
15 | * 数据
16 | * 数据强一致性:在支付、赎回、跑批等核心交易场景下,CAP原则中更偏向于C,为此要用到分布式锁或分布式事务(TCC/FMT/XA模型)等同步机制
17 | * 安全
18 | * 应用安全:加密加签、防重放、防重复交易、敏感字段加密脱敏、防DDoS/CSRF等常规攻击、身份认证/授权/审计
19 | * 数据安全:数据库/Log敏感字段加密脱敏、密码硬件加密、文件加密存储、不同机构数据物理隔离、反欺诈、操作留痕记录
20 | * 网络安全:网络分区(DMZ/SF/PTR)、专线传输
* 风险控制:风控实现既定的损失目标下的收益最大化,用于多种金融业务。以贷款为例,风控模型公式要实现`LOSS<=目标`,其中`LOSS=∑ PDi*EADi*LGDi`,PD(Probability of Default)是违约概率、EAD(Exposure at Default)是违约风险敞口、LGD(Loss Given Default)是违约损失率,这些参数由欺诈管理、还款能力、还款意愿、还款能力和意愿的稳定性、贷后管理等数据计算所得
21 | * 强监管:面向监管机构(一行两会/外管局/财政部/本地监管部门),提供投诉通道、监视能力;面向被监管机构,提供自查能力。一般在跑批交易中将关键数据写入文件,方便监管使用
* 多以文件形式批量同步数据:由于不同关联系统间的差异,数据同步难以实时完成,一般采用文件形式将一段时间内的数据进行定时传输同步
22 |
23 | ## 非传统银行企业的金融科技服务
24 | ### 传统银行组建的信科企业
25 | 有非常完整的银行业务系统,向上能够对接线上(手机银行等)、线下(柜面等)、第三方(人行系统等)全渠道,向下能够对接多家金融机构。
26 |
27 | 与银行传统系统在架构上差别不大,对接成本相对低。技术上,鉴于金融系统高稳定高安全标准,改造老系统成本很大,因此对开源技术和新技术标准较保守:去IOE不彻底;采用虚拟化技术,一般不采用容器;兼容jsp,有大量的XML。
28 |
29 | ### 金融领域IT技术服务提供商/技术外包商
30 | 这类公司包括神州数码、文思海辉、恒生电子、长亮科技、东软、汉得等,在银行金融业深耕多年,有大量的银行客户,有非常完整的银行核心业务系统。
31 |
32 | 技术上紧随市场,拥抱开源技术,关键技术和对应的元问题:服务化(研发效率)、分布式(可用性)、异步(性能)、缓存(性能)、流控(可用性)、集群(可用性)、虚拟化(资源使用效率)、海量数据处理(基于RMDB处理结构化数据)、大数据处理(基于NoSQL处理非结构化数据)。
33 |
34 | 出于成本和场景的限制,他们使用的都是非常成熟的开源技术,几乎不使用新技术,也不对开源技术做定制化修改。好处是成本小,隐患是无技术壁垒、难以快速响应场景或业务的变化(新技术面向的都是可期的未来场景或业务),导致可替代性高。
35 |
36 | 不同公司的经营策略有所不同:
37 |
38 | * 系统集成:将国内外的各类银行产品为客户搭建起来,并集成到银行老系统上。通过系统集成,营收涨得快,但是毛利不高。这里的代表是神州数码
39 | * 软件开发:这是大部分属于劳动密集型工作,主要针对不同客户的需求做定制化开发,很难输出一套标准。这里主要是东软、中软的地盘
40 | * 代理实施:基于国外产品进行包装和实施。汉得属于这一种
41 |
42 | ### 数据服务商
43 | 数据服务商主要解决传统银行的数据元问题。传统银行系统诞生于DT数据时代之前,大多不具备对数据的处理能力,需要依靠数据服务商完成对行内数据的存储、处理、挖掘。
44 |
45 | TalkingData是代表企业之一,提供了包括统计系统和数据营销系统在内的一系列数据平台:
46 |
47 | * 数据统计系统:定义了两个概念,一个是维度(一个维度是一个任意若干个字段组合),一个是度量(包括各种类SQL的计算类型:count、sum、group by等)。这样的设计十分友好,无需技术人员,产品团队就可以用来做数据统计,他们只需配置各种数据维度并指定合适的度量计算,就能得到常用的数据统计结果(比如uv、pv等)
48 | * 数据营销系统(获客):基于标签的预测,根据个人信息、交易行为、出行、购物等历史数据,为每个用户打标签,以标签集合作为特征向量训练出一套标签关联度模型,用以预测用户未来在符合某些标签时的行为
49 |
50 | ### 互联网银行
51 | 互联网银行是一种新型的银行,一般不设置线下网点,所有业务全部搬到线上。
52 |
53 | 微众银行是其代表之一,基于大量的腾讯技术完成新一代面向互联网的分布式银行系统,其主要解决的元问题包括:
54 |
55 | * 伸缩性:以客户为单位的可控分布,一个客户的所有信息都在同一个数据节点上(这里的节点是逻辑部署单位,并非物理机器,每个节点都有备节点),设计一个GNS(全局命名服务)为每个新客户分配节点。随着客户数增加,采用scale out增加数据节点,随着交易频率上升单个客户数据增加,采用scale up增加单个节点资源
56 | * 可用性:不同客群分布在不同数据节点上,某个节点故障只会影响到该节点上的客户,不会影响其他客户,实现了非常高的可用性
57 | * 数据:基于TDSQL,一套腾讯自研的基于mysql的分布式数据库
58 | * 安全:WeBank信息安全体系,包括应用安全、网络安全、数据安全、主机安全
59 |
60 | ### 金融科技公司
61 | 这类公司主营互联网金融业务,为传统银行提供流量渠道。中小型金融科技公司包括宜人贷、点融等,大型金融科技公司包括蚂蚁金服、京东数科、平安陆金所等。
62 |
63 | 这类公司在开源技术上相对开放很多,不光大量采用开源技术和二次开发开源技术,大型金融科技公司甚至输出不少自研开源技术。整体而言,这类公司系统更关注开发效率、数据、安全、可用性四个元问题。
64 |
65 |
--------------------------------------------------------------------------------