Your SlideShare is downloading. ×
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Golangintro
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Golangintro

2,585

Published on

introduction to golang

introduction to golang

Published in: News & Politics, Technology
0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,585
On Slideshare
0
From Embeds
0
Number of Embeds
28
Actions
Shares
0
Downloads
34
Comments
0
Likes
7
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. INTRODUCTION TO GOLANG 傅理 @TAP4FUN
  • 2. 历史 2007年 : 在google秘密启动go语言研发 2009年 : 官方宣布go语言项目的存在,开始用于部分google生产系 统 2012年 : 第一个1.0正式版发布 当前版本: 1.1.2 https://code.google.com/p/go/downloads/list
  • 3. 主要目标 并行计算 • 利用好Google内部百万台服务器,摩尔定律在多核上的继续生 效,使得世界迫切需要一门并行的语言。(erlang前两年的突然 流行,就是一个写照,但由于其过高的门槛(函数式编程), 在工程上并没有大面积的普及。) 解决C++编译速度问题 • 软件规模庞大,C++编译速度不可忍受,google内部大型项目 编译时间通常以天计算。
  • 4. 主要目标 Bruce Eckel 表示: C++的复杂性(新C++更复杂),对于生产效率的影响,已经不 再合理了。程序员为了使用好C++, 需要跳过很多坑,是完全说不 通的,只是浪费你的时间和精力。现在,go对C++想要解决的那类 问题,做的更合理。 ( Bruce Eckel: The complexity of C++ (even more complexity has been added in the new C++), and the resulting impact on productivity, is no longer justified. All the hoops that the C++ programmer had to jump through in order to use a C- compatible language make no sense anymore -- they‗re just a waste of time and effort. Now, Go makes much more sense for the class of problems that C++ was originally intended to solve.)
  • 5. AUTHORS Go语言的所有开发者,总共有400多位。 来源: http://golang.org/CONTRIBUTORS 其中有几位非常著名的人物。
  • 6. AUTHORS Robert Griesemer Chrome浏览器的Javascript engine (V8) 作者,目前是全世界最快的 浏览器,参与过Java Hotspot VM研发.
  • 7. AUTHORS Rob Pike(1956~) Plan9,Inferno 操作系统设计者, UTF-8创造者,在贝尔实验室工作。 ―设备即文件‖就由这位大神提出,深刻的影响了UNIX设计。
  • 8. AUTHORS Ken Thompson(1943~) Thompson和Ritchie在1971年共同发明了C语言。1973年 Thompson和Ritchie用C语言重写了UNIX,从此开创了UNIX时 代。 1983年的图灵奖获得者。 谁说超过30就不能写程序来着?
  • 9. GOLANG FEATURES GO语言基本特征
  • 10. 基本特征 1. 一门系统编程语言 2. 开源(社区性) 3. 语法接近C语言(因为Ken的一脉相承) 4. 多平台支持 (Linux,Mac,Windows,BSD) 5. 静态语言,需要编译 6. 自动内存管理,垃圾回收(GC) 官方主页: http://golang.org 官方教程: http://tour.golang.org/ (极其重要,一定要拿100分!)
  • 11. 关键字 共25个关键词(ANSI C语言32 个,C++ 63个,Lua 20个,Java50个) GO语言在关键词使用上是非常吝啬的。 Keep it simple, make it general, and make it intelligible. --- Douglas McIlroy break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var
  • 12. 运算符 优先级 运算符 最高 * / % << >> & &^ +-|^ == != < <= > >= <- && 最低 || 1. 省掉了~取反符,单目^表示取反, 2. 调整了 & 和 == 的优先级 (在C语言中 位运算的 & ^ | 竟比比较运算 < == 优先级还低这一点不科学,因为合乎情 理的写法是 a&1 == 0,但实际上必须写 (a&1) == 0。不过,这一问题的来源是历史 遗留,在 C89 之前,K&R C 并没有逻辑运算 符 && 和 ||,而是使用 & 和 | 来表示这一 意义,按使用的上下文区分意义。后来 C89 标准化,虽然增加了语义更合理的 && 和 ||, 但大概是为了兼容旧代码,也没有把这个优先 级改过来,结果就将错就错了。) int main(void) { int a = 2; printf("%dn", a&1==0); printf("%dn", (a&1)==0); } (奇偶性)
  • 13. int8 int16 int32 int64 uint8 uint16 uint32 uint64 float32 float64 string rune complex64 complex128 ... 基础数据类型 基本数据类型完全符合C99标准<stdint.h>, 即确定长度的严格数 据类型, 如: UTF-8字符 原生支持 (想想Rob Pike) go源码亦为utf8 rune [ruːn]: n. 诗歌;古代北欧文字;神秘的记号 LONG, LONG LONG ? NO!
  • 14. 复合数据类型 Array 类似于C的固定长度数组(不常用) Slice 可变长度数组,切片(最常用,类似stl vector),最终 指向一个Array Map 映射表(key->value), Hash表 Struct 结构体,和C语言结构体意义相同
  • 15. 与JSON的对比 JSON规范中,定义了: object = { string:value } 无穷“名称/值”对 array = [value,value,value] 无穷 有序集合 value = string, number…array… 基本类型 并可形成一种无穷嵌套关系: object:{ object:{ object:{object:,...}, ...},...} 在go语言中,go的数据结构和json是同构的。从go语言的数据结构导入导出 json格式,只需要一行代码。(想到robert了么,还想到了什么?) map[string]interface{} --> 无穷键值对 slice interface{} --> 无穷有序集合 在GO构造一个 LOOP试试
  • 16. 编译过程回顾(C语言) 假设: 有C1, C2, C2三个C文件, 都引用了―common.h‖ 那么: common.h需要被编译3次 在google曾经的某个项目中,一个.h文件,被编译了30多万次,不 必要的重复include导致编译速度急剧下降。(想想.pch, .gch的作 用). C++诞生过若干解决编译速度的设计,例如: Pimpl技术
  • 17. GO提高编译速度的途径 Go语言强制规定:如果有引用后没有使用到的包,编译时报错。 这样保证了,只有具有真正的依赖关系的 编译链条被建立。 保证: 一个文件,最多只被编译一次 一次都没有引用到的,决不编译。 一个上万行的工程,go只需要几秒编译。
  • 18. 循环依赖 编译过程中的循环依赖是不被允许的,即 引用关系不能形成环!(黑话叫DAG,有向无 环图)
  • 19. 一个编译的例子 问题: 假如存在如右图的编译依赖关系,在一个双核 的CPU上怎么编译最快。 答案: 第一步:D 第二步:C,F 第三步:B,E 第四步: A 即通过依赖关系寻找到最远的一个节点, 并回溯各条路径。 (思考:如果是4核CPU该如何编译?)
  • 20. 相比其他语言 Go语言如是说: 编译比我快的,运行速度没我 快;运行速度比我快的,编译没我 快!
  • 21. 系统编程 Go语言重新封装了所有的系统调用,具有和C语言相同的对系统的操 作能力。(PS: thompson和pike都设计过OS) 25 TEXT runtime·open(SB),7,$0 26 MOVL $5, AX // syscall-5 - open 27 MOVL 4(SP), BX // pathname 28 MOVL 8(SP), CX // flag 29 MOVL 12(SP), DX // mode 30 CALL *runtime·_vdso(SB) 31 RET int open(const char *pathname, int flags, mode_t mode); 引自: http://golang.org/src/pkg/runtime/sys_linux_386.s 系统调用是内核所 能提供的全部功能
  • 22. 系统编程举例 Go语言能直接处理UNIX Signal,因此,可以通过信号实现一些有用的功能,例 如通过注册一个信号接收通道: signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT) 然后通过kill给进程发消息: $kill –HUP PID 就可以执行诸如重新加载游戏数值,或者在收到QUIT消息的时候,执行一些数据 库持久化操作,优雅的退出(shutdown gracefully)。 更多的系统调用,见: http://golang.org/pkg/syscall/
  • 23. C++ PERSPECTIVE OF GOLANG 从C++的角度来看GOLANG
  • 24. 名字空间和类 VS. 目录 1. 一个目录中的所有文件同属一个namespace (朴素思想来源?) 2. 同一目录、不同文件的变量,函数,互相可见,因此也类似于 Class 3. 小写字母开头的为private函数/变量 func test() var a  对其他目录不可见 4. 大写字母开头的为public函数/变量 func Test() var A  对其他目录可见
  • 25. 对象方法 VS. 接收函数 func (slice ByteSlice) Append(data []byte) []byte { } Go语言没有类的概念,比较接近的是结构体。 可以定义一个结构体,并给这个结构体定义一个接收函数,然后通过 ―对象 点 方法‖的形式去调用一个struct 方法(函数指针?)例如 aSlice.Append(…) 但没有完整意义上的对象方法, 也没有隐含的this指针.
  • 26. 继承 VS. 嵌入 type A struct { a int } type B struct { b int } 嵌入: type AB struct { A B } 可以将两个struct内的字段合并到一 个新的结构体。
  • 27. 构造函数 VS. INIT() 结构体没有构造函数,但一个目录/一个文件中可以有若干个init 函数,init函数是全局的, 程序启动一次性的执行. (在main函数执 行之前) var names = map[string]int func init() { names = make(map[string]int) } 类似于C++在程序启动的时候,全局object执行构造函数。
  • 28. INTERFACE VS. VTABLE Go语言提供了interface类型,表示一个接口方法集。仸何一个数据 类型,都实现了空方法集 interface{},类似于 void * go语言支持reflect, 即仸何数据都有对应的元数据,包含其类型的 详细信息。如同 dynamic_cast 需要检查的vtable,也包含了类型 信息(RTTI),typeid()函数。 可以通过 type assertion 方法执行转换。 value.(typeName)  type assertion 类似于C++中 dynamic_cast<typeName*>(ptr)
  • 29. EXCEPTIONS VS. PANIC/RECOVER panic()触发异常, recover()捕获异常 通过go语言的closure(闭包) 可以获得和C++ try -> catch() 相同效果 因为go语言支持多值返回,大部分的错误都在返回值中处理 了,因此panic确实应该在真正的―异常‖中触发。 “千万不要把错误当异常” a, b = b, a
  • 30. DEFER OR FINALLY defer 的作用类似于finally: C++代码 try { fd = fopen(―xxx‖) } catch(…) {} finally() { close(fd) } 等价于: f, err := os.Open(―xxx‖) If err!= nil ….. defer f.close()
  • 31. HOW DOES A GO PROJECT LOOK LIKE? 工程组织
  • 32. RULE #1 用树状目录的方式 组织工程 一个目录下所有.go文件共同构成一个功能模块, 为一个目的服务. Example: /src/agent/ agent.go buffer.go proxy.go session_work.go
  • 33. RULE #2: 用 模块 + 接口 的方式去构建系统 而不是OOP的方式
  • 34. CONCURRENT PROGRAMMING IN GOLANG GOLANG并发编程
  • 35. THREADS回顾 进程(线程)是操作系统的一个调度实体,进程上下文切换成本较 高,可以通过下面两项计算: 1. 切换开销: Intel E5520: ~4500ns/context switch(csw) 2. CPU cache 失效导致的内存开销
  • 36. THREADS回顾 问题: 假设一个4核系统有10000个线程正在运行,每个线程每秒处理 10条消息(被调度10次), 问: 一个线程被调度到的最大延迟是多 长? 计算: (只需要按单个核分布的线程数计算) 调度开销 := (1/4 ) * 10 * 10000 * 4500 ns = 113 ms 同时假设:处理每个数据包用1000条指令,每条指令花费1ns,那么最大延迟为: 113ms + 25000 * 1000 * 1ns = 138ms (注意,以上的计算,并没有计算指令中还有IO的调度,网卡中断,也没有计算 CPU cache失效的开销,实际情况远大于这个。)
  • 37. THREADS回顾 内存开销: 一个THREAD的基本的内存开销为:2MB的固定长度Stack 计算10000个threads启动需要的基本内存为: 10K * 2M = 20 G (PS. 进程的Stack 默认是 8M,用ulimit –s 查看) On Linux/x86-32, the default stack size for a new thread is 2 megabytes.http://man7.org/linux/man- pages/man3/pthread_create.3.html
  • 38. GOROUTINE goroutine是一种轻量级的线程,不是操作系统中的线程,称为纤程更为合适。 goroutine本质上来说只代表一个独立的执行路径,即对应了地址空间中的一个独立 的Call Stack, 这个stack是自增长的。 goroutine对应的结构体如下: struct G{ uintptr stackguard; uintptr stackbase; Defer* defer; Panic* panic; Gobuf sched; uintptr stack0; … } http://golang.org/src/pkg/runtime/runtime.h
  • 39. GOROUTINE ―goroutine之所以被叫做goroutine,是因为现存 的一些术语——线程、协程和进程等等——都 传达了错误的涵义。‖ —摘自官方文档《Effective Go》
  • 40. 传统IO模型-- SELECT select()/poll() 可以用于探测一组fd中是否有一个可读或可写, poll(struct pollfd fds[], nfds_t nfds, int timeout); 因为每次都要传递一组fd到内核,所以其复杂度是O(n),连接数在 1000以内是可以接受的选择,在互联网泡沫破灭之前(<2000年) 是最佳的选择。 PS:select/poll于1986年在SVR3 UNIX出现。
  • 41. EPOLL 2003年epoll在linux 2.5中出现,称为目前linux服务器的主要使用的 IO模型。 Epoll通过在内核中创建一个专用的结构体,纪录所监控的fd, 一旦 有一个在该fd上触发的事件,那么通知应用程序。 其算法复杂度为O(1),因此其伸缩性极强。 http://en.wikipedia.org/wiki/Epoll
  • 42. 使用GOROUTINE的 网络模型 goroutine相对于thread只有一定的内存开销(最少4K),因此,在 go语言网络模型设计中采用 1:1 的模式,即一个goroutine处理一个 连入的玩家,在内存层面是可以接受的。 计算1万个goroutine需要耗费的stack内存最少为: 4K * 10000 = 40M
  • 43. GOROUTINE + EPOLL listener, err := net.ListenTCP("tcp", tcpAddr) for { conn, err := listener.Accept() if err != nil { continue } go handleClient(conn) } That's all for a C10K server.... 网络接口部分,golang在Linux的IO模型是基于epoll的,并对上层完全透明 因此,网络部分非常简单,并且高效。 (BSD是kqueue实现)
  • 44. 多仸务调度策略 • Cooperative multitasking/time-sharing 协作式: 正在执行的代码片段主动让出CPU,并切换到另一条执 行路径。 • Preemptive multitasking/time-sharing 抢占式(剥夺式): 对正在执行的代码,被操作系统强行中断执 行路径(例如通过时钟中断),并切换到另一个执行路径。 Go语言是典型的协作式,绝大部分用户态的程序,只能做到协作 式,例如LUA的coroutine。
  • 45. GOROUTINE调度点 1. channel send/recv/select/close (收、发、选、关) 2. malloc (内部内存分配) 3. garbage collection 4. goroutine sleep 5. syscalls (所有系统调用) 基本上,golang的设计在所有可能阻塞的调用中,都尝试主动让出 CPU,让其他任务继续执行。 例如,系统调用 open(),其调度的 有效性,基本等同于操作系统级别(进程遇到阻塞操作会被put to sleep)。
  • 46. GOROUTINE的调度延迟 问题: 在一个4核的CPU上,开启10000个goroutine, 平均每个goroutine每 秒处理10个数据包。(假设每个数据包用1000条指令,每条指令花费 1ns) 问:最长响应时间为多长? 计算: 1. 每个CPU平摊 2500个goroutine ,不存在进程切换问题。 2. 最大延迟 := CPU时间 * 2500 即最大延迟为: 1000 * 25000 * 1ns = 25ms
  • 47. IPC IN GOLANG GO语言的IPC
  • 48. CLASSICAL IPC PATTERNS • mutex(互斥) -- pthread_mutex_init() • rwlock(读写锁) -- pthread_rwlock_init() • shm (共享内存) -- shm_open() • semaphore(信号量) -- sem_open() • pipe(管道) -- pipe() • socket -- socket • condwait(条件等待) –- pthread_cond_wait() • signal(信号) -- kill() 传统的UNIX系统中,以上所有机制在都是由内核提供的功能。 Go语言也提供以上的IPC机制。
  • 49. IPC相关技术出现的 年代整理 process 60年代早期(冷战早期) semaphore 1965年由 dijkstra 提出(PV) signal 1970年在贝尔实验室的Unix系统出现。 pipe 1973年,ken thompson在unix中实现,最初由数学 家 Douglas McIlroy提出 message queue 1980年出现在VRTX和pSOS+(vxworks前生)。 sockets 1983年首次出现于4.2BSD系统,直到1989年才免于 版权保护。 poll 1986年在SVR3 Unix出现。 threads 直到90年代晚期都没有太多的支持,本世纪才普及。 kqueue 2000年在freebsd 4.1中出现。 epoll 2002年在linux 2.5.44中出现。
  • 50. 通信顺序进程(CSP) CSP := Communication Sequential Processing is a formal language for describing patterns of interaction in concurrent systems. 一种用于定义并行交互的形式语言. eg: COPY = *[c:character; west?c → east!c] CSP was first described in a 1978 paper by C. A. R. Hoare(東尼·霍爾) • 1980年,获颁图灵奖。 • 1982年,成为英国皇家学会院士。 • 2000年,因为他在计算机科学与教育方面的杰出贡献,获得英国王室颁赠爵士头衔。 (思考:霍爾还做过什么算法?)
  • 51. CSP定义的交互: 1. 通过Send/Receive实现通信 2. 保证收发同时成功 ―不要通过共享内存进行通信. 恰恰相反,通过通 信来共享内存‖ --- Effective Go
  • 52. CHAN 在go语言中, 提供了一种叫做CHAN的通道,用于goroutine之间交互,CHAN 有两种形式:带缓冲的(有一定的容量)和不带缓冲的,其中不带缓冲的chan 符合CSP的语义,是一种重要的同步机制。 chan类似于pipe(FIFO)的行为,区别在于: 1. pipe是单向的(返回两个fd),一端进,一端出, chan是双向的。 2. pipe没有数据类型的约束,数据也没有边界,chan则可以定义能够传递的 数据类型。 3. 由于历史原因,pipe容量非常有限,只有64K,并且不可调整。 (思考:和message queue有什么关系?)
  • 53. CSP举例 package main func main() { ch := make(chan int,10) ch <- 1 v := <-ch println(v) } 不符合CSP语义, 异步消息 package main func main() { ch := make(chan int) ch <- 1 v := <-ch println(v) } 运行出错,但符合CSP语义, 因为收发不可能同时成功 (思考,会报什么错)
  • 54. CHAN本质是带锁的FIFO队列 发送: LOCK ENQUEUE UNLOCK 接收: LOCK DEQUEUE UNLOCK GO语言的队列是高效的无锁队列,LOCK操作是基于futex()的。 FUTEX: 现代CPU因为多核的出现,增加了原子操作的指令(如:CAS),新的futex()系统函 数支持这些指令,减轻了传统mutex在内核中调用的消耗(进入内核,进程休眠等), Go语言中的atomic包提供大量原子操作函数,例如: CompareAndSwapInt32, AddInt32 .L3: lock addl $1, foo+4(%rip) addl $1, -4(%rbp) 原子操作的本质: 锁内存总线
  • 55. 一个基本的消息循环 for { select { case msg, ok := <-in: // 来自网络 case msg, ok := <-sess.MQ: // 来自其他goroutine case _ = <-std_timer: // 定时器 } } 检查每个case语句: 1. 如果有仸意一个chan是send or recv read,那么就执行该block 2. 如果多个case是ready的,那么随机找1个并执行该block 3. 如果都没有ready,那么就block and wait 4. 如果有default,而且其他的case都没有ready,就执行该default 注意: select channel 没有优先级之分,但能通过调整代码模拟。(思考用什么?)
  • 56. CHAN OR LOCK? 如果仅能通过chan对某一个数据进行访问,那么,访问是串行的, 相当于数据持有一个mutex。 |REQn| --> | REQn-1| REQn-2|...|REQ1| 在很多高并发读取场景中, 通过channel的并发远远比不上读写锁。 因此,根据实际数据的读写比例,来权衡设计。
  • 57. CGO & C 用CGO调用C程序
  • 58. CGO & C CGO是GO同C语言交互的方法: package rand /* #include <stdlib.h> */ import "C" func Random() int { return int(C.rand()) } func Seed(i int) { C.srand(C.uint(i)) } 有时候我们需要调用一些 C写的程序, 例如嵌入 LUA,以获得脚本的能力。 Go和C的血缘关系,使得 嵌入C非常容易。 目前在github上已经出现 了以golua, luar为代表的 package,使用非常方便。
  • 59. CGO & C 但是: 由于go的调度机制--协作式的,一旦程序进入C的领域,等于进入 了一个黑盒子,交出了控制权。在C中如果有阻塞的调用,例如 fopen(),那么必然会影响整个go语言的调度。 设计考虑: 1. 在C中(或LUA脚本中),只做CPU运算,绝对不能执行IO阻 塞操作。 2. 运算类的脚本尽量少,脚本执行效率远低于go语言,(慢100 倍以上),响应时间变长。(前面公式) 3. 永远不要忘记释放C中分配的内存。 4. 保持头脑清晰!
  • 60. 一个CGO的应用 $ telnet localhost 8800 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GameServer LUA Console > a=1 > b=2 > print(a+b) 3 > users = gsdb.ListAll() > print(users) []int32 有时候我们需要像ruby console 那样的灵活性,可以在线的时候 观察,或者改变某些数值。 我们通过CGO嵌入LUA,可以 实现一个类似的Console,在调 试的时候尤其重要。
  • 61. THANKS “Embrace the future!”
  • 62. REFERENCES http://www.usingcsp.com/cspbook.pdf http://www.akkadia.org/drepper/futex.pdf https://docs.google.com/a/nibirutech.com/presentation/d/1Xrv yfegrkTfqbB3jEwWRtvXGKHuf87UXi8CjMr6n_mc/edit?usp=sharing gonet框架开源部分: https://github.com/xtaci/gonet E-mail: daniel820313@gmail.com Blog: http://bullshitlie.blogspot.com Github: https://github.com/xtaci

×