0%

目标

  • 训练性能
  • 资源占用
  • 模型效果

baseline

训练性能

  • IO:缓存,pipeline,并行处理,数据存储方式(稀疏化存储)
  • 计算:超参调整(batch-size),优化器选择,梯度更新方式,稀疏表示运算,线程数量调整(线程太多,切换代价高)

资源占用

  • 流式读取训练数据

  • buffer size

  • 优化器

  • 模型编写方式

模型效果

  • 模型结构
  • 优化器
  • 超参

分布式

阿姆达尔定律评估加速上限,提供理论指导。

尽量提高计算访存比。

训练性能

  • 分布式模式选择:AllReduce vs PS

  • 数据切分方式:文件名切分

  • 梯度合并方式:提高计算/通信比。提高并发,降低延迟

  • PS模式下,大tensor切分

  • Horovod模式下,梯度融合传递

  • 调度策略(bin-pack, spread)

模型效果

  • 超参调整:lr

高级话题

  • 特征动态增删(Dynamic embedding)
  • 实时训练
  • 可伸缩分布式训练(elasticDL,FTlib)

梯度下降思想

Gradient Descent 的核心思想非常的简单。针对一个模型,我们最终希望它的(非负的)损失函数最小:𝜽 = argmin J(𝜽)

根据 Gradient Descent on Wikipedia,GD 秉持一个最朴素的思想:J'(𝜽) 指向哪里,沿着它的反方向 -J'(𝜽) 走(即 𝜽_next = 𝜽_now - J'(𝜽)),就能让 J(𝜽) 降低。

梯度下降公式:

1
2
3
𝜽_next = 𝜽_now - 𝜺*J'(𝜽)

𝜺为学习率。

BGD

每次计算整个训练集损失函数的均值,然后求导进行更新。

由于这种方法是在一次更新中,就对整个数据集计算梯度。

存在两个问题:

  • 计算速度慢
  • 计算占用内存大

所以几乎无法投入实际使用

SGD

每次对单个样本计算损失函数和梯度,然后进行更新。

存在问题:

  • 存在较大的噪声,损失函数存在较大的震荡。

BGD可能收敛到局部最优点,SGD震荡可以跳出局部最优点,到达更好的最优点。

Mini-batch SGD

结合BGD和SGD的特点,每次利用训练集的一部分样本更新模型。

存在问题:

但是每次使用小部分数据更新模型,这小部分数据的梯度与整体数据的梯度可能存在偏差,还是会存在震荡。

Momentum

单批样本更新存在偏差,那么就不要全速更新,一定程度上保留之前的梯度。颇有引入惯性定理的味道。

1
2
3
4
v_t+1 = 𝝁v_t - 𝜺J'(𝜽_t)
𝜽_t+1 = 𝜽_t + v_t+1

𝜺 是学习率,𝝁 则为一个取值 [0,1] 的动量系数。

Nesterov Momentum

Momentum算法的改进版

1
2
v_t+1 = 𝝁v_t - 𝜺J'(𝜽_t + 𝝁v_t)
𝜽_t+1 = 𝜽_t + v_t+1

区别在于,将梯度计算放在施加当前速度之后。相当于计算梯度时,按照全局趋势先走半步,进一步减小偏差。

以上算法在各个维度上的下降速度是一致的。然而在实际情况中,损失函数对不同的维度的敏感度是不一样的,因此对不同维度设置不同的学习率是一种比较好的方式。以下自适应学习率的方法即是这种思想的体现。

Delta-bar-delta

基于一种简单的思想:

  1. 如果过去两次更新的梯度符号相同,代表方向正确,增大更新时的步长
  2. 如果过去两次更新的梯度符号相反,代表方向不定,缩小更新时的步长

这种方法只能用在全批量优化中,在 Mini-Batch 上却不好。每个 Mini-Batch 之间的差别会使得每一次更新本身的步长就会有很大的差异,这一点和全量数据更新时有比较明显的差异。

Adagrad

累计所有参数的历史梯度平方和,参数学习率与梯度平方和2次方根成反比。达到的效果是:

  • 在陡坡,学习率变小

  • 在平地,学习率变大

1
2
3
r = r + g^2

𝜽_t+1 = 𝜽_t - e/(o + r)^0.5 * g

存在问题:

  • 学习率过度减小

适合凸函数优化

  • 优化器需要要对每个参数保存一个中间值,占用内存参数的两倍。

RMSProp

Hinton改进Adagrad算法适用于非凸优化场景。

加入指数衰减,丢弃遥远过去的历史:

1
2
3
r = p*r + (1-p)g^2

𝜽_t+1 = 𝜽_t - e/(o + r)^0.5 * g

这种算法被证明是深度学习模型中非常有效的一种方法。

  • 优化器需要要对每个参数保存一个中间值,占用内存参数的两倍。

Adadelta

1
2
3
4
5
6
r = p*r + (1-p)g^2

对梯度下降的速度也做一个估计,用来当作学习率
e = p*e + (1-p)𝜽^2

𝜽_t+1 = 𝜽_t - e^0.5/(o + r)^0.5 * g

不用设定学习率。

  • 优化器需要要对每个参数保存2个中间值,占用内存参数的三倍。

Adam

集合RMSProp和动量算法

1
2
3
4
5
6
7
8
9
10
11
m_t = 𝜷_1*m_t-1 + (1 - 𝜷_1)*J'(𝜽)

v_t = 𝜷_2*v_t-1 + (1 - 𝜷_2)*J'(𝜽)^2

对期望和方差做修正:
m_hat_t = m_t / (1 - 𝜷_1^t)
v_hat_t = v_t / (1 - 𝜷_2^t)

𝜽_w,t = 𝜽_w,t-1 - 𝜂*m_hat_t/(sqrt(v_hat_t) + 𝜺)

𝜺 是一个用以保证数值稳定的一个很小的值。
  • 优化器需要要对每个参数保存2个中间值,占用内存参数的三倍。

优化器效果对比

如果数据是稀疏的,就用自适用方法,即 Adagrad, Adadelta, RMSprop, Adam。

RMSprop, Adadelta, Adam 在很多情况下的效果是相似的。

Adam 就是在 RMSprop 的基础上加了 bias-correction 和 momentum,

随着梯度变的稀疏,Adam 比 RMSprop 效果会好。

整体来讲,Adam 是最好的选择

很多论文里都会用 SGD,没有 momentum 等。SGD 虽然能达到极小值,但是比其它算法用的时间长,而且可能会被困在鞍点

如果需要更快的收敛,或者是训练更深更复杂的神经网络,需要用一种自适应的算法。

原文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
举一个我面试中遇到过的目前最牛的例子,简称M。我一般负责和人吃饭闲聊。这位先生以前在Intel,只大概看过机器学习的一些东西。
我:目前深度学习当中用mapreduce的比较少,因为我们经常要SGD,
M:哦我猜一下,所以你们用MPI,然后你要优化Allreduce。
我:。。。对的,然后很多时候网络会有瓶颈,
M:恩,因为你们不想上infiniband。我:。。。对的,
M:然后你们 网络的 吞吐速度是够的,但是延迟不理想。
我:。。。对的,
M:所以你们想要有异步通信,但是同时又要控制 模型不发散。
我:。。。对的。
我:然后我们的模型在部署的时候希望尽量高速,一个方法是 找稀疏解,
M:但是你们发现稀疏性不够,目前的运行库需要1-5%的稀疏性才行。
我:。。。对的,
M:但是你们估计最多只有30%上下的稀疏性,否则模型质量就下降。
我:对的。但是还是有一些办法,因为部署的时候模型是固定的,
M:所以可以做即时编译然后内联参数。
我:。。。对的。

所以面试的加分项就是面试官还没问问题,你就先回答完了。装b结束了,说说我大概想表达的意思吧:机器学习里面了解怎么推公式,怎么解释算法等等,都是停留在技术的层面上。技术牛肯定是有加分的,但是如果要找一位算法/系统架构师,更容易加分的是了解这些技术背后真正解决的问题,在出现 需求的时候能很快解释背后的原理,然后找到解决问题的思路。这也是为啥我在面试的时候不问具体技术细节的缘故 - 开个玩笑,好的架构师只需要写头文件就行。。。

=== 因为评论说没看懂,具体展开说 ===

SGD+MPI+Allreduce:这个问的是机器学习当中SGD算法怎样并行化,以及如何处理并行当中的计算模式,讨论可以见Allreduce (or MPI) vs. Parameter server approaches(https://hunch.net/?p=151364)

网络瓶颈:需要正确估计计算时间和网络通讯之间的比例,保证可以实现pipeline:Pipeline (computing)

Infiniband以及ethernet:问的是大规模系统架构的时候的硬件选择问题,不想上Infiniband是因为成本问题,以及容易被少数vendor lock in的风险。

网络的吞吐和延迟的区别:What is the difference between latency and throughput?

异步通信的时候对于收敛性的影响:可以参见google的async sgd,sync sgd revisited,Eric Xing的latency bounded sgd,以及EASGD等一系列文章的讨论。

模型的稀疏解:这个涉及到对于模型稀疏化能力的正确估计,目前的很多网络要保证不丢准确率的情况下,很难做到传统稀疏矩阵的那种要求。

稀疏矩阵库的计算效率问题:这个如果做过数值优化的话应该是常识,但是五秒钟里面能从上一条跳到这一条然后找到瓶颈还是让我很惊讶的。

即时编译和内联参数:这个比较涉及到数值优化的一系列可能性,以及对于编译器的期望,本身就是一道可以展开说一个钟头的问题。。。比如说,我们在做矩阵乘法的时候是需要pre-pack矩阵的,如果我们知道参数矩阵不会变,我们就可以预先pack,然后如果你可以自己写编译器来利用这些特性的话,你甚至可以没有那个矩阵,直接出一个程序把参数都写在指令里头。一般面试是不会涉及到那么多问题的,其中一两个就够讨论了,M先生属于特别牛的那种。。。

知识点解读

深度学习并行计算模式

MPI vs PS

single-machine vs. multi-machine

deterministic vs. nondeterministic

网络瓶颈

计算通信比决定是否要pipeline。当计算通信比接近1:1时,pipeline的效果是比较明显的。

网络硬件选择

Infiniband vs ethernet,Infiniband成本比较高,且有vendor locked in的风险。

网络带宽和延迟

带宽足够,但是延迟高。那么可以加大并行通信的数量来进行优化。比如采用异步通信,充分利用带宽,提高单位时间数据传输量。

异步通信对模型收敛的影响

一般来说,异步通信会使得模型收敛性变差。

模型稀疏解

模型稀疏的好处:

  • 特征自动选择

  • 可解释性

  • 稀疏模型可采用算法压缩,容易存储和传输,加快模型部署

稀疏矩阵库计算效率

矩阵越稀疏,稀疏矩阵库的计算速度越快

即时编译和参数内联

矩阵稀疏度不够,利用模型不变的特性,可以从编译方面对速度进行优化

黄教主演讲

  • GPU在各个行业中的应用:游戏,电影,模拟软件,HPC(基因测序),AI…
  • Cuda on Arm,加速高性能计算
  • AI 复兴: alexnet图像领域, bert NLP领域
  • AI引擎-GPU
  • 深度推荐系统-互联网的引擎
    • 百度
    • 阿里
  • TensorRT 5
    • CNN
    • 30多种变换
    • 自动混合精度
  • TensorRT 7
    • CNN and RNN,Transformer
    • 1000多种变换
    • 低延迟,会话AI成为可能
  • 为智能云提供支持
  • 万物智能革命,边缘计算
  • 边缘智能,AI靠近作用点
  • 机器人平台,4种SDK
    • 自动驾驶
      • 迁移学习
      • 联邦学习,共享模型,不共享数据
    • 医疗
    • 非结构化环境导航和关节控制
    • 通用机器人

GPU虚拟化

  • vGPU10.0发布
  • vGPU性能比GPU损耗10%左右
  • GPU虚拟化的价值:适配多种场景需求
  • GPU虚拟化下的产品形态
    • IaaS产品:云桌面,云电脑
    • 行业产品:云游戏
  • 云上的实践:弹性容器服务
  • vGPU价值:安全性,故障监测,热迁移,粒度更细,池化
  • vGPU在云上可以做到算力细化和算力多样化,算力匹配可以降低客户成本。灵活性,可用性,安全性

美团案例分享

  • TF-serving应用

腾讯如何构建AI+强化学习平台

  • 算力挖掘:CPU, GPU虚拟化: 人->物理机-》人->卡-》任务->卡
  • 并行强化框架
  • 训练加速
    • 通信优化:NCCL2+ RDMA,梯度融合(多次梯度传输合并为一次),混合层次算法
    • IO优化:IO隐藏,多线程+无锁队列+共享内存
    • fp16数据输入
  • GPU推理加速
    • 性能
      • 使用GPU TensorRT
      • Refit
      • tensorflow model -> tensorrt model
    • 流量
      • GPU推理和训练机器在同一IDC机房

飞浆并行训练与应用

  • 同步算法,任务特点:通信密集,延迟敏感,显存瓶颈
  • 基础组件:Collective Operators
  • MPI原语封装为高级api,提高易用性。Fleet API
  • 低配网络环境下的GPU并行
    • 变频通信。降低通信次数,每块卡训练多步通信一次。
    • 稀疏通信,梯度稀疏化
  • 有限显存下提升batch的方式
    • 显存回收,删除中间变量,需要用到时重新计算(重新计算时与通信进行overlap,减少计算时间)。时间换空间
  • 基于模型并行的超大规模分类
    • item过多,最后一层全连接层太大,内存装不下——模型并行:分治策略+模型并行训练。

阿里云飞天AI加速解决方案

  • AI三大要素:数据,算法,算力
  • 飞天AI加速器(一个库?),统一加速各种训练框架。加速器有个接入层,接入到各个框架。IaaS层面优化,计算,通信,存储等。
  • 即刻构建——AI工作流:省时,省钱,易用
  • K8s容器AI解决方案
  • 分布式训练加速性能优化
    • 通信计算重叠 异步通信梯度
    • 延迟优化 去中心化方式聚合梯度
    • 带宽优化 拓扑结构分级通信,混合精度传输梯度,多梯度融合通信,动态分片通信,融合粒度分片通信动态调整

GPU云计算平台给AI实现真正普惠

  • AI产业价值:物流,城市,金融。。。
  • AI开放平台
  • 京东云解决方案
  • 技术能力对外输出:AI,区块链,大数据,物联网,安全,云计算

参数解析——argparse

python解析命令行参数时,可以使用sys.argv,optparse,argparse模块。

其中argparse模块功能最丰富友好,是optparse库的升级版,推荐使用。optparse自从3.2和2.7版本已经不再开发。

getopt模块是为了照顾习惯C语言里面的getopt库而开发的,如果用户不熟悉C语言中的getopt,没有必要使用。

日志——logging

非常全面灵活的日志库。

高级文件和目录操作——shutil

shutil库主要对复制文件,删除文件,压缩文件具有良好的支持。对单个文件进行操作可以使用os库。

压缩文件功能依赖zipfile和tarfile库。

os包含与操作系统有关的接口,参考os库介绍:

本模块提供了一种使用与操作系统相关的功能的便捷式途径。 如果你只是想读写一个文件,请参阅 open(),如果你想操作文件路径,请参阅 os.path 模块,如果你想读取通过命令行给出的所有文件中的所有行,请参阅 fileinput 模块。 为了创建临时文件和目录,请参阅 tempfile 模块,对于高级文件和目录处理,请参阅 shutil 模块。

HTTP库——Requests

Urllib3是Requests库实现的基石

负载均衡的层次

二层负载均衡(mac)
一般是用虚拟mac地址方式,外部对虚拟MAC地址请求,负载均衡接收后分配后端实际的MAC地址响应。

三层负载均衡(ip)
一般采用虚拟IP地址方式,外部对虚拟的ip地址请求,负载均衡接收后分配后端实际的IP地址响应。

四层负载均衡(tcp)
虚拟ip+port接收请求,再转发到对应的真实机器。

七层负载均衡(http)
虚拟的url或主机名接收请求,再转向相应的处理服务器。

通常使用的是四层和七层负载均衡,下面详细说明这两种负载均衡。

四层和七层负载均衡的区别

所谓的四层或七层负载均衡,就是在对后台的服务器进行负载均衡时,依据四层的信息或七层的信息来决定怎么样转发流量。 比如四层的负载均衡,就是通过发布三层的IP地址(VIP),然后加四层的端口号,来决定哪些流量需要做负载均衡,对需要处理的流量进行NAT处理,转发至后台服务器,并记录下这个TCP或者UDP的流量是由哪台服务器处理的,后续这个连接的所有流量都同样转发到同一台服务器处理。七层的负载均衡,就是在四层的基础上(没有四层是绝对不可能有七层的),再考虑应用层的特征,比如同一个Web服务器的负载均衡,除了根据VIP加80端口辨别是否需要处理的流量,还可根据七层的URL、浏览器类别、语言来决定是否要进行负载均衡。举个例子,如果你的Web服务器分成两组,一组是中文语言的,一组是英文语言的,那么七层负载均衡就可以当用户来访问你的域名时,自动辨别用户语言,然后选择对应的语言服务器组进行负载均衡处理。

技术原理

  • 所谓四层负载均衡,也就是主要通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。

    以常见的TCP为例,负载均衡设备在接收到第一个来自客户端的SYN 请求时,即通过上述方式选择一个最佳的服务器,并对报文中目标IP地址进行修改(改为后端服务器IP),直接转发给该服务器。TCP的连接建立,即三次握手是客户端和服务器直接建立的,负载均衡设备只是起到一个类似路由器的转发动作。在某些部署情况下,为保证服务器回包可以正确返回给负载均衡设备,在转发报文的同时可能还会对报文原来的源地址进行修改。

  • 所谓七层负载均衡,也称为“内容交换”,也就是主要通过报文中的真正有意义的应用层内容,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。

    以常见的TCP为例,负载均衡设备如果要根据真正的应用层内容再选择服务器,只能先代理最终的服务器和客户端建立连接(三次握手)后,才可能接受到客户端发送的真正应用层内容的报文,然后再根据该报文中的特定字段,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。负载均衡设备在这种情况下,更类似于一个代理服务器。负载均衡和前端的客户端以及后端的服务器会分别建立TCP连接。所以从这个技术原理上来看,七层负载均衡明显的对负载均衡设备的要求更高,处理七层的能力也必然会低于四层模式的部署方式。

应用场景

七层因为可以代理任意修改和处理用户的请求,所以可以使整个应用更加智能化和安全,代价就是设计和配置会更复杂。所以是否有必要使用七层负载均衡是一个需要权衡的问题。

现在的7层负载均衡,主要还是着重于应用HTTP协议,所以其应用范围主要是众多的网站或者内部信息平台等基于B/S开发的系统。 4层负载均衡则对应其他TCP应用,例如基于C/S开发的ERP等系统。

在写python项目的时候,遇见一个python拷贝对象的坑,在此总结记录一下python中的赋值,浅拷贝与深拷贝。

不可变数据类型:数字,字符串和元组

在python中,不可变对象的赋值,浅拷贝,深拷贝都指向同一个地址,三者没有区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
>>> n1 = 1
>>> n2 = n1
>>> n3 = copy.copy(n1)
>>> n4 = copy.deepcopy(n1)
>>> n1 = 2
>>> print(n1)
2
>>> print(n2)
1
>>> print(n3)
1
>>> print(n4)
1
>>> print(id(n1))
4503975584
>>> print(id(n2))
4503975552
>>> print(id(n3))
4503975552
>>> print(id(n4))
4503975552
>>> s = (1,2,3)
>>> s1 = s
>>> s2 = copy.copy(s)
>>> s3 = copy.deepcopy(s)
>>> s = (1,2)
>>> print(s)
(1, 2)
>>> print(s1)
(1, 2, 3)
>>> print(s2)
(1, 2, 3)
>>> print(s3)
(1, 2, 3)
>>> print(id(s))
4508458760
>>> print(id(s1))
4508464256
>>> print(id(s2))
4508464256
>>> print(id(s3))
4508464256

对于不可变类型,当修改的时候会替换旧的对象,(进行新的内存分配)产生一个新的地址

元组对象比较特殊

元组是不可变对象,但是元素可以是可变对象,还是可以改变元组的元素 。

当元组元素是不可变类型时,赋值,浅拷贝,深拷贝都指向同一个地址。

当元组元素是可变类型时,赋值和浅拷贝指向同一地址,深拷贝会另辟空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
>>> sf = ([1,2],[3,4])
>>> sf1 = sf
>>> import copy
>>> sf2 = copy.copy(sf)
>>> sf3 = copy.deepcopy(sf)
>>> print(id(sf))
4387941064
>>> print(id(sf1))
4387941064
>>> print(id(sf2))
4387941064
>>> print(id(sf3))
4387952776
>>> sf[1].append(5)
>>> print(id(sf))
4387941064
===================================
>>> sd = (1,2)
>>> sd1 = sd
>>> sd2 = copy.copy(sd)
>>> sd3 = copy.deepcopy(sd)
>>> print(id(sd))
4387950856
>>> print(id(sd1))
4387950856
>>> print(id(sd2))
4387950856
>>> print(id(sd3))
4387950856

可变数据类型:列表,字典和集合

一图胜千言:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
>>> import copy
>>> a = [1,2,3,[1,2,3]]
>>> b = a
>>> c = copy.copy(a)
>>> d = copy.deepcopy(a)
>>> a.append(4)
>>> a[3].append(4)
>>> print(a)
[1, 2, 3, [1, 2, 3, 4], 4]
>>> print(b)
[1, 2, 3, [1, 2, 3, 4], 4]
>>> print(c)
[1, 2, 3, [1, 2, 3, 4]]
>>> print(d)
[1, 2, 3, [1, 2, 3]]
>>> a_dict = {'1':1,'2':2,'3':{'1':1,'2':2,'3':3}}
>>> b_dict = a_dict
>>> c_dict = copy.copy(a_dict)
>>> d_dict = copy.deepcopy(a_dict)
>>> a_dict['4'] = 4
>>> a_dict['3']['4'] = 4
>>> print(a_dict)
{'1': 1, '2': 2, '3': {'1': 1, '2': 2, '3': 3, '4': 4}, '4': 4}
>>> print(b_dict)
{'1': 1, '2': 2, '3': {'1': 1, '2': 2, '3': 3, '4': 4}, '4': 4}
>>> print(c_dict)
{'1': 1, '2': 2, '3': {'1': 1, '2': 2, '3': 3, '4': 4}}
>>> print(d_dict)
{'1': 1, '2': 2, '3': {'1': 1, '2': 2, '3': 3}}

注意,集合的元素不能为可变类型,所以对于集合浅拷贝和深拷贝没有区别。

总结

对于可变类型对象,赋值只是增加了一个对象的引用,它们指向内存中的同一个对象;浅拷贝会开辟新内存将对象拷贝一份,但是不会拷贝子对象;深拷贝会开辟新内存将对象拷贝后,连带子对象也拷贝,与原对象完全独立。

MVC模式

mvc模式如下图所示:

Model 是数据,View 是数据的显示结果,同时也接受用户的交互动作,也就是事件。从这个意义来说,说 Model 是 Input 并不严谨,View 接受的用户交互,也是 Input 的一部分。

Controller 负责 Process(处理),它接受 “Model + 由 View 转发的事件” 作为 Input,处理的结果(Output)仍然是 Model,它更新了 Model 的数据。

Model数据更新之后,会产生一个事件datachanged发给view,view进行视图的更新。

当Controller监听data changed事件,然后更新view时,就变成了另一种模式:mvp。

理解Model层

model层应该自然体现业务逻辑,不仅包括数据,也包括接口。

单一职责原则:不要干多件事,也不要啥事不干。

Model层的核心价值是业务需求,是稳定点;datachanged事件是面对需求变化点的对策。

model层越厚越好,这是跨平台最容易的部分。

理解View层

View层的首要责任是界面呈现,可以自己调用GDI画,也可以用子View画。

其次,响应用户事件,应该委托出去给别人干。

View层局部更新太复杂时,一般会引入ViewModel层,它的数据组织接近于View。

理解Controller层

Model层和View都是一个整体,而Controller层不是。它由一个一个不相关的controller和它需要的辅助view组成。controller之间可以完全不知道对方的存在。

应用程序将MVC各个模块串起来,在程序开始的时候,把Model,Controller,View层创建好,建立彼此关联。

工程师文化

  • 事务与工程:防止掉入事务陷阱,花更多时间在工程上才有利于职业发展。
  • 彻底解决问题:清楚定义目标,以自动化方式把事务彻底解决掉。
  • 系统化与批判:去除不必要的代码,保证所有的代码行都有必须存在的目的。

持续更新。。。

趁着双十一,组装了一台日常使用的台式机。配件清单如下:

配件 价格 说明
主板+CPU套装(技嘉Z390 AORUS PRO WIFI+ i5-9600KF) 2758
金士顿(Kingston) DDR4 2666 16GB 内存条 495
七彩虹(Colorful)iGame GeForce GTX 1660 Ti 1899
三星970 EVO Plus 500G固态硬盘 799
希捷(Seagate)2TB 256MB 7200RPM 机械硬盘 369
安钛克(Antec)HCG750金牌全模组电源 799
九州风神(DEEPCOOL) 玄冰400 CPU散热器 89
爱国者(aigo)炫影黑京东专供版 电脑机箱 199
AOC I2490PXH5 23.8英寸显示屏 799
总计 8206

基本概念

netfilter是一个免费的包过滤防火墙,位于内核空间,替代商业防火墙方案,完成封包过滤,封包重定向,网络地址转换(NAT)等功能。

iptables是一个命令行工具,位于用户空间,用来操作netfilter。

  • 5条链(关卡):

到本机某进程的报文:prerouting–>input

本机某进程发出的报文:output–>postrouting

本机转发的报文:prerouting–>forward–>postrouting

  • 4张表:

表中包含一条条规则

规则组成:

匹配条件+处理动作

匹配条件:source ip,port destination ip, port

处理动作:accept, drop, reject, snat, masquerde, dnat, redirect, log

表的作用:

filter:过滤

nat:网络地址转换

mangle:拆解报文,作出修改,重新封包

raw:关闭nat表启用的连接追踪机制

规则查看

iptables -t 表名 -L 链名

选项:

-v 显示详细信息

-n 不解析IP地址

-x 显示计数器精确值

–line-number 规则显示编号

规则管理

  • 添加规则:
1
2
3
iptables -t filter -I INPUT -s ip -j ACCEPT

iptables -t filter -A INPIT -s ip -j ACCEPT

-I 在表头插入规则

-A 在表尾追加规则

规则的顺序很重要,如果某个报文已经被前面的规则匹配了,那么后面的规则不会处理。

  • 删除规则

按序号删除

1
2
3
iptables --line -vnL INPUT

iptables -t filter -D INPUT 3

按匹配条件和处理动作删除

1
iptables -t filter -D INPUT -s ip -j ACCEPT
  • 修改规则

命令语法:iptables -t 表名 -R 链名 规则序号 规则原本的匹配条件 -j 动作

示例:iptables -t filter -R INPUT 3 -s 192.168.1.146 -j ACCEPT

修改默认规则

命令语法:iptables -t 表名 -P 链名 动作

示例:iptables -t filter -P FORWARD ACCEPT

!!! 修改iptables都是临时修改,iptables服务或者机器重启后都会丢失,一定要保存。

service iptables save

匹配条件

匹配条件可以取反

基本匹配条件源ip 目的ip

扩展匹配条件 源端口, 目的端口,需要 -m参数指定模块

  • 扩展匹配模块

tcp:匹配tcp协议 –sport –dport –tcp-flags –syn

multiport:匹配多个端口

iprange: 匹配连续IP范围

string: 匹配报文中包含的字符

time: 匹配报文的时间段区

connlimit:匹配单个ip连接数量

limit: 匹配单位时间内的报文数量

udp: 匹配udp协议

icmp: 匹配icmp协议

state: 匹配连接状态,实现连接追踪

黑白名单机制

  • 黑名单机制

INPUT链默认策略设置为ACCEPT,添加动作为DROP的规则。不够安全

  • 白名单机制

1)链默认策略设置为RDROP,添加动作为ACCEPT的规则,但是规则清空后,管理员也无法登录主机。

2)链默认策略设置为ACCEPT,设置放行规则完毕之后,在最后设置一条拒绝所有连接的规则。

自定义链

方便管理各种规则,普通规则的动作设置为自定义链,符合条件的报文会经过自定义链的规则。

Docker一般也会添加自定义链。

1
2
3
4
5
6
7
8
# 添加自定义链
iptables -t filter -N IN_WEB
# 引用
iptables -t filter -I INPUT -p tcp --dport 80 -j IN_WEB
# 修改名称
iptables -E IN_WEB WEB
# 删除
iptables -X WEB

动作总结

  • accept
  • drop
  • log
  • snat 用来隐藏内网IP或者共享公网IP。
  • dnat 用来暴露内网服务

snat dnat 都用到了snat dnat两个动作,snat在前就叫snat, dnat在前就叫dnat

  • masquerde 和snat相似,区别在于不绑定具体ip,而绑定网卡上的可用ip,用在外网ip地址常变动场景下。
  • redirect 本机端口映射

套路总结

  • 规则顺序很重要,如果前面规则匹配,后面规则不会执行,所以相同服务的规则,更严格的规则要放前面
  • 规则中多个匹配项存在“与“的关系
  • 不考虑第一条规则的情况下,使用频率更高的规则放前面,节省主机计算资源。
  • iptables作为防火墙时,要考虑双向性,从内到外,从外到内。
  • 设置白名单机制时,通常将链默认策略设置为ACCEPT,设置放行规则完毕之后,在最后设置一条拒绝所有连接的规则。