undefined

通信安全性

http 传输数据是明文的,且是不安全的。整个传输过程完全透明,任何人都能够在链路中截获、修改或者伪造请求/响应报文,数据不具有可信性。对于网络购物、证券交易需要高度信任的应用场景是致命的

通常认为,如果通信过程具备了四个特性,就可以认为是安全的,四个特性是:机密性、完整性、身份认证和不可否认

  • 机密性:对数据的保密,只能由可信的人访问,对其他人是不可见的秘密
  • 完整性:也称为一致性,数据在传输过程中没有被篡改,保持原状
查看更多

undefined

Go语言调度器

虽然线程比较轻量,但是在调度时也有比较大的额外开销。每个线程会都占用 1M 以上的内存空间,在切换线程时不止会消耗较多的内存,恢复寄存器中的内容还需要向操作系统申请或者销毁资源,每一次线程上下文的切换都需要消耗 ~1us 左右的时间1,但是 Go 调度器对 Goroutine 的上下文切换约为 ~0.2us,减少了 80% 的额外开销。

一、Go语言调度器的演进

1. 单线程调度器 — Go 0.x 版本

程序中只能存在一个活跃线程,由 G-M 模型组成。执行流程

  • 获取调度器的全局锁
  • 保存栈寄存器和程序计数器
查看更多

undefined

for 和 range

一、遍历切片或数组

1. 遍历数组的同时修改数组元素

1
2
3
4
5
6
7
8
func main() {
arr := []int{1,2,3}
for _, v := range arr {
arr = append(arr, v)
}
fmt.Println(arr)
}
输出:[1 2 3 1 2 3]

如上,结果表明,循环只遍历了原始切片中的三个元素,在遍历切片时追加的元素不会增加循环的执行次数,因此循环最终还是会停下来。

原理:Go语言源码中,对于所有的 range 循环,Go 语言都会在编译期将原切片或者数组赋值给一个新变量,在赋值过程中就发生了复制,并且又使用 len 关键字预先获取了切片的长度,所以在循环中追加新元素不会改变循环执行的次数。

2. range 返回变量的地址

1
2
3
4
5
6
7
8
9
10
11
func main() {
arr := []int{1,2,3}
newArr := []*int{}
for _, v := range arr {
newArr = append(newArr, &v)
}
for _, v := range newArr {
fmt.Printf("%d ", *v)
}
}
输出:3 3 3

查看更多

undefined

1. channel (go)实现

首先明确go语言的设计模块:不要通过共享内存的方式进行通信,而是应该通过通信的方式共享内存。这样在我看来让 go 语言代码更加整洁。因此 go 语言中 Goroutine 之间会通过 Channel 传递数据。基于go 1.15 版本,Channel 的实现:

1.1 Channel 底层数据结构

chan 的底层数据结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
type hchan struct {
qcount uint // 元素个数
dataqsiz uint // 环形队列的长度
buf unsafe.Pointer // 指向环形队列的指针
elemsize uint16 // 环形队列中每个元素的大小
closed uint32 // chan 是否被关闭
elemtype *_type // 环形队列中元素的类型
sendx uint // 环形队列中发送操作处理到的位置
recvx uint // 环形队列中接收操作处理到的位置
recvq waitq // 处于阻塞状态的接收 Goroutine 双向链表
sendq waitq // 处于阻塞状态的发送 Goroutine 双向链表
lock mutex // 互斥锁
}

chan 使用 make 关键字创建,可以带缓冲区的异步 Channel 和不带缓冲区的同步 Channel。这里对创建过程不做赘述。基本上分为三种情况:

查看更多

undefined

sync.mutex 锁详解

基于 Go 语言 1.15 版本

一、互斥锁的性质

互斥锁有两种状态:正常状态和饥饿状态

在正常状态下,所有等待锁的 Goroutine 按照 FIFO(先进先出)的顺序等待。唤醒的 Goroutine 不会直接拥有锁,而是会和新请求锁的 Goroutine 竞争锁的拥有。新请求锁的 Goroutine 具有优势,他正在 CPU 上执行,而且可能有好几个,所以刚刚唤醒的 Goroutine 有很大可能在锁竞争中失败。在这种情况下,这个被唤醒的 Goroutine 会加入到等待队列的前面。如果一个等待的 Goroutine 超过 1ms 没有获取到锁,那么他将会把锁变为饥饿模式

在饥饿模式下,锁的所有权会直接交给等待队列最前面的 Goroutine。新来的 Goroutine 在该状态下将不会尝试去获取锁,即使锁看起来是 unlock 状态,也不会尝试自旋操作,而是放在等待队列的尾部

如果一个等待的 Goroutine 获取了锁,并且满足以下其中的任何一个条件,他会将锁的状态转换为正常状态

查看更多

undefined

select

  • select 控制结构中包含 default 语句时,能在 channel 上进行非阻塞的收发操作
  • select 在遇到多个 channel 同时响应时,会随机执行一种情况。引入随机性就是为了避免饥饿问题发生

一、实现原理

select 语句在编译期间会被转换成 OSELECT 节点。每个 OSELECT 节点都会持有一组 OCASE 节点,每一个 OCASE 节点既包含执行条件,也包含满足条件后执行的代码。如果 OCASE 的执行条件为空,就意味着这是一个 default 节点。

查看更多

undefined

内存管理的基础

程序中的数据和变量都会被分配到程序所在的虚拟内存中,内存空间包括两个重要区域:栈区、堆区。

  • 函数调用的参数、返回值以及局部变量大都会被分配到栈上,这部分内存会由编译器进行管理
  • 堆中的对象由内存分配器分配并由垃圾收集器回收,或者程序员自己管理(c/c++)

查看更多