goroutine整理

前言

虽然写了不少go的代码了,但是对于go本身的了解还处于比较片面的阶段,因此在后续几天内打算将基础慢慢补回来。

就从goroutine部分开始整理吧

什么是协程

多核的流行

由于单核性能提升的成本越来越高,出于性能和成本的考虑,多核心逐渐成为主流。但虽然硬件上多核已经有了实现,能充分利用多核特性的程序比例仍然比较小。

进程、线程、协程

多进程、线程或协程都可以利用多核心的特性,但是他们具有不同的特点:

进程是资源分配的最小单位,同一时间执行的进程数不会超过核心数目,由于进程的上下文切换需要切换页目录、内核栈和硬件上下文,其上下文切换开销较高。同时页缓存的切换会导致cache失效,使得cache的命中率降低。

线程上下文切换不需要切换页目录(虚拟内存空间相同),而只需要切换内核栈和硬件上下文,因此其开销低于进程,相对来说更加轻量。多核中线程一般是利用开发者接口,由内核完成到处理器的映射,实现并行。但由于内核栈和硬件上下文的切换仍然需要操作系统协助,需要陷入内核态,因此依旧存在一定开销。

协程占用资源小,由用户进行调度(内核无感知),因此不必陷入内核中(没有内核上下文切换)。由于通常情况下用户调度会使用比线程更轻量的上下文(通常是更少的寄存器和动态大小的栈),因此创建开销以及上下文切换开销会比线程更低。除此之外,同一线程上的协程不会出现写变量冲突(因为它们本身就不会同时执行),因此控制共享资源不必加锁而只要判断状态。

如何根据情况来使用

多进程(稳定性):由于进程通常各用独立的内存区间,进程的崩溃不会影响到其它进程的运行,因此具有更好的稳定性,相比之下线程和协程的崩溃会导致进程崩溃,进而影响到同进程下的其它线程或协程(因为它们共享相同的资源)。

多线程(并行计算任务):多核处理器情况下,并行依靠线程,由内核完成线程到处理器映射的。协程的轻量化设计是为了降低上下文切换开销,并不会在并行计算上带来优势(同一线程上的协程同时只会有一个在执行,因此单线程上开大量协程并不能提高计算的并行度)。更加轻量的上下位在计算过程可能反而引起额外开销(动态栈或共享栈的设计引发的额外栈检查、栈内存分配和栈内存复制操作等)

协程(阻塞频繁的任务):不必进入内核态和更轻量的上下文使其在面临阻塞的时候被调度的开销更低,因此当任务存在大量频繁的阻塞和唤起时,协程的开销会小于线程(提高了并发度,而非并行度)。同时由于同线程内的协程不会出现写变量冲突。

三者的使用中就个人理解:进程倾向于隔离(安全性),线程倾向于并行,而协程倾向于异步。

协程的特点

综上总结之后,协程的特点如下:

  • 由用户自己进行调度,减少上下文切换,提高效率
  • 栈更小,在同内存中可以比线程开启更多的数量
  • 协程在同一线程上,可以避免竞争关系而使用锁
  • 适用于被阻塞且需要大量并发的场景,但不适用于大量计算并发场景,计算型场景更适合使用线程实现

goroutine

goroutine有些类似于协程,但它不像是nodejs那种单线程下的携程,它是基于内核线程上的轻量抽象,使用GMP模型抽象(下详述)。

goroutine的调度方式是协同式的,在协同式调度中,没有时间片的概念。为了并行执行goroutine,调度器会在以下几个时间点对其进行切换:

  • Channel接收或者发送会造成阻塞的消息
  • 当一个新的goroutine被创建时
  • 可以造成阻塞的系统调用,如文件和网络操作
  • 垃圾回收

尽管调度方式是协同式的,但是go考虑到协程饿死的情况,针对其抢占机制(详见参考资料《Implemention of golang》),同时由于我们不能保证goroutine全都在同一个线程上,因此它不具有协程中不会引起冲突的特点,开发时我们仍需要考虑临界区等因素。

相比nodejs的回调函数方式解决异步的处理方式带来的繁杂嵌套,golang使用了goroutine+channel的设计思路,让异步代码编写时能以一种更加线性的思维来完成,降低了开发人员的精神负担。

GMP模型

Golang的调度器中存在三个概念:

  • Processor(P)
  • OSThread(M)
  • Goroutines(G)

详情待续,也可参考参考资料中《Go的并发机制:线程模型》

参考资料

待看

Go的并发机制:线程模型
golang设计文档

已参考

进程、线程、协程
进程、线程上下文切换的开销
多进程编程和多线程编程优缺点
微信libco协程介绍
Golang协程详解和应用
Golang 之协程详解
Go语言中Goroutine与线程的区别
Go语言程序设计 读书笔记
Go语言的栈空间管理
Go 运行程序中的线程数
Implemention of golang

cgo中向C部分函数传入go的slice

注意事项:

传入C函数中的slice需要以&array[0]的形式进行,而不能单纯使用&array,因为go中的slice数据结构不仅仅具有首地址。

类型对应表

type C go
char C.char byte
signed C.schar int8
unsigned char C.uchar uint8
short int C.short int16
short unsigned int C.ushort uint16
int C.int int
unsigned int C.uint uint32
long int C.long int32/int64
long unsigned int C.ulong uint32/uint64
long long int C.longlong int64
long long unsigned int C.ulonglong uint64
float C.float float32
double C.double float64
wchar_t C.wchar_t
void * unsafe.Pointer

例子

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
package main

/*
struct test_t{
int a;
};

void setInt(void *t){
unsigned long *arr = (unsigned long *)t;
for(int i=0;i<10;i++){
arr[i] = i;
}
}

void setTest(void *t){
struct test_t *m = (struct test_t *)t;
m->a = 10;
}
*/
// #cgo 386 CFLAGS: -DX86=1
import "C"
import (
"fmt"
"unsafe"
)

type test struct {
a int
}

func main() {
arr := make([]C.ulong, 10)
t := test{}
C.setInt(unsafe.Pointer(&arr[0]))
C.setTest(unsafe.Pointer(&t))
fmt.Println(arr)
fmt.Println(t)
}

爬虫

前端保存结构代码(代码源于网络)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(function (console) {
console.save = function (data, filename) {
if (!data) {
console.error('Console.save: No data')
return;
}
if (!filename) filename = 'console.json'
if (typeof data === "object") {
data = JSON.stringify(data, undefined, 4)
}
var blob = new Blob([data], { type: 'text/ json' }),
e = document.createEvent('MouseEvents'),
a = document.createElement('a')
a.download = filename
a.href = window.URL.createObjectURL(blob)
a.dataset.downloadurl = ['text / json', a.download, a.href].join(': ')
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
a.dispatchEvent(e)
}
})(console)

// 使用方法:console.save(obj)

Python爬取的编码转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests
from bs4 import BeautifulSoup
# 获取当前网站编码并转换
r = requests.get(website)

# 获取当前网站编码并转换
if r.encoding == 'ISO-8859-1':
encodings = requests.utils.get_encodings_from_content(r.text)
if encodings:
encoding = encodings[0]
else:
encoding = r.apparent_encoding
else:
encoding = r.encoding
encode_content = r.content.decode(
encoding, 'replace').encode('utf-8', 'replace')
soup = BeautifulSoup(encode_content, features="html.parser")

HyperV与其它虚拟机的冲突

参考资料:
https://www.zhihu.com/question/306350820
https://www.zhihu.com/question/38841757

原因是现代硬件虚拟化技术基于独占的技术
没找到不重启的情况下切换的方法,但是找到了在开机的时候可以选择选项的方法

1
2
bcdedit /copy {current} /d "Windows10 no Hyper-V"
bcdedit /set {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} hypervisorlaunchtype OFF

其中第二条的{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} 是执行第一条的输出

需要在cmd下执行,PowerShell下会出现参数错误的问题,原因是PowerShell下需要转义花括号,而转移在PowerShell中是反引号`而不是反斜杠\

写了个自动化指令,用正则表达式提取GUID,这样第二行就不需要自己复制了
(虽然并没有什么意义)

1
$output=bcdedit /copy `{current`} /d "No Hyper-V"; $output -match '\{.+\}'; bcdedit /set $matches[0] hypervisorlaunchtype OFF

配置时钟同步ntpd服务

安装NTP

NTP在安装的时候会有人工设置的选项,因为想要自动化安装,所以希望可以跳过人为设置时区这一步

查询得知是tzdata安装导致的原因,可以设置环境变量来禁止交互

1
2
3
4
ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
export DEBIAN_FRONTEND=noninteractive
apt-get install -y tzdata
dpkg-reconfigure --frontend noninteractive tzdata

参考资料:https://stackoverflow.com/questions/44331836/apt-get-install-tzdata-noninteractive

然后再安装ntp就不会有提示了

1
apt install ntp -y

修改/etc/ntp.conf配置改变ntp服务,然后启动

1
service ntpd start

在此之前可以考虑使用ntpdate来预先同步一下时间

1
2
apt install ntpdate -y
ntpdate cn.pool.ntp.org

正则表达式相关

支持情况

.NET

支持平衡组(目前已知提供支持的还包括Perl、PHP)
正则语法的支持情况相当全

JavaScript

总算支持前向查询了,可喜可贺
不支持平衡组(网上的JS平衡组教程其实是PHP版的……至少在2020/3/16日之前,自己验证是不支持的)

  • VSCode查询上使用Javascript正则(源码上的感觉,但是没有具体文档指出,据说是魔改过的)

系统监控工具调查

问题

utilization, saturation, and error metrics for all resources

CPU utilization, memory usage, disk utilization, and network throughput

CPU利用率检测到目前仍然有瑕疵,部分没有指标可以导出

Block-I/O Latencies、File System Latency、CPU Scheduling Latency

参考资料:
https://www.tuicool.com/articles/aE3U7jf

系统内的

img

http://techblog.netflix.com/2015/11/linux-performance-analysis-in-60s.html

1
uptime

查看系统平均负载,会显示最近1,5,15分钟中,运行队列中的平均进程数
想要运行到CPU的进程以及被非中断性的I/O阻塞的进程
(与时间和CPU相关的指标,显示负载随时间的变化)

1
dmesg | tail

显示最新的10条系统消息
(与日志相关的指标,显示系统消息)

1
vmstat 1

1是参数,代表每隔多少秒统计一次
虚拟内存统计,对虚拟内存,CPU,进程等整体情况进行监视。

参数解释:https://zhuanlan.zhihu.com/p/35879028


参数解释

procs:
- r - 等待CPU的进程数目
- b - 不可中断的休眠(等待IO)的进程数目。
memory
- swapd - 多少块被换出磁盘(页面交换)
- free - 空闲的(未被使用)
- buff - 被用作缓冲区,cache - 被用作操作系统缓存。
swap
- si - 每秒有多少块正在被换入(从磁盘)
- so - 换出(到磁盘)。
io
- bi - 多少块从块设备读取
- bo - 多少块从块设备写出
system
- in - 每秒中断数
- cs - 每秒上下文切换数。
cpu:显示所有的CPU时间花费在各类操作的百分比
- us - 执行用户代码(非内核)
- sy - 执行系统代码(内核)
- id - 空闲
- wa - 等待IO

1
2
3
4
5
6
7
mpstat -P ALL 1
pidstat 1
iostat -xz 1
free -m
sar -n DEV 1
sar -n TCP,ETCP 1
top

大家介绍的

github上找的

StackOverflow上的

产业界的

Atlas

https://netflixtechblog.com/introducing-atlas-netflixs-primary-telemetry-platform-bd31f4d8ed9a

Vector

https://netflixtechblog.com/introducing-vector-netflixs-on-host-performance-monitoring-tool-c0d3058c3f6f

外网推荐的

论文里有的

方法论

The USE Method

http://www.brendangregg.com/usemethod.html

Google’s SRE teams

https://landing.google.com/sre/sre-book/chapters/monitoring-distributed-systems/#xref_monitoring_golden-signals

ebpf的最大特点就是极低的负载

因为有了极低的负载,所以对很多之前做起来开销大的检查,现在都能够做了。

Minikube安装记录

参考资料:https://minikube.sigs.k8s.io/docs/start/linux/

安装kubectl

安装docker

下载minikube并安装

1
2
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 \
&& sudo install minikube-linux-amd64 /usr/local/bin/minikube

由于云服务器不支持虚拟化,所以使用虚拟化使用None

1
2
3
minikube start --image-mirror-country cn \
--iso-url=https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/iso/minikube-v1.7.3.iso \
--registry-mirror=https://xxxxxx.mirror.aliyuncs.com
|