基本语法 HelloWorld简单示例1 2 3 4 package  mainfunc  main ()     fmt.Println("Hello World!" ) } 
终端运行
1 2 $ go run helloworld.go Hello World 
go run表示直接运行 直接编译 go 语言并执行应用程序,一步完成 也可以先 go build helloworld.go || ./helloworld.go build成静态语言然后在执行
go 语言的语言, 写出规范的一致代码是很重要的 比如写函数的时候’{‘ 必须和函数名在同一行 建议使用 go 的默认格式化程序来约束
上面的执行流程是
package main : 定义了包名 必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main 表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包import “fmt” : 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。func main() : main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)fmt.Println(…) : 可以将字符串输出到控制台,并在最后自动增加换行字符 \n。 使用 fmt.Print(“hello, world\n”) 可以得到相同的结果。 Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。变量声明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package  mainimport  "fmt" var  x, y int var  (	a int   = 1      b bool  = true  ) var  c, d int  = 1 ,2 var  e, f = 123 , "yueyun" func  main ()     g,h := 123 , "explosion"           _,t = nil , 'Explosion'      fmt.Println(g,h.t) } 
声明⼀个变量 默认的值是 0 var a intvar b int = 100var c = 100e := 100 (不支持全局)
常量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package  mainimport  "fmt" func  main () 	const  a int  = 10  	const  ( 		b int     = 20  		c string  = "Hello"  	) 	 	const  ( 		BEJing   = 10  * iota   		SHANGHAI              		SHENZHEN              	)          const  (                 _           = iota                                     KB ByteSize = 1  << (10  * iota )                        MB                                                    GB                                                    TB                                                    PB                                                    EB                                                    ZB                                                    YB                                                ) 	fmt.Println(a, b, c, BEJing, SHANGHAI, SHENZHEN)      } 
函数 基本示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package  mainimport  "fmt" func  foo1 () 	fmt.Println("foo1" ) } func  foo2 (a int , b string ) int , string ) {	fmt.Println("foo2" , a, b) 	return  a + 1 , b + "explosion"  } func  foo3 (a int , b string ) int , d string ) {	 	fmt.Println("foo3" , a, b, c, d) 	c = a + 1  	d = b + "explosion"  	return  } func  main () 	foo1() 	foo2(1 , "hello" ) 	foo3(2 , "world" ) } 
函数传参 函数传递参数包括值传递和引用传递
这里首先介绍一下指针
值传递 值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。
以下定义了 swap() 函数:
1 2 3 4 5 6 7 8 9 10 11 12 func  swap (x, y int ) int  {   var  temp int     temp = x     x = y        y = temp     return  temp; } 
接下来,让我们使用值传递来调用 swap() 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package  mainimport  "fmt" func  main ()        var  a int  = 100     var  b int  = 200     fmt.Printf("交换前 a 的值为 : %d\n" , a )    fmt.Printf("交换前 b 的值为 : %d\n" , b )        swap(a, b)    fmt.Printf("交换后 a 的值 : %d\n" , a )    fmt.Printf("交换后 b 的值 : %d\n" , b ) } func  swap (x, y int ) int  {   var  temp int     temp = x     x = y        y = temp     return  temp; } 
引用传递(指针传递) 引用传递指针参数传递到函数内,以下是交换函数 swap() 使用了引用传递:
1 2 3 4 5 6 7 func  swap (x *int , y *int )    var  temp int     temp = *x        *x = *y          *y = temp     } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package  mainimport  "fmt" func  main ()        var  a int  = 100     var  b int = 200     fmt.Printf("交换前,a 的值 : %d\n" , a )    fmt.Printf("交换前,b 的值 : %d\n" , b )        swap(&a, &b)    fmt.Printf("交换后,a 的值 : %d\n" , a )    fmt.Printf("交换后,b 的值 : %d\n" , b ) } func  swap (x *int , y *int )    var  temp int     temp = *x        *x = *y          *y = temp     } 
init 函数与导包 现在在一个 go 的工程目录下有下面的文件
1 2 3 4 5 6 7 8 9 tree /F /A E:. |   main.go | +---lib1  |       lib1.go | |---lib2          lib2.go 
main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 package  mainimport  (	"fmt"  	"/go/lib1"  	"/go/lib2"  ) func  main () 	fmt.Println("main()...." ) 	lib1.Lib1Test() 	lib2.Lib2Test() } 
lib1.go
1 2 3 4 5 6 7 8 9 10 11 12 package  lib1import  "fmt" func  Lib1Test () 	fmt.Println("Lib1Test()...." ) } func  init () 	fmt.Println("Lib1 init()...." ) } 
lib2.go
1 2 3 4 5 6 7 8 9 10 11 12 13 package  lib2import  "fmt" func  Lib2Test () 	fmt.Println("Lib2Test()...." ) } func  init () 	fmt.Println("Lib2 init()...." ) } 
import 导包的时候
import _ “fmt”: 给 fmt 包起⼀个别名,匿名, ⽆法使⽤当前包的⽅法,但是 会执⾏当前的包内部的 init()⽅法 import aa “fmt” 给 fmt 包起⼀个别名,aa, aa.Println()来直接调⽤ import . “fmt” 将当前 fmt 包中的全部⽅法,导⼊到当前本包的作⽤中,fmt 包中 的全部的⽅法可以直接使⽤ API 来调⽤,不需要 fmt.API 来调⽤ defer defer 的执行顺序 
执行顺序是 fun3() –> fun2() –> fun1()
defter 和 return 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package  mainfunc  deferFunc () int  {	println ("defer func called... " ) 	return  0  } func  returnFunc () int  {	println ("return func called... " ) 	return  0  } func  returnAndDefer () int  {	defer  deferFunc() 	return  returnFunc() } func  main () 	returnAndDefer() } 
return 之后的语句先执⾏,defer 后的语句后执⾏
切片和 slice 数组 数组长度是固定的 固定长度的数据在传参的时候是严格匹配数组类型的
1 2 3 4 5 6 7 8 9 10 package  mainfunc  main ()     var  myArray1 = [10 ]int       myArray2 := [10 ]int {1 ,2 ,3 ,4 }     myArray3 := [4 ]int {11 ,22 ,33 ,44 } } func  printArray (myArray [4]int )      } 
动态数组 slice 动态数组的参数上是引用传递 而且不同元素长度的动态数组他们的形参是一致
声明方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package  mainimport  (	"fmt"  ) func  main () 	myArray := []int {1 , 2 , 3 , 4 }  	 	slice := []int {1 , 2 , 3 } 	var  slice1 []int                 	slice1 = make ([]int , 3 )         	var  slice2 = make ([]int , 3 , 5 )  	slice3 := make ([]int , 3 )        	fmt.Println(slice, slice1, slice2, slice3) 	fmt.Println(myArray) } func  printArray (myArray []int ) 	for  _, v := range  myArray { 		fmt.Println(v) 	} } 
使用方式 切片的容量和追加:切片的长度和容量不同,长度表示左指针至右指针的距离,容量表示左指针至底层数组末尾的距离
切⽚的扩容机制,append的时候,如果⻓度增加后超过容量,则将容量增加2倍
切片截取 可以通过设置下限及上限来设置截取切片*[lower-bound:upper-bound]*,实例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package  mainimport  "fmt" func  main ()        numbers := []int {0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 }       printSlice(numbers)        fmt.Println("numbers ==" , numbers)        fmt.Println("numbers[1:4] ==" , numbers[1 :4 ])        fmt.Println("numbers[:3] ==" , numbers[:3 ])        fmt.Println("numbers[4:] ==" , numbers[4 :])    numbers1 := make ([]int ,0 ,5 )    printSlice(numbers1)        number2 := numbers[:2 ]    printSlice(number2)        number3 := numbers[2 :5 ]    printSlice(number3) } func  printSlice (x []int )    fmt.Printf("len=%d cap=%d slice=%v\n" ,len (x),cap (x),x) } 
执行以上代码输出结果为:
1 2 3 4 5 6 7 8 len=9 cap =9 slice=[0 1 2 3 4 5 6 7 8] numbers == [0 1 2 3 4 5 6 7 8] numbers[1:4] == [1 2 3] numbers[:3] == [0 1 2] numbers[4:] == [4 5 6 7 8] len=0 cap =5 slice=[] len=2 cap =9 slice=[0 1] len=3 cap =7 slice=[2 3 4] 
append() 和 copy() 函数 如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package  mainimport  "fmt" func  main ()    var  numbers []int     printSlice(numbers)        numbers = append (numbers, 0 )    printSlice(numbers)        numbers = append (numbers, 1 )    printSlice(numbers)        numbers = append (numbers, 2 ,3 ,4 )     printSlice(numbers)        numbers1 := make ([]int , len (numbers), (cap (numbers))*2 )        copy (numbers1,numbers)    printSlice(numbers1)    } func  printSlice (x []int )    fmt.Printf("len=%d cap=%d slice=%v\n" ,len (x),cap (x),x) } 
输出结果
1 2 3 4 5 len=0 cap =0 slice=[] len=1 cap =1 slice=[0] len=2 cap =2 slice=[0 1] len=5 cap =6 slice=[0 1 2 3 4] len=5 cap =12 slice=[0 1 2 3 4] 
map 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package  mainimport  (    "fmt"  ) func  main ()          var  test1 map [string ]string           test1 = make (map [string ]string , 10 )      test1["one" ] = "php"      test1["two" ] = "golang"      test1["three" ] = "java"      fmt.Println(test1)           test2 := make (map [string ]string )     test2["one" ] = "php"      test2["two" ] = "golang"      test2["three" ] = "java"      fmt.Println(test2)           test3 := map [string ]string {         "one"  : "php" ,         "two"  : "golang" ,         "three"  : "java" ,     }     fmt.Println(test3)      language := make (map [string ]map [string ]string )     language["php" ] = make (map [string ]string , 2 )     language["php" ]["id" ] = "1"      language["php" ]["desc" ] = "php是世界上最美的语言"      language["golang" ] = make (map [string ]string , 2 )     language["golang" ]["id" ] = "2"      language["golang" ]["desc" ] = "golang抗并发非常good"           fmt.Println(language)                                                        fmt.Println(language) } 
面向对象特征 封装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package  mainimport  "fmt" type  Human struct  {	name string  	sex  string  } func  (this *Human) 	fmt.Println(this.name, "Humen.Eating()..." ) } func  (this *Human) 	fmt.Println(this.name, "Humen.Walking()..." ) } func  (this *Human) string ) {	this.name = name } func  main () 	humanTest := Human{name: "月晕" , sex: "男" } 	humanTest.Eat() 	humanTest.Walk() 	humanTest.setName("月晕2" ) 	humanTest.Eat() 	humanTest.Walk() } 
(this Human) 代表的只是值拷贝 而(this *Human)是传递的*Human 类型 go 会取地址传进去 即可以修改到原属性的值
类名、属性名、方法名 首字母大写表示对外(其他包)可以访问,否则只能够在本包内访问
继承 父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type  Human struct  {	name string  	sex  string  } func  (this *Human) 	fmt.Println(this.name, "Humen.Eating()..." ) } func  (this *Human) 	fmt.Println(this.name, "Humen.Walking()..." ) } func  (this *Human) string ) {	this.name = name } 
子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 type  SuperMan struct  {	Human 	level int  } func  (this *SuperMan)     fmt.Println("SuperMan.Eat()... " ) } func  (this *SuperMan)     fmt.Println("name = " , this.name)      } 
定义子类
1 2 3 4 5 var  s SuperMans.name = "yueyun"  s.sex = "♂"  s.level = 98  
多态 基本要素
有一个父类(有接口)
1 2 3 4 5 6 type  AnimalIF interface {    Sleep()     GetColor() string       GetType() string   } 
有子类(实现了父类的全部接口调用方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 typo Cat struct  {     color String  } func  (this *Cat)     fmt.Println("Cat is Sleep" ) } func  (this *Cat) string  {    return  this.color } func  (this *Cat) string  {    return  "Cat"  } 
父类型的变量(指针)指向(引用)子类的具体数据变量
1 2 3 4 5 6 7 8 var  animal AnimalIF animal = &Cat{"Green" } animal.Sleep()  func  showAnimal (animal AnimalIF)     animal.Sleep()       fmt.Println("color =" , animal.GetColor) } 
interface 通用万能类型 interface{} – 空接口
int string float等 都是先了interface{} 就可以用interface类型引用任意的数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package  mainimport  "fmt" func  myFunc (arg interface {}) 	 	fmt.Println("myFunc is called..." ) 	fmt.Println(arg)     value, ok := arg.(string )     if  !ok { 		fmt.Println("arg is not string type" )     } else  {              } } func  main () 	var  i int  = 5  	var  s string  = "Hello world"  	myFunc(i) 	myFunc(s) } 
类型断言
1 2 3 4 5 6 7 8 9 func  myFunc (arg interface {}) 	     value, ok := arg.(string )     if  !ok { 		fmt.Println("arg is not string type" )     } else  {              } } 
反射 pair 
pair是一个对 不管怎么传递都不会变化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package  mainimport  "fmt" func  main () 	var  a string  	 	a = "yueyun"  	var  allType interface {} 	 	 	allType = a 	str, _ := allType.(string ) 	fmt.Println(str) } 
pair在传递的过程中保持不变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package  mainimport  (	"fmt"  	"io"  	"os"  ) func  main () 	 	tty, err := os.OpenFile("/dev/tty" , os.O_RDWR, 0 ) 	if  err != nil  { 		fmt.Println("open file error: " , err) 		return  	} 	 	var  r io.Reader 	 	r = tty 	 	var  w io.Writer 	 	w = r.(io.Writer) 	w.Write([]byte ("hello world\n" )) } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package  mainimport  "fmt" type  Writer interface  {	WriteBook() } type  Reader interface  {	ReadBook() } type  Book struct  {} func  (this *Book) 	fmt.Println("write book" ) } func  (this *Book) 	fmt.Println("read book" ) } func  main () 	 	b := &Book{} 	 	var  r Reader 	 	r = b 	r.ReadBook() 	var  w Writer 	 	w = r.(Writer)  	w.WriteBook() } 
pair 是一直存在而且不会变化 
reflect包 包括基本函数 Valueof || Typeof
1 2 3 4 func  Valueof  (i interface {}) func  Typeof  (i interface {}) 
reflect基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package  mainimport  (	"fmt"  	"reflect"  ) func  reflectNum (arg interface {}) 	fmt.Println("type: " , reflect.TypeOf(arg)) 	fmt.Println("value: " , reflect.ValueOf(arg)) } func  main () 	var  num float64  = 1.114514  	reflectNum(num) } 
复杂类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package  mainimport  (	"fmt"  	"reflect"  ) type  User struct  {	Id   int  	Name string  	Age  int  } func  (this User) 	fmt.Println("User is called..." ) 	fmt.Printf("%v\n" , this) } func  main () 	user := User{1 , "yueyun" , 18 } 	DoFiledAndMethod(user) } func  DoFiledAndMethod (input interface {}) 	 	inputType := reflect.TypeOf(input) 	fmt.Println("inputType is :" , inputType.Name()) 	 	inputValue := reflect.ValueOf(input) 	fmt.Println("inputValue is :" , inputValue) 	 	 	 	 	for  i := 0 ; i < inputType.NumField(); i++ { 		field := inputType.Field(i) 		value := inputValue.Field(i).Interface() 		fmt.Printf("%s: %v = %v\n" , field.Name, field.Type, value) 	} 	 	for  i := 0 ; i < inputType.NumMethod(); i++ { 		m := inputType.Method(i) 		fmt.Printf("%s: %v\n" , m.Name, m.Type) 	} }	 
结构体标签 json 解编码 orm映射关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package  mainimport  (	"fmt"  	"reflect"  ) type  resume struct  {	Name string  `info:"name" docs:"我的名字"`  	Sex  string  `info:"sex"`  } func  findTag (str interface {}) 	t := reflect.TypeOf(str).Elem() 	for  i := 0 ; i < t.NumField(); i++ { 		taginfo := t.Field(i).Tag.Get("info" ) 		tagdoc := t.Field(i).Tag.Get("docs" ) 		fmt.Println("info:" , taginfo, "docs:" , tagdoc) 	} } func  main () 	var  re resume 	findTag(&re) } 
json转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package  mainimport  (	"encoding/json"  	"fmt"  ) type  Moive struct  {	Title  string    `json:"title"`  	Year   int       `json:"year"`  	Price  int       `json:"price"`  	Actors []string  `json:"actors"`  } func  main () 	movie := Moive{"喜剧之王" , 2000 , 10 , []string {"周星驰" , "张柏芝" }} 	 	jsonStr, err := json.Marshal(movie) 	if  err != nil  { 		fmt.Println("json marshal error:" , err) 		return  	} 	fmt.Printf("%s\n" , jsonStr) 	 	myMoive := Moive{} 	err = json.Unmarshal(jsonStr, &myMoive) 	if  err != nil  { 		fmt.Println("json unmarshal error:" , err) 		return  	} 	fmt.Printf("%v\n" , myMoive) } 
高级语法 goroutine基本模型和调度设计策略 早期的单线程操作系统
存在的问题
单一执行流程, 计算机只能一个一个任务的去处理 进程阻塞所带来的CPU浪费时间(密集IO处理 ) 多线程/多进程操作系统
多进程/多线程 解决了阻塞的问题
但是当cpu工作在切换线程的时候会存在很高的切换成本 导致CPU利用率很低, 进程/线程数量越大 切换的成本就越大 也就越浪费 多线程 随着同步竞争 (如 锁 竞争资源冲突等) 开发设计会变得越来越复杂 且内存占用多
协程
co-routine 的设计
Goland 对协程的处理: 内存很小可以大量 灵活调度 可以经常切换
Goland对早期调度器的处理
创建 销毁 调度G都需要每个M获取锁 形成了激烈的锁竞争延迟和额外的系统负载 
GMP
go现在的设计
调度器的设计策略 复用线程 wrok stealing 机制 hand off 机制 利用并行 GOMAXPROCS限定P的个数 = cpu核数 / 2
抢占 平均特点 抢占式
全局G队列 goroutine goroutine普通函数写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package  mainimport  (	"fmt"  	"time"  ) func  newTask () 	i := 0  	for  { 		i++ 		fmt.Println("new Goroutine: i = " , i) 		time.Sleep(1  * time.Second) 	} } func  main () 	 	go  newTask() 	i := 0  	for  { 		i++ 		fmt.Println("main Goroutine: i = " , i) 		time.Sleep(1  * time.Second) 	} } 
goroutine匿名函数写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func  main () 	go  func ()  		defer  fmt.Println("A.defer" ) 		func ()  			defer  fmt.Println("B.defer" ) 			 			runtime.Goexit() 			fmt.Println("B" ) 		}() 		fmt.Println("A" ) 	}() 	for  { 		time.Sleep(time.Second * 1 ) 	} } 
1 2 3 4 5 6 7 8 9 10 func  main () 	go  func (a int , b int ) bool  { 		defer  fmt.Println("A.defer" ) 		fmt.Println("a=" , a, "b=" , b) 		return  false  	}(10 , 20 ) 	for  { 		time.Sleep(time.Second * 1 ) 	} } 
runtime.Goexit() 退出当前的goroutine
channel 管道 基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func  main () 	 	ch := make (chan  string ) 	 	go  func ()  		 		defer  println ("func goroutine exit." ) 		println ("func goroutine starts sending..." ) 		ch <- "EXPLOSION!!!"   	}() 	 	println ("main goroutine starts receiving..." ) 	str := <-ch  	println ("main goroutine received:" , str) 	defer  println ("main goroutine exit." ) } 
基本分析图示
chan 管道具有连接两个协程并且具有同步阻塞的功能
具有缓冲的channel 无缓冲的channel
两个goroutine都达到通道 左侧goroutine开始发送数据到通道中 这时右侧的goroutine会在通道中被锁住 直到交换完成 右侧的goroutine开始接受数据 左侧的goroutine也将会被锁住 交换数据完成 这时两个goroutine会被解锁 可以继续跑下面的流程 有缓冲的channel
左侧的goroutine 传输数据 直到管道写满或者自己停止 右侧的goroutine获取数据 左侧的goroutine新发送数据 左侧发送新增 右边接受 这两种操作既不是同步的也不会相互阻塞 所有的发送和接受完毕 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func  main () 	c := make (chan  int , 3 )  	fmt.Println("len(c)=" , len (c), "cap(c)=" , cap (c)) 	go  func ()  		defer  fmt.Println("子go程结束" ) 		for  i := 0 ; i < 5 ; i++ { 			c <- i 			fmt.Println("子go程正在运行,发送的元素=" , i, "len(c)=" , len (c), "cap(c)=" , cap (c)) 		} 	}() 	time.Sleep(2  * time.Second) 	for  i := 0 ; i < 5 ; i++ { 		 		num := <-c 		fmt.Println("num=" , num) 	} 	fmt.Println("main go程结束" ) } 
当channel已经满 在向里面写数据就会阻塞 当channel为空 从里面取数据也会阻塞 channel的关闭 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func  main () 	c := make (chan  int ) 	go  func ()  		for  i := 0 ; i < 5 ; i++ { 			c <- i 		} 		close (c)  	}() 	for  { 		if  data, ok := <-c; ok { 			fmt.Println(data) 		} else  { 			break  		} 	} 	fmt.Println("main go程结束" ) } 
channel 不像文件一样要经常的打开和关闭 只有当确定了没有任何发送的数据了或者想结束range循环才会选择去关闭channel 关闭channel后 无法在向channel 再发送数据(引发panic) 关闭channel后 可以继续从channel接受数据 关于nil channel 无论收发都会去阻塞 channel和range 1 2 3 4 5 6 7 8 9 10 11 12 13 14 func  main () 	c := make (chan  int ) 	go  func ()  		for  i := 0 ; i < 5 ; i++ { 			c <- i 		} 		close (c)  	}() 	 	for  data := range  c { 		fmt.Println(data) 	} 	fmt.Println("main go程结束" ) } 
channel和select 单个流程下一个go只能监听一个channel的状态 select可以完成监控多个channel的状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func  main () 	c := make (chan  int ) 	quit := make (chan  int ) 	go  func ()  		for  i := 0 ; i < 10 ; i++ { 			fmt.Println(<-c) 		} 		quit <- 0  	}() 	 	fibonacci(c, quit) } func  fibonacci (c, quit chan  int ) 	x, y := 0 , 1  	for  { 		 		select  { 		case  c <- x: 			x, y = y, x+y 		case  <-quit: 			fmt.Println("quit" ) 			return  		} 	} } 
goland context 实现原理 context是goland中的经典工具,主要在异步场景中用于实现并发协调以及对 goroutine 的生命周期控制. 除此之外,context 还兼有一定的数据存储能力.
核心数据结构 context.Context Go modules go modules 模式 使用go mod和go modules 进行项目依赖管理
Go PATH的工作模式弊端: 无版本控制的概念, 无法同步一致第三方版本号 无法指定当前
go mod 的一些命令
go mod init 生成go.mod 文件go mod download 下载go.mod文件中指明的所有依赖go mod tidy 整理现有的依赖go mod vendor 导出项目所有的依赖到vendor目录… 等等等 go mod 的环境变量
GO111MODULE: auto||on||off 设置 go env -w GO111MODULE=on GOPROXY: 默认值 国内代理: “https://mirrors.aliyun.com/goproxy/  “ $ go env -w GOPROXY=https://goproxy.cn,directdirect 用于指示Go回源到模块版本的源地址去抓取 GOSUMDB: 用于在拉取模块版本时(无论是从源站拉取还是通过 Go module proxy 拉取)保证拉取到的模块版本数据未经过篡改,若发现不一致,也就是可能存在篡改,将会立即中止 GONOPROXY/GONOSUMDB/GOPRIVATE: 建议直接设置 GOPRIVATE,它的值将作为 GONOPROXY 和 GONOSUMDB 的默认值,所以建议的最佳姿势是直接使用 GOPRIVATE 。$ go env -w GOPRIVATE="git.example.com,github.com/eddycjy/mquote" 不经过 GOPROXY, GOSUMDB go env 来查看环境变量
go modules 初始化项目 开启Go Modules模块GOMODULE=on
初始化项目
1 $ mkdir  -p ~/project/module_test 
创建go mod 文件同时起当前项目名
1 $ go init github.com/yueyun/module_test 
生成go.mod文件 内容为
1 2 module github.com/yueyun/module_test go 1.21.6 
在该项目编写代码, 如果源代码依赖某个库 如(github.com/apprehen/zinx/znet)可以
go mod 文件下面会新增加一行代码
1 require github.com/apprehen/zinx v0.0.0-20200315083925-f09df55dc746,indirect 
含义当前模块依赖github.com/apprehen/zinx 依赖版本是后面的
//indirct 表示间接依赖 因为项目直接依赖的是znet包 间接依赖的是zinx包
生成一个go.sum 文件h1:hash: 表示整体项目的zip文件打开之后的全部文件的校验和来生成的hash 如果不存在 则表示依赖的库可能用不上
Go语言项目案例 IM-System 构建基础的server main.go 1 2 3 4 5 6 package  mainfunc  main  ()     server := NewServer("127.0.0.1" , 8800 )	     server.Start() } 
server.go server类型 1 2 3 4 type  Server struct  {    IP 		string      Port 	int  } 
server方法 创建一个server对象
1 2 3 4 5 6 7 func  NewServer  (IP string ,Port int )     server := &Server{         IP: IP,         Port: port     }     return  server } 
启动 server服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func  (this *Server)          listener,err := net.listen("tcp" ,fnt.Sprintf("%s:%d" ,this.IP,this.Port))     if  err = !nil  {         fmt.PrintLn("net.Listen err:" ,err)     	return      }          defer  listener.Close()     for  {                  conn,err = listener.Accept()         if  err!= nil  {             fmt.Println("listener.Accept err:" ,err)             continue          }                  go  this.Handler(conn)     } } 
处理连接业务
1 2 3 func  (this *Server) 	fmt.Println("建立连接成功" ) } 
用户上线功能 user.go user类型 1 2 3 4 5 6 type  User struct  {    Name string      Addr string      C chan  string      conn net.Conn } 
方法 创建一个user对象
1 2 3 4 5 6 7 8 9 10 11 12 func  NewUser  (conn net.Conn)     userAddr := conn.RemoteAddr().String     user := &User {         Name: userAddr         Addr: userAddr         C: make (chan  string )         conn: conn     }          go  user.ListenMessage()     return  user } 
监听消息user对应的channel消息
1 2 3 4 5 6 func  (this *User)     for  {         msg := <- this.C         this.conn.Write([]byte (msg + "\n" ))     } } 
server.go server类型 新增OnlineMap和Message属性
1 2 3 4 5 6 7 8 9 type  Server struct  {    IP   string  	Port int  	 	OnlineMap map [string ]*User 	mapLock   sync.RWMutex 	 	Message chan  string  } 
在处理客户端上线的Handler创建并添加用户
1 2 3 4 5 6 7 8 9 10 11 12 func  (this *Server) 	fmt.Println("建立连接成功" ) 	 	user := NewUser(conn) 	this.mapLock.Lock() 	this.OnlineMap[user.Name] = user 	 	this.BroadCast(user, "已上线" ) 	this.mapLock.Unlock() 	 	select  {} } 
新增广播消息方法
1 2 3 4 func  (this *Server) string ) {	sendMsg := "["  + user.Addr + "]"  + user.Name + ":"  + msg 	this.Message <- sendMsg } 
新增监听广播消息channel方法
1 2 3 4 5 6 7 8 9 10 11 func  (this *Server) 	for  { 		msg := <-this.Message 		 		this.mapLock.Lock() 		for  _, cli := range  this.OnlineMap { 			cli.C <- msg 		} 		this.mapLock.Unlock() 	} } 
用一个goroutine单独监听Message
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 func  (this *Server) 	 	listener, err := net.Listen("tcp" , fmt.Sprintf("%s:%d" , this.IP, this.Port)) 	if  err != nil  { 		fmt.Println("net.Listen err:" , err) 		return  	} 	 	defer  listener.Close() 	 	go  this.ListenMessager()  	for  { 		 		conn, err := listener.Accept() 		if  err != nil  { 			fmt.Println("listener.Accept err:" , err) 			continue  		} 		 		go  this.Handler(conn) 	} } 
用户消息广播机制 server.go 完善handle处理业务的方法 启动一个针对当前客户端的读goroutine
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 func  (this *Server) 	 	user := NewUser(conn) 	this.mapLock.Lock() 	this.OnlineMap[user.Name] = user 	 	this.BroadCast(user, "已上线" ) 	this.mapLock.Unlock() 	 	go  func ()  		buf := make ([]byte , 4096 ) 		for  { 			n, err := conn.Read(buf) 			if  n == 0  {  				this.BroadCast(user, "下线" ) 				return  			} 			if  err != nil  && err != io.EOF { 				fmt.Println("conn.Read err:" , err) 				return  			} 			 			msg := string (buf[:n-1 ]) 			 			this.BroadCast(user, msg) 		} 	}() 	 	select  {} } 
用户业务层封装 user.go user类型新增server关联
1 2 3 4 5 6 7 type  User struct  {	Name   string  	Addr   string  	C      chan  string  	conn   net.Conn 	server *Server } 
新增Online方法
1 2 3 4 5 6 7 func  (this *User) 	this.server.mapLock.Lock() 	this.server.OnlineMap[this.Name] = this 	 	this.server.BroadCast(this, "已上线" ) 	this.server.mapLock.Unlock() } 
新增Offline方法
1 2 3 4 5 6 7 func  (this *User) 	this.server.mapLock.Lock() 	delete (this.server.OnlineMap, this.Name) 	 	this.server.mapLock.Unlock() 	this.server.BroadCast(this, "下线" ) } 
新增DoMessage方法
1 2 3 func  (this *User) string ) {	this.server.BroadCast(this, msg) } 
server.go 之前业务的替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func  (this *Server) 	 	user := NewUser(conn, this) 	user.Online() 	 	go  func ()  		buf := make ([]byte , 4096 ) 		for  { 			n, err := conn.Read(buf) 			if  n == 0  {  				user.Offline() 				return  			} 			if  err != nil  && err != io.EOF { 				fmt.Println("conn.Read err:" , err) 				return  			} 			 			msg := string (buf[:n-1 ]) 			 			user.DoMessage(msg) 		} 	}() 	 	select  {} } 
在线用户查询 user.go 提供SendMsg向对象客户端发送消息API
1 2 3 func  (this *User) string ) {    this.conn.Write([]byte (msg)) } 
在DoMessage()方法中 加上对who指令的处理 返回在线用户的信息
1 2 3 4 5 6 7 8 9 10 11 12 13 func  (this *User) string ) {	 	if  msg == "who"  { 		 		this.server.mapLock.Lock() 		for  _, user := range  this.server.OnlineMap { 			onlineMsg := "["  + user.Addr + "]"  + user.Name + ":"  + "在线...\n"  			this.SendMsg(onlineMsg) 		} 	} else  { 		this.server.BroadCast(this, msg) 	} } 
修改用户名 消息格式为rename 然后修改服务器内部状态改成AwaitUserName在对消息处理设置成新的用户名
修改 DoMessage方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func  (this *User) string ) {	switch  this.state { 	case  NormalState: 		if  msg == "who"  { 			 			this.server.mapLock.Lock() 			for  _, user := range  this.server.OnlineMap { 				onlineMsg := "["  + user.Addr + "]"  + user.Name + ":"  + "在线...\n"  				this.SendMsg(onlineMsg) 			}             this.server.mapLock.unlock() 		} else  if  msg == "rename"  { 			this.state = AwaitUserNameState 			 			this.SendMsg("请输入新的用户名: " ) 			 		} else  { 			this.server.BroadCast(this, msg) 		} 	case  AwaitUserNameState: 		this.Rename(msg) 		this.state = NormalState 	} } 
新增加 ReName方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func  (this *User) string ) {	if  newName == ""  { 		this.SendMsg("用户名不能为空\n" ) 		return  	} 	_, ok := this.server.OnlineMap[newName] 	if  ok { 		this.SendMsg("用户名已经被使用\n" ) 		return  	} else  { 		this.SendMsg("用户名正在更改...\n" ) 		this.server.mapLock.Lock() 		delete (this.server.OnlineMap, this.Name) 		this.server.OnlineMap[newName] = this 		this.server.mapLock.Unlock() 		this.Name = newName 		this.SendMsg("您已经更新用户名:"  + this.Name + "\n" ) 	} } 
超时强踢功能 在用户的hander()中 goroutine中 添加用户活跃的channel 一旦有消息 就向该channel发送数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 func  (this *Server) 	 	user := NewUser(conn, this) 	user.Online() 	isLive := make (chan  bool )  	 	go  func ()  		buf := make ([]byte , 4096 ) 		for  { 			n, err := conn.Read(buf) 			if  n == 0  {  				user.Offline() 				return  			} 			if  err != nil  && err != io.EOF { 				fmt.Println("conn Read err:" , err) 				return  			} 			 			msg := string (buf[:n-1 ]) 			 			user.DoMessage(msg) 			isLive <- true   		} 	}() 
在用户的hander()中 添加定时器功能 超时则强踢
1 2 3 4 5 6 7 8 9 10 11 12 13 select  {	case  <-isLive: 		 	case  <-time.After(time.Second * 10 ): 		 		user.SendMsg("你被踢了\n" ) 		 		close (user.C) 		 		conn.Close() 		 		return  } 
实现私聊功能 消息格式是 to|yueyun|你好啊我是...
user.go 在DoMessage()方法中 加上对to|yueyun|你好啊 指令的处理 返回在线用户的信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 if  msg == "who"  {			 			this.server.mapLock.Lock() 			for  _, user := range  this.server.OnlineMap { 				onlineMsg := "["  + user.Addr + "]"  + user.Name + ":"  + "在线...\n"  				this.SendMsg(onlineMsg) 			} 			this.server.mapLock.Unlock() 		} else  if  msg == "rename"  { 			this.state = AwaitUserNameState 			 			this.SendMsg("请输入新的用户名: " ) 		} else  if  len (msg) > 4  && msg[:3 ] == "to|"  { 			 			 			remoteName := strings.Split(msg, "|" )[1 ] 			if  remoteName == ""  { 				this.SendMsg("消息格式不正确,请使用\"to|yueyun|消息内容\"格式\n" ) 				return  			} 			 			remoteUser, ok := this.server.OnlineMap[remoteName] 			if  !ok { 				this.SendMsg("该用户名不存在\n" ) 				return  			} 			 			content := strings.Split(msg, "|" )[2 ] 			if  content == ""  { 				this.SendMsg("无消息内容,请重发\n" ) 				return  			} 			remoteUser.SendMsg(this.Name + "对你说:"  + content + "\n" ) 		} else  { 			this.server.BroadCast(this, msg) 		}