- func test() func(int) {
- return func(x int) {
- println("x:", x)
- }
- }
2. defer定义延迟调用,无论函数是否出错都确保结束前被调用
3. ok-idiom(A跌目)模式:多返回值中用一个名为ok的布尔值来标记操作是否成功
4. 结构中的匿名字段,结构的实例可以直接调用匿名字段的方法和属性
5. 计算机中变量是一段或者多段用来存储数据的内存,类型决定变量内存的长度和存储格式,所以我们只能修改变量值不能修改类型
6. 内存分配发生在运行时,编译后的机器码不使用变量名而是直接使用内存地址访问目标数据,所以编码阶段采用易于阅读的变量名
7. 惯例建议以组的方式整理多行变量定义 var {x,y int } type{ xxx }
8. 简短声明一般用于函数多返回值,以及if for switch等语句中定义局部变量
9. 未使用的局部变量会编译出错,全局变量不报错
10. 命名建议字母或下划线开始,多字母数字和下划线组合,局部变量优先短名
11. 常量实在预处理阶段展开成指令数据,变量是在运行期分配存储内存.(所以常量无法寻址,没有地址)
12. byte是uint8的别名 rune是int32的别名 别名直接可以相互赋值不需要类型转换
13. 拥有相同的底层结构不代表就属于别名
14. new为指定类型分配零值内存返回指针;make是引用类型专用的创建函数(内存分配和属性初始化)
15. 未命名类型:数组、切片、字典、通道等类型与具体元素类型或长度等属性相关的类型,可以用type将其改变成命名类型
16. 对于未命名类型 struct tag不同也属于不同类型,字段顺序不同也属于不同类型。
17. 乘幂和绝对值运算在math包的Pow和Abs中
18. 自增自减只能作为独立语句
19. 指针是实体会分配内存空间,内存地址是内存中每个字节单元的唯一编号
20. 指针类型指向相同地址或nil则相等,但是不能做加减和类型转换
21. unsafe.Pointer将指针转换为uintptr进行加减运算,但可能造成非法访问
22. 指针不能用->,统一使用.
23. 复合类型初始化,必须包含类型标签;左花括号必须在类型尾部;多成员都好隔开;多行右侧必须是逗号或者花括号
24. switch 无需显式执行break,但是想顺序执行需要显式执行fallthrough
25. range迭代是复制数据
26. goto只能跳转到同级代码,不能跨级别
27. break用于switch for select,终止整个语句块执行
28. continue只用于for循环,终止后续逻辑立即进入下一轮循环
29. 函数无需前置声明;不支持命名嵌套定义;不支持同名重载;不支持默认参数;支持不定长参数;支持多返回值;支持命名返回值;支持匿名函数和闭包
30. 函数类型只支持nil判断,不支持其他比较操作
31. 从函数返回局部变量指针是安全的,编译器会通过逃逸分析来决定是否在堆上分配内存;所以参数尽量减少值拷贝
32. 函数建议命名规则:动词+名称;避免不必要的缩写(printError优于printErr);避免使用类型关键字;使用习惯用语(init表示初始化,is/has返回布尔值);用反义词命名行为相反的函数
33. 不管是指针、引用类型还是其天涯类型参数,都是值拷贝传递,区别在于拷贝的目标对象
34. 指针传递坏处在于延长该变量的声明周期,也可能导致他分配到堆上增加性能消耗
35. 函数参数在函数内部有效,作用域是整个函数内部
36. 变参 func test(a ...int){} test(a[:]...)
37. 命名返回值的问题: 新定义的同名局部变量会引起同名遮蔽:xx is shadowed during return ;此时实名return即可
38. 闭包 匿名函数能够使用上下文的环境中的数据(最终数据)
39. 延迟调用defer 常用于资源释放 解除锁定 错误处理等 先入后出。 延迟调用开销很大,性能要求高压力大的算法尽量避免使用
40. error是接口类型
41. panic会引发函数中断执行defer ,在defer中使用recover捕获panic提交的错误对象(recover只能在defer中执行才有效)
42. 多个panic仅最后一个被捕获
43. runtime/debug.PrintStrack()可以打印完整的堆栈信息
44. 不可恢复性、导致系统无法正常工作的错误才会使用panic (文件系统没权限操作、服务端口被占用、数据库未启动等)
45. 字符串是不可变字节(byte)序列,可用len获取长度,不可用cap; 支持跨行;允许字节数组访问,单不允许字节数组取地址
46. 用切片指向数组时,底层还是指向该字符串
47. range遍历可以打印出汉字,len遍历出的汉字是乱码
48. append可以向[]byte追加 =》var bs []byte bs=append(bs,"abc"...)
49. 字符串加法运算每次都会重新分配内存,构建大字符串性能极差;方法1:strings.Join 方法2:bytes.Buffer 小字符串拼接使用fmt.Sprintf text/template等
50. utf8.RuneCountInString(s)代替len获取带汉字的字符串长度
51. 长度是数组的类型组成部分,元素类型相同长度不同的数组不是同一类型
52. 多维数组,只第一维支持... => [...][10]
53. 如果元素支持== !=操作,则数组也支持
54. 数组是值类型
55. 切片:不是动态数组或数组指针;内部通过指针引用底层数组,设定相关属性将数据读写操作限定在指定区域内。可以理解为数组指针的包装
56. 切片本身是只读对象,工作机制类似数组指针的包装 右半开区间 数组必须addressable
- type slice struct{
- array unsafe.Pointer
- len int
- cap int
- }
57. 切片引用数组时,切片指针会指向数组地址;访问越界会报错;append会追加数组,当长度大于cap时会重新分配地址,则切片和数组就相互独立了
58. 切片 var a[]int 为nil,仅代表他为初始化,但依旧分配内存;且a[:]依旧是nil
59. 如果切片长时间占用大数组的少量数据,建议切片单独分配地址,以让大数组尽早释放
60. 可将字符串直接复制到[]byte => b:=make([]byte,3) n:=copy(b,"abcdefhg")=>n=3,b=[97 98 99]
61. 字典的key必须支持== != 如数字、字符串、指针、数组、结构、接口
62. if v,ok:=m["d"];ok{存在} 使用ok-idiom模式判断key是否存在
63. delete(m,"d"),删除不存在的key不报错
64. map使用range迭代每次顺序不定
65. map被设计成 no addressable,所有没法修改value的成员(如果value是个结构或者数组等) ;改进方法1:先获取完整value,修改后再赋值回去;方法2:value采用指针类型。因为value是指针,所有可以通过指针修改指针指向的数据。
66. map并发操作,某任务针对map写操作,其他任务对该map的读写删除都会导致进程崩溃;可用sync.RWMutex实现同步(不要使用defer)
67. map对象本身就是指针包装,传参不需要取地址
68. map创建时和slice一样要预选分配足够地址,减少扩张时不必要的内存分配和重新哈希操作=>make(map[int]int,1000)
69. 对于海量小对象,应该直接用字典存储键值数据拷贝而不是指针,这样减少扫描对象的数量缩短垃圾回收时间。
70. 字典不会收缩内存,适当替换新对象是有必要的
71. 结构推荐命名初始化,以防扩充结构时报错
匿名结构:
- u:=struct{
- name string
- }{
- name:"xxx",
- }
72. 只有所有成员都支持==操作时,结构才支持相等操作
73. 匿名字典隐式的以类型名为字段名称,使用时可以直接饮用匿名字段的成员,但是初始化时必须当做独立字段。(但是隐式字段是外部类型的话,隐式名称不包含包名)
74. 除接口指针 多级指针外的任何命名类型都可作为匿名类型
75. 字段标签是对字段描述的元数据,是类型的组成部分;运行期间可用反射获取标签信息,通常作为格式校验和数据库关系映射等
- p1:=p{
- name:"xxx",
- sex:1,
- }
- v:=reflect.ValueOf(p1)
- t:=v.Type()
- for i,n:=0,t.NumField();i
- fmt.Printf("%s:%v\n",t.Field(i).Tag,v.Field(i))
- }
76. reflect.StructTag提供了更完善的功能
77. 前置实例接收参数-receiver
78. receiver是基础类型则会被复制,指针类型则必须能获取实例地址
79. receiver类型选择:不修改的小对象或固定值用T;引用类型、字符串、函数等指针包装对象用T;修改实例状态用*T;包含Mutex等同步字段用*T,大对象或不确定情况用*T;
80. 匿名类型的方法也存在同名遮蔽的特性。(可实现类似覆盖操作)
81. T的方法集是 receiver T;*T的方法集是receiver T+*T
82. 匿名嵌入S,T包含 receiver S;匿名嵌入*S,T包含 receiver S+*S; 匿名嵌入S或*S,*T都包含 receiver S+*S;
83. 方法集仅影响接口实现和方法表达式转换。匿名字段就是为方法集准备的
84. Chan:
- 一次性事件使用chan的close效率更高
- 向close的chan发数据panic
- 从已关闭的chan接收数据返回已缓存数据或零值
- 无论收发,nil通道都会阻塞
85. Chan和锁的选择:
- 同步问题应该用锁或原子变量来操作
- 对性能要求较高时,赢避免使用defer unlock
- 读写并发时,用RWMutex性能更好
- 对单个数据的读写保护建议使用读写锁
- 严格测试,尽可能打开数据竞争检查
- 通道倾向于解决逻辑层次的并发处理架构
- 锁用来保护局部范围内的数据安全
86. FieldByName不支持多级名称,如有同名遮蔽需要匿名字段二级获取
87. 可用发射提取struct tag还能自动分解,常用于ORM映射或数据格式验证
88. 反射可通过Interface方法进行类型推断和转换
89. 对性能要求较高的地方需要谨慎使用反射
90. Go语言1.5版本实现的自举
Map/ silce/channel初始化
m := make(map[string]int,10)//初始化map
keyType表示map的key类型,valueType表示map的value类型。size是一个整型参数,表示map的存储能力,该参数可省略。
s := make([]int,5)//初始化silce
cap可以省略。当cap省略时,默认等于len。此外cap >= len >= 0的条件必须成立。
c := make([]chan int,5,10)//初始化channel
使用make创建channel,第一个参数是channel类型。size表示缓冲槽大小,是一个可选的大于或等于0的整型参数,默认size = 0。当缓冲槽不为0时,表示通道是一个异步通道。
make和new的区别
new的作用是初始化一个指向类型的指针(*T)。使用new函数来分配空间,传递给new函数的是一个类型,不是一个值。返回的是指向这个新分配的零值的指针。
make的作用是为slice、map或chan初始化并返回引用(T)。make仅仅用于创建slice、map和channel,并返回它们的实例。
并发和并行的区别
goroutine的启动
将要并发执行的任务包装成一个函数,调用函数的时候前面加上go关键字,就可以开启一个goroutine去执行该函数的任务
goroutine对应的函数执行完,该goroutine结束
程序启动的时候回自动创建一个goroutine去执行mian函数
mian函数结束,则程序结束,由该程序启动的所有goroutine也全部结束
goroutine的本质
goroutine的调度模型GMP
M:N 把m个goroutine分配给n个操作系统线程。
Goroutine(协程)主要概念如下:
M必须拥有P才可以执行G中的代码,P含有一个包含多个G的队列,P可以调度G交由M执行
G(Goroutine): 即Go协程,每个go关键字都会创建一个协程。
M(Machine): 工作线程,在Go中称为Machine。
P(Processor): 处理器(Go中定义的一个摡念,不是指CPU),包含运行Go代码的必要资源,也有调度goroutine的能力。
goroutine和操作系统线程(OS线程)的区别?
goroutne是用户态的线程,比内核态的线程更轻量级一点,初始时只占用2kb的栈空间
runtime.GOMAXPROCS
Go1.5之后默认就是操作系统的逻辑核心数,默认跑满CPU
runtime.GOMAXPROCS(1):只占用一个核心系统CPU
work pool模式
开启一定数量的goroutine去干活
sync.WaitGroup
声明:var wg sync.WaitGroup
wg.Add(1) 计数器加1
wg.Done() 计数器减1
wg.Wait() 等待
channel
声明:var ch chan 元素类型
初始化:ch = make(chan 元素类型,[缓冲区大小])
发送:ch <-100
接收:ret <- ch
关闭:close(ch) 重复关闭channel 会panic
select
同一时刻有多个通道要操作的场景下使用
5.1可处理一个或多个channel的发送/接收操作
5.2如果多个case同时满足,select会随机选择一个
5.3如果没有case的select()会一直等待,可用于阻塞mian函数
RPC(远程过程调用) Remote Procedure Call
框架是基于 HTTP 协议实现的,底层使用到了 Netty 框架的支持。,是一种技术思想而非一种规范或协议,常见 RPC 技术和框架有:
应用级的服务框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud。
远程通信协议:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)。
通信框架:MINA 和 Netty。
注意:
OSI七层模型架构:物、数、网、传、会、表、应
TCP四层架构:链、网、传、应
gRPC 是什么?
gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,
使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,
并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。
Protobuf
是google开发的的一套用于数据存储,网络通信时用于协议编解码的工具库。protobuf是一种灵活高效的独立于语言平台的结构化数据表示方法。在通信协议和数据存储等领域中使用比较多
(1)优点:
序列化后体积相比Json和XML很小,适合网络传输
支持跨平台多语言
消息格式升级和兼容性还不错
序列化反序列化速度很快,快于Json的处理速度
(2)缺点:
应用不够广(相比xml和json)
二进制格式导致可读性差
编译指令
protoc --go_out=plugins=grpc:./ *.proto
编写注意事项
1、massage成员编号,可以不从1开始,但是不能重复。不能使用19000-19999
2、可以使用message嵌套
3、定义数组,切片使用repeated关键字
4、可以使用枚举enum
5、可以使用联合体,oneof关键字,成员编号不能重复
添加rpc服务
service 服务名{
rpc 函数名(参数,消息体) return(返回值,消息)
}
例:
- message People{ string name = 1;}
- message Student{ int32 age = 2;}
- service hello{
- rpc HelloWorld(People) return (Student)
- }
微服务架构
什么是微服务架构?
架构方式:单一应用拆成一组小的服务,服务见相互协调,相互配合
围绕业务:每个服务都围绕业务构建,独立部署
轻量级通信:每个服务有独立的进程,采用轻量级交互,通常是http
独立性:
业务独立性:保证微服务有独立业务能力,自动化独立部署能力
团队自主性:不超10人,由不同技能,不同角色的成员组成
微服务技术体系主要包括?
开发框架 spring cloud或者dubbo
微服务持续交付流水线
微服务部署
微服务运维
微服务的好处?
开发效率高,沟通成本低,交付周期短,技术选择宽,服务独立扩展强
微服务缺点?
运维成本高,分布式复杂度,接口成本高,重复性劳动
微服务架构设计原则?
高内聚低耦合:单一职责、轻量级通信方式、服务之间的契约
高度自治:能独立开发/部署/发布、进程隔离、独立的代码库、流水线
以业务为中心:每个服务代表特定的业务逻辑、更快响应业务变化、围绕业务组织团队
弹性设计:容错、服务降级
日志与监控:日志聚合、监控与警告
自动化:持续集成、持续交付
go-mirco
有服务发现后,client,server工作流程
1、每个server启动时,都将自己的ip,port和服务名注册给服务发现
2、当client向服务发现发起服务请求时,服务发现会自动找一个可用的服务,将其ip/port/服务名返回给当前client
3、client再借助服务发现,访问server
服务发现的种类
1、consul:常应用于go-mirco
2、mdns:go-mirco中默认自带的服务发现
3、etcd:k8s内嵌的服务发现
4、zookeeper:java中比较常用
consul
consul关键特性
1、服务发现:consul提供服务,服务端主动向consul发起注册
2、健康检查:定时发送消息,类似于“心跳包”,保证客户端得到的一定是健康的数据
3、键值存储:consul提供,常用redis
4、多数据中心:可轻松搭建集群
consul agent命令 常用参数
-bind=0.0.0.0 指定consul所在机器的ip地址,默认值0.0.0.0
-http-port=8500 consul自带一个web访问的默认端口 8500
-client=127.0.0.1 表明那些机器可以访问consul。默认本机 0.0.0.0所有机器均可访问
-config-dir=foo 所有主动注册服务的描述信息
-data-dir=path 储存所有注册过来的srv机器的详细信息
-dev 开发者模式,直接以默认的配置启动
-node=hostname 服务发现的名字
-rejoin consul启动的时候,加入的consul集群
-server 以服务方式开启consul,允许其他的consul连接到开启的consul上(形成集群)。如果不加-server表示已客户端的方式开启不能被连接
-ui 可以使用web页面来查看服务发现的详情
etcd
高可用的分布式键值(key-value)数据库,应用较多的场景时服务发现
特色:
简单:安装配置简单,而且提供了HTTP API进行交互,使用也很简单
安全:支持SSL证书验证
快速:根据官方提供的benchmark数据,单实例支持每秒2k+读操作
可靠:采用raft算法,实现分布式系统数据的可用性和一致性