2021/06/06 GO语言(二)

21丨构建可复用的模块(包)

包作为go语言里最基本的可复用模块。
以大写字母的申明,比如函数,struct,活struct里的成员,都是可以被包里的代码访问的。
2.代码的package可以和所在的目录不一致(java一般把包名和目录名设置成一致)

在这里插入图片描述
在这里插入图片描述
加一个path路径变量
在这里插入图片描述
在这里插入图片描述
idea也可以直接加path
在这里插入图片描述
继续使用斐波那契数列,可以用复用包的方式来引用这个代码

调用上面创建好的包,目录是要从src开始

在这里插入图片描述
小写的申明是不可以被包外访问的
在这里插入图片描述
小写的函数名是不可被包外访问的

在这里插入图片描述
修改成大写

在这里插入图片描述
这样就可以被访问了

在这里插入图片描述
package里特殊的方法,init,初始化方法,在所有main函数调用前,所有依赖的package的init方法都会被执行。
2.不同包的init函数按照包导入的依赖关系进行执行。
3.包和包之间还有依赖顺序
4.每个包可以含多个init方法

在这里插入图片描述
代码展示:
在这里插入图片描述
同一源码文件,定义两个init方法

在这里插入图片描述
在这里插入图片描述
如何去使用别人的远程的package,下面是一个线程安全的map,syncmap适合读90%以上的,性能会很好
在这里插入图片描述
通过go get命令导入
在这里插入图片描述
写一个测试程序,已经在程序里引用外部的远程的包
在这里插入图片描述
在这里插入图片描述
第三方的package,如何写的,把src目录放进去,就库可以获取 到了
在这里插入图片描述

22丨依赖管理

go语言的依赖管理有点不足。
1.同一开发环境下,不同项目使用的包不是一个版本,每个项目里的gopath都不一样,可能会冲突。
2. 缺少对特定版本的管理 。
在这里插入图片描述
go在1.5之后,提供 了一个vendor路径,可以添加到当前project当前的路径下。
查找依赖包的路径的解决方案如下:
1.先去找当前包下的vendor目录。
2.向上级目录找,直接找到src下的vendor目录。
3.在gopath下面查找依赖包。
4.在GOROOT目录下查找。

在这里插入图片描述
第三方的依赖软件,早期有godep,golide用的比较,dep比较新。

在这里插入图片描述
mac下可以这么安装

在这里插入图片描述
按照提示就行,会提示你选择concurrent_map的版本

在这里插入图片描述
在这里插入图片描述

会生成一个yaml文件,并且有版本
在这里插入图片描述
glide install安装依赖
在这里插入图片描述
install以后会生产一个vendor路径
在这里插入图片描述
vendor目录已经加入到了依赖的所属路径中,等于可以指定依赖包的路径,不同的package也可以依赖不同版本的同一个依赖了

在这里插入图片描述

23丨协程机制

协程是一个更轻量的线程。
jdk5以后,java的默认线程是1M
协程只有2K
第二点,就是和系统内核对象(KSE)的对应关系。
java的线程和系统进程是1比1,
协程和KSE对象是多对多

在这里插入图片描述
下面是一个多对多的图,kernel entity就是系统线程有cpu直接调度,调度的效率很高,这种情况会有一个问题,线程会发生切换,会牵扯到任何对象的切换,这是一个非常大的消耗。
如果有多个协程或进程都是一个系统线程里,他们之间的切换消耗就少了

在这里插入图片描述
M是系统线程,P是go语言实现的协程处理器,每个processor挂一个协程队列,可以有一个协程是正在运行状态的,依次运行这些协程。(是否会有一个协程执行时间太长,后面都等着)
当go起来的时候,会有一个守护进程去做一个计数,这个processor完成的数量,当发现某个processor完成的数量一段时间都没有发生变化,就会往协程的栈里插入特别的标记,当协程运行的时候,遇到非函数的时候,就会读到这个标记,就会把自己叉掉等候协程的队尾,换别的协程继续执行。
当某一个协程被系统中断了,(就是IO需要等待的时候)为了提高并发,processor会把自己移动到另一个可使用的系统线程中,继续执行它所挂的队列里的其他的协程,这个被中断的协程苏醒后,会把自己加入到其中某一个processor的协程等待队列里或者是加入到全局等待队列当中。
注意:当一个协程被中断的时候,寄存器里的状态会保存到协程对象里,当协程再次获得运行的机会的时候, 这些会读取状态继续运行。

在这里插入图片描述

代码里如何去启动一个协程在这里插入图片描述
**测试程序速度快,可以加上等待,每一次执行到时候,可以看到顺序不一致,这是因为协程调用的顺序,不是按照我们方法的顺序去调用的 **

在这里插入图片描述
很多人会犯这样的错误,i这个变量在所在的test的协程里以及它启动的其他协程里被共享了,共享变量就存在竞争条件,要用锁的机制完成

在这里插入图片描述
下面的方式为什么可以,因为go的方法调用都是值传递,传递i的时候,在i里的值会复制了一份,每个协程里拥有的地址不一样,不存在竞争关系,所以下面 的可以正确执行
在这里插入图片描述

24丨共享内存并发机制

java的程序员很熟悉,就是用锁来进行并发控制
在这里插入图片描述

go里可以用下面的,其实java里也是有的
在这里插入图片描述
有一个变量counter,协程里对counter做自增,总共是5000个协程

在这里插入图片描述
**得出的结果不符合预期,是4678,采用的couter在不同的协程里做自增,不是一个线程安全的程序 **
在这里插入图片描述
要想线程安全,就需要进行一个锁的保护,使用mutex,使用锁一般会使用defer,类似一般在finally,释放lock,避免出现异常,不释放锁

有锁的情况下,counter的值符合预期
在这里插入图片描述
sync另外一个同步线程的方法,相当于java里join的功能,等待其他线程执行

在这里插入图片描述
**WaitGroup真正意思是只有当我wait所有的这些线程完成之后,才能继续 往下执行 **

当sleep去掉后,其实执行的结果就 不符合预期了。在这里插入图片描述
可以用更准确 的方法来做这个
在这里插入图片描述
有一个等待事务完成了就 wg.Done
在这里插入图片描述
**这样是符合预期的,但是第二种方法更好,因为第一种是不知道5000的计算时间是多少,强制给 了个1秒,下面的方法更快,因为正确等待了每个协程的结束 **
在这里插入图片描述
wait会阻塞住,等待之前所有的任务完成,才会向下运行
在这里插入图片描述
rwlock和mutex的区别是,rwlock对于读锁和写锁进行了一个分开,当一个共享内存被读锁锁住的时候,另一个读锁去 锁的时候,可以知道不是一个互斥锁,线程是可以去读共享内存,写锁有这样的情况是互斥的

25丨CSP并发机制

go语言特有的csp并发机制,主要是建立一个通道 ,完成两个 通讯实体的协调
在这里插入图片描述
在这里插入图片描述
csp和actor model俩个之间还是有不少的差别,actor model是一种直接通信的机制,不需要中间的channel管道,而csp是通过channel来完成的,它相当于是有一个中间人,让两个 通讯之间的耦合性相对松一些。
另外actor model是 通过mailbox进行消息存储的。
go里的channel是有容量的限制 。
go的协程会主动去channel里去处理channl传来的消息和actor modle被动处理模式的模式是不一样的。

在这里插入图片描述
go的channel机制有两种,第一张机制是比较常见,通信的两方必须要在channel上才能完成交付,另一方不在的时候,另外一方也会阻塞等待,等待接收方出现,才能继续向下执行 。
另一种机制叫buffer channel,是消息的发送者和接收者是可以松耦合的关系了,hannel可以设置为容量,在容量还没有满的情况下,放消息的人往channel里放都是可以放进去的,如果channel满了,就必须等待另一放拿走消息,channel容量部满的情况。

在这里插入图片描述
java里有一种机制是future,当启动一个函数或者test的时候。并不需要马上知道结果,需要知道结果的时候,才去调取结果,在这个期间可以执行其他的操作。在catch结果的时候,如果结果还没有出来,就留在哪里,结果出来了,就可以得到结果,继续向下执行。
这样可以大大提升程序的效益。

在这里插入图片描述

代码里如何通过csp的方式来实现,一个service来做延迟,otherTask做其他操作,延迟输出一些信息
5分17秒
客户端先调用service,再去调用otherTask
在这里插入图片描述
输出结果
在这里插入图片描述
进行改造,类似java的future一样运行。
1.对service进行了一个包装,当运行的时候调用另一个协程去运行,而不是阻塞在当前的协程。
2.当返回的结果的时候,只是返回一个channel的话,外面需要结果可以在channel里等待,运行完再返回channel。

在这里插入图片描述
需要结果的时候,把数据从channel里取出来,channel里放数据和取数据,是用<-箭头的方式。

在这里插入图片描述
申明一个channel

在这里插入图片描述
由于service的还没执行完,所以otherTask的就先打印了。
在这里插入图片描述
最后打印服务退出。只要之间的协程没处理完,这条service-exited就被阻塞住了
在这里插入图片描述
可以做一个改进,channel改成一个buffer的channel,和普通chanel申明的区别在于,就是设置容量的大小。

在这里插入图片描述
和上一次相比,执行结果发生了变化,service exited在return完就立即 执行 了,service就退出了,释放掉了 ,没有阻塞住service,这是一种更高效的写法。

在这里插入图片描述

26丨多路选择和超时

在这里插入图片描述
在这里插入图片描述
看着像switch,每个case后面跟的是一种阻塞事件,从不同的channel上等待一个消息。只要case后面任何一个 阻塞事件处于非阻塞状态,channel里有消息就会执行case定义的事件部分。不要依赖case的顺序,这是没有保证的。
如果所有channel都没有准备好,都是阻塞住的, 如果有default这个selectionn存在,直接走default

在这里插入图片描述
**利用这种机制可以进行超时的一个 控制,想要从channel获取消息的时候,不希望等待事件超过一个时间,可以用time.after,在某一个duration之后返回一个消息,没有达到时间限制后,case会阻塞住,case达到要求后,消息会返回,阻塞中止。
**

在这里插入图片描述
在这里插入图片描述
在service完成的时候,一直会处于阻塞状态,一直到service指定的时间。在这里插入图片描述

加上一个超时机制,service多长时间没有返回,就可以直接中断,超时运行代码,slowresponse是一个比quickfailure还要可怕的错误。

第一个正常返回输出结果,第二个错误,timeout
在这里插入图片描述
这个时候程序正常返回,因为耗时只有50Ms

在这里插入图片描述
改成500ms试试
在这里插入图片描述
**程序现在是错误状态,因为走了timeout分支 **

在这里插入图片描述

27丨channel的关闭和广播

如何关闭channel和 关闭channel引起的一种广播机制

程序中经常会有,其中一个线程或者协程是一个消息的产生者,另外是一个消费者。中间通常是一个buffer来缓冲消费者需要的数据。
要做线程安全,就需要一个锁的机制。

在这里插入图片描述
生产者不停往channel里放数据
wg就是waitGroup,它主要是希望演示程序要等dataProducer执行完

在这里插入图片描述
另一个就是receiver,不断从channel里得到数据,打印出来

在这里插入图片描述
在这里插入图片描述
打印出0-9的数据

在这里插入图片描述
有一个问题,dataproducer在往channel里放数据的时候,有可能 数据已经放完 了,receiver可能不知道数据有没有放完,所以要做一个约定,往里面放一个特殊的token,比如放-1,就代表数据完了。
但是如果recevier有多个,那么producer就需要知道有都少个receiver,就要放很多-1,否则无法让所有的receiver停止。
这样写程序,耦合度很高

go里面有一个特殊操作,叫关闭channel,数据发送好后,可以把channel关闭
在这里插入图片描述
receiver如何知道channel被关闭,channel关闭后,即便receiver处于阻塞状态,也会立即返回,这个时候可以获得两个返回值,一个是数,另一个是布尔值。当等于false的时候,是一个关闭状态。
在这里插入图片描述
channel如果关闭,没有设置ok这个布尔值,通道 也会马上返回,这样返回是这个通道类型的0值(比如通道是int型,就返返回0)

在这里插入图片描述
修改recvier写法
在这里插入图片描述
输出是一样的

在这里插入图片描述
还可以有多个receiver
在这里插入图片描述
顺序会有变化,因为协程调度的问题

在这里插入图片描述
如果继续往关闭的通道发消息,程序会panic
在这里插入图片描述
通道关闭,如果不判断,直接接收,改成11,就多一次接收
在这里插入图片描述
但是程序依然退出,并没有阻塞在这里,关闭了通道,如果第11次想要从通道里读取数据,虽然没有判断channel是否关闭,但是会立即返回,返回这个通道 定义类型的0值。

在这里插入图片描述
正常这里会加上一个布尔值的接收,true和false来判断通道是否关闭

在这里插入图片描述
在这里插入图片描述

28丨任务的取消

如何做一个任务的取消,判断一个变量true和false,把任务取消掉,用csp发的模式发消息退出任务。
在这里插入图片描述
isCancelled去检查是否从cancelChan里收到一个消息。如果收到消息就返回true,如果没有收到消息,就default,消息被阻塞的时候,会执行default的分支。
在这里插入图片描述
在这里插入图片描述

起了5个,去无限循环任务
在这里插入图片描述
运行之后发现只有第4个被cancelld掉,因为channel传递过去的只有一个cancel的信号,有5个协程,其他协程都没有取消,如果有5个需要取消,就设置成5个,这样就比较耦合,需要事先知道有多少个render需要cancel掉。
可以用 channel关闭机制,实际上是一种广播机制,关闭同样会使阻塞状态的channel接收数据被唤醒,执行下去,可以利用这种机制来做取消。

在这里插入图片描述
换成第二种方法,close掉。
在这里插入图片描述
所有的协程都受到了close channel,是一个广播的机制,所有都被cancel掉了。
在这里插入图片描述

29丨Context与任务取消

取消任务有更大的挑战,任务有关联,起了相关的子任务。
在这里插入图片描述
如果取消叶子节点的任务是毫无问题的

在这里插入图片描述

在这里插入图片描述
取消中间层的任务,就需要把关联的任务也取消掉
在这里插入图片描述
在golang的1.9以后把context正式并入到包里了,在跟节点的上下文,

在这里插入图片描述
ctx可以传到子任务里,cancel取消之后,子任务传下去也会依次取消
在这里插入图片描述
ctx.Done()看看是否有取消通知
在这里插入图片描述
在这里插入图片描述
不需要channel,创建一个context
在这里插入图片描述
在这里插入图片描述

30丨只运行一次

下面的java代码,确保多线程下的线程安全就是,只运行一次,叫单例模式
在这里插入图片描述
在多线程的情况下,可以确保每一次只调用一次

在这里插入图片描述
在这里插入图片描述
方法只执行一次,所有不需要判断是否为空
在这里插入图片描述
尝试在多个线程里调用这个
在这里插入图片描述
在这里插入图片描述
只输出了一次create obj,这个对象只被创建了一次,下面的地址也都是一个地址,是同一个对象。

在这里插入图片描述

31丨仅需任意任务完成

GO的csp控制模式
在这里插入图片描述
要做多少个taskrunner,就起多少个协程去做这个事情,如何做到第一个返回就返回
在这里插入图片描述
就可以create一个channel,channel有一种机制,当第一个人往里面放了消息之后,那么在接收channel的receiver可以从阻塞种被i唤醒,代码继续运行

在这里插入图片描述
函数最后return就是尝试从channel里获取数据,一旦拿到数据就会renturn出去。

在这里插入图片描述
这里会有潜在的问题,NumGoroutine会输出当前系统中的协程数。
输出2,有两个协程,
按照道理已经完成所有的任务了,但是结束了还有11条协程,如果一个服务器每次调用,都会有很多协程被阻塞,是很严重的,会导致资源耗尽的问题。

在这里插入图片描述
如何fix这个问题,可以用 buffer channel,每一个消息的产生者,只要有buffer就不需要等接收者拿走消息,是一个解耦的关系
在这里插入图片描述
这样就利用了buffer channel,避免协程泄露
在这里插入图片描述

32丨所有任务完成

如何用channel 下的csp的机制来做

在这里插入图片描述
直到取到runner的个数结果,才会return

在这里插入图片描述
在这里插入图片描述

33丨对象池

可以把所有的对象现在池里放好,每次用就get,还的时候再还到这个channel上
在这里插入图片描述
创建一个对象池,所有有new的方法
在这里插入图片描述
这个对象是一个空结构,先预置,初始化一些链接

在这里插入图片描述
在这里插入图片描述
多路选择器,最好在取对象的时候有一个超时控制。
在这里插入图片描述
当对象放不进channel会阻塞,防止阻塞,这里走default分支 ,就立刻返回异常。
在这里插入图片描述
下面是一个测试的pool程序,一个循环去get,设定一个超时,get完之后,release掉。

在这里插入图片描述
在这里插入图片描述

在不返回的情况下,对10个对象,进行11次拉取
在这里插入图片描述
最后 一个等待1秒后依然拿不到对象,就出现了timeout
在这里插入图片描述
在慢的池里再i放一个对象,就得到了一个overflow异常

在这里插入图片描述
如果想要做一个存不同对象的池,这里可以换成一个空接口,空接口可以放任何的pool对象。每次出来都要断言,要知道是什么对象
在这里插入图片描述
实际还是建议不同的池缓存不同的对象

34丨sync

它其实是一个对象的缓存

在这里插入图片描述
sync.Pool是跟 processor有关 的,在每一个processor都分两个部分,对于sync.pool,一个叫私有对象(协程安全的,写入的时候不需要锁),一个叫共享池(协程不安全,写入的时候需要锁)。
每次获取对象都会从私有对象去取,如果存在就获得,如果不存在会去当前的processor所在的共享池里去获取,还没有就会去其他的processor的共享池里去获取。
如果所有池子都是空的,就是用户sync.pool指定的new函数产生一个新的对象返回。

在这里插入图片描述
放回也是类似的,私有对象里存在就会往共享池里放,

在这里插入图片描述

创建新对象 的方法,会在所有方式都没找到对象的时候,就会调用这个方法进行创建对象
在这里插入图片描述
获取的时候(int)做一个断言,下面返回
在这里插入图片描述
每一次GC都会清除一个sync.pool的对象,当GC完成后,你的sync.pool里的对象都被清空了。但是GC一般都是通过系统来调度的,是没有办法干预的。

在这里插入图片描述
在这里插入图片描述
取出的时候因为是空接口,所以要创建一下
在这里插入图片描述
再去取出
在这里插入图片描述
取出一个值是100,放回一个3

在这里插入图片描述
虽然放了一个3对象,但是在GC的时候被清空了,下次访问的时候还是看到 了create a new object和100

在这里插入图片描述
实际上每次如果不做put操作,每次创建的对象都是被拿出来了

在这里插入图片描述
创建一个池,初始化值是10,往里面put3个对象

在这里插入图片描述
起10条协程,输出每次从里面 get的结果。

在这里插入图片描述
在这里插入图片描述
取出3个100后,后面的processor取不到对象了,就去创建
在这里插入图片描述
sync.pool实现了线程安全,会有锁的开销就要和创建对象进行比对,看哪个更值得。
降低复杂对象的创建和GC代价。
生命周期受GC影响,不适合于做连接池等,需要自己管理生命周期的资源的池化。

在这里插入图片描述

35丨单元测试

在这里插入图片描述
**在上面的基础上加test,一般在做功能测试的时候,都加上表格测试法,准备一组输入的组合。
然后expected是对组合期待的值,然后一个程序去把所有组合都去尝试,然后输出错误信息 **
在这里插入图片描述
把原来的改改错误
在这里插入图片描述
输出就可以看到期望是多少,实际能得多少
在这里插入图片描述
上面是非常常规的单元测试写法

在这里插入图片描述
fail和error的区别就是测试失败,剩下的代码还会执行

在这里插入图片描述
、faillnow和fatal,就是测试失败,剩下的代码中止,其他测试继续执行

在这里插入图片描述
上面依然可以看到end的输出。fatal,start输出后,end就没有再输出,也就是程序中断了

在这里插入图片描述
命令行要加-v

在这里插入图片描述
其实再idea里一直能看到测试的覆盖率

在这里插入图片描述
在这里插入图片描述
就可以看到和idea里相同的输出信息

在这里插入图片描述
不加cover就没有cover的信息了

在这里插入图片描述
java的程序员习惯junit后,习惯使用很多的断言,go里也可以使用,就可以去下载下面的
在这里插入图片描述
获取成功后就可以使用了

在这里插入图片描述
在这里插入图片描述
这样也是可以完成相同工作的

在这里插入图片描述

36丨Benchmark

测试其实还可以用benchmark,更多的是对某段程序代码的性能做测评,调用要以Benchmark开头
在这里插入图片描述
现在有两个测试程序
第一个是把字符串拼在一起,用+=链接。
第二个是用bytes.buffer,不停地往buffer里写

在这里插入图片描述
写一个benchmark,在原有程序上就可以改造,resetimer和测试无关,主要是把测试部分包起来
在这里插入图片描述
测试使用stringbuffer的函数

在这里插入图片描述
看一下对比,实际上使用buffer的性能要比+=的性能来的强

在这里插入图片描述
要研究为什么快,为什么慢,可以加参数 -benchmem

在这里插入图片描述
在这里插入图片描述
内存大小和次数,一次allocs操作和
string每次操作都是回分配一个新的string,有一次内存的操作,buffer就一次

在这里插入图片描述

37丨BDD

在这里插入图片描述
在项目验收的时候,会有一个是不是bug的争议

在这里插入图片描述
沟通是软件开发中最困难的事情了,客户和工程师的词汇是不一样的,BDD是很大程度上来解决这个问题的

在这里插入图片描述
敏捷开发一定会见到story card(反面应该是写这个方案怎么被验收的)

在这里插入图片描述
通常可以用下面的来描述,given是描述某一个初始化的backgroud,当一个什么样的事件发生后,和对什么样的期待这样一个部分。
这样就能让专家知道,这个story是如何被验收的

在这里插入图片描述
所以go里有常用的BDD的一些框架,常用的goconvey
在这里插入图片描述
安装

在这里插入图片描述
加上这个点,说明impor这个方法所有名词是在当前名称空间的

在这里插入图片描述
就可以直接使用这个方法
在这里插入图片描述
方法可以做为参数传入

在这里插入图片描述
直接给了一个断言

在这里插入图片描述
在这里插入图片描述
这个package还提供了一个web界面
在这里插入图片描述
显示了我们代码的运行结果

在这里插入图片描述
故意把这个程序改错

这样就出现了一个问题

在这里插入图片描述

38丨反射编程

go语言的instance会返回两个类型,一个是类型,一个是值

在这里插入图片描述
拿到一个instance如何去做反射,拿到一个instance可以反射它的类型,和它的值

在这里插入图片描述

在这里插入图片描述
内置了一个枚举

在这里插入图片描述
传入一个空接口,空接口可以传入任何类型,选择了几个类型
在这里插入图片描述
传入了一个64位浮点,checktype

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这些先注释掉

在这里插入图片描述
在这里插入图片描述
假如给一个指针64位

在这里插入图片描述
在这里插入图片描述

返回的类型应该是64位指针

在这里插入图片描述
在这里插入图片描述
反射可以做一些灵活的程序,比如用字符串来调用程序方法
在这里插入图片描述
在这里插入图片描述
定义了一个方法updateage

在这里插入图片描述
在这里插入图片描述
update变成了一个新的值

在这里插入图片描述
反射还经常做另外一种标记,java和c++里一些struct的annotation

在这里插入图片描述
拿到反射的值

在这里插入图片描述
在这里插入图片描述

39丨万能程序

反射内置的有意思的语法,deepequal,前面说切片和map之间不能比较,想要比较也可以用reflect里的deepequal

在这里插入图片描述
在这里插入图片描述
先创建两个map实例

在这里插入图片描述
下面创建了一系列切片

在这里插入图片描述
在这里插入图片描述
进行一些比较的关系

在这里插入图片描述
在这里插入图片描述
下面是一些结构体

在这里插入图片描述
把map对应的值填上

在这里插入图片描述
这个方法是如何实现的,空接口接收任何参数,然后后面对map的值进行设定,需要填充的值

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这是核心部分,有这个field就去填充值,而且value类型要和field类型一致
在这里插入图片描述
传进来的是一个指针类型,二真正fieldbyname是一个结构类型,所以要用Elem这样一个方法,可以帮你获得指针指向的结构
在这里插入图片描述
然后就可以调用fieldbyname
在这里插入图片描述
初始化都是空的,然后进行填充

在这里插入图片描述
填充项都建议输出进去了

在这里插入图片描述
修改值

在这里插入图片描述
在这里插入图片描述
有很多注重配置的程序可以用反射来写,但是debug更加困难了,反射会大大降低程序的性能

40丨不安全编程

go语言里不支持强制类型转换,如果使用了不安全指针unsafe.pointer其实可以转换成任何类型

在这里插入图片描述
使用unsafe把指针取出来

在这里插入图片描述
强制转换成一个浮点
在这里插入图片描述
上面是指针,下面是unsafe的值

在这里插入图片描述
下面这种方式转换时比较安全的

在这里插入图片描述
官方的package里有storepoint提供原子操作,为了安全,可以 把写的地方,放在另外一个地方,写完后替换读的地方,切换的时候有一个线程安全的动作

在这里插入图片描述

在这里插入图片描述
值是一个unsafe.poniter

在这里插入图片描述
写100个数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
读的操作是输出的buffer的指针,内容

在这里插入图片描述
这样就实现了安全的buffer共享读写

在这里插入图片描述

41丨实现pipe-filter framework

在这里插入图片描述
在这里插入图片描述
pipe-filter适合做一些数据处理

在这里插入图片描述
请求过来-执行处理-验证-解析-对选出来的进行排序,等于对静态的数据做转换,生成另一个数据

在这里插入图片描述
在这里插入图片描述
pipe-filter其实是一个数据封装处理的过程,只要传过去的数据filter能够处理,就会处理,是一种松耦合的状态

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
字符串封装成字符数组

在这里插入图片描述
如果都可以转换成int,就变成int切片

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
接口只有一个就是Process处理数据
在这里插入图片描述
都是空接口,起了一个别名

在这里插入图片描述
先去检测类型

在这里插入图片描述
sum也是一样,先去检查
在这里插入图片描述
分别创建三个filter

在这里插入图片描述
在这里插入图片描述
把上有上一个结果做位下一个输入传进去
在这里插入图片描述
在这里插入图片描述
pipe filter 是最常用的数据处理架构模式,上一次的结果作为下一次的输入

42丨实现micro-kernel framework

优点是易于扩展,错误隔离,非常能够保持好架构一致

在这里插入图片描述

在这里插入图片描述
定义一个接口,不同的接口可以作为插件插入进来,作为一个扩展点
在这里插入图片描述
接口定义,启动停止,初始化
在这里插入图片描述
agent对象的定义,cancel和ctx是agent相关的,如果程序两次调用agent,agent启动两次,是否会启动两次colllector的start方法,那也许就会多起一个collector的协程,所以就必须有一个state状态,能够在超过正确的情况下,执行正常的操作

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
agent实现了一个onevent方法

在这里插入图片描述
第一步注册collector到buffer里

在这里插入图片描述
调用collector的start方法,使用context让它在一个协程里运行,我们可以在agent;里关注它 的一个协程运行模型,不必在collector里关注

在这里插入图片描述
在这里插入图片描述
每个collector收到的事件都会存到一个buffer里

在这里插入图片描述
用一个扩展点扩展接口,通过kernel进行一些简单的插件管理,对多个插件做统一的操作
在这里插入图片描述
在这里插入图片描述
一些公共流程都是放在kernel里的

在这里插入图片描述
在这里插入图片描述
start只是进行一个无限的循环,每隔一段事件就去onevent里放对象,content

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
内部用了channel的方法来stop
在这里插入图片描述
stop住了才会进行下面的

在这里插入图片描述
外面调用agent的canel方法会调用每一个cancel
在这里插入图片描述
起了两个collector,注册到agent里,1秒以后关闭agent
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
两次start,有一次状态不对,agent给了一个错误信息
在这里插入图片描述
所以就只能看到一次start的输出

在这里插入图片描述

43丨内置JSON解析

使用反射的性能比较低

在这里插入图片描述
三个结构
在这里插入图片描述
在这里插入图片描述
使用json内置的unmarshal空对象,最后输出一个string、
在这里插入图片描述
这样就完成了
在这里插入图片描述

44丨easyjson

尽量少的使用反射来使用json解析,因为性能很差 ,推荐使用easyjson
在这里插入图片描述
安装

在这里插入图片描述
这次就不使用struct type了,因为是代码生成的
在这里插入图片描述

在这里插入图片描述
会在你的package下自动生成一段代码

在这里插入图片描述
使用的是umashall,把json字符串放进去
在这里插入图片描述
在这里插入图片描述
使用benchmark对代码进行了一个包裹
在这里插入图片描述
性能的对比
在这里插入图片描述

45丨HTTP服务

起一个http服务
在这里插入图片描述
不同路由的处理逻辑
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
返回当前时间
在这里插入图片描述
有一个默认的路由

在这里插入图片描述
在这里插入图片描述
最长匹配规则,匹配最长的规则
在这里插入图片描述
在这里插入图片描述

46丨构建Restful服务

实际生活中使用的第三方库
在这里插入图片描述
也可以使用第三方的handler

在这里插入图片描述
访问对应的一个key,帮助不仅仅在解析url上。
在这里插入图片描述
使用的prefix tree 性能比传统的http match性能要高很多

在这里插入图片描述
go get自己安装一下
在这里插入图片描述

在这里插入图片描述
handler里会有httprouuter.Params这样一个参数。

在这里插入图片描述
在这里插入图片描述
可以看到后面的字符串被解析出来了

在这里插入图片描述
restful在使用的时候会使用一个resource-oriented architecture

在这里插入图片描述
在这里插入图片描述
假设有一个Employee的结构

在这里插入图片描述把传进来的转换成json

在这里插入图片描述
是json就做序列化和反序列化

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
都是返回的json信息

在这里插入图片描述

47丨性能分析工具

在这里插入图片描述
要使用go的性能工具就要进行安装的一些安装的准备工作
在这里插入图片描述
这是一个火炬图,有些人喜欢用火炬图来分析性能,1.1之后,go-torch已经内置在go的版本里了

在这里插入图片描述
在这里插入图片描述
**调用两个函数,初始化了一个二维数组,随机的往里面添加随机数 **
在这里插入图片描述

在这里插入图片描述

打开注释,第一部分是利用go内置的性能分析的api做一个cpu的profile。
创建一个文件,把profile的信息输出到文件里 。

在这里插入图片描述
做了一次dump
在这里插入图片描述
下面做了一个当前协程的profile,lookup函数

在这里插入图片描述
需要别的api信息可以查看这个链接


运行代码,执行二进制,相关的profile都生成了
在这里插入图片描述
**可以进行查看,top查看top的cpu的运行情况。
flat是这个函数本身执行的事件和所占的比例,cum就是如果调用其他函数,两个加在一起,所占的比例 **
在这里插入图片描述
list命令可以详细分析,给一个大概对应函数的模糊名就行,会自己做一个最大匹配
在这里插入图片描述
大部分耗时都在这上面

在这里插入图片描述
可以生成一个svg图

在这里插入图片描述
每一个方块展示一个调用关系,占用时间大,方块越大
在这里插入图片描述
在这里插入图片描述
生成一个火炬图,90.33%都是metrics函数

在这里插入图片描述
还可以点进去看

在这里插入图片描述
查看一下memory使用的情况

在这里插入图片描述
在初始化的时候使用比较大
在这里插入图片描述
看看先GC再dump,内存使用情况

在这里插入图片描述
重新build文件
在这里插入图片描述
现在的状况就跟刚才不一样了

在这里插入图片描述
上面的文件方式适合在短时间批量运行的,不是细粒度的一种分析,还有一种方式是通过http做到的

在这里插入图片描述
profile有一个采样时间,30秒,如果要采10秒,就修改后面的值即可
在这里插入图片描述
斐波那契数列
在这里插入图片描述

在这里插入图片描述
在调用/fb的适合区执行斐波那契数列

在这里插入图片描述
在这里插入图片描述

尝试访问一下程序
在这里插入图片描述
这样打印了一个斐波那契数列
在这里插入图片描述
访问profile
在这里插入图片描述
在这里插入图片描述
做一个cpu的采样,有一个30秒的采样时间
在这里插入图片描述
等待的时候发几次请求
在这里插入图片描述
在这里插入图片描述
top的时候可以按照累计时间进行排序

在这里插入图片描述
可以看到耗时的部分
在这里插入图片描述
可以使用go-torch做火炬图
在这里插入图片描述
htto请求最长的还是get斐波那契数列

在这里插入图片描述
点进去可以看到细节
在这里插入图片描述

48丨性能调优示例

在这里插入图片描述
在这里插入图片描述
程序运行的绝对时间和某个函数的运行的绝对时间其中有阻塞,等待外部响应,影响你的绝对时间

在这里插入图片描述
cpu消耗时间

在这里插入图片描述
阻塞时间

在这里插入图片描述
内存分配

在这里插入图片描述
GC次数
在这里插入图片描述
构造一个请求,在payload放一些数字,序列化成json
在这里插入图片描述
在服务器端区process这个请求,主要调优这个process这个过程
在这里插入图片描述
下面是测试程序,构造一个请求,加到切片里去处理

在这里插入图片描述
payload里是数组
在这里插入图片描述
为了验证调优结果,还使用了benchmark

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
每一次操作是35384ns

在这里插入图片描述
现在开始优化,看一下这个profile,分析哪里可以优化
在这里插入图片描述
在这里插入图片描述
processrequest比重很大
在这里插入图片描述
剩下的就是json,unmarshall相关的
在这里插入图片描述
umarshall消耗了相当多的时间

在这里插入图片描述
第一步优化json,目前使用的json解析是go语言 内置的,利用反射的解析json的方式,效率比较低,可以使用easyjson的方式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
为了方便做比较,先复制一份

在这里插入图片描述
原来的称为OLD

在这里插入图片描述
替换unmarshal的一个方法在这里插入图片描述

在这里插入图片描述
结果依然是正确的

在这里插入图片描述
有了明显的提高

在这里插入图片描述
在做一个profile,分析一下
在这里插入图片描述
list查看详细的

在这里插入图片描述
在这里插入图片描述
go里的string是不可变的size,每次相加都回生成一个新的string,会生成的新的内存地址,原有的地址会对GC有影响。
在这里插入图片描述
在这里插入图片描述
查看内存 相关的profile

在这里插入图片描述
查看详情,看到了1.55gb,一个非常大的占用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

具体为什么这么优化,是需要一些个人积累的,使用一个string.builder来做优化
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
单元测试,程序运行符合期待

在这里插入图片描述
从5位数变成了4位数
在这里插入图片描述
在这里插入图片描述

现在已经降到了180,有近一步下降了,时间越短,其实benchmark会运行更多的次数,所以累计数可能会更长,因为运行的次数多

在这里插入图片描述
在这里插入图片描述
内存使用了132兆,io内存少了,所以整体运行的时间也就少了

在这里插入图片描述
现在来做benchmark,彻底和老的程序做比较
在这里插入图片描述
新的和老的运行时间

在这里插入图片描述

49丨别让性能被锁住

虽然分析出了性能差的地方,但是有时候因为经验缺失还是不能知道怎么去优化的
在这里插入图片描述
map当成一个cache来用

在这里插入图片描述
第一个是读,所以完全不需要什么互斥

在这里插入图片描述
wg=waitgroup,等所有的协程完毕

在这里插入图片描述
用了读写锁,但是只用到了读锁的部分,那么有些人就会想读锁不互斥,所有协程去用读锁,对i性能是不是没有什么影响
在这里插入图片描述
,

在这里插入图片描述
没有读锁和有读锁差了一个数量级

在这里插入图片描述
制作profile进行分析
在这里插入图片描述
Rlock对cpu是有一个消耗的

在这里插入图片描述
go的 map是不支持线程安全的,以前都线程的时候都喜欢rwlock把lock包一层,就是get的时候加读锁,write的时候加写锁。
但是锁对cpu 的消耗比较大,锁的时候会影响整个map。
后来go语言加了sync.map包,sync.map是一个线程安全的,去减少一种锁的冲突。分了连个区域,一个是只读区域,一个是读写区域。
syncmap在只读区域使用了一个原子的指针去指向实际的value,他们都共同指向data,避免data在两边不断sync,但是这样会导致一个空间变大,有时候dirty和sync操作都是非常耗时的,它就适合读多写少的环境

在这里插入图片描述
读写都很频繁可以用concurrent map,这是java的
lock的时候是整个map,所以锁冲突的几率是非常大的,concurrent map把一个大的map分成了小的map,这样很多读写操作访问的时候不同的小的map,降低了锁冲突的概率,提高了读写速度

在这里插入图片描述
看一下性能的比较

在这里插入图片描述
为了测map的性能,定义了一个公共测map的接口,只要把不同的map塞进去 就好
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述set就是写锁

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
sync_map
在这里插入图片描述
concurrent map

在这里插入图片描述
在这里插入图片描述
针对不同map的一个benchmark

在这里插入图片描述
在这里插入图片描述
把三个map放到测试程序里去

在这里插入图片描述
concurrent map明显性能好很多

在这里插入图片描述
改成100次

在这里插入图片描述

在这里插入图片描述
修改次数,依然是concurrent map性能最好
在这里插入图片描述
在这里插入图片描述
假如有十分之一的写,syncmap的性能就好一点。
写多读少,使用concurrent map
读多写少 ,使用syncmap

在这里插入图片描述
在这里插入图片描述
非常高性能的数据交换队列
在这里插入图片描述

50丨GC友好的代码

go的GC和java的GC有很大不同
在这里插入图片描述
定义了一个结构体,content,比较大的一个数组

在这里插入图片描述
一个是直接传进数组,一个是传进数组的指针
在这里插入图片描述
下面做benchmark

在这里插入图片描述
性能差距是很大的

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
输出了一些GC的日志
在这里插入图片描述
运行了50次,但是GC次数达到了80次

在这里插入图片描述
运行了20000000次,GC15次,这里就可以看出,编程方式不同,对GC的 影响是很大的

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
生成了一个可以引用的trace文件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到一些栈的信息。gc

在这里插入图片描述
另外 一个 整个GC比上面的差别是非常大的

在这里插入图片描述
在这里插入图片描述

go语言里有切片,切片是可以自增长的,要初始化合理的大小,因为泰校,会不断地申请内存。
在这里插入图片描述

下面代码上面部分是 直接申请了一个切片,让它自增长在这里插入图片描述
在这里插入图片描述
第一个是自增长,,第二个是合适 的大小,一个初始化8倍查看差异是否大
在这里插入图片描述

51丨高效字符串连接

常用的就是把字符串链接起来

在这里插入图片描述
字符串相加

在这里插入图片描述
使用bytes buffer,可以用一个buffer连续存储空间,把字符串放进去

在这里插入图片描述
string builder是在go 1.1.0之后出现的,wirte部分其实和buffer是一致的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最后一个stringadd的性能其实是比较差的,string是go的不可变类型,每次add的时候都会生成一个新的string

在这里插入图片描述
整体推荐使用bytes buffer,1.10后使用string builder

在这里插入图片描述

52丨面向错误的设计

一定要先去接收系统是错误的这种设定
在这里插入图片描述
当系统发生错误的时候,隔离错误,尽量减少对其他部分的影响
在这里插入图片描述
微内核就是其中之一
在这里插入图片描述
微服务之间的依赖,当一个服务挂掉了,其他服务可以进行降级措施
在这里插入图片描述
充重用和隔离不应该对立起来,如果逻辑上是单体,那我们部署的时候就可以做一个高可用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当一台宕机了,那么1500次还是打到一台机器上,最终也会宕机
在这里插入图片描述
避免上面的情况,可以使用限流,一般使用token bucket来实现
每个token就是一滴水, bucket就是一个桶,来一个请求的时候就看看还有没有水滴,有水滴就可以进行进一步处理,当水滴没有了,请求会reject掉,请求超出了频率

在这里插入图片描述
使用了一个连接池,所有链接都被用完的时候,所有的query非常非常慢。对所有查询没有超时操作,服务就像宕机一样

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

当错误发生时,一般会用断路器,当后端发现错误,前面的服务就会直接把cache返回给你,服务降级,防止错误传递
在这里插入图片描述

53丨面向恢复的设计

要对失败做一些恢复设计
在这里插入图片描述
在这里插入图片描述
任何情况下都需要做recover,强调一个let int crash,如果一个错误并不知道时什么错误,也不知道真正怎么recover,干脆就把进程退掉

在这里插入图片描述
在这里插入图片描述
无状态的服务可以用任何一个instance替代
在这里插入图片描述

在这里插入图片描述

54丨Chaos Engineering

一个问题经常发生,就需要对它进行一个解决方案,但是不经常发生,就没有应对措施,所以在生产上尽可能产生一些错误
在这里插入图片描述
chaos engineering,会在生产上去注入一些错误,这些错误一般会包含任意关掉生产上的主机,模拟慢响应,在所有的都是在一些可控范围之内的

在这里插入图片描述
在这里插入图片描述
Netflix有一个项目叫chaosmonkey

在这里插入图片描述
可以在云上实现一个自定义的terminate,来实现你对错误的监测,查看你线上的metrics反应。
在这里插入图片描述
在这里插入图片描述
整个项目可以用申明的方式,把面向恢复的方式把你的核心代码包起来
在这里插入图片描述

55丨结束语

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值