golang struct/结构体

golang struct/结构体 //定义一个struct type Student struct { id int name string address string age int } 匿名结构体 //匿名结构 person := struct { Name string Age int }{Name: "name0", Age: 1} jsonBytes, _ := json.Marshal(person) fmt.Println("person:", string(jsonBytes)) type foo struct { Field0 string `json:"field0"` Field1 struct { Field2 string `json:"field2"` } `json:"field1"` Field3 int `json:"field3"` } https://github.com/jemygraw/TechDoc/blob/master/Go%E8%BD%BB%E6%9D%BE%E5%AD%A6/go_tutorial_8_struct_interface.md https://blog.csdn.net/books1958/article/details/22720033

2017-10-10 · 1 min · 57 words · -

golang 定时器, timer, ticker

golang 定时器, timer, ticker Go 可以借助 time.After/time.Ticker 来实现延迟/定时触发器, 主要原理是借助无缓冲 channel 无数据时读取操作会阻塞当前协程, Go 会在给定的时间后向 channel 中写入一些数据 (当前时间), 故阻塞的协程可以恢复运行, 达到延迟或定时执行的功能。 time.Ticker ticker 只要定义完成,从此刻开始计时, 不需要任何其他的操作, 每隔固定时间都会触发。 ticker := time.NewTicker(500 * time.Millisecond) go func() { for t := range ticker.C { fmt.Println("Tick at", t) } }() 立即执行一次 ticker := time.NewTicker(period) for ; true; <-ticker.C { // ... } timer 使用timer定时器,超时后需要重置,才能继续触发。 d := time.Duration(time.Second*2) t := time.NewTimer(d) defer t.Stop() for { <- t.C fmt.Println("timeout...") // need reset t.Reset(time.Second*2) } 执行若干次后退出 func main() { // 创建一个计时器 timeTicker := time.NewTicker(time.Second * 2) i := 0 for { if i > 5 { break } fmt.Println(time.Now().Format("2006-01-02 15:04:05")) i++ <-timeTicker.C } // 清理计时器 timeTicker.Stop() } https://my.oschina.net/u/943306/blog/149395 ...

2017-09-10 · 1 min · 170 words · -

golang rand, 随机数

golang 随机数 http://www.cnblogs.com/baiyuxiong/p/4545032.html math/rand 包用于生成随机数。 package main import "fmt" import "math/rand" func main() { fmt.Println(rand.Intn(100)) // 产生0-100的随机整数 fmt.Println(rand.Float32()) fmt.Println(rand.Float64()) // 产生0.0-1.0的随机浮点数 s1 := rand.NewSource(42) // 用指定值创建一个随机数种子 r1 := rand.New(s1) fmt.Print(r1.Intn(100), ",") fmt.Print(r1.Intn(100)) fmt.Println() s2 := rand.NewSource(42) // 同前面一样的种子 r2 := rand.New(s2) fmt.Print(r2.Intn(100), ",") fmt.Print(r2.Intn(100)) fmt.Println() } func GenRandomBytes(size int) (randomBytes []byte) { rand.Seed(time.Now().UnixNano()) randomBytes = make([]byte, size) rand.Read(randomBytes) return randomBytes } 返回结果: ...

2017-07-27 · 1 min · 106 words · -

gomock

gomock go install github.com/golang/mock/mockgen@v1.6.0 mockgen -source feeds/feeds.go -destination feeds/mocks/feeds_mock.go -package mocks -source:设置需要模拟 (mock)的接口文件 -destination:设置 mock 文件输出的地方,若不设置则打印到标准输出中 -package:设置 mock 文件的包名,若不设置则为 mock_ 前缀加上文件名 (如本文的包名会为 mock_person)

2017-05-04 · 1 min · 23 words · -

协程, coroutine, goroutine

协程, coroutine, goroutine routine, [ruːˈtiːn], 例程 coroutine, [kəruːˈtiːn], 协同程序, 协程 Goroutine 是 Go 中最基本的执行单元。每一个 Go 程序至少有一个 goroutine:主 goroutine. 当程序启动时, 它被自动创建。 goroutine 采用了一种 fork-join 的模型 [[fork-join#ForkJoin]] 每个协程至少需要消耗 2KB 的空间 调度器 进程 跑在一个cpu里面的并发都需要处理上下文切换的问题。进程就是这样抽象出来个一个概念,搭配虚拟内存、进程表之类的东西,用来管理独立的程序运行、切换。 协程,又称微线程,纤程。英文名 coroutine 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了 C, C 执行完毕返回,B执行完毕返回,最后是A执行完毕。 所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。 子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。 协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。 注意,在一个子程序中中断,去执行其他子程序,不是函数调用,有点类似CPU的中断。比如子程序A、B: def A(): print ‘1’ print ‘2’ print ‘3’ def B(): print ‘x’ print ‘y’ print ‘z’ 假设由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果可能是: x y z 但是在A中是没有调用B的,所以协程的调用比函数调用理解起来要难一些。 看起来A、B的执行有点像多线程,但协程的特点在于是一个线程执行,那和多线程比,协程有何优势? 最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。 第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。 因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。 一个goroutine锁使用的最小的栈大小是2KB ~ 8 KB (go stack) Goroutine 有着和 Java 线程完全不同的调度机制,Java 线程模型中线程和 KSE (Kernel space Entity) 是 1:1 的关系,一个用户线程对应一个 KSE。而 Groutine 和 KSE 是多对多的对应关系。虽然,Groutine 的调度机制不如由内核直接调度的线程机制效率那么高,但是由于 Groutine 间的切换可以不涉及内核级切换,所以代价小很多。 ...

2017-03-24 · 2 min · 353 words · -

golang 字符串/string

golang 字符串/string 判断字符串开头 import ( "fmt" "strings" ) func main() { myString := "www.topgoer.com" // Option 1: (Recommended) if strings.HasPrefix(myString, "www") { fmt.Println("Hello to you too") } else { fmt.Println("Goodbye") } } go, string, join func main() { // 将字符串数组 拼接成 字符串 // 参数: 要拼接的数组,拼接的内容 str := strings.Join([]string{`你好`, `世界`}, `,`) // 输出拼接好的字符串 println(str) } package main import "strings" func main(){ a := "hahaha" b := "hehehe" c := strings.Join([]string{a,b},",") println(c) 字符串截取 s := "abcdefg" s = string([]byte(s)[:3]) fmt.Println(s) //得到 "abc" s := "abcdefg" s = string([]byte(s)[3:]) fmt.Println(s) //得到 "efg" s := "12121211122" first3 := s[0:3] last3 := s[len(s)-3:] 字符串比较 fmt.Println("go"=="go") fmt.Println("GO"=="go") fmt.Println(strings.Compare("GO","go")) fmt.Println(strings.Compare("go","go")) fmt.Println(strings.EqualFold("GO","go")) Compare函数,区分大小写,比自建方法"==“的速度要快 ...

2017-02-17 · 1 min · 147 words · -

golang 数组/切片/slice

golang 数组 array, 切片, slice Go 的切片类型为处理同类型数据序列提供一个方便而高效的方式。 切片有些类似于其他语言中的数组, 但是有一些不同寻常的特性。 Go的(Slice)切片是在(Array)数组之上的抽象数据类型, 因此在了解切片之前必须要先理解数组。 数组, array 声明一个数组 // var identifier [len]type var foo [5]int // 声明时没有指定数组的初始化值, 因此所有的元素都会被自动初始化为默认值 0。 //Go 语言中的数组是值类型,因此还可以用 new 来创建 bar := new([5]int) // new 返回类型的指针,因此 foo bar 的区别在于: foo 的类型为 [5]int,bar 的类型为 *[5]int。 var a [4]int a[0] = 1 i := a[0] 数组的长度在声明它的时候就必须给定,并且之后不会再改变。可以说, 数组的长度是其类型的一部分。比如, [1]string 和 [2]string 就是两个不同的数组类型。 数组可以以常规的索引方式访问,表达式 s[n] 访问数组的第 n 个元素。 golang array 特点: golang中的数组是值类型, 也就是说, 如果你将一个数组赋值给另外一个数组, 那么, 实际上就是整个数组拷贝了一份 如果 golang 中的数组作为函数的参数, 那么实际传递的参数是一份数组的拷贝, 而不是数组的指针 array 的长度也是 Type 的一部分, 这样就说明 [10]int 和 [20]int 是不一样的。 foo:= [5] int {1,2,3,4,5} // 长度为5的数组,其元素值依次为: 1,2,3,4,5 [5] int {1,2} // 长度为5的数组,其元素值依次为: 1,2,0,0,0 。在初始化时没有指定初值的元素将会赋值为其元素类型int的默认值0,string的默认值是"" [...] int {1,2,3,4,5} // 长度为5的数组,其长度是根据初始化时指定的元素个数决定的 [5] int { 2:1,3:2,4:3} // 长度为5的数组,key:value,其元素值依次为: 0,0,1,2,3。在初始化时指定了2,3,4索引中对应的值: 1,2,3 [...] int {2:1,4:3} // 长度为5的数组,起元素值依次为: 0,0,1,0,3。由于指定了最大索引4对应的值3,根据初始化的元素个数确定其长度为5 // 遍历 for i, value := range arrayOrSlice { log.Printf("%v: %v", i, value) } 切片/Slices slice 切片类型是一个引用类型, 是一个动态的指向数组切片的指针。 slice 是一个不定长的, 总是指向底层的数组 array 的数据结构。 ...

2016-11-24 · 5 min · 956 words · -

Go map set

go map map 是由 key-value 对组成的;key 只会出现一次。 Map 是一种无序的键值对的集合 map 是 mutable 的, 可以对其进行修改,map 不是线程安全的. 从map中读取一个不存在的key的时候,返回0值 Go 语言的 map 用的是哈希查找表(Hash table),并且使用链表解决哈希冲突。 Go 语言中 map 是一种特殊的数据结构,一种元素对(pair)的无序集合,pair 对应一个 key(索引)和一个 value(值),所以这个结构也称为关联数组或字典,这是一种能够快速寻找值的理想结构,给定 key,就可以迅速找到对应的 value。 map 这种数据结构在其他编程语言中也称为字典(Python)、hash 和 HashTable 等。 维基百科里这样定义 map: In computer science, an associative array, map, symbol table, or dictionary is an abstract data type composed of a collection of (key, value) pairs, such that each possible key appears at most once in the collection. ...

2016-11-23 · 4 min · 661 words · -

goroutine 退出, waitgroup, channel

如何优雅地等待所有的 goroutine 退出 WaitGroup sync包中的 Waitgroup 结构,是Go语言为我们提供的多个goroutine之间同步的好刀。下面是官方文档对它的描述: A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. Then each of the goroutines runs and calls Done when finished. At the same time, Wait can be used to block until all goroutines have finished. 通常情况下,我们像下面这样使用waitgroup: 创建一个Waitgroup的实例,假设此处我们叫它wg 在每个goroutine启动的时候,调用wg.Add(1),这个操作可以在goroutine启动之前调用,也可以在goroutine里面调用。当然,也可以在创建n个goroutine前调用wg.Add(n) 当每个goroutine完成任务后,调用wg.Done() 在等待所有goroutine的地方调用wg.Wait(),它在所有执行了wg.Add(1)的goroutine都调用完wg.Done()前阻塞,当所有goroutine都调用完wg.Done()之后它会返回。 import ( "fmt" "sync" "time" ) func main() { wg := &sync.WaitGroup{} wg.Add(1) go func() { for i := 0; i < 10; i++ { fmt.Println("goroutine-0: ", i) time.Sleep(1 * time.Second) } wg.Done() }() go goroutine1(wg) wg.Wait() } func goroutine1(wg *sync.WaitGroup) { defer func() { wg.Done() }() for i := 0; i < 10; i++ { fmt.Println("goroutine-1: ", i) time.Sleep(1 * time.Second) } } 通过 Channel 传递退出信号 goroutine 和 channel 是 Go 语言非常棒的特色,它们提供了一种非常轻便易用的并发能力。但是当您的应用进程中有很多goroutine的时候,如何在主流程中等待所有的 goroutine 退出呢? ...

2016-10-12 · 1 min · 199 words · -

Go 指针, pointer

Go 指针, pointer 普通指针 uintptr unsafe.Pointer 对于Go语言, 严格意义上来讲, 只有一种传递, 也就是按值传递 (by value)。当一个变量当作参数传递的时候, 会创建一个变量的副本, 然后传递给函数或者方法, 你可以看到这个副本的地址和变量的地址是不一样的。 当变量当做指针被传递的时候, 一个新的指针被创建, 它指向变量指向的同样的内存地址, 所以你可以将这个指针看成原始变量指针的副本。当这样理解的时候, 我们就可以理解成Go总是创建一个副本按值转递, 只不过这个副本有时候是变量的副本, 有时候是变量指针的副本。 Go 语言保留着C中值和指针的区别, 但是对于指针繁琐用法进行了大量的简化,引入引用的概念。所以在Go语言中,你几乎不用担心会因为直接操作内寸而引起各式各样的错误。Go语言的指针, 基本上只剩下用于区分 by ref 和 by val 语义。 指针地址和指针类型 一个指针变量可以指向任何一个值的内存地址,它所指向的值的内存地址在 32 和 64 位机器上分别占用 4 或 8 个字节,占用字节的大小与所指向的值的大小无关。当一个指针被定义后没有分配到任何变量时,它的默认值为 nil。指针变量通常缩写为 ptr。 每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用在变量名前面添加&操作符 (前缀)来获取变量的内存地址 (取地址操作),格式如下: ptr := &v // v 的类型为 T 其中 v 代表被取地址的变量,变量 v 的地址使用变量 ptr 进行接收,ptr 的类型为 *T,称做 T 的指针类型,* 代表指针。 // &: 取地址 // *: 解析地址 var p *int // p 的类型是: int 型的指针 // Foo, Bar 的类型是指针 type TestItem struct { Foo *string //指针类型 Bar *string } package main import "fmt" func main() { var cat int = 1 var str string = "banana" // 0xc00001a0b0 0xc000010230 fmt.Printf("%p %p", &cat, &str) } package basic import ( "fmt" ) func PointerTest() { var i int // i 的类型是 int 型 i = 1 // i 的值为 1; var p *int // p 的类型是 int型的指针 p = &i // p 的值为 i 的地址 fmt.Printf("i=%d;p=%d;*p=%d\n", i, p, *p) *p = 2 // *p 的值为 [[i的地址]的指针] (其实就是i), 这行代码也就等价于 i = 2 fmt.Printf("i=%d;p=%d;*p=%d\n", i, p, *p) i = 3 // 验证想法 fmt.Printf("i=%d;p=%d;*p=%d\n", i, p, *p) } 这段代码执行结果: ...

2016-10-12 · 7 min · 1431 words · -

Go unit test, 单体测试

Go unit test, 单体测试 执行某一个测试文档 go test foo_test.go go test -v foo_test.go // 强制 go test 运行一次,不使用缓存, 在配置了 -count 参数的情况下,go test 会忽略缓存 // -count, 执行测试的次数 go test -count=1 foo_test.go # 执行某一个文件中的某一个或几个函数 go test path/to/foo_test.go -run "^TestFunc0$" go test 会默认缓存测试运行的结果(Test Cache),缓存判断的依据是: 测试本身的代码(如 *_test.go 文件) 测试依赖的源文件是否发生变更 测试的相关环境参数是否发生变化(环境变量、命令参数等) Go 语言推荐测试文件和源代码文件放在一块,测试文件以 _test.go 结尾。比如,当前 package 有 calc.go 一个文件,我们想测试 calc.go 中的 Add 和 Mul 函数,那么应该新建 calc_test.go 作为测试文件。 calc_test.go package main import "testing" func TestAdd(t *testing.T) { t.Log("test foo") if ans := Add(1, 2); ans != 3 { t.Errorf("1 + 2 expected be 3, but %d got", ans) } if ans := Add(-10, -20); ans != -30 { t.Errorf("-10 + -20 expected be -30, but %d got", ans) } } 测试用例名称一般命名为 Test 加上待测试的方法名。 测试用的参数有且只有一个, 在这里是 t *testing.T ...

2016-07-13 · 4 min · 644 words · -

golang 异常处理, err, error, panic, recover

golang 异常处理, err, error, panic, recover 基础知识 错误指的是可能出现问题的地方出现了问题, 比如打开一个文件时失败, 这种情况在人们的意料之中;而异常指的是不应该出现问题的地方出现了问题, 比如引用了空指针, 这种情况在人们的意料之外。可见, 错误是业务过程的一部分, 而异常不是。 自定义异常 return errors.New("string") 打印调用栈 using runtime defer func() { if p := recover(); p != nil { logger.Errorf("panic: %v", p) //打印调用栈信息 buf := make([]byte, 2048) n := runtime.Stack(buf, false) stackInfo := fmt.Sprintf("%s", buf[:n]) logger.Errorf("panic stack info %s", stackInfo) } }() using debug if p := recover(); p != nil { logs.Error("message push panic: %v", p) //打印调用栈信息 debug.PrintStack() } Go语言追求简洁优雅, 所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。在Go语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,也就是说,遇到真正的异常的情况下 (比如除数为0了) 。才使用Go中引入的Exception处理: defer, panic, recover。 ...

2016-07-01 · 2 min · 251 words · -

golang 程序 too many open files

golang 程序 too many open files Go 程序 报错 too many open files 程序 报错之后 查看 进程 打开的 文件 lsof -p 14092 |wc -l, 1032 排除掉 内存映射文件(mem), 列标题, cwd, rtd, txt, 刚好 1024, lsof -p 14092 看到打开的 fd 最大值 1023u shell 下 查看系统的文件数设置 ulimit -a ulimit -Hn ulimit -Sn 65535 systemd limit https://unix.stackexchange.com/questions/345595/how-to-set-ulimits-on-service-with-systemd Go rlimit https://stackoverflow.com/questions/17817204/how-to-set-ulimit-n-from-a-golang-program wangyue.dev/lsof wangyue.dev/ulimit wangyue.dev/systemd/script

2015-09-02 · 1 min · 59 words · -

golang 数据类型

golang 数据类型 常量 const ROOT_PATH = "/" Boolean 布尔值的类型为 bool,true或false,默认false var isActive bool // 全局变量声明 var enabled, disabled = true, false // 忽略类型的声明 func test() { var available bool // 一般声明 valid := false // 简短声明 available = true // 赋值操作 } 数值类型 整数类型 整数类型有无符号和带符号两种。 Go 同时支持 int 和 uint, 这两种类型的长度相同, 但具体长度取决于不同编译器的实现。 Go 里面也有直接定义好位数的类型: rune, int8, int16, int32, int64 和 byte, uint8, uint16, uint32, uint64。 整数 int8 (-128 -> 127) int16 (-32768 -> 32767) int32 (-2,147,483,648 -> 2,147,483,647) int64 (-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807) 无符号整数 uint8 (0 -> 255) uint16 (0 -> 65,535) uint32 (0 -> 4,294,967,295) uint64 (0 -> 18,446,744,073,709,551,615) 其中rune是int32的别称 byte是uint8的别称 浮点数 浮点数的类型有float32和float64两种 (没有float类型) ,默认是float64。 (IEEE-754 标准) ...

2015-04-27 · 3 min · 584 words · -

golang bufio 处理 TCP 粘包

golang bufio 处理 TCP 粘包 http://feixiao.github.io/2016/05/08/bufio/ 我们经常需要自定义协议,然后将自己定义的协议打包成二进制数据发送到对端,然后对端根据协议解包,TCP是流式传输所以我们需要自己从数据中找到数据的分隔点, 解析我们的数据包。 经常看到自定义的协议设计类似这样: 第一和第二个字节表示版本号,如V1, 第三、四字节表示数据的大小(不包括前面的四个字节),后面的就是这个数据包的大小。 // 类似这种结构 type Package struct { Version [2]int8 Datalen int16 Data []byte } Golang里面处理这个包的方式之一如下: 1: 一直阻塞读取第一个第二个字节,获取版本号(如果错误就做错误处理); 2: 然后读取第三、四个字节,获取数据的大小; 3: 然后根据第二步中的数据大小,后面下面的数据; 4: 重复上面的过程; NSQ 就是采取这种方式。 还有一种方式是我下面介绍的,我遇到的问题是这样: 我解析RTP Over RTSP数据,一个数据流里面有两种协议数据,所以我刚开始想到的方式就是,先从conn里面读取数据然后缓存,然后不断peek数据拿来分析(我不能拿走数据,因为数据可能不完整,所以一直做peek),自己管理buffer,其实这种方式很傻,golang的标准库其实已经给我们提供了实现。 使用Scanner就可以完成我们的需求, 实现如下: scanner, 分离函数, 分割函数, split func main() { // 创建一个包,版本是V1,数据是ABCDEFGHIJK,大小是11 var pkg Package pkg.Version[0] = 'V' pkg.Version[1] = 1 pkg.Data = []byte("ABCDEFGHIJK") pkg.Datalen = int16(len(pkg.Data)) fmt.Println(&pkg) // 打包成二进制数据 var buf bytes.Buffer pkg.Pack(&buf) // 从二进制数据里面获取数据 var pkg1 Package pkg1.Unpack(&buf) fmt.Println(&pkg1) // 模拟数据流,打包三个数据包 pkg.Pack(&buf) pkg.Pack(&buf) pkg.Pack(&buf) // 创建Scanner,分析buf数据流(r io.Reader,换成net.Conn对象就是处理tcp数据流,自己连数据都不需要去收取) scanner := bufio.NewScanner(&buf) // 数据的分离规则,根据协议自定义 split := func(data []byte, atEOF bool) (advance int, token []byte, err error) { if !atEOF && data[0] == 'V'{ if len(data) > 4 { var dataLen int16 binary.Read(bytes.NewReader(data[2:4]),binary.BigEndian,&dataLen) if int(dataLen) + 4 <= len(data) { return int(dataLen) + 4, data[:int(dataLen)+4],nil } } } return } // 设置分离函数 scanner.Split(split) // 获取分离出来的数据 for scanner.Scan() { fmt.Println(scanner.Bytes()) } if err := scanner.Err(); err != nil { fmt.Printf("Invalid input: %s", err) } } // 自定义协议的组包和拆包 type Package struct { Version [2]int8 Datalen int16 Data []byte } func (p *Package) String() string { return fmt.Sprintf("Version:%d DataLen:%d Data:%s", p.Version,p.Datalen,p.Data) } func (p *Package)Pack(w io.Writer) { binary.Write(w, binary.BigEndian,p.Version) binary.Write(w, binary.BigEndian,p.Datalen) binary.Write(w,binary.BigEndian,p.Data) } func (p *Package)Unpack(r io.Reader) { binary.Read(r,binary.BigEndian,&p.Version) binary.Read(r,binary.BigEndian,&p.Datalen) if p.Datalen > 0 { p.Data = make([]byte,p.Datalen) } binary.Read(r,binary.BigEndian,&p.Data) }

2015-04-27 · 2 min · 215 words · -

golang defer

golang defer 不要在for循环直接用defer https://studygolang.com/articles/12061 panic Golang里比较常见的错误处理方法是返回error给调用者,但如果是无法恢复的错误,返回error也没有意义,此时可以选择go die: 主动触发panic。 除了代码中主动触发的panic,程序运行过程中也会因为出现某些错误而触发panic,例如数组越界。 panic 会停掉当前正在执行的程序 (注意,不只是协程) ,但是与os.Exit(-1)这种直愣愣的退出不同,panic的撤退比较有秩序,他会先处理完当前 goroutine 已经 defer 挂上去的任务,执行完毕后再退出整个程序。 而defer的存在,让我们有更多的选择,比如在defer中通过recover截取panic,从而达到try..catch的效果。 panic允许传递一个参数给他,参数通常是将出错的信息以字符串的形式来表示。panic会打印这个字符串,以及触发panic的调用栈。 defer func() { if e := recover(); e != nil { var buf [4096]byte n := runtime.Stack(buf[:], false) fmt.Printf("==> %s\n", string(buf[:n])) os.Exit(1) } }() defer defer 和 go一样都是Go语言提供的关键字。defer用于资源的释放,会在函数返回之前进行调用。一般采用如下模式: f,err := os.Open(filename) if err != nil { panic(err) } defer f.Close() 如果有多个defer表达式,调用顺序类似于栈,越后面的defer表达式越先被调用。 不过如果对defer的了解不够深入,使用起来可能会踩到一些坑,尤其是跟带命名的返回参数一起使用时。在讲解defer的实现之前先看一看使用defer容易遇到的问题。 defer使用时的坑 先来看看几个例子。例1: ...

2015-04-08 · 2 min · 226 words · -

Go bufio

golang bufio bufio 对 io 进行了包装, 提供了缓冲. bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。 简单的说就是bufio会把文件内容读取到缓存中 (内存),然后再取读取需要的内容的时候,直接在缓存中读取,避免文件的i/o操作。同样,通过bufio写入内容,也是先写入到缓存中 (内存),然后由缓存写入到文件。避免多次小内容的写入操作I/O。 bufio.Read(p []byte) 的思路如下: 1、当缓存区有内容的时,将缓存区内容全部填入p并清空缓存区 2、当缓存区没有内容的时候且len(p)>len(buf),即要读取的内容比缓存区还要大,直接去文件读取即可 3、当缓存区没有内容的时候且len(p)<len(buf),即要读取的内容比缓存区小,缓存区从文件读取内容充满缓存区,并将p填满 (此时缓存区有剩余内容) 4、以后再次读取时缓存区有内容,将缓存区内容全部填入p并清空缓存区 (此时和情况1一样) https://www.cnblogs.com/ricklz/p/13188188.html http://www.cnblogs.com/golove/p/3282667.html // bufio 包实现了带缓存的 I/O 操作 type Reader struct { … } // NewReaderSize 将 rd 封装成一个带缓存的 bufio.Reader 对象, // 缓存大小由 size 指定 (如果小于 16 则会被设置为 16) 。 // 如果 rd 的基类型就是有足够缓存的 bufio.Reader 类型,则直接将 // rd 转换为基类型返回。 func NewReaderSize(rd io.Reader, size int) *Reader // NewReader 相当于 NewReaderSize(rd, 4096) func NewReader(rd io.Reader) *Reader ...

2015-01-16 · 7 min · 1357 words · -

golang http 文件下载

golang http 文件下载 golang + gin c.Writer.WriteHeader(http.StatusOK) c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s.bin", "foo")) c.Header("Content-Type", "application/octet-stream") c.Header("Content-Transfer-Encoding", "binary") c.Header("Accept-Ranges", "bytes") c.Header("Connection", "keep-alive") c.Header("Content-Length", fmt.Sprintf("%d", len(paramsBytes))) c.Writer.Write(paramsBytes) vuetify import fileDownload from 'js-file-download' exportCsv (): void { Axios.get('/export', { headers: { Authorization: 'Bearer ' + localStorage.getItem('JWT_TOKEN') }, params: { foo: this.foo, start: this.dateStart + ' ' + this.timeStart, end: this.dateEnd + ' ' + this.timeEnd }, responseType: 'blob' }).then(response => { const fileName = response.headers['content-disposition'].match(/filename=(.*)/)[1] console.log('data: ' + response.data) fileDownload(response.data, fileName) }).catch(error => { console.log({ error }) }) } http://blog.csdn.net/androidmylove/article/details/8881573 // Ajax 文件下载 jQuery.download = function(url, data, method){ // 获得url和data if( url && data ){ // data 是 string 或者 array/object data = typeof data == 'string' ? data : jQuery.param(data); // 把参数组装成 form 的 input var inputs = ''; jQuery.each(data.split('&'), function(){ var pair = this.split('='); inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />'; }); // request发送请求 jQuery('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>') .appendTo('body').submit().remove(); }; }; 文章结束给大家分享下程序员的一些笑话语录: 一个合格的程序员是不会写出 诸如 “摧毁地球” 这样的程序的,他们会写一个函数叫 “摧毁行星"而把地球当一个参数传进去。

2014-02-26 · 1 min · 159 words · -

gorm

gorm db.Table("go_service_info").Select("go_service_info.serviceId as service_id, go_service_info.serviceName as service_name, go_system_info.systemId as system_id, go_system_info.systemName as system_name").Joins("left join go_system_info on go_service_info.systemId = go_system_info.systemId").Scan(&results) db.Table("table0"). Select("table0.field0, table0.field1"). Joins("join table1 on table0.field0=table1.field0"). Where("table0.field0=? and table1.field1=?", foo, bar). First(&obj) https://gorm.io/zh_CN/docs/index.html https://cloud.tencent.com/developer/article/1674450

2013-06-20 · 1 min · 34 words · -

go benchmark

go benchmark 稳定的测试环境 当我们尝试去优化代码的性能时,首先得知道当前的性能怎么样。Go 语言标准库内置的 testing 测试框架提供了基准测试(benchmark)的能力,能让我们很容易地对某一段代码进行性能测试。 性能测试受环境的影响很大,为了保证测试的可重复性,在进行性能测试时,尽可能地保持测试环境的稳定。 机器处于闲置状态,测试时不要执行其他任务,也不要和其他人共享硬件资源。 机器是否关闭了节能模式,一般笔记本会默认打开这个模式,测试时关闭。 避免使用虚拟机和云主机进行测试,一般情况下,为了尽可能地提高资源的利用率,虚拟机和云主机 CPU 和内存一般会超分配,超分机器的性能表现会非常地不稳定。 超分配是针对硬件资源来说的,商业上对应的就是云主机的超卖。虚拟化技术带来的最大直接收益是服务器整合,通过 CPU、内存、存储、网络的超分配(Overcommitment)技术,最大化服务器的使用率。例如,虚拟化的技能之一就是随心所欲的操控 CPU,例如一台 32U(物理核心)的服务器可能会创建出 128 个 1U(虚拟核心)的虚拟机,当物理服务器资源闲置时,CPU 超分配一般不会对虚拟机上的业务产生明显影响,但如果大部分虚拟机都处于繁忙状态时,那么各个虚拟机为了获得物理服务器的资源就要相互竞争,相互等待。Linux 上专门有一个指标,Steal Time(st),用来衡量被虚拟机监视器(Hypervisor)偷去给其它虚拟机使用的 CPU 时间所占的比例。 # 运行当前 package 内的用例, 只运行 test, 不运行 benchmark # go test 命令默认不运行 benchmark 用例 go test # 只运行 benchmark go test -bench=. -run=none go test -bench . -run none # 运行 test 和 benchmark go test -bench . # -bench 参数支持传入一个正则表达式,匹配到的用例才会得到执行,例如,只运行以 Fib 结尾的 benchmark 用例 go test -bench=Fib$ . go test -bench='Fib$' . https://geektutu.com/post/hpg-benchmark.html ...

2012-05-23 · 2 min · 242 words · -