Tag - GoLang

Go-compiled binary running on alpine

One command is all you need:

apk add libc6-compat

via

Go 语言 switch/case 的一个小坑

前几天竟然被 switch/case 坑了一小下,坦白讲,能掉进这个坑,纯粹是基本语法不过关,把 Go 当成 C++ 了。

下面是一个简化后的粟子,通过一个人的名字来判断我认不认识他,最初写下这段代码的期望结果是:如果 nameJohnKenny,把 i_knew_him 置为 true,表示我认识这两个人,别人(默认)不认识。熟悉 C++ 的同学一定知道我为什么这样写。

var i_knew_him bool = false
switch name {
case "John":
case "Kenny":
    i_knew_him = true
}

但我们现在写的是 Go 语言,执行结果不一样了(敲黑板

我的好朋友 John 迎面走了过来,我却假装不认识他,i_knew_him = true 根本没有执行到,因为这条表达式只属于 case "Kenny"……

正确的写法是什么样的呢?

case "John", "Kenny":
    i_knew_him  = true

详见 Go 语言规范

<全文完>

《Go 语言编程》笔记

今天花了 2.5 小时看完这本书,总体来说这本书的内容偏浅,拿来入门比较合适——当然对于我这种从不看书、只靠文档 + 实践学习语言的来说,一些基础的知识点还是有必要补补的。看书的过程简单记了下笔记,太基础的直接跳过了,只记了部分我认为比较关键的知识点。全书最核心的部分当数第四章——并发编程。

@author migege@github
@version 170622:1

语言特点

  • 并发与分布式
  • 软件工程
  • 编程哲学

基础

多返回值

func getName() (firstName, middleName, lastName, nickName string) {
    firstName = "A"
    middleName = "B"
    lastName = "C"
    nickName = "D"
    return
}

匿名函数和闭包

f := func(x, y int) int {
    return x + y
}

反射

type Bird struct {
    Name string
    LifeExpectance int
}

func main() {
    sparrow := &Bird{"Sparrow", 3}
    s := reflect.ValueOf(sparrow).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())
    }
}

Cgo

package main

/*
#include <stdio.h>
#include <stdlib.h>
*/
import "C"
import "unsafe"

func main() {
    cstr := C.CString("Hello, world!")
    C.puts(cstr)
    C.free(unsafe.Pointer(cstr))
}

工程管理

运行

go run

构建

go build

单元测试

go test

GDB 调试

gdb xxx

调试信息为 DWARFv3,>= gdb1.7

语法

预定义常量

const (
    c0 = iota
    c1 = iota
    c2 = iota
)

枚举

const (
    Sunday = itoa
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
    numberOfDays
)

数组切片

cap()len()

goto

竟然支持 goto

defer / panic() / recover()

defer func() {
    if r := recover(); r != nil {
        // do something
    }
}

foo()

passed by value

类型都是值传递

面向对象编程

匿名组合

虚基类

type Job struct {
    Command string
    *log.Logger
}

func (job *Job) Start() {
    job.Log("starting...")
}

非侵入式接口

一个类只需要实现了接口要求的所有函数,就认为这个类实现了该接口。

接口查询

var file1 Writer = ...
if file5, ok := file1.(two.IStream); ok {
}

类型查询

switch v := arg.(type) {
    case int:
    case string:
    default:
    // ...
}

并发编程

goroutine

go 关键字

并发通信

不要通过共享内存来通信,而应该通过通信来共享内存。

channel

ch <- 1

channel 在被读取前,写入操作是阻塞的。

<- ch

channel 在被写入前,读取操作是阻塞的。

基本语法

var ch chan int
var m map[string]chan bool
ch := make(chan int)

select

select {
    case <- ch1:
    case <- ch2:
    default:
}

缓冲机制

ch := make(chan int, 1024)

即使没有读取方,写入方也可以一直往 channel 里写入,在缓冲区被填满之前都不会阻塞。

超时机制

timeout := make(chan bool, 1)
go func() {
    time.Sleep(1e9)
    timeout <- true
}

select {
    case <- ch:
    // ...
    case <- timeout:
    // timeout
}

channel 可被传递

可以用来实现 pipe。

单向 channel

var ch1 chan<- float64 // 仅写入
var ch2 <-chan int // 仅读取

关闭 channel

close(ch)

x, ok := <-ch

多核并行化

runtime.GOMAXPROCS(runtime.NumCPU())

出让时间片

runtime.Gosched()

同步

同步锁

sync.Mutexsync.RWMutex

var l sync.Mutex
func foo() {
    l.Lock()
    defer l.Unlock()
}

全局唯一性操作

var once sync.Once
once.Do(...)

sync/atomic 子包

func CompareAndSwapUint64(val *unit64, old, new uint64) (swapped bool)

网络编程

Socket 编程

Dial()


### HTTP 编程

```net/http

RPC 编程

rpc.Register()

工程管理

gofmt

godoc

开发工具

gocode

其它

libtask

Go 语言实现「外网 IP 查询」服务

外网 IP 查询服务——这应该是我写过最简单、但又有实际用处的 TCP 服务了。

流程

  1. 服务启动,监听本地 6666 端口
  2. 接受连接,返回客户端 IP
  3. 断开连接

用 Go 语言实现,加上空行还不到 30 行代码,请自行前往 GitHub 获取

部署

通过 go build 命令编译,在 macOS 上编译 Linux 版本需要指定 GOOS 参数:

GOOS=linux go build main.go

生成的 main 可以直接运行了:

./main

或者更高级点,通过 supervisor 配置成服务,使用 supervisorctl 控制。

使用

telnet 连接部署机器的 6666 端口即可,如 migege.com:6666

telnet migege.com 6666
Trying 120.27.122.8...
Connected to migege.com.
Escape character is '^]'.
1xx.2xx.2xx.1xxConnection closed by foreign host.

再也不用担心 ns1.dnspod.net 没有响应了。