《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

PM10 和 PM2.5 双爆表

有点突破认知了。

@北京

编译 VIM 配置参数(Python 2/3 支持)

for Python 2.n

./configure --with-features=huge --enable-pythoninterp --enable-cscope

for Python 3.n

./configure --with-features=huge --enable-python3interp --enable-cscope

DNSPod 如何查看域名解析的 domain_id 和 record_id

本文介绍调用 API 获取 DNSPod 域名解析需要的 domain_idrecord_id 参数的方法,所有的 DNSPod API 请求都必须提供 login_token 作为公共参数以验证用户身份是否合法。

获取 login_token

DNSPOD > 用户中心 > 安全设置 > API Token

使用英文 , 将 ID 和 Token 连接起来即公共请求参数 login_token

获取 domain_id

curl 'https://dnsapi.cn/Domain.List' -d 'login_token=<your_login_token>&format=json'

根据响应中的 domains 得到域名对应的 domain_id

获取 record_id

curl 'https://dnsapi.cn/Record.List' -d 'login_token=<your_login_token>&format=json&domain_id=<your_domain_id>'

根据响应中的 records 得到子域名记录对应的 record_id

阿里云短信接口 (Python)

废话不多说,直接贴代码,有兴趣的访问我的 github

#!/usr/bin/env python
# -*- coding:utf8 -*-
import sys

reload(sys)
sys.setdefaultencoding('utf8')


def sendsms(phone, param_string, access_key_id, access_key_secret, template_code, sign_name):
    from datetime import datetime
    import uuid
    import requests

    url = 'https://sms.aliyuncs.com/'
    ts = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
    once = str(uuid.uuid4())
    data = {
        'Action': 'SingleSendSms',
        'SignName': sign_name,
        'TemplateCode': template_code,
        'RecNum': phone,
        'ParamString': param_string,
        'Format': 'JSON',
        'Version': '2016-09-27',
        'AccessKeyId': access_key_id,
        'SignatureMethod': 'HMAC-SHA1',
        'Timestamp': ts,
        'SignatureVersion': '1.0',
        'SignatureNonce': once,
    }

    def __percent_encode(s):
        import urllib
        s = str(s)
        s = urllib.quote(s.decode('utf8').encode('utf8'), '')
        s = s.replace('+', '%20')
        s = s.replace('*', '%2A')
        s = s.replace('%7E', '~')
        return s

    def __gen_signature(data, req_method, secret):
        import hashlib
        import hmac
        import base64

        sorted_data = sorted(data, key=lambda v: v[0])
        vals = []
        for k, v in sorted_data:
            vals.append(__percent_encode(k) + '=' + __percent_encode(v))

        params = '&'.join(vals)
        string_to_sign = req_method + '&%2F&' + __percent_encode(params)
        key = secret + '&'
        signature = base64.encodestring(hmac.new(key, string_to_sign, hashlib.sha1).digest()).strip()
        return signature

    try:
        headers = {
            'Content-Type': 'application/x-www-form-urlencoded',
        }

        signature = __gen_signature(data.items(), 'POST', access_key_secret)
        data['Signature'] = signature

        r = requests.post(url, data=data, headers=headers)
        print r.text
    except Exception, e:
        print 'EXCEPT:', e


if __name__ == '__main__':
    pass

最后想说的是,阿里云文档中关于 base64 后的签名值还需要做一次 URLEncode 编码的说法是有问题的,事实证明只会报错:Specified signature is not matched with our calculation.,掉这坑里差点没爬上来。

MiLog 可以用 python 写插件啦

MiLog 发布一年多了,昨天才第一次真正有动力实现插件系统——总觉得用一个异步的外部脚本做 anti-spam,不怎么得劲,还是写一个插件系统比较靠谱,本来插件系统也是 MiLog 的规划功能之一,更重要的是:

  1. 简单的非核心功能并不想用 Go 写进 MiLog 里,最好只保持一个轻量级的 Core;
  2. 最好可以用 Python 写插件,没别的原因,我喜欢 Python 就足够了;

于是乎,MiLog Plugins System 诞生了,目前只支持用 Python 编写插件,最简单的插件只需要两个函数(参考 speak-chinese-please):

  1. register() 用于 MiLog 加载插件时注册事件;
  2. <事件名>() 用于处理事件回调;

将编写好的插件放在任意目录下,并在 conf/app.conf 中增加一行,指向该目录就行:

plugindir = <path_to_plugin_directory>

哦对了,做完以上步骤后 MiLog 需要重启,注意通过日志观察插件是否注册成功。

Now, enjoy!

Speak Chinese Please

最近一段时间本 blog 被个别 spammer 盯上了,成功绕过验证码狂发垃圾广告,本来打算抽空做个简单的朴素贝叶斯做反垃圾,无奈本人实在太懒,迟迟没有动手,倒是收集了不少内容做样本……

考虑到这些 spam 内容无一例外都是英文,暂时先写个检测中文的脚本顶一阵吧,于是在几分钟前,speak-chinese-please 上线了——我太懒,简单脚本管用绝不写复杂的,顺便观察下 spammer 学习能力如何,10 年前玩 WordPress 的时候已经有机器人可以随机输入中文绕过该机制了,就看这次它会不会了。

分享一个 python 检测中文字符的函数吧,这也是 speak-chinese-please 的核心:

def any_chinese(s):
    return any(u'\u4e00' <= c <= u'\u9fff' for c in s.decode('utf-8'))

嗯,就一行,注意 any() 函数。

Python PIP 升级所有包 & 配置国内源(阿里云)

升级所有包

pip freeze --local | grep -v '^\-e' | cut -d = -f 1  | xargs pip install -U

via

配置国内源(阿里云)

Mac & Linux

修改 ~/.pip/pip.conf 文件(Mac & Linux 系统)或 %HOMEPATH%\pip\pip.ini(Windows 系统),如果不存在就新建一个,把下面的内容粘进去:

[global]
trusted-host=mirrors.aliyun.com
index-url=http://mirrors.aliyun.com/pypi/simple

迅雷离线助手的设置齿轮不见了

迅雷离线助手的设置齿轮不见了,怎么设置 jsonrpc?

开发者工具 -> Application -> Storage -> Local Storage -> http://dynamic.cloud.vip.xunlei.com

key value 说明
TLE_aria2_jsonrpc <你的 jsonrpc 地址> jsonrpc 地址

via

macOS Sierra 上的 Karabiner 替代:Control_L + hjkl 组合方向键

2017-12-02: Karabiner-Elements-11.3.0 已经支持在 macOS High Sierra (ver 11.13.1) 系统上实现 Vi Style Arrows——安装 Karabiner-Elements 后,在这个页面上找到「Vi Style Arrows」,点击右侧的 Import 按钮将配置导入并 enable 即可。

作为 vim + HHKB 键盘 + Karabiner 的重度依赖患者,某天晚上升级到 macOS Sierra 后突然傻眼了,Control_L + hjkl 作为方向键的设置全部失效了……

接下来的几天强迫自己重新适应 MBP 自带键盘、强迫自己适应 HHKB 默认的方向组合键,都觉得丑陋不堪,已然唯有 Control_L + hjkl 才是正确打开方式,于是踏上了寻找 Karabiner 临时替代者的不归路,直到刚才看到这个:[KeyMove] Control_L+hjkl to Left/Down/Up/Right #103

这里提到了一款收费软件:Keyboard Maestro,按照 seangaffney 的设置就可以轻松实现!不管了,这是目前为止找到体验最完美的替代软件了——当然,我的需求只是 Control_L + hjkl 实现方向键。

希望用到 Trial 结束前,the Next Generation Karabiner 可以完工哈,虽然感觉可能性不大,给作者捐了 $5,加油……

ps. BetterTouchTool、ControllerMate 都试过,前者貌似没法实现 key repeat,后者折腾半天不知道究竟如何正确设置组合键,遂都放弃了……