brew link python3 出错


$ brew install python3
Warning: python3 3.6.3 is already installed, it's just not linked.
You can use `brew link python3` to link this version.
$ brew link python3
Linking /usr/local/Cellar/python3/3.6.3... Error: Permission denied @ dir_s_mkdir - /usr/local/Frameworks

发现 /usr/local/ 下没有路径 /usr/local/Frameworks,需要新建该路径,并修改权限。

解决:

$ sudo mkdir /usr/local/Frameworks
$ sudo chown $(whoami):admin /usr/local/Frameworks

成功:

$ brew link python3
Linking /usr/local/Cellar/python3/3.6.3... 1 symlinks created
阅读全文

HTTP 传输内容的压缩


HTTP 压缩,在 HTTP 协议中,其实是内容编码的一种。
在 HTTP 协议中,可以对内容(也就是 body 部分)进行编码,可以采用 gzip 这样的编码,从而达到压缩的目的。也可以使用其他的编码把内容搅乱或加密,以此来防止未授权的第三方看到文档的内容。
所以 HTTP 压缩,其实就是 HTTP 内容编码的一种。
HTTP 压缩是指: Web 服务器和浏览器之间压缩传输的"文本内容"的方法。HTTP 采用通用的压缩算法,比如 gzip 来压缩
HTML、JavaScript、CSS 文件。能大大减少网络传输的数据量,提高了用户显示网页的速度。
首先,浏览器发送一个请求(request)给 Web 服务器,支持一个压缩格式如(gzip),服务端会将原来的源码压缩之后,通过
HTTP 响应(response)信息返回给 Web 浏览器,浏览器接收之后,显示出来。

HTTP 压缩的过程:
1.浏览器发送 HTTP Request 给 Web 服务器,Request 中有 Accept-Encoding:gzip、deflate。
2.Web 服务器接到 Request 后,生成原始的 Response,其中有原始的 Content-Type 和 Content-Length。
3.Web 服务器通过 gzip,来对 Response 进行编码,编码后 header 中有 Content-Type 和 Content-Length(压缩后的大小),并且增加了 Content-Encoding:gzip,然后把 Response 发送给浏览器。
4.浏览器接到 Response 后,根据 Content-Encoding:gzip 来对 Response 进行解码。获取到原始 Response 后,然后显示出网页。











阅读全文

Go cond 锁定期唤醒锁


package main

import (
    "fmt"
    "sync"
    "time"
)

var locker = new(sync.Mutex)
var cond = sync.NewCond(locker)

func test(x int) {
    // 获取锁
    cond.L.Lock()
    // 等待通知  暂时阻塞
    cond.Wait()
    fmt.Println(x)
    time.Sleep(time.Second * 1)
    // 释放锁
    cond.L.Unlock()
}
func main() {
    for i := 0; i < 40; i++ {
        go test(i)
    }
    fmt.Println("start all")
    time.Sleep(time.Second * 3)
    fmt.Println("broadcast")
    // 下发一个通知给已经获取锁的goroutine
    cond.Signal()
    time.Sleep(time.Second * 3)
    // 3秒之后 下发一个通知给已经获取锁的goroutine
    cond.Signal()
    time.Sleep(time.Second * 3)
    // 3秒之后 下发广播给所有等待的goroutine
    cond.Broadcast()
    time.Sleep(time.Second * 60)
}
阅读全文

Go 单例模式


Lazy Loading:

type singleton struct {
}

// private
var instance *singleton

// public
func GetInstance() *singleton {
    if instance == nil {
        // not thread safe
        instance = &singleton{}
    }
    return instance
}

带锁:

type singleton struct {
}

var instance *singleton
var mu sync.Mutex

func GetInstance() *singleton {
    mu.Lock()
    defer mu.Unlock()
    if instance == nil {
        // unnecessary locking if instance already created
        instance = &singleton{}
    }
    return instance
}

带检查锁:

// <-- Not yet perfect. since it's not fully atomic
if instance == nil {
    mu.Lock()
    defer mu.Unlock()
    if instance == nil {
        instance = &singleton{}
    }
}
return instance

阅读全文

Go 打印函数执行时间


package main

import (
    "fmt"
    "time"
)

func timeCost(start time.Time) {
    terminal := time.Since(start)
    fmt.Println(terminal)
}

func main() {
    defer timeCost(time.Now())
    fmt.Println("start program")
    time.Sleep(5 * time.Second)
    fmt.Println("finish program")
}
阅读全文

Go 中如何阻塞等待所有 goroutines 都完成


方案一:

package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

// 定义一个同步等待的组
var wg sync.WaitGroup

// 定义一个Printer函数用于并发
func Printer(a int) {
    time.Sleep(2000 * time.Millisecond)
    fmt.Printf("i am %d\n", a)
    defer wg.Done()
}

func main() {
    // 获取cpu个数
    maxProcs := runtime.NumCPU()
    // 限制同时运行的goroutines数量
    runtime.GOMAXPROCS(maxProcs)
    for i := 0; i < 10; i++ {
        //为同步等待组增加一个成员
        wg.Add(1)
        //并发一个goroutine
        go Printer(i)
    }
    // 阻塞等待所有组内成员都执行完毕退栈
    wg.Wait()
    fmt.Println("WE DONE!!!")
}

阅读全文

Go json 反序列化成 interface{} 对 Number 的处理


json 的规范中,对于数字类型,并不区分是整型还是浮点型。
go json number.gif
对于如下 json 文本:

{
    "name": "ethancai",
    "fansCount": 9223372036854775807
}

如果反序列化的时候指定明确的结构体和变量类型

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name      string
    FansCount int64
}

func main() {
    const jsonStream = `{"name":"ethancai", "fansCount": 9223372036854775807}`
    var user User  // 类型为User
    err := json.Unmarshal([]byte(jsonStream), &user)
    if err != nil {
        fmt.Println("error:", err)
    }
    fmt.Printf("%+v \n", user)
}

Output:

{Name:ethancai FansCount:9223372036854775807}

如果反序列化不指定结构体类型或者变量类型,则 json 中的数字类型,默认被反序列化成 float64 类型:

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

func main() {
    const jsonStream = `{"name":"ethancai", "fansCount": 9223372036854775807}`
    // 不指定反序列化的类型
    var user interface{}
    err := json.Unmarshal([]byte(jsonStream), &user)
    if err != nil {
        fmt.Println("error:", err)
    }
    m := user.(map[string]interface{})
    fansCount := m["fansCount"]
    fmt.Printf("%+v \n", reflect.TypeOf(fansCount).Name())
    fmt.Printf("%+v \n", fansCount.(float64))
}

Output:

float64
9.223372036854776e+18

另一个程序

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name      string
    // 不指定FansCount变量的类型
    FansCount interface{}
}

func main() {
    const jsonStream = `{"name":"ethancai", "fansCount": 9223372036854775807}`
    var user User
    err := json.Unmarshal([]byte(jsonStream), &user)
    if err != nil {
        fmt.Println("error:", err)
    }
    fmt.Printf("%+v \n", user)
}

Output:

{Name:ethancai FansCount:9.223372036854776e+18}

从上面的程序可以发现,如果 fansCount 精度比较高,反序列化成 float64 类型的数值时存在丢失精度的问题。

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
    "strings"
)

func main() {
    const jsonStream = `{"name":"ethancai", "fansCount": 9223372036854775807}`
    decoder := json.NewDecoder(strings.NewReader(jsonStream))
    // UseNumber causes the Decoder to unmarshal a number into an interface{} as a Number instead of as a float64.
    decoder.UseNumber()
    var user interface{}
    if err := decoder.Decode(&user); err != nil {
        fmt.Println("error:", err)
        return
    }
    m := user.(map[string]interface{})
    fansCount := m["fansCount"]
    fmt.Printf("%+v \n", reflect.TypeOf(fansCount).PkgPath() + "." + reflect.TypeOf(fansCount).Name())
    v, err := fansCount.(json.Number).Int64()
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    fmt.Printf("%+v \n", v)
}

Output:

encoding/json.Number
9223372036854775807

上面的程序,使用了 func (*Decoder) UseNumber 方法告诉反序列化 json 的数字类型的时候,不要直接转换成 float64,而是转换成 json.Number 类型。
json.Number内部实现机制:

// A Number represents a JSON number literal.
type Number string

// String returns the literal text of the number.
func (n Number) String() string { return string(n) }

// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
    return strconv.ParseFloat(string(n), 64)
}

// Int64 returns the number as an int64.
func (n Number) Int64() (int64, error) {
    return strconv.ParseInt(string(n), 10, 64)
}

json.Number 本质是字符串,反序列化的时候将 json 的数值先转成 json.Number,其实是一种延迟处理的手段,待后续逻辑需要时候,再把 json.Number 转成 float64 或者 int64。

阅读全文

Go json 库 json-iterator


高效json库.png

直接替换
json.Marshal 替为 jsoniter.Marshal

type ColorGroup struct {
    ID     int
    Name   string
    Colors []string
}
group := ColorGroup{
    ID:     1,
    Name:   "Reds",
    Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}
b, err := jsoniter.Marshal(group)

阅读全文