piekenius' Blog

Go

Word count: 2.6kReading time: 11 min
2023/08/04

Go简单实践

实战案例一:猜谜游戏

1.

1
2
3
4
5
6
   maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
fmt.Println("The secret number is ", secretNumber)

fmt.Println("Please input your guess")

使用**math/rand/**库:rand.Seed(time.Now().UnixNano())实现一个纳秒级更新的随机种子库;然后使用rand.Intn()函数实现随机数的生成。

rand.Intn(Number)函数是在一个左开右闭的区间内生成,rand.Int(Number)函数在一个全闭范围内生成。

2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   reader := bufio.NewReader(os.Stdin)
//for{
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("An error occured while reading input. Please try again", err)
//continue
}
input = strings.Trim(input, "\r\n")

guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Invalid input. Please enter an integer value")
//continue
}
fmt.Println("You guess is", guess)

使用bufio库:reader=NewReader(os.Stdin)创建一个新的对象,用于从标准输入端读入用户数据,即为reader;使用readString函数将输入放入变量Input中,碰到换行符就停止读入。如果存在错误信息(err不等于nil,相当于none),就打印错误提示并完成程序。
最后,在Input变量中只用strings.trim函数去除换行符和回车符,并且将输入字符串转化为整型变量guess。

3.

1
2
3
4
5
6
7
8
9
if guess > secretNumber {
fmt.Println("Your guess is bigger than the secret number. Please try again")
} else if guess < secretNumber {
fmt.Println("Your guess is smaller than the secret number. Please try again")
} else {
fmt.Println("Correct, you Legend!")
//break
}
//}

加上判断,完成一次程序。需要多次输入,我们需要for循环。

该程序所用到的go库为:bufio,fmt,math/rand,os(os.Stdin),strconv,strings,time

实战案例二:在线词典

http通信包括:请求方法-请求头-请求体-响应-响应状态码-读取响应体-错误处理

  1. 首先在需要连接的网页中获取cURL并编写http请求代码如下(使用curlconverter.com)进行代码生成.
  2. 由于请求体需要的数据不局限于以上代码,则需要使用json序列化来实现请求体的变化。
  3. 通过oktools.net生成结构体代码解析response
  4. 输出相应的音标,解释等。将在线词典功能封装为一个函数;在主函数中读取命令行中的参数,载入word,运行程序。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    package main

    import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    )
    //通过json序列化 实现请求体的变化
    type DictRequest struct{
    Transtype string `json:"trans_type"`
    Source string `json:"source"`
    }
    //解析response
    type DictResponse struct {
    Rc int `json:"rc"`
    Wiki struct {
    } `json:"wiki"`
    Dictionary struct {
    Prons struct {
    EnUs string `json:"en-us"`
    En string `json:"en"`
    } `json:"prons"`
    Explanations []string `json:"explanations"`
    Synonym []string `json:"synonym"`
    Antonym []string `json:"antonym"`
    WqxExample [][]string `json:"wqx_example"`
    Entry string `json:"entry"`
    Type string `json:"type"`
    Related []interface{} `json:"related"`
    Source string `json:"source"`
    } `json:"dictionary"`
    }

    func query(word string){
    client := &http.Client{} //创建一个http客户端,用于发送给http请求
    request:=DictRequest{Transtype:"en2zh",Source:word}
    buf,err:=json.Marshal(request)
    if err!=nil{
    log.Fatal(err)
    }
    var data=bytes.NewReader(buf)
    req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data) //创建一个http post请求实例,被发送至中间该地址
    if err != nil {
    log.Fatal(err)
    }
    //各式各样的请求头
    req.Header.Set("authority", "api.interpreter.caiyunai.com")
    req.Header.Set("accept", "application/json, text/plain, */*")
    req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
    req.Header.Set("app-name", "xy")
    req.Header.Set("content-type", "application/json;charset=UTF-8")
    req.Header.Set("device-id", "6434127fee6f4eed253afdc2e6dc8683")
    req.Header.Set("origin", "https://fanyi.caiyunapp.com")
    req.Header.Set("os-type", "web")
    req.Header.Set("os-version", "")
    req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
    req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"`)
    req.Header.Set("sec-ch-ua-mobile", "?0")
    req.Header.Set("sec-ch-ua-platform", `"Windows"`)
    req.Header.Set("sec-fetch-dest", "empty")
    req.Header.Set("sec-fetch-mode", "cors")
    req.Header.Set("sec-fetch-site", "cross-site")
    req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.188")
    req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")

    resp, err := client.Do(req) //发送http请求并等待响应
    if err != nil {
    log.Fatal(err)
    } //出现错误则打印错误结果并终止程序
    defer resp.Body.Close() //延迟关闭响应的主体
    bodyText, err := ioutil.ReadAll(resp.Body) //读取相应主体的内容
    if err != nil {
    log.Fatal(err)
    } //错误则
    var dictResponse DictResponse
    err=json.Unmarshal(bodyText,&dictResponse)
    if err!=nil{
    log.Fatal(err)
    }
    fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
    for _,item:=range dictResponse.Dictionary.Explanations{
    fmt.Println(item)
    }
    } //遍历切片,忽略下标值,输出每一个解释

    func main() {
    if len(os.Args)!=2{ //`os.Args`是一个字符串切片,检查命令行参数的数量是否等于2
    fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
    example:simpleDict hello
    `) //如果命令行参数数量不等於2,将说明信息打印到标准错误输出(stderr),`os.stderr`表示标准错误输出流
    os.Exit(1) //终止程序执行,返回1(非零的状态码表示程序异常终止)
    }
    word:=os.Args[1] //读取命令行的第二个参数(下标为1)
    query(word)
    }

我真的要疯了.jpg之实战案例三:socks5代理,完全听不懂,大哥。

Go语言进阶与依赖管理

  1. 并发(多线程单核√)与并行(多线程多核)

    1. Goroutine:协程(√)/线程
    2. CSP:通过通信实现并发处理,而不共享数据
    3. Channel
    4. 并发安全 Lock
    5. waitgroup
  2. 依赖管理

    1. 依赖管理演进:GOPATH-Go Vender-Go Module
    2. 依赖管理三要素
      1. 配置文件,描述依赖(go.mod)
        1. 依赖管理基本单元,原生库,单元依赖
        2. version:语义化/commit伪版本
        3. indirect间接依赖
        4. incompatible
      2. 依赖分发-Proxy
      3. 本地工具 go mod:init,download,tidy

Go语言工程实践之测试

  1. 单元测试
  • _test.go结尾
  • func TestXxx(*testing.T)
  • 初始化逻辑放到TestMain中:测试前准备工作;测试后的收尾工作
  • testify/assert包可实现结果的equal/notequal
  • 代码覆盖率
  • 依赖
  • mock
  1. 基准测试

  2. 设置一个简单的社区话题页面

    1. 需求:

      • 展示话题(标题,文字描述)和回帖列表
      • 实现一个本地web服务
      • 话题和回帖数据用文件存储
    2. ER图

    3. 分层结构
      File-数据层-逻辑层-视图层-Client

高质量编程及编码规范

  1. 编码规范

    1. 简单性、可读性、生产力
    2. 编码规范
      1. 代码格式:gofmt自动格式化代码/goimprts=gofmt+依赖包管理
      2. 注释:代码作用、如何做的、实现原因、什么情况会出错
        · 公共符号始终要注释
      3. 命名规范:
      • variable
        • 缩略词全大写,位于变量开头且不需要导出时,使用全小写
        • 全局变量需要在名字中提供更多上下文信息
          function
        • 不携带包名的上下文信息
        • 当名为foo的包某个函数返回类型Foo时,可以省略类型信息而不出现歧义
        • 当名为foo的包某个函数返回类型T时,可以在函数名中加入类型信息
      • package
        • 只有小写字母组成
        • 不与标准库同名
        • 不适用常用变量名作为包名
        • 使用单数而不是复数
        • 谨慎使用缩写
      1. 控制流程
        • 避免嵌套
      2. 错误和异常处理
        • 简单错误:优先使用errors.New(string)创建匿名变量直接表示简单错误;如果有格式化要求,使用fmt.Errorf
        • 错误的Wrap(包装)和Unwrap(解包装):提供一个error嵌套另一个error,生成error的跟踪链;在fmt.Errorf中使用%w来将一个错误关联至错误链中
        • 错误判定:判定一个错误是否为特定错误,使用error.ls,可以判断错误链上的所有错误是否含有特定错误;使用errors.As在错误链上获取特定种类的错误
        • panic:不建议
        • recover:只能在被defer的函数中使用;嵌套无法生效;只在当前goroutine生效;defer的语句是后进先出。如果需要更多上下文信息,可以recover后在log中记录当前的调用栈
  2. 性能优化建议

    1. Benchmarkgo test -bench=. -benchmen
    2. Slice预分配内存;大内存未释放(copy代替re-slice)
    3. Map预分配内存
    4. 字符串处理:使用strings.Builder/strings.Buffer做字符串拼接
    5. 空结构体:节省内存
    6. atomic包:效率比锁高;sync.Mutex应该用来保护一段逻辑而不是保护一个变量;对于非数值操作,而可以使用atomic.Value
  3. 性能调优

    1. 性能分析工具pprof:可视化和分析性能分析数据,可以知道程序在什么地方耗费了多少CPU、Memory
      https://github.com/wolfogre/go-pprof-practice
      1
      2
      3
      4
      5
      go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"   //进入pprof
      top //查看占用资源最多的函数
      list 关键字 //查找代码行
      web //调用关系可视化
      go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap" //处理堆内存问题
      Mindmap
  4. 性能调优案例

    1. 业务服务优化
    • 建立服务性能评估手段
    • 服务性能评估方式
    • 请求流量构造
    • 压测范围
      • 单机器压测
      • 集群压测
    • 性能数据采集
      • 单机性能数据
      • 集群性能数据
    • 分析性能数据(使用库不规范、高并发场景优化不足…)
    • 重点优化项改造(diff)
    • 优化效果验证
      • 重复压测验证
      • 上限评估优化结果:关注服务监控、逐步放量、收集性能数据
    • 进一步优化,服务整体链路分析
      • 规范上有服务调用接口
      • 分析链路,提升服务性能
    1. 基础库优化
    • AB实验SDK优化
      • 设计完善改造方案、数据按需获取、数据序列协议优化
      • 内部压测验证
      • 对光业务服务落地验证
    1. Go语言优化
    • 编译器优化:内存分配、代码编译流程、内部压测验证、推广业务服务落地验证
CATALOG
  1. 1. Go简单实践
    1. 1.1. 实战案例一:猜谜游戏
    2. 1.2. 实战案例二:在线词典
  2. 2. Go语言进阶与依赖管理
  3. 3. Go语言工程实践之测试
  4. 4. 高质量编程及编码规范