Golang Snippets
Golang
install
Getting Started - The Go Programming Language
首先下载得到 go1.11.2.linux-a md64.tar.gz
tar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gz
之后在 /usr/local 有个 go1.11.2 的目录
然后加入 path
export PATH=$PATH:/usr/local/goxxx/bin
export GOROOT=/usr/local/goxxx
Go都是值传递 不用指针函数中得到的就是一个copy
func(f F) Foo {
f.xx ... //调用aaa.Foo函数时会产生aaa的一个copy
}
func(f *F) Foo {}// 这样就不会
Array 是值传递 但是map slice channel 表现的很像是引用
First, and most important, does the method need to modify the receiver? If it does, the receiver must be a pointer. (Slices and maps act as references)
https://golang.org/doc/faq#references
Error
实现了Error方法的struct 都是error的实现 很方便的用于定制error类型
type error interface {
Error() string
}
JSON
map to string
fmt.Println("fill map::", rsFillmap)
if jsonByte, err := json.Marshal(rsElemap); err != nil {
fmt.Println(err)
} else {
fmt.Println("..............", string(jsonByte))
}
string to map
jsonStr := `
{
"name":"liangyongxing",
"age":12
}
`
var mapResult map[string]interface{}
if err := json.Unmarshal([]byte(jsonStr), &mapResult); err != nil {
t.Fatal(err)
}
Slice
仅声明 需要用make 但是初始化不用 所以一般用最后一种
arr := make([]int, 5)
// 实际上这里是一个slice
args := []string{"what", "ever", "you", "like"}
b := []string{} //这样也可以创建一个slice 没有必要make
关于interface
mmm := map[string]interface{}{}
data := `{"key": [{"haha": {"xixi": 10}}]}`
json.Unmarshal([]byte(data), &mmm)
if rs, ok := mmm["key"].([]map[string]interface{}); ok {
fmt.Println("ok", rs)
}else{
fmt.Println("not ok")
}
//not ok
if rs, ok := mmm["key"].([]interface{}); ok {
fmt.Println("ok", rs)
if rs2, ok2 := rs[0].(map[string]interface{}); ok2 {
fmt.Println("ok2", rs2)
}
}else{
fmt.Println("not ok")
}
//ok2 map[haha:map[xixi:10]]
//ok [map[haha:map[xixi:10]]]
第一个if 结果是not ok 原因是go只知道key下面是一个数组 起元素都是interface{} 类型 不能知道这里的元素是 map[string]interface{}
所以这么断言是not ok
map init
仅声明不赋值 需要用make
初始化的同时赋值 可以用后一种
m := make(map[string]int)
m["haha"] = 1
mm := map[string]int{
"Bell Labs": 1,
"MMM":2,
}
fmt.Printf("%+v %+v", m, mm)
Map range / Map iterate
for k, v := range m {
fmt.Printf("k=%v, v=%v\n", k, v)
}
删除某个key
var sessions = map[string] chan int{};
delete(sessions, "moo");
map to struct
go - Converting map to struct - Stack Overflow
import "github.com/mitchellh/mapstructure"
mapstructure.Decode(myData, &result)
map key exists
if _, ok := map[key]; ok {
//存在
}
map & mutex
Map不是线程安全的 当有多个资源同时写入&读取就会有问题,
此时往往要用到锁 mutex
这意味着线程将有序的对同一变量进行访问
Exception Handing
func Log(t interface{}) {
defer func() {
if p := recover(); p != nil {
fmt.Printf("panic recover !!! p: %v", p)
debug.PrintStack()
}
}()
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n", i,
typeOfT.Field(i).Name, f.Type(), f.Interface())
}
}
String to Byte (byte to string)
[]byte(str)
string(bytes)
String To Int/Float
#string to int
int,err:=strconv.Atoi(string)
#string to int64
int64, err := strconv.ParseInt(string, 10, 64)
#int to string
string:=strconv.Itoa(int)
#int64 to string
string:=strconv.FormatInt(int64,10)
还可以使用 Sprint 更通用 对于 float 也可以处理
fmt.Sprint(5.03)
#string到 float
iiii, _ := strconv.ParseFloat("32.23", 64)
float to int
int(float)
int to float
float(int) float64(xxx)
[]int to string
strings.Join 只能接收 []string
strings.Trim(strings.Replace(fmt.Sprint(a), " ", delim, -1), "[]")
:=
:= 左边有新变量即可
#string到int
int,err:=strconv.Atoi(string)
#string到int64
int64, err := strconv.ParseInt(string, 10, 64)
#int到string
string:=strconv.Itoa(int)
#int64到string
string:=strconv.FormatInt(int64,10)
swap
nums[I], nums[j] = nums[j], nums[I]
defer
PS 即使return defer 也会执行
func f() (result int) {
defer func() {
result++
}()
return 0
}
上面函数返回1,因为defer中添加了一个函数,在函数返回前改变了命名返回值的值。是不是很好用呢。但是,要注意的是,如果我们的defer语句没有执行,那么defer的函数就不会添加,如果把上面的程序改成这样:
func f() (result int) {
return 0
defer func() {
result++
}()
return 0
}
上面的函数就返回0了,
if else 作用域
if 中声明的变量 在同一层else中也有效
if bytes, err := json.Marshal(bizResp); err != nil {
errMsg := fmt.Sprintf("fail to encode biz-resp : %v", bizResp)
log.Error(errMsg)
return nil, errors.New(errMsg)
} else {
refType := reflect.TypeOf(bizResp)
w := &clueThrift.ThriftProtocolResponseWrapper{
BaseResp: &base.BaseResp{},
JsonData: string(bytes),
OriginTypeName: refType.PkgPath() + "/" + refType.Name(),
}
return w, nil
}
[]interface 数组赋值传参
Frequently Asked Questions (FAQ) - The Go Programming Language
当我们把一个string的slice 试图赋值给 一个interface的slice的时候
cannot use arr (type []string) as type []interface {}
sss := []string{"xxx"}
AA(sss)
func A(pp []interface{}){
fmt.Println(pp)
}
可以这么做 (在作为参数传递时尤其如此) (PS 这是比较trick的做法)
dest := []interface{}{}
queries :=[]interface{}{}
queries = append(queries, instanaceIds)
dest = queries
通过反射 调用interface{} 函数
如果一个函数装进了interface{} 中,如何通过反射调用他呢?
如何裸写一个goroutine pool | Legendtkl
type worker struct {
Func interface{}
Args []reflect.Value
}
wk := worker{
Func: func(x, y int) {
fmt.Println(x + y)
},
Args: []reflect.Value{reflect.ValueOf(i), reflect.ValueOf(i)},
}
reflect.ValueOf(ch.Func).Call(ch.Args)
断言
v := varI.(T) //T是你要将接口转换的类型 // unchecked type assertion
varI 必须是一个接口变量,否则编译器会报错:
再看一个断言的例子
package main
import "fmt"
import "reflect"
type Xixi struct{
A string
}
func main() {
test(Xixi{A:"aaa"})
testSlice([]Xixi{Xixi{A:"aaa"}, Xixi{A:"abb"}, Xixi{A:"acc"}})
}
func test(any interface{}){
v := reflect.ValueOf(any)
fmt.Printf("%+v %+v\n", v, any) //{A:aaa} {A:aaa} //但是此时并不能 v.A //因为go并不知道这究竟是哪种类型的数据
if realV, ok := any.(Xixi); ok {
fmt.Printf("%+v %+v ...\n", realV, realV.A)
}
}
func testSlice(any interface{}){
v := reflect.ValueOf(any) //可以将any识别出是 [] //此时就可以循环了
for i := 0; i < v.Len(); i++ {
fmt.Printf("%+v\n", v.Index(i))
}
//当然可以用断言一步到位
if realV, ok := any.([]Xixi); ok{
fmt.Println(len(realV), realV[0]) //3 {aaa}
}else{
fmt.Println("err")
}
}
得到变量类型
reflect.TypeOf(x)
b := "123"
fmt.Println(reflect.TypeOf(b), reflect.ValueOf(b).Kind())
string string
var x float64 = 3.4
fmt.Println("value:", reflect.ValueOf(x).String())
将interface{} 还原成原来实参(reflect)
Range a interface that holds a slice (or map)
当一个interface{} 里实际存着是一个slice的时候 如何range这个interface{} 呢
这里不是最好
的办法 最好的方式是用断言
reflect.TypeOf(t) 返回Person (也就是struct的名字 )
reflect.TypeOf(t).Kind() 返回 struct 也就是更基本的类型
package main
import "fmt"
import "reflect"
func main() {
data := []string{"one","two","three"}
test(data)
moredata := []int{1,2,3}
test(moredata)
ddd := make(map[int]string)
ddd[1]= "xixi"
test(ddd)
}
func test(t interface{}) {
switch reflect.TypeOf(t).Kind() {
case reflect.Slice:
s := reflect.ValueOf(t)
for i := 0; i < s.Len(); i++ {
fmt.Println(s.Index(i))
}
case reflect.Map:
v := reflect.ValueOf(t)
for _, key := range v.MapKeys() {
strct := v.MapIndex(key)
fmt.Println(key.Interface(), strct.Interface())
}
}
}
<< && >>
n << x 表示 n * (2^x).
y >> z 表示 y / (2^z).
枚举类型实现
type ModelField int8
const (
NULL ModelField = iota
AdvId
InstanceId
Name
ComponentType
CreateTime
Status
IsDel
LotteryPool
// ......
)
fmt.Print("id::", InstanceId, AdvId) //得到 2 和 1
当然了 ModelField = iota 也可以替换成 int = itoa
这样写成 type 有个好处就是可以为这个类型增加一个方法
作用域
如果一个type 定义在func 中 那么只有这个func 才能使用这个type 外面并不能访问
打印指针
a1 := AA{}
a2 := AA{}
a1.A = A{Ha:"xx"}
fmt.Printf("... %p ... %p", &a1, &a2)
修改map中的值
map里是struct等复杂的对象 是不可以被直接修改的
比如
map[key] = A{}
然后又想 map[key].xx = xx
这样不行哦
很多时候要把map中的元素传到另一个func 中去修改 那么就要传指针 然而
https://github.com/golang/go/issues/11865
spec: can take the address of map[x]
也就是说不能够 &map[key]
那怎么办? 干脆在创建map的时候就用value的指针 而不是 value
s := make(map[string]*Student)
s["chenchao"] = &Student{
Name:"chenchao",
Id:111,
}
s["chenchao"].Id = 222
为基础类型增加方法(自定义 Int)
type Int int
func (i Int) Add(j Int) Int {
return i + j
}
func main() {
i := Int(5)
j := Int(6)
fmt.Println(i.Add(j))
fmt.Println(i.Add(j) + 12)
}
不定长参数
函数接收一个不定长参数
和ES6有些不同的是
golang中
…XXX 是将多个item 合并到一个[]
XXX… 是打散[]
但是ES6中 形参 …xxx 是将多个item合并到xxx 比如function a(…xxx){}
如果 …xxx 这样的写法作为实参 就是打散 a(…xxx)
type Xixi struct{
A string
}
func main() {
f := func() interface{} {
return 1
}
test(1, "wowo", Xixi{A: "aia"}, f)
}
func test(haha ...interface{}) {
//haha 是一个[] 数组OR slice
fmt.Println(reflect.TypeOf(haha)) //[]interface {}
fmt.Printf("%+v \n", haha) //[1 wowo 0x108f470]
fmt.Printf("%+v\n", reflect.ValueOf(haha[2])) //{A:aia}
test2(haha) //得到 1 注意这样传下去 是把整个[]传到下一个函数了
test2(haha...) //把[]打散作为多个实参 得到4 //注意这里和ES6语法上的不同
}
func test2(xixi ...interface{}){
fmt.Printf("%+v\n", len(xixi))
}
注意和ES6的区别
var s = (...haha) => {
console.log(haha)
arr = []
arr = arr.concat(...haha)
console.log(arr)
s2(...haha)
}
var s2 = (...xixi) => {
console.log(xixi)
}
s(1,2,3)
//=======================================
var xxx = [1,2,3,4]
function vvv(a,b,c,d){
console.log(a,b,c,d)
}
vvv(...xxx) //打散
组合
组合有一点点像继承,可以直接访问到父类的成员变量
type Proto struct {
BaseField string
}
type Zero struct {
Proto
ZeroFiled string
}
func main() {
zero := Zero{
Proto: Proto{
BaseField: "1",
},
ZeroFiled: "sd",
}
fmt.Printf("%+v %+v", zero.BaseField, zero.Proto.BaseField)
}
interface 多态
golang实际上是通过组合实现的继承
Golang中的面向对象继承
struct 可以嵌套 struct
struct 可以嵌套 interface{}
interface 也可以嵌套 interface{}
interface 继承 method-has-a-pointer-receiver 问题
type Pet interface {
SetName() string
}
type Dog struct {
Name string
}
func (g *Dog) SetName() string {
g.Name = "..."
return "..."
}
func Test_inter(t *testing.T) {
var p Pet
g := Dog{}
p = g //这里会报错 method has a pointer receiver
p = &g //这是最简单的一种解决方案
p.SetName()
fmt.Printf(g.Name)
}
参考 go - X does not implement Y (… method has a pointer receiver) - Stack Overflow
第一个回答还提到了另一个解决方案. 用另外一个结构体包装一下
用结构体包装一下 实际上还是借用指针
import "fmt"
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
type MyType2 struct {
*MyType
}
func main() {
var s Stringer
m := MyType{value: "something"}
// s = m // has a pointer receiver error
m2 := MyType2{MyType: &m}
s = m2
fmt.Printf(s.String())
}
go testing!!
首先待测文件命名要是 xxx_test.go
需要进入到待测文件所在目录
运行某个待测文件
go test -v xxx_test.go
-v 表示显示log
运行某一个方法
go test -count=1 -v -run Test_service_ListSites$ (Test_service_ListSites 是待测的方法的regexp表达式)
package listing
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_service_ListSites(t *testing.T) {
assert := assert.New(t)
assert.True(true)
}
for range
第一个参数是idx
for _, p := range lotterInstanceData.Prize{
p.AdvId = advId
}
error and panic
在go里面 错误和异常是不同的
错误是自己手动创建出来的一个类型 异常就像其他语言需要try起来的部分
func funcA() error {
defer func() {
if p := recover(); p != nil {
fmt.Printf("panic recover! p: %v\n", p)
}
}()
return funcB()
}
func funcB() error {
// simulation
// panic("foo")
return errors.New("!funb erroR!")
}
Err to string
err.Error()
func test() {
err := funcA()
if err == nil {
fmt.Printf("test err is nil\\n")
} else {
fmt.Printf("test err is %v\\n", err)
}
}
func main() {
test()
}
json to map
json to map 之后 数值类型都是float64
mmm := map[string]interface{}{}
data := `{"key": 10}`
json.Unmarshal([]byte(data), &mmm)
fmt.Printf("\n %+v \n", reflect.TypeOf(mmm["key"]))
//float64
struct简写 property
type server struct {
*app.App
}
相当于
type server struct {
App *app.App
}
在同一个目录里有多个main函数
文件开头加上 // +build OMIT
dlv build
go build -o ./cmd/insight/dlv -gcflags "all=-N -l" ./cmd/insight/main.go
然后
dlv --listen=:2345 --headless=true --api-version=2 exec ./dlv
此时会等待 goland 的debug连接
点开虫子图标,就启动辣
**#!/usr/bin/env bash**
CURDIR=**$***(pwd)*
*echo*$CURDIR
*rm*./cmd/meteor-api/dlv
*go*build -o ./cmd/meteor-api/dlv -gcflags "all=-N -l" ./cmd/meteor-api/main.go
*cd***$**{CURDIR}/cmd/meteor-api/
*dlv*--listen=:2345 --headless=true --api-version=2 exec ./dlv
time format 当前时间
fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
// Y. M .D
thisMonth.AddDate(0, 0, -1).Format(DATE_FORMAT)
theTime.Unix() //转化为时间戳 类型是int64
// 生成时间对象
startTime, err = time.Parse("2006-01-02T15:04:05.000Z", "2019-01-18T16:00:00.000Z")
//时间戳 to 时间
tm := time.Unix(1531293019, 0)
fmt.Println(tm.Format("2006-01-02 15:04:05")) //2018-07-11 15:10:19
go mod 1.11
Modules · golang/go Wiki · GitHub
默认 GO111MODULE 的值是 auto
如果你的项目在 go path 下 但是仍然希望使用新的包管理
需要设置
set -x GO111MODULE on
需要go mod init YOUR_MOD_NAME 新建一个go.mod
go build go get go run 等 go 命令会更新 go.mod 文件 (也是在GO111MODULE on 的情况下)
GO111MODULE on 的情况下才能使用 go mod vendor
dep
如果是 go1.11之前的 推荐使用 dep 包管理
dep ensure -add xxx@master
rand
(100) //产生0-100的随机整数
匿名字段
type Human struct {
name string
age int
weight int
}
type Student struct {
Human // 匿名字段,那么默认Student就包含了Human的所有字段
speciality string
}
匿名结构体
json.Marshal(struct{
Name string
age int
}{"name",18})
func (t AAA) GetType() struct {
IsSDK bool
} {
return struct{
IsSDK bool
}{
IsSDK: false,
}
}
litter 方便好用的 print
import "github.com/sanity-io/litter"
var (
Dump = litter.Dump
Sdump = litter.Sdump
)
函数是一等公民
type A struct {
Count func(c int) int
}
// 这样是表示在 A 类型中有一个 property Count 它是一个函数
// 然后这样赋值
c := A{
Count: func(c int) int { return 12 },
}
嵌套结构体初始化
type Account struct {
Id uint32
Name string
Nested struct {
Age uint8
}
}
//方法1 不推荐 太麻烦 而且容易出错
account := &Account{
Id: 10,
Name: "jim",
Nested: struct {
Age uint8
}{
Age: 20,
},
}
//方法2 推荐
acc ;= new Account()
acc.Nested.Age = 20
OR
acc := Account{}
acc.Nested.Age = 29
enum
go 没有枚举关键字 但是可以通过 const + itoa 来实现
itoa + 1 表示从1 开始
type State int
const (
Phone State = iota + 1
Form
MapSearch
)
const (
Haha int = 5
)
switch
type State int
const (
Phone State = iota + 1
Form
MapSearch
)
day := State(1)
switch day {
case Phone:
fmt.Print(day)
default:
fmt.Print(0)
}
str :="5"
switch str {
case "5","3":
fmt.Print("hahah in!")
case "2":
fmt.Print("hahah 222!")
}
获取当前程序所在目录
func getCurrentFilePath() string {
dir, err := os.Getwd()
if err != nil {
logs.Debug("current file path err %+v", err)
}
fmt.Printf("current dir : %+v", dir)
return dir
}
channel
go语言之行—golang核武器goroutine调度原理、channel详解 - W-D - 博客园
Goroutine本质上是协程,可以理解为不受内核调度,而受go调度器管理的线程。
协程与线程主要区别是它将不再被内核调度,而是交给了程序自己而线程是将自己交给内核调度,所以也不难理解golang中调度器的存在
golang协程——通道channel阻塞 - Go语言中文网 - Golang中文社区
并发的存在,就涉及到线程通信。在当下的开发语言中,线程通讯主要有两种,共享内存与消息传递。
golang对并发的处理采用了协程的技术。golang的goroutine就是协程的实现。协程的概念很早就有,简单的理解为轻量级线程。
goroutine就是为了解决并发任务间的通信而设计的
golang解决方案是消息传递机制,消息的传递通过channel来实现
CSP : Communicating Sequential Process 的简称, 是一种并发编程模型,由 Tony Hoare 于 1977 年提出
Example1
Channel 一般是用在协程之间OR和主线程通信用的 一般不会再同一个线程中写入和读取,这么做会有 dead lock 报错
如果一定要这么做,那么这个channel 必须有缓冲区
package main
import (
"fmt"
"time"
)
func writeMessages(messages chan string) {
time.Sleep(1000 * time.Millisecond)
messages <- "ping"
}
func main() {
messages := make(chan string)
//如果整个程序中没有给这个 channel 写入值 会有 dead lock 报错
//因为从逻辑上说会永远卡在<-messages
go writeMessages(messages)
rs := <-messages
fmt.Printf("rs %+v", rs)
close(messages)
fmt.Println("just end")
}
Example2
写入和读取发生在同一个线程OR协程,必须带有缓冲区的 channel
messages := make(*chan*string, 2)
messages <- "buffered1"
fmt.Println(<-messages)
messages <- "buffered2"
fmt.Println(<-messages)
messages <- "buffered3"
messages <- "buffered4"
fmt.Println(<-messages)
fmt.Println(<-messages)
Example 3 range
package main
import (
"fmt"
"time"
"strconv"
)
func makeCakeAndSend(cs chan string, count int) {
for i := 1; i <= count; i++ {
cakeName := "Strawberry Cake " + strconv.Itoa(i)
time.Sleep(1 * time.Second)
cs <- cakeName //send a strawberry cake
}
}
func receiveCakeAndPack(cs chan string) {
for s := range cs {
fmt.Println("Packing received cake: ", s)
}
}
func main() {
cs := make(chan string)
go makeCakeAndSend(cs, 5)
go receiveCakeAndPack(cs)
//sleep for a while so that the program doesn’t exit immediately
time.Sleep(13 * 1e9)
}
channel with direction
When using channels as function parameters, you can specify if a channel is meant to only send or receive values. This specificity increases the type-safety of the program.
package main
import "fmt"
func ping(pings chan<- string, msg string) {
pings <- msg
}
func pong(pings <-chan string, pongs chan<- string) {
msg := <-pings
pongs <- msg
}
func main() {
pings := make(chan string, 1)
pongs := make(chan string, 1)
ping(pings, "passed message")
pong(pings, pongs)
fmt.Println(<-pongs)
}
Select{}
select关键字用于多个channel的结合
package main
import (
"fmt"
"time"
)
func server1(ch chan string) {
time.Sleep(6 * time.Second)
ch <- "from server1"
}
func server2(ch chan string) {
time.Sleep(3 * time.Second)
ch <- "from server2"
}
func main() {
output1 := make(chan string)
output2 := make(chan string)
go server1(output1)
go server2(output2)
// 这里会阻塞 其中任何一个 channel 可以取出值就可以放行
// 如果同时两个 channel 都满足 会随机选一个 channel
// 当你把 server1时间也设置3 有时结果是2 有时是1
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}
fmt.Println("just end")
}
关于select case 中的 default
messages := make(chan string)
select {
case messages<-"箱":
fmt.Printf("111")
default:
fmt.Printf("xxxx")
}
这里完全没有开启任何一个协程就在向这个 channel 写数据
按理说会 dead lock 但是因为有 default 语句 虽然第一个 messages 的 case 无法执行 但是有 default 所以输出 xxx
empty select
Block forever
package main
import (
"fmt"
"time"
)
func backgroundTask() {
ticker := time.NewTicker(1 * time.Second)
for _ = range ticker.C {
fmt.Println("Tock")
}
}
func main() {
fmt.Println("Go Tickers Tutorial")
go backgroundTask()
// This print statement will be executed before
// the first `tock` prints in the console
fmt.Println("The rest of my application can continue")
// here we use an empty select{} in order to keep
// our main function alive indefinitely as it would
// complete before our backgroundTask has a chance
// to execute if we didn't.
select {}
}
Another example
package main
import (
"fmt"
"time"
)
func server1(ch chan string) {
for {
time.Sleep(1 * time.Second)
ch <- "from server1"
}
}
func getVal(ch chan string) {
for {
fmt.Printf(" %v \n", <-ch)
}
}
func main() {
output1 := make(chan string)
go server1(output1)
go getVal(output1)
select {}
fmt.Println("just end")
}
Ticker Channel Coroutine 协程
Go by Example: Timers and Tickers
package main
import "time"
func main() {
timer := time.NewTimer(time.Second * 2)
<- timer.C //will block until has value
println("Timer expired")
}
Ticker Channel simulates setInterval
package main
import "time"
import "fmt"
var ticker *time.Ticker
func main() {
ticker = time.NewTicker(time.Millisecond * 100)
go func() {
for {
select {
case rs,ok := <-ticker.C:
fmt.Println("Tick at",ok, rs)
}
}
}()
time.Sleep(time.Millisecond * 1500)
ticker.Stop()
fmt.Println("Ticker stopped")
}
timeChan := time.NewTimer(time.Second).C
tickChan := time.NewTicker(time.Millisecond * 400).C
doneChan := make(chan bool)
go func() {
time.Sleep(time.Second * 2)
doneChan <- true
}()
for {
select {
case <- timeChan:
fmt.Println("Timer expired")
case <- tickChan:
fmt.Println("Ticker ticked")
case <- doneChan:
fmt.Println("Done")
return
}
}
go func() 并行
func main() {
go func() {
time.Sleep(1 * time.Second)
fmt.Println("11111")
}()
go func() {
time.Sleep(3 * time.Second)
fmt.Println("3333")
}()
}
// 注意这样并不能得到输出 因为主进程执行完了就退出了 那么两个协程也没了
需要这样
func main() {
go func() {
time.Sleep(1 * time.Second)
fmt.Println("11111")
}()
go func() {
time.Sleep(3 * time.Second)
fmt.Println("3333")
}()
time.Sleep(5 * time.Second)
fmt.Println("55555")
}
func UnblockGet(requestUrl string) chan string {
resultChan := make(chan string)
go func() {
request := httplib.Get(requestUrl)
content, err := request.String()
if err != nil {
content = "" + err.Error()
}
resultChan <- content
} ()
return resultChan
}
waitGroup
Notice, the Add must go ahead of Done
// This example fetches several URLs concurrently,
// using a WaitGroup to block until all the fetches are complete.
func ExampleWaitGroup() {
var wg sync.WaitGroup
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.somestupidname.com/",
}
for _, url := range urls {
// Increment the WaitGroup counter.
wg.Add(1)
// Launch a goroutine to fetch the URL.
go func(url string) {
// Decrement the counter when the goroutine completes.
defer wg.Done()
// Fetch the URL.
http.Get(url)
}(url)
}
// Wait for all HTTP fetches to complete.
wg.Wait()
}
if 和 表达式
u, err := url.Parse("https://siongui.github.io/pali-chanting/zh/archives.html")
if err != nil {
log.Fatal(err)
}
parts := strings.Split(u.Hostname(), ".")
domain := parts[len(parts)-2] + "." + parts[len(parts)-1]
fmt.Println(domain)
用下面的方式变量 u 只能在 if 和 else if 中使用
if u, err := url.Parse(urlstr); err != nil {
logs.Error("url parse error ::: %+v url:::%+v", err, urlstr)
*return*urlstr
} *else*{
parts := strings.Split(u.Hostname(), ".")
domain := parts[len(parts)-2] + "." + parts[len(parts)-1]
*return*domain
}
go regexp
pat := `(((abc.)def.)ghi)`
src := `abc-def-ghi abc+def+ghi`
fmt.Println(regexp.MatchString(pat, src))
// true <nil>
fmt.Println(regexp.QuoteMeta(pat))
go slice join
import strings
stringFiles := strings.Join(fileSlice[:], ",")
//Back to Slice again
import strings
fileSlice := strings.Split(stringFiles, ",")
reverse slice
实际生产环境可以使用linq 这个库
package main
import (
"fmt"
"reflect"
)
func reverse(s []interface{}) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
func reverseAny(s interface{}) {
n := reflect.ValueOf(s).Len()
swap := reflect.Swapper(s)
for i, j := 0, n-1; i < j; i, j = i+1, j-1 {
swap(i, j)
}
}
func main() {
s := []interface{}{1, "2", uint(3), byte(4), float64(5)}
reverse(s)
fmt.Println(s)
reverseAny(s)
fmt.Println(s)
}
还可以使用https://github.com/ahmetb/go-linq 这个库
这个库似乎虽然是linq 但有一些slice的功能
http req resp
方法1 不推荐 resp.Body 只能被读一次
resp, err := http.Post(url, “application/json”, bytes.NewBuffer([]byte(sql)))
json.NewDecoder(resp.Body).Decode(&respData)
方法2 ioUtil
req, err := http.NewRequest("GET", url, nil)
req.Header.Add("X-Orange-Caller", "ad.tetris.site_server")
resp, err := client.Do(req)
body, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
logs.Debug(string(body))
buf
resp, err := http.Post(url, "application/json", bytes.NewBuffer([]byte(sql)))
buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
s := buf.String()
block goroutine & play as sleep
https://blog.sgmansfield.com/2016/06/how-to-block-forever-in-go/
Other way describe in the article needs go func(){….}
<-time.After(time.Duration(math.MaxInt64))
<-time.After(time.Duration(2 * time.Second))
fmt.Printf("........")
获取函数名
pc, _, _, _ := runtime.Caller(1)
// Retrieve a Function object this functions parent
functionObject := runtime.FuncForPC(pc)
// Regex to extract just the function name (and not the module path)
extractFnName := regexp.MustCompile(`^.*\.(.*)$`)
name := extractFnName.ReplaceAllString(functionObject.Name(), "$1")
json unmarshal 字段类型不统一
https://github.com/francoispqt/gojay#decoding
package gojay
import (
"log"
"reflect"
"strconv"
"testing"
)
type user struct {
id int
name string
email string
}
// implement gojay.UnmarshalerJSONObject
func (u *user) UnmarshalJSONObject(dec *Decoder, key string) error {
switch key {
case "id":
var tmp interface{}
var err error
var intVal int
err = dec.Interface(&tmp)
if err != nil {
return err
}
log.Printf(":::%+v", reflect.TypeOf(tmp).Kind())
if reflect.TypeOf(tmp).Kind().String() == "string" {
intVal, err = strconv.Atoi(tmp.(string))
u.id = intVal
} else if reflect.TypeOf(tmp).Kind().String() == "float64" {
u.id = int(tmp.(float64))
}
return err
case "name":
return dec.String(&u.name)
case "email":
return dec.String(&u.email)
}
return nil
}
func (u *user) NKeys() int {
return 3
}
func TestDecoderMe(t *testing.T) {
u := &user{}
d := []byte(`{"id":"1213","name":"gojay","email":"gojay@email.com"}`)
err := UnmarshalJSONObject(d, u)
if err != nil {
log.Fatal(err)
}
}
pprof
Profiling Go Programs - The Go Blog
先起一个9876端口的服务
go func() {
fmt.Println("pprof start...")
fmt.Println(http.ListenAndServe(":9876", nil))
}()
http://x.x.x.x:9876/debug/pprof/ 会打开一个界面
关于goroutine的话 可以借用这个来分析
先
go tool pprof http://x.x.x.x:9876/debug/pprof/goroutine
再
web
之后会生成一个svg