Golang为什么比别的语言跟擅长并发:
首先是因为Goroutine,算是go的一个最大的特色
是轻量级的线程,创建一个goroutine的开销非常小,大约几KB,且调度开销很低
并且goroutine的调度,并不依赖操作系统的线程调度器,而是使用了GMP模型
其次就是channel,也是go的特色
channel
通信机制:channel 算是锁的一个升级,可以避免显示锁的使用,channel可以传递数据,用于异步通信,降低并发编程的难度
提到了GMP模型讲一下
G:groutine
- goroutine是go语言中的轻量级线程,每个goroutine都有自己的栈和执行状态
- Goroutine的栈空间是动态扩展的,初始栈的空间很小几kb,可以根据需要自动增长和缩小
M:Machine
- 每一个M对应一个内核线程,M是执行G的实体
- 每个M绑定一个操作系统线程,负责执行G的代码
P:Processor
- 管理G队列,每一个P维护一个运行队列,保存待执行的G
- P是G调度的核心单位,M需要从P获取G才能执行
- P一般与CPU核心数相同,可以在runtime.MAXPROCS(n)来设置P的数量
调度过程大致是:
G的创建与调度:
- 当创建一个新的G时,它会被放入到某个P的本地队列
- M从P的本地队列中获取G进行执行,如果P的本地队列为空,尝试从全局队列或者其他P的队列中窃取
M和P的绑定
- M在运行时需要绑定一个P,才能从P的队列中获取g
- 所有P都被占用,有额外的M需要执行,那么这些M会阻塞等待,直到某个P可用
P的数量控制
- P的数量决定了可以同时并行执行G的最大数量
- 通过runtime包中的GOMAXPROCS()来设置
调度的公平性和抢占式调度
- 实现抢占式调度,可以在长时间运行的G中插入检查点,确保其他G也能获得执行机会
- 通过抢占机制防止某个G长时间占用CPU,提高系统的公平性和响应性
工作窃取
- 如果本地P为空则尝试从全局队列或者其他本地队列中获取G
- 这样可以提高负载均衡,减少因局部队列空闲造成的资源浪费
如何检测golang的内存泄漏
使用runtime包提供的一些对内存使用情况进行查看的方法
初步判断内存是否泄露
使用pprof工具进行分析 go tool pprof
使用pprof.WriteHeapProfile函数生成内存快照
然后使用 go tool pprof来进行分析
常见内存泄漏原因:
未关闭goroutine
未关闭文件或网络连接
缓存和数据结构:不必要的数据保存在全局变量或长生命周期的结构
内存泄漏是指程序运行过程中,已经不再需要使用的内存无法被释放,从而造成内存资源的良妃.内存泄漏会使得应用程序的内存使用量不断增加,最后可能导致系统内存耗尽,应用崩溃或者性能下降
得物-Golang-记一次线上服务的内存泄露排查_golang 解决线上问题-CSDN博客
select和channel关键字
select和channel是处理并发编程的两个重要工具
channels
是go语言提供的一种通信机制,用于在go之间传递数据,无需使用锁
类型:有缓冲的,无缓冲的
select
用于在多个channel中进行选择,类似于多路复用器
select会阻塞,直到其中一个case可以继续执行
default
select
语句使得在多个channel上进行非阻塞和超时控制变得容易。
关于Mysql的性能优化:
首先就是在资金充足的情况下,冲高性能服务器
在建表的时候:选择最合适的字段属性
尽量把字段设置为NOT NULL 这样查询的时候数据库不用比较NULL值
使用连接代替子查询
- MySQL以前是join几张表几个for循环嵌套,
- 8.0做了优化将一张表存入内存减少循环嵌套
- 子查询结果会在内存临时创建表存储,浪费资源
索引时使用最左前缀规则 (联合索引进行查询)
模糊查询不能利用索引
不要过多创建索引
- 过多的索引会占用空间,且每次crud都会重建索引
索引长度尽量短
索引更新不能太频繁
避免使用select * 用什么查什么就好
合适的情况下,使用的合适的索引
数据量少的时候,全表扫描的速度比索引的速度快
开启慢查询日志
grpc是基于http几
http2,HTTP2提供了一些关键特性
多路复用:
- 支持单个TCP连接上同时发送多个请求和响应,减少连接的开销和延迟
流量控制
- 提供了更细粒度的流量控制,允许客户端和服务器控制数据流的速率和优先级,这对于高吞吐量和低延迟的通信非常的重要
头部压缩
- http/2使用HPACK压缩算法对HTTP头部进行压缩,减少了请求和响应的大小,提高了传输效率
服务器推送
- 可以在服务器请求之前发送资源
介绍一下http1.0/http1.1/http2/http3
1.0
- 单个请求响应模型
- 无状态
- 基本的缓存控制
- 缺乏持久连接
1.1
- 持久连接
- 管道化
- 更好的缓存控制
- 分块传输编码
- 带宽优化和内容协商
有队头阻塞问题
多请求复用单连接效果有限
2
- 二进制分帧
- 多路复用
- 头部压缩
- 服务器推送
3
- 基于QUIC协议
- 减少连接建立时间
- 消除队头阻塞
- 更快的握手过程
- 改进丢包的处理
HTTP/1.0:简单的单请求-响应模型,适合早期Web应用。
HTTP/1.1:引入持久连接和更好的缓存机制,但仍存在队头阻塞问题。
HTTP/2:二进制分帧、多路复用和头部压缩显著提升性能。
HTTP/3:基于QUIC协议,通过UDP实现更快速和可靠的连接,进一步优化传输性能
访问网页全过程:
用户输入url
DNS 解析:
- 浏览器缓存
- 操作系统缓存
- 本地hosts文件
- 递归DNS查询
TCP连接建立
三次握手
发送HTTP请求
服务器处理请求
请求解析
生成响应
服务器发送响应
- 分片发送
- 数据包处理
浏览器接收和渲染
数据包处理细节
- ip数据包处理
- TCP数据包处理
- 数据包重组
TLS和SSL
如果是加密的三次握手之后要加上:
- 客户端发送client Hello消息,包含支持加密算法和TLS版本
- 服务器回应severHello消息,选择加密算法和TLS版本,并发送服务器证书
- 客户端验证证书,发送Pre-Master Secret,双方生成会话密钥
- 双方使用会话密钥进行加密通信
常用的docker命令:
docker version
docker pull
docker exec -it bin/bash
docker-compose up
docker-compose down
docker-compose logs
docker-compose ps
docker ps
docker network ls
docker images
docker pull
docker push
docker build -t
docker rmi
docker run
docker start
restart stop rm logs
容器和镜像的关系,容器是镜像的一个实例
容器异常排查异常原因:
docker logs
dockers ps -a
进入容器进行查看
检查docker事件
docker events --since "1h"
显示过去一小时内的dockers事件日志
k8s管理docker
Docker 负责容器的创建、运行和管理,是容器化应用的基础。
Kubernetes 负责容器的编排和集群管理,提供了自动化部署、扩展和管理的功能。
k8s的基本概念
Master Node
Master Node
:位于图的上方中央,用于管理和协调整个 Kubernetes 集群。它包含以下组件:
- API Server:处理所有的 API 请求。
- Scheduler:负责将 Pod 分配到适当的 Node。
- Controller Manager:管理控制循环,确保集群处于期望状态。
- etcd:存储集群的所有数据。
Node
Node
:实际运行应用程序的工作节点。图中展示了三个 Node(Node 1、Node 2、Node N),每个 Node 上运行以下组件:
- kubelet:管理该 Node 上的 Pod 和容器。
- kube-proxy:处理 Pod 网络规则。
- 容器运行时(如 Docker 或 containerd):实际运行容器。
Pod
- Pod:Kubernetes 中的最小部署单元,每个 Node 上可以运行多个 Pod。图中展示了每个 Node 上运行的 Pod。
Namespace
- Namespace:用于逻辑上隔离和组织资源的机制。图中展示了一个包含所有 Node 和 Pod 的虚线框表示的 Namespace。
Controller
- Controller(如 Deployment、StatefulSet 等):通过自动化的控制循环管理 Pod 的生命周期。图中用箭头和文字标示在 Master Node 下方。
HPA (Horizontal Pod Autoscaler)
- HPA:根据负载自动调整 Pod 的副本数,图中在中央用箭头和文字标示。
Service
- Service:定义了一组 Pod 的逻辑集合,并提供稳定的访问方式。图中在右侧用箭头和文字标示。
tcp拥塞控制
TCP拥塞控制是TCP协议中的一个重要功能,用于在网络拥塞时调整数据传输的速率,以避免网络拥塞进一步加剧或导致丢包。TCP拥塞控制主要通过四个算法来实现:
- 慢启动(Slow Start):TCP连接刚建立时,发送方会将拥塞窗口(Congestion Window)初始化为一个较小的值,然后随着时间的推移,拥塞窗口逐渐增加,指数增长,直到达到一个阈值(慢启动阈值)。
- 拥塞避免(Congestion Avoidance):一旦拥塞窗口的大小达到了慢启动阈值,TCP发送方就会进入拥塞避免阶段。在这个阶段,拥塞窗口的增长速率变为线性增长,而不再是指数增长,以避免过快地向网络注入数据。
- 快重传(Fast Retransmit):当发送方连续收到三个重复的确认(ACK)时,它会认为有一个数据包丢失,并立即重传该数据包,而不必等待超时发生。
- 快恢复(Fast Recovery):在快重传之后,TCP连接进入快恢复状态,拥塞窗口的大小会减半,然后采用拥塞避免的方式逐渐增加。
这些算法共同作用,使得TCP连接可以根据网络拥塞情况动态调整发送速率,从而维持网络的稳定性和公平性。
进程线程协程三者
进程(Process)、线程(Thread)和协程(Coroutine)是计算机中用于实现并发执行的重要概念,它们分别在不同的层次和场景下提供了并发执行的机制。
进程(Process):
- 进程是操作系统进行资源分配和调度的基本单位,每个进程有自己独立的地址空间和系统资源,包括内存、文件句柄、CPU时间等。
- 进程之间通常是独立的,彼此隔离,不能直接访问对方的资源,通信需要通过进程间通信(IPC)机制来实现,比如管道、消息队列、共享内存等。
- 创建和销毁进程的开销相对较大,因为需要为每个进程分配独立的地址空间和系统资源。
线程(Thread):
- 线程是进程内的一个独立执行单元,多个线程共享相同的地址空间和系统资源,包括内存、文件句柄等。
- 线程之间可以通过共享内存等方式进行通信,但需要考虑同步和互斥问题,以避免数据竞争和资源争用。
- 创建和销毁线程的开销相对较小,因为它们共享所属进程的资源。
协程(Coroutine):
- 协程是一种轻量级的线程,它可以在同一个线程中实现并发执行,通过协作式调度实现多个任务之间的切换。
- 不同于线程的抢占式调度,协程是通过程序员主动让出执行权给其他协程来实现的,因此不需要进行显式的同步和互斥操作。
- 协程通常用于高效的异步编程,可以有效地利用单个线程的资源,实现大规模并发,提高系统的响应速度和性能。
在实际应用中,开发者需要根据具体的需求和场景选择合适的并发模型。通常情况下,进程适用于需要隔离和安全性较高的场景,线程适用于需要共享资源和较小开销的场景,而协程适用于需要高效利用单个线程资源和实现大规模并发的场景。
三者区别:
进程(Process)、线程(Thread)和协程(Coroutine)都是用于并发执行的概念,它们之间有一些关键区别:
并发模型:
- 进程:进程是操作系统进行资源分配和调度的基本单位,每个进程有自己独立的地址空间和系统资源。进程之间通常通过进程间通信(IPC)进行通信。
- 线程:线程是进程内的一个独立执行单元,多个线程共享相同的地址空间和系统资源。线程之间可以通过共享内存等方式进行通信,但需要考虑同步和互斥问题。
- 协程:协程是一种轻量级的线程,它可以在同一个线程中实现并发执行。不同于线程的抢占式调度,协程是通过协作式调度实现的,即协程主动让出执行权给其他协程。
资源消耗:
- 进程:每个进程有自己独立的地址空间和系统资源,创建和销毁进程的开销较大。
- 线程:线程共享所属进程的资源,包括内存、文件句柄等,创建和销毁线程的开销相对较小。
- 协程:协程是在同一个线程内部执行,因此创建和销毁协程的开销很小。
并发性和并行性:
- 进程:不同进程之间是独立的,可以在多核处理器上实现真正的并行执行。
- 线程:线程是进程内的执行单元,在单核处理器上通过线程切换实现并发执行,在多核处理器上也可以实现并行执行。
- 协程:协程在同一个线程内部执行,不能利用多核处理器实现真正的并行执行,但通过协作式调度可以实现并发执行。
通信机制:
- 进程:进程之间通信通常通过进程间通信(IPC)机制,如管道、消息队列、共享内存等。
- 线程:线程之间可以通过共享内存等方式进行通信,但需要考虑同步和互斥问题。
- 协程:协程之间通常通过消息传递或共享数据进行通信,但由于是在同一个线程内部执行,通常不需要考虑同步和互斥问题。
总的来说,进程、线程和协程都是用于实现并发执行的机制,但它们的设计思想和适用场景有所不同,开发者需要根据具体的需求和场景选择合适的并发模型。