0%

架构,貌似对我们来说并不陌生,因为工作中时不时会提到架构,如今市面上也有各种XXX架构师的职位。
更多时候我们还会调侃架构师只是一群画图做PPT的人…但,我们真的理解什么是架构吗?
确实有必要梳理下概念,有个整体的认识,以便更好的深耕细分领域,共勉!

什么是架构

架构这个词起源于建筑,是一个比较宽泛的概念,业内也未有标准的定义。

架构,又名软件架构,是有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。– 摘自百度百科

软件架构(Software Architecture)是一系列相关的抽象模式,从本质上来看,属于一种系统草图。

几个概念

软件架构中几个有关系而又相似的概念。

架构 与 设计

所有架构都是设计,但并非所有设计都是架构。架构反映了使一个系统成型的重要设计决策,而重要性则通过改变的成本来衡量。

从本质上讲,重要决策即架构,其他的都是设计。重要决策包括:

  • 系统的形态,eg. 客户端-服务器、基于Web、原生移动客户端、分布式、异步等等。
  • 软件系统的结构,eg. 组件、层、交互等等。
  • 技术选择,eg. 编程语言、部署平台等等。
  • 框架选择,eg. Web MVC框架、持久层/ORM框架等等。
  • 设计方法/模式选择,eg. 针对性能、可伸缩性、可用性等的方法。

架构是软件的整体结构设计。

系统 和 子系统

系统是由相互作用相互依赖的若干组成部分结合而成的,具有特定功能的有机整体,而且这个有机整体又是它从属的更大系统的组成部分。– 摘自百度百科

系统是一个可以独立存在的完整实体,由一组完成特定任务的功能组成。
系统和子系统的概念是相对的,当作为另一个系统的一部分时,系统就成为一个子系统。
比如电商系统有支付系统、订单系统、用户系统等等子系统。

模块 和 组件

模块和组件都是系统的组成部分,只是从不同的角度拆分系统:

  • 从逻辑层面,系统可拆分为各个模块,如 登录注册模块、权限管理模块等;划分模块是为了职责分离。
  • 从物理层面,系统可拆分为各个组件,如 Web服务器、数据库、缓存中间件等;划分组件是为了单元复用。

另,模块和系统一般情况下没有本质区别,但是不能独立工作的模块一般不称之为系统。

框架 和 架构

软件框架(Software Framework),通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品。– 摘自百度百科

框架是组件规范,eg. MVC、MVP、MVVM等开发规范;框架是提供基础功能的产品,eg. 国际化、日志记录等等。
框架关注的是“规范”,架构关注的是“结构”。
但很多时候,并没有完全界定,或者说框架是架构的具体实施。

阅读全文 »

一个常见问题

1
2
3
$f1 = 0.58;
$f2 = 0.50;
echo (int)(($f1 - $f2) * 100);

结果为什么是 7 而不是 8,PHP出BUG了?我们换 Java 和 Go 试试。

1
2
3
float f1 = 0.58f;
float f2 = 0.50f;
System.out.println((int)((f1 - f2) * 100));
1
2
3
var f1 float32 = 0.58
var f2 float32 = 0.50
fmt.Println((int)((f1 - f2) * 100))

结果也都是7。
看来这是个“通病”。要弄清楚原因,我们得先回顾下以前学的基础知识。
alt

阅读全文 »

任务类型

程序任务类型一般分 CPU密集型 和 IO密集型。
CPU密集型任务,指:进行大量计算,消耗CPU资源的任务,eg. 图像处理、音视频编码解码、复杂的科学计算等。
IO密集型任务,指:涉及大量网络、磁盘等比较耗时的输入输出任务,eg. Web服务、数据库操作等。
不论CPU密集型任务还是IO密集型任务,为了提高处理能力,从软件层面入手,有几种主流方式:启动多个服务实例(多进程、多线程、多协程)、IO多路复用、异步IO。
实际应用时,会结合任务类型在上述方式中选择多种进行组合,以此搭建适合的高性能服务框架,eg. Apache 属于多进程多线程并配合 select 模型进行IO处理、Nginx 主要是 epoll 模型(IO多路复用) + 多进程。

IO 和 Socket

Socket 又称 套接字,是一种进程间通信机制,在系统层面上封装了通过 IP + Port 进行 TCP / UDP 数据传输的接口,介于 OSI(Open System Interconnection)模型的传输层与应用层之间。

严格来说,Socket 不归属于 OSI 的任何一层,它是应用层与 TCP/IP 协议族通信的中间软件抽象层。
在设计模式中,Socket 其实就是一个门面模式,它把复杂的 TCP/IP 协议族隐藏在 Socket 接口后面。
Socket 还可以用在同一主机不同进程间的通信(本地 Socket),且建立的是双向的通信。本地 Socket 与 网络 Socket 共用同一套接口,只是地址结构与某些参数不同。
进程间通信(IPC,Inter-Process Communication)方式,除了 Socket,还有:管道 Pipe命名管道 FIFO信号 Signal消息队列 Message Queue共享内存 Shared Memory

网络IO的本质是通过 Socket 对网卡进行读写,Socket 在 linux 系统被抽象为流,IO可以理解为对流的操作。

普通的网络传输步骤如下:

  1. 操作系统将数据从磁盘复制到操作系统内核的页缓存(内核缓冲区)中。
  2. 应用将数据从内核缓冲区复制到应用的进程缓冲区中。
  3. 应用将数据从进程缓冲区写回内核的 Socket 缓冲区中。
  4. 操作系统将数据从 Socket 缓冲区复制到网卡,然后将其通过网络发出。

注:上述过程可以通过 Zero-Copy(零拷贝机制) 进行优化,关于 Zero-Copy 后续再开篇来讲。

同步 / 异步 / 阻塞 / 非阻塞

单从字面上看这几个概念,我们很容易把同步等同于阻塞,异步等同于非阻塞,但实际上,不同的层面有不同的理解。

  1. 进程通信层面
    • “阻塞”是指进程在发起一个系统调用(System Call)后,由于该系统调用的操作(一般是涉及I/O操作,比较耗时)不能立即完成,需要等待一段时间,于是内核将进程挂起为等待(waiting)状态,以确保它不会被调度执行,占用CPU资源。
    • 非阻塞I/O系统调用(Non-Blocking I/O System Call)指一个非阻塞调用不会因I/O操作而挂起执行程序,而是会立即返回。
    • 异步I/O系统调用(Asychronous I/O System Call)也属于非阻塞系统调用(Non-Blocing System Call),不同的是,I/O操作完成后,操作系统会通知调用进程(设置一个用户空间特殊的变量值 或者 触发一个 Signal 或者 产生一个软中断 或者 调用应用程序的回调函数)。
      不同于非阻塞I/O系统调用,异步I/O系统调用的结果必须是完整的,但这个操作完成的通知可以延迟到将来的某个时间点,即,返回结果的方式和内容有所差别。
  2. IO系统调用层面
    (单线程背景下)多数I/O系统调用是同步的,真正的异步I/O是需要内核级支持的,eg. libaio。

非阻塞系统调用(Non-Blocking I/O System Call 与 Asynchronous I/O System Call)可以用于实现线程级别的 I/O 并发,与通过多进程实现的 I/O 并发相比可以减少内存消耗以及进程切换的开销。

举个栗子:我们知道,JS是单线程的,那为什么 Ajax 可以实现异步 HTTP 请求?
本质还是事件驱动,即,把事件加入队列,通过 Event Loop 去轮询队列,待JS线程空闲时再执行回调函数。
所以,对于JS线程来讲,Ajax 是异步的,但在I/O调用上确实还是同步的。
注:JS引擎确实是单线程运行的,而浏览器会另开线程专门处理 Event Loop、HTTP 请求、界面渲染等。

阅读全文 »

几个概念

存储

进制(二进制、八进制、十进制、十六进制)、位(bit)、字节(byte)、字符

进制回顾

计算机是用二进制存储的,即 1 和 0,表示一位。
进制的转换:八进制(取 3 位二进制计算数值),十六进制(取 4 位二进制计算数值),十进制。
负数怎么表示?原码、反码(正数反码 = 原码,负数反码 = 正数逐位取反 & 符号位为1)、补码(反码 + 1)

CPU电路中只有加法器,减法是通过补码实现的。

补码为什么是反码 + 1?
反码 + 1 只是补码的一种计算方式,真正的理论基础是 模 和 同余数。
拿时钟来说,一圈 12 小时,超过 12 就重新计算,所以 12 称为
时钟运算中,减去一个数,其实就相当于加上另外一个数(这个数与减数相加正好等于 12,也称为同余数)。
eg. 当前为 10 点,回到 6 点,我们需要减去 4,但实际加上 8 也能让时针指向 6 点。

字节

计量单位,1byte = 8bit(即,1b = 8B),1KB = 1024bytes,MB / GB / TB / PB /…。

字符

即各种符号,包括字母、数字、运算符号、标点符号和其他符号,以及一些功能性符号。

编码

  • 字符集 - 可以理解为字典,即,为每一个「字符」分配一个唯一的码位(也称码点 / Code Point),eg. Unicode。
  • 编码规则 - 将「码位」转换为字节序列的规则(编码 / 解码 可以理解为 加密 / 解密 的过程),eg. UTF-8。

常见的字符编码

ASCII

全称 American Standard Code for Information Interchange,美国信息交换标准代码…>>
用一个字节(8位)存储,最高位为 0(保留),剩余7位二进制可表示 128 个字符,涵盖了所有的大小写字母、数字(0…9)、标点符号,以及在美式英语中使用的特殊控制字符。
扩展ASCII码:启用原先保留的最高二进制位,用于确定附加的 128 个特殊符号字符、外来语字母和图形符号。

Unicode

又称 万国码,是 多语言软件制造商组成的统一码联盟(一个国际组织)定义的编码…>>
广义的 Unicode 是一个标准,定义了一个字符集以及一系列的编码规则,即,Unicode 字符集和 UTF-8 / UTF-16 / UTF-32 等等编码规则。

UTF 表示 Unicode Transformation Format,即,Unicode 转码格式。

阅读全文 »

安装配置

安装

直接 yum 安装即可。

1
yum -y install rsync

配置

Rsync 分服务端和客户端,涉及三个配置文件:

  • rsyncd.conf - 具体的配置,放于目标端,也就是服务端提供的同步模块。
  • rsyncd.secrets - auth users用户列表的账号密码。
  • rsync.key - daemon模式时的密码文件,用于校验目标端 secrets。

比如从A(客户端)同步文件到B(服务端),则配置对应如下:
alt

阅读全文 »