提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- go学习
- zookeeper
- Rabbitmq
- 消息中间件
- 零碎知识
go学习
使用go的一个目的就是解决并发的问题
go语言特点:天生支持高并发 语法简单 内置runtime,支持垃圾回收 可以直接编译成机器码,不依赖其他库 有丰富的标准库,可跨平台编译
快捷键及快速生成代码片段
行注释:shift+/ 块注释:shift+Alt+a 查找文件:ctrl+e
go基础
循环
go中的循环只有for循环,使用更加简洁:for 、for range
流程控制关键字
break continue goto
切片,拷贝
由于数组长度定义好之后不能改变,所以我们可以使用切片的方式来使其可以进行增删改查;赋值的方式是直接将地址赋值,所以我们一般在修改其中一个,另一个也会随之改变;而使用拷贝的方式就只会拷贝其中的值,修改拷贝之后的不会对原有的进行改变
函数
go中的函数就是一级公民,没有类的概念
闭包
闭包+函数+引用环境
defer语句
go语言中的defer语句会将其后便跟随的语句进行延迟处理。且将延迟处理的语句按defer定义的逆序进行执行;
按照其直到return前才会执行的特性,可以用来做资源清理;
用途:关闭文件句柄 锁资源释放 数据库连接释放
格式化输出
%% %字面量
%b 一个二进制整数,将一个整数格式转化为二进制的表达方式
%c 一个Unicode的字符
%d 十进制整数
%o 八进制整数
%x 小写的十六进制数值
%X 大写的十六进制数值
%U 一个Unicode表示法表示的整型码值
%s 输出以原生的UTF8字节表示的字符,如果console不支持utf8编码,则会乱码
%t 以true或者false的方式输出布尔值
%v 使用默认格式输出值,或者如果方法存在,则使用类性值的String()方法输出自定义值
%T 输出值的类型
go语言随机数rand.seed()
go语言中的随机数是通过rand.Seed(种子值seedNum)设置种子值,rand.Intn(n int64)获取小于n的随机数。那么获取随机数n之前,需要设置种子值,作用是同一种子值下,同一n,rand.Intn(n),获取的随机数是相同的。不同种子值,同一n,rand.Intn(n),获取的随机数是不同的。尽量保持种子值的随机性,一般采用系统时间的毫秒值作为种子值,因为系统时间的毫秒值是一直变动的。
包管理
一个文件夹下只能有一个package(这里指直接包含的go文件,如果有子目录,那么子目录和父目录分开谈)
一个package文件不能在多个文件夹下,如果一个go文件需要使用不同目录下的同名package,需要在import这些目录时为每个目录指定一个package别名;多个文件夹下有重名的package,其他他们彼此无关
并发编程
除了使用channel实现之外还可以使用mutex互斥锁
goroutine
Goroutine的创建成本很低,可以轻松创建成千上万个
Goroutine的栈空间会根据需要自动增长或收缩
Goroutine之间的通信通过channel进行,更安全方便
Goroutine可以通过调度器在多个操作系统线程之间进行调度,实现并发执行
channel
channel是Goroutine之间进行通信的管道,用于发送和接收值,作用是实现Goroutine之间的同步和数据传递,通过channel,不同的Goroutine可以安全的共享数据
设计模式
工厂模式
也就是创建一个工厂类负责实例化对象;如工厂负责创建产品,客户端访问工厂类负责实例化具体产品
单例模式
可以保证该系统中,使用该模式的类一个类只有一个实例,即一个类只有一个对象实例:垃圾回收器、数据库连接池都是单例模式
抽象工厂模式
抽象工厂模式是围绕一个超级工厂,创建其他的工厂;在工厂模式中,一个具体的工厂对应一种具体的产品,但是,有时候我们需要工厂可以提供多个产品对象,而不是单一产品对象
适配器模式
适配器模式是作为两个不兼容接口之间的桥梁,将一个类的接口转换为另一个类的接口,使得原本因为接口不兼容而不能一起工作的类可以一起工作
享元模式
减少内存的使用,提高程序的性能;可用sync.Pool实现享元模式,可用于重用和存储对象,对象池可以减少对象的创建和销毁,从而提供程序性能
并发编程
协程原理
1、协程是一种轻量级线程,可以在一个操作系统线程中
2、同时运行多个协程
3、协程的调度和执行由go运行时自动管理,我们无需手动控制线程和协程的创建和销毁
4、协程采用了M:N调度器的模型,M调度器用于管理和调度操作系统线程,N调度器用于管理和调度goroutine
5、当一个goroutine遇到IO操作或者阻塞时,M调度器会将其从操作系统线程中移除,转而将其存放到一个专门的goroutine队列中等待唤醒
创建协程
使用go关键字创建一个协程
停止协程
使用context.Context停止一个协程
使用channel进行协程的关闭
使用布尔变量
线程休眠、协程休眠
线程休眠:可以用time.sleep()来实现
协程休眠:线程休眠的本质是让当前线程进入阻塞状态,而协程休眠就是使用协程来实现非阻塞的休眠
协程状态
分为四种:新建状态 运行状态 阻塞状态 死亡状态
协程安全
协程安全的解决办法:
使用互斥锁
使用读写锁
使用通道
避免共享状态
共享变量和临界区
共享变量指多个线程和协程都可以访问和修改同一个变量,临界区是指在程序执行过程中,访问共享变量的代码块
在编写并发程序时,要仔细设计共享变量和临界区且使用适当的同步机制来保证协程安全,例如添加互斥锁
协程安全数据类型
在使用协程时,要注意哪些数据类型是协程安全的、哪些是不安全的。避免因数据竞争问题导致程序出现异常
基本数据类型、字符串、管道都是协程安全的,可以在多个协程间共享使用
切片虽然是协程安全的,但需要注意在多个协程之间对同一切片进行读写操作时,可能出现数据竞争问题;Map是非协程安全的
channel通道
可以通过make()来创建 可以通过chan来定义
通道缓冲
是发送方可以在接受方准备好接受数据之前发送多个值,而不必等待接收方,当缓冲区满之后就会阻止发送方,直到有空间
超时处理
可以用time.After()实现超时处理
非阻塞通道
select语句和default分支实现了非阻塞通道
定时器
go中可以用time包创建定时器,分为单次定时器和重复定时器
单次定时器:time.After()
重复定时器:time.Tick() time.NewTicker()
Ticker
Ticker类型是一个定时触发的定时器,与Timer类型相似,都是基于是时间的调度器;不过Ticker会重复触发,而Timer只会触发一次
协程同步
指多个协程之间按照一定的顺序执行,从而避免数据竞争和死锁
可以通过协程通信来实现:互斥锁 条件变量 信号量 原子操作
协程通信方法
channel通道 共享内存 waitgroup Mutex互斥锁 Atomic原子操作
等待和通知
可以用以下三种方法:channel sync包中的waitgroup Cond类型来实现
go中协程同步和共享内存的方法
sync.Mutex和sync.Cond channel WaitGroup Atomic Select
golang协程可见性问题也可以用锁和原子操作来保证
go中锁的类型
Mutex RWMutex Cond Once
条件变量Cond
Cond常用方法:
Wait() 等待条件变量,阻塞当前协程,释放锁
Signal() 通知一个等待的协程继续执行,通常与lock()Unlock()一起使用
Broadcast() 通知所有等待的协程继续执行,通常与lock()Unlock()一起使用
golang死锁
发生的原因:
Mutex的嵌套使用 预分配资源不足 通道读写顺序不当
避免死锁的方法:
避免互斥锁的嵌套使用,采用更高级的同步原语:读写锁、条件变量(Cond)等
避免使用全局变量,尽量使用局部变量或者通道传递数据
使用带缓冲区的通道,可避免协程阻塞等待通道的读写操作
使用超时机制
使用可重入函数,避免函数内部调用一个可能会阻塞的函数
协程池
协程池是一个管理和复用协程的技术
golang Context在协程中的使用
例如使用WithTimeout方法创建超时上下文防止后台任务无限期等待
通过使用context可以有效控制协程之间的通信和请求处理,并避免无限期等待和死锁等问题
sync.Once
通过使用Once可以确保一个操作只会被执行一次,避免重复执行导致的问题;例如编写单例模式就可以使用到它
GMP
GMP是golang中调度器的三个重要组成部分之一,是Goroutine执行器
G 代表轻量级线程 M 代表调度器线程 p 代表逻辑处理器
GMP为Golang提供了高效、轻量级的并发模型
GMP调度器的主要任务是将协程绑定到线程上,然后调度器执行线程;为了高效调度,调度器使用了很多优化手段:如抢占式调度
网络编程
tcp流行框架
gRPC
Gnet
TcpServer
Gorilla websocket
epoll相关
网卡将数据写入内存之后,网卡向cpu发出一个中断信号,操作系统便能知道有新数据到来,再通过网卡中断程序去处理数据
进程阻塞不会占用cpu资源是因为进程A阻塞之后,它的引用会被添加到等待队列中,A不会继续往下执行代码,所以也不会占用cpu资源
操作系统如何知道数据对应哪个socket
一个socket对应一个端口号,网络数据包中包含了ip和端口信息,内核可以通过端口号找到对应的socket
如何同时监听多个socket数据
select:西安准备一个数组存放所有需要监视的socket,调用select,如果所有socket都没有数据,就会被阻塞,知道其中有socket接收到数据,select返回并唤醒进程;最后再遍历存放socket的数组,就可以找到就绪的socket
epoll设计思路:(功能分离、就绪列表)
先用epoll_ctl维护等待队列,再用epoll_wait阻塞进程
内核维护一个就绪列表,引用收到数据的socket,当进程别唤醒之后,只要获取就序列表的内容就可以知道哪些socket收到了数据
epoll的原理和流程
创建epoll对象:某个进程调用epoll_create方法时,内核就会创建一个eventpoll对象;由于内核要维护就序列表等数据,而这些数据都可以是eventpoll的成员
维护监视列表 :当socket收到数据后,中断程序会操作eventpoll对象,而不是直接操作进程。
接收数据:当socket收到数据后,中断程序会给eventpoll的"就绪列表"添加socket引用;eventpoll对象相当于是socket和进程之间的中介,socket的数据接收并不直接影响进程,而是通过改变eventpoll的就绪列表来改变进程状态。当程序执行到epoll_wait时,如果rdlist已经引用了socket,那么epoll_wait直接返回,如果rdlist为空,阻塞进程
阻塞和唤醒进程:当socket接收到数据,中断程序一方面修改rdlist,另一方面唤醒eventpoll等待队列中的进程,进程A再次进入运行状态(如下图)。也因为rdlist的存在,进程A可以知道哪些socket发生了变化。
CSRF攻击
在web应用中CSRF是一种常见的安全威胁,为了防御它,可以在cookie中添加CSRF token,每次请求进行验证,来保证安全
url组成
由协议、服务器、端口、路径、查询五部分组成
Restful风格编程
相对于SOAP和XML-RPC,REST更加简单明了;他是一个资源定位、资源操作的风格,只是一种风格;
HttpRouter
HttpRouter是一种轻量级高性能的golang HTTP请求路由器;
gin框架就是以HttpRouter为基础开发的
单元测试
创建测试用例测试函数
可以使用helper函数让报错定位更加准确
setup/teardown视为在调用m.Run()前后做一些额外的准备和回收工作
反射
Go语言中的反射是指在运行时检查程序的结构、变量类型和值,并可以动态地操作它们的能力。反射包(reflect package)提供了一组函数,可以在运行时进行类型检查、获取和修改变量的值、调用方法等。
时间处理库Carbon
一些零碎内容
bcrypt:更加高级的加密算法,密码一般都会用这个加密算法进行加密
访问控制框架: casbin
使用swagger生成api接口文档;它可以根据注释来生成接口文档
jwt:是一种跨域认证解决方案,它规定了一种token实现方式,多用于前后端分离项目中
viper:golang配置管理库,很多项目都用它来构建,像docker等等
新特性工作区workspace;用在一个大型项目分为几个小项目,想要互相导包就需要使用这个工作区,否则不同的项目之间不能互相导包
泛型:就是函数的参数,支持更多的类型,而并不是某一特定的类型
cast:golang类型转换工具;cast.ToString()等等
flag:用flag包处理命令行参数
zookeeper
特点
顺序一致性 原子性 单一视图 可靠性 实时性(伪实时性)
数据模型
树形结构,是由ZNODE节点组成的树形结构
CAP理论
指在一个分布式系统中,一致性、可用性、分区容错性只能同时满足两个
选举机制
使用基于ZAB协议的leader选举机制;节点间相互通信,通过提议和投票确定新的leader,当收到多数节点的票数即成为新的leader
Rabbitmq
消息中间件
基于队列与消息传递技术,在网络环境中提供同步或异步,可靠的消息传输的支撑性软件系统
消息中间件的模式:点对点模式 发布订阅模式
kafka
为了对kafka消息进行分类,保存在kafka中的数据按照主题(topic)进行了逻辑分类;为了实现负载均衡和水平扩展,kafka将主题划分为多个分区(partition),会根据分区规则选择把消息存储在哪个分区中;kafka副本能够对抗部分服务器宕机带来的数据不可用情况
kafka集群中每个服务器被称作broker
kafka不会向消费者推送消息,消费者必须从kafka主题分区中提取消息
消费者组最大的并行度等于该主题的分区数
零碎知识
seed
seed通常用于生成随机数序列,种子的选择对生成的随机数序列具有重要影响。如果使用相同的种子,将获得相同的随机数序列。因此,在实际应用中,通常会使用不可预测的种子,例如使用时间戳或其他随机数来设置种子,以获得更随机的结果。
salt
"salt"(盐)是一种随机生成的数据,用于增加密码哈希的安全性。哈希函数是将输入数据转换为固定长度的输出,通常用于存储密码的散列值。
使用盐可以防止相同的密码在哈希过程中产生相同的散列值。通过将盐与密码组合,并进行哈希运算,可以生成一个唯一的散列值。这样,即使两个用户使用相同的密码,由于使用了不同的盐,最终生成的散列值也会不同。
在Go语言中,可以使用crypto/rand包来生成随机的盐。例如,可以使用rand.Read()函数生成随机的字节切片作为盐。然后,将盐与密码进行组合,并使用哈希函数(如SHA256)对其进行哈希运算,以生成最终的散列值。