Skip to content

XcXerxes/go-playground

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go 的基本数据类型

  bool
  string 字符型
  int int8 int16 int32 int64 整型
  uint uint8 uint16 uint32 uint64 uintptr 无符号整型
  byte // alias for uint8 无符号8位整型
  rune // alias for int32,represents a unicode code point 字符编码
  float32 float64 浮点类型
  complex64 complex128 复数类型

go 的类型转化

  GO 语言的类型转换非常严格1Go 语言不允许隐式类型转换
  2别名和原有类型也不能进行隐式类型转换

go 指针类型

  1支持指针类型但不支持指针运算
  2string 是值类型其默认的初始化值为空字符串而不是 nil

go 算术运算符

  GO 语言没有前置的 ++, -- (++a) // 不支持
  GO 语言支持后置的 ++, -- (a++) // 支持

  数组之间如果长度不一样是不能进行比较的会直接报错

go 循环

  GO 语言仅支持循环关键字 for
  for i := 0; i < count; i++ {
    // 循环体
	}
  

if 条件

  1condition 表达式结果必须为 布尔值 eg // if xxx 必须为bool

  2支持变量赋值if var declaration; condition {
      // ...
    }

switch 条件

  1条件表达式 不限制 为常量或者整数

  2单个 case 可以出现多个结果选项使用逗号分隔

  3 C 语言等规则相反GO 语言不需要用break来明确退出一个 case
  
  4可以不设定 switch 之后的条件表达式在此种情况下整个 switch 结构与 多个 if...else... 的逻辑作用等同

数组的声明

var a [3] int // 声明并初始化为默认值
a[0] = 1

b := [3]int{1, 2, 3} // 声明同时初始化
c := [2][2]int{{1, 2}, {3, 4}} // 多维数组初始化

数组的截取

a[开始索引(包含), 结束索引(不包含)]

a := [...]int{1, 2, 3, 4, 5}
a[1:2] // 2
a[1:3] // 2, 3
a [1:len(a)] // 2, 3, 4, 5
a [1:] // 2, 3, 4, 5
a [:3] // 1, 2, 3

切片的声明

  var s0 []int

  s0 = append(s0, 1) // 添加

  s := []int{}
   
  s1 := []int{1, 2, 3}

  s2 := make([]int, 2, 4)
  /*
    []type, len, cap
    其中 len 个元素会被初始化为默认零值,未初始化元素不可以访问
  */
  切片就像数组的引用它不存储任何数据更改切片的元素 会修改 其底层数组中对应的元素, 而且与之共享底层数组的切片都会被修改

  切片文法类似于没有长度的数组文法:
    
    [1]bool{true}
  
  切片文法:
    []bool{false}
  
  切片的默认行为
    切片下界的默认值为 0上界则是改切片的长度a[0:10] == a[:10] == a[0] == a[:]
  
  用make 内置函数 创建 切片
    a := make([]int, 0, 5) // 第一个参数是类型 第二个参数是长度,第三个参数是容量

切片 vs 数组

  1切片容量可以伸缩数组不行
  
  2数组可以进行比较, 切片之间不能比较只能  nil 比较

Map声明

  m := map[string]int{"one": 1, "two": 2, "three": 3}

  m1 := map[string]int{}

  m2["one"] = 1

  m3 := make(map[string]int, 10) // 第一个参数 map 类型 第二个参数是 容量
  // 为什么不初始化len  切片len初始化的时候会默认给为 0 值,而map无法


  注意map 在访问 key 不存在时仍会返回零值不能通过返回 nil 来判断元素是否存在!

实现 Set

Go 的内置集合中没有 Set 实现可以 map[type]bool

1元素的唯一性

2基本操作
  1) 添加元素
  2) 判断元素是否存在
  3) 删除元素
  4) 元素个数

字符串 string

1string 是数据类型不是引用或指针类型

2string 是只读的 byte slice, len 函数可以它所包含的 byte 

3string  byte 是数组可以 存放 任何数据

Unicode UTF8

1Unicode 是一种字符集 (code point)

2UTF8  unicode 的存储实现 (转换为字节序列的规则)

函数 一等公民

1、可以有多个返回值

2、所有参数都是值传递:slice,map,channel 会有传引用的错觉

3、函数可以作为变量的值

4、函数可以作为参数和返回值

函数的可变参数

  func sum (ops ...int) int {
    s := 0
    for _, op := range ops {
      s += op
    }
    return s
  }

defer 函数

func TestDefer(t *testing.T) {
  defer func() {
    t.Log("Clear resources")
  }()
  t.Log("Started")
  panic("Fatal error") // defer 仍会执行
}

结构体定义

  type Employee struct {
    Id string
    Name string
    Age int
  }

行为方法 定义

与其他主要编程语言的差异

type Employee struct {
	Id string
	Name string
	Age      int
}
// 第一种定义方式 在实例对应方法被调用时,实例的成员会进行值赋值
func (e Employee) String() string {
  return fmt.Sprintf("Id:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}

// 通常情况为了避免内存拷贝我们使用第二种定义方式
func (e *Employee) String() string {
  return fmt.Sprintf("Id:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}

接口实现

接口定义
type Programmer interface {
  WriteHelloWorld() Code
}

接口实现
type GoProgrammer struct {

}

func (p *GoProgrammer) WriteHelloWorld() Code {
  return "fmt.Println(\"Hello World!\")"
}
特点1接口为非入侵性的实现不依赖于接口定义

2所以接口的定义可以包含在接口使用者包内

接口变量

  var prog Coder = &GoProgrammer{} // prog 是变量 &GoProgrammer{} 是内容
  type GoProgrammer struct {

  } // 这个是类型

空接口与断言

1空接口可以表示任何类型

2通过断言来将空接口转换为指定类型
v, ok :=p.(int) // ok = true 时为转换成功

GO 接口最佳实践

1倾向于使用小的接口定义很多接口只包含一个方法

2较大的接口定义可以由多个小接口定义组合而成

3只依赖于必要功能的最小接口

Go 的错误机制

1没有异常机制

2error类型实现了 error 接口

3可以通过 errors.New 来快速创建错误实例
  type error interface {
    Error() string
  }
  errors.New("n must be in the range[0, 1]")

4及早失败避免嵌套 // 先返回错误的信息,再处理逻辑

panic

1panic 用于不可以恢复的错误

2panic 退出前会执行 defer 指定的内容

panic VS os.Exit

1、 os.Exit 退出时不会调用 defer 指定的函数

2、os.Exit 退出时不输出当前调用栈的信息

当心! recover 成为恶魔

  1形成僵尸服务进程导致 health check 失效

  2、"Let it Crash" 往往是我们恢复不确定性最好的方法

package 包

1基本复用模块单元
   以首字母大写来表明可被包外代码访问

2代码的 package 可以和所在的目录不一致

3同一目录的 Go 代码的 package 保持一致

CSP 并发机制

1CSP 模式 是通过 Channel 进行通讯的更松耦合
2Go  channel 是有容量限制并且独立于处理Groutine

多路选择机制和超时

select

多渠道的选择
select {
  case ret := <-retCh1:
    t.Log("result", ret)
  case ret := <-retCh2:
    t.Log("result", ret)
    default:
    t.Error("No one returned")
}
超时控制
select {
  case ret := <-retCh1:
    t.Log("result", ret)
  case <-time.After(time.Second * 1) // 通过判断程序执行等待的时间,来返回是否超时
    t.Error("No one returned")
}

Context 与任务取消

1 Context: 通过 context.Background() 创建

2 Context: context.WithCancel(parentContext) 创建
  ctx, cancel := context.WithCancel(context.Background())

3当前 Context 被取消时基于他的子 context 都会被取消

4接收取消通知 <-ctx.Done()

并发任务(只执行一次)

单例模式 (懒汉式线程安全)

type Singleton struct {
}

var singleInstance *Singleton
var once sync.Once

func GetSingletonObj() *Singleton {
	once.Do(func() {
		fmt.Println("Create Obj")
		singleInstance = new(Singleton)
	})
	return singleInstance
}

func TestSingletonObj(t *testing.T) {
	var wg sync.WaitGroup
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			obj := GetSingletonObj()
			fmt.Println("obj==========", unsafe.Pointer(obj))
			wg.Done()
		}()
	}
	wg.Wait()
}

内置单元测试框架

1Fail Error: 该测试失败该测试继续其他测试继续执行

2FailNow, Fatal 该测试失败该测试中止其他测试继续执行

Benchmark 基准测试

func BenchmarkConcatStringByAdd(b *testing.B) {
  // 与性能测试无关的代码
  b.ResetTimer()
  for i :=0; i<b.N; i++ {
    // 测试代码
  }
  b.StopTimer()
  // 与性能测试无关的代码
}

测试命令
go test -bench=. [-benchmem]

-bench=<相关benchmark测试>
Windows 下使用 go test 命令行时-bench=.应写为-bench="."

反射编程

reflect.TypeOf vs reflect.ValueOf

1、reflect.TypeOf 返回类型(reflect.Type)

2、reflect.ValueOf 返回值(reflect.Value)

3、可以从 reflect.Value 获得类型

4通过 Kind 的来判断类型

访问 StructTag

type Employee struct {
	EmployeeID string
	Name       string `format:"normal"`
	Age        int
}
if nameField, ok := reflect.TypeOf(*e).FieldByName("Name"); !ok {
		t.Error("Faile")
	} else {
		fmt.Println("Tag:format", nameField.Tag.Get("format"))
	}

DeepEqual 比较切片和map

http服务的默认路由

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
  handle := sh.srv.Handler
  if handler == nil {
    handler = DefaultServerMux // 使用缺省的Router
  }
  if req.RequestURI == '*' && req.Method =="OPTIONS" {
    handler = globalOptionsHandler{}
  }
  handler.ServeHTTP(rw, req)
}

http服务的路由规则

1、URL分为两种。末尾是/ :表示一个子树,后面可以跟其他子路径;
  末尾不是 /,表示一个叶子,是固定的路径

2、它采用最长匹配原则,如果有多个匹配,一定采用路径最长的进行匹配

3、如果没有找到任何匹配项,会返回404错误

sync.Map

1适合读多写少且Key相对稳定的环境

2采用了空间换时间的方案并且采用指针的方式间接实现值的映射所以存储空间会较 built-in map 

别让性能被锁住

1减少锁的影响范围

2减少发生锁冲突的概率
  sync.Map
  ConcurrentMap

3避免锁的使用

内存回收 友好的代码

1复杂对象尽量传递引用
  数组的传递
  结构体的传递

2避免内存分配和复制
  初始化至合适的大小
  复用内存

打开 GC(内存回收)日志

只要在程序执行之前加入环境变量 GODEBUG=gctrace=1 eg:
  GODEBUG=gctrace=1 go test -bench=.
  GODEBUG=gctrace=1 go run main.go

面向错误的设计

1隔离错误-设计

2限流

3不要无休止的等待 - 给阻塞操作一个期限

4断路器模式 - 防止错误传递

Chaos Engineering 原则

如果问题经常发生 人们就会学习和思考解决它的方法

1

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages