#93 Go 1.21
Golang 2023-07-05#92 gotip 命令
Golang 2023-05-30gotip 是官方推出的,从最新的开发分支下拉代码,编译生成 go 运行时的工具。
如果希望体验最新特性,可以在编译成功之后,可以直接用 gotip 取代 go 命令用来执行 go 程序。
gotip 就可以理解为开发版的 go。
go install golang.org/dl/gotip@latest
gotip
gotip: not downloaded. Run 'gotip download' to install to C:\Users\nosch\sdk\gotip
gotip download
Cloning into 'C:\Users\nosch\sdk\gotip'...
...
Building Go cmd/dist using C:\Program Files\Go. (go1.20.5 windows/amd64)
Building Go toolchain1 using C:\Program Files\Go.
Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.
Building Go toolchain2 using go_bootstrap and Go toolchain1.
Building Go toolchain3 using go_bootstrap and Go toolchain2.
Building packages and commands for windows/amd64.
成功的时候:
---
Installed Go for windows/amd64 in C:\Users\nosch\sdk\gotip
Installed commands in C:\Users\nosch\sdk\gotip
Success. You may now run 'gotip'!
失败的时候:
# runtime/cgo
gcc_libinit_windows.c: In function '_cgo_beginthread':
gcc_libinit_windows.c:143:27: error: implicit declaration of function '_beginthread'; did you mean '_cgo_beginthread'? [-Werror=implicit-function-declaration]
143 | thandle = _beginthread(func, 0, arg);
| ^~~~~~~~~~~~
| _cgo_beginthread
cc1: all warnings being treated as errors
go tool dist: FAILED: C:\Users\nosch\sdk\gotip\pkg\tool\windows_amd64\go_bootstrap install std: exit status 1
Success. You may now run 'gotip'!
#91 Go set 类型
Golang 2023-05-29#90 Golang 实现简单反向代理
Golang 2023-04-27看到微信公众号 Go语言教程 的文章《golang 实现简单网关》才知道还有 httputil.ReverseProxy 这么个东西。
PS:这玩意儿有什么必要放在标准库?
还挺有趣,在作者示例的基础之上完善了一下,实现多服务,多后端节点的一个负载均衡(还可以再补上权重)。
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"math/rand"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"time"
)
func main() {
addr := "127.0.0.1:2002"
backends := map[string][]string{
"service1": {"http://127.0.0.1:2003", "http://127.0.0.1:2004"},
}
reversePorxy := NewReverseProxy(backends)
log.Println("Starting httpserver at " + addr)
log.Fatal(http.ListenAndServe(addr, reversePorxy))
}
func reqConvert(req *http.Request, target *url.URL) {
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
if target.RawQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = target.RawQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = target.RawQuery + "&" + req.URL.RawQuery
}
if _, ok := req.Header["User-Agent"]; !ok {
req.Header.Set("User-Agent", "")
}
req.Header.Set("X-Real-Ip", strings.Split(req.RemoteAddr, ":")[0])
}
func NewReverseProxy(backends map[string][]string) *httputil.ReverseProxy {
var targets = make(map[string][]*url.URL)
for srv, nodes := range backends {
for _, nodeUrl := range nodes {
target, _ := url.Parse(nodeUrl)
targets[srv] = append(targets[srv], target)
}
}
director := func(req *http.Request) {
segments := strings.SplitN(req.URL.Path, "/", 3)
if len(segments) != 3 {
return
}
srv := segments[1]
req.URL.Path = segments[2]
if _, ok := targets[srv]; !ok {
log.Printf("unknown path: %s", req.URL.Path)
return
}
rand.Seed(time.Now().UnixNano())
randomIndex := rand.Intn(len(targets[srv]))
target := targets[srv][randomIndex]
reqConvert(req, target)
}
modifyFunc := func(res *http.Response) error {
if res.StatusCode != http.StatusOK {
oldPayLoad, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
newPayLoad := []byte("hello " + string(oldPayLoad))
res.Body = ioutil.NopCloser(bytes.NewBuffer(newPayLoad))
res.ContentLength = int64(len(newPayLoad))
res.Header.Set("Content-Length", fmt.Sprint(len(newPayLoad)))
}
return nil
}
return &httputil.ReverseProxy{Director: director, ModifyResponse: modifyFunc}
}
func singleJoiningSlash(a, b string) string {
aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/")
switch {
case aslash && bslash:
return a + b[1:]
case !aslash && !bslash:
return a + "/" + b
}
return a + b
}
#89 Golang 程序中内嵌 Lua
Golang 2023-04-19#88 Golang 多版本方案
Golang 2023-03-09#87 《企业级 Go 项目开发实战》目录
Golang 2023-02-27主要是参考一下这个目录,对照着查漏补缺。
#86 Golang 变量初始化顺序问题
Golang 2022-12-15#85 Go DNS 解析
Golang DNS 2022-10-30#84 gob:Golang 二进制编码格式(标准库)
Golang 2022-10-01package main
import (
"bytes"
"encoding/gob"
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
// 编码
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(Person{"John", 30})
if err != nil {
panic(err)
}
// 解码
dec := gob.NewDecoder(&buf)
var p Person
err = dec.Decode(&p)
if err != nil {
panic(err)
}
fmt.Println(p)
}
#83 Golang 源码结构
Golang 2022-09-16#82 Golang UUID
Golang 2022-09-13#81 Go 谚语
Golang 2022-09-13得知了 Go 谚语这么个东西,搜索一番,找到:
- https://go-proverbs.github.io/
- https://github.com/jboursiquot/go-proverbs
- https://www.youtube.com/watch?v=PAAkCSZUG1c
原来是类似 Python 之禅(import this
)一样的东西。
- Don't communicate by sharing memory, share memory by communicating. 别通过共享内存通信,要通过通信共享内存。
- Concurrency is not parallelism. 并发不是并行。
- Channels orchestrate; mutexes serialize.
- The bigger the interface, the weaker the abstraction. 接口越大, 抽象越弱。
- Make the zero value useful.
interface{}
says nothing.- Gofmt's style is no one's favorite, yet gofmt is everyone's favorite. 别争论风格问题,不管喜不喜欢,用 gofmt 就是了。
- A little copying is better than a little dependency. 少量的复制好过引入新的依赖。
- Syscall must always be guarded with build tags.
- Cgo must always be guarded with build tags.
- Cgo is not Go. 别用 cgo。
- With the unsafe package there are no guarantees. 慎用 unsafe 包(安全无保障)
- Clear is better than clever. 清晰的代码比聪明的代码好。
- Reflection is never clear. 慎用反射(代码不清晰)。
- Errors are values.
- Don't just check errors, handle them gracefully.
- Design the architecture, name the components, document the details.
- Documentation is for users. 文档是给用户的(注意代码本身的可读性)。
- Don't panic. 不要滥用
panic
。
#80 Viper: Go 项目配置管理
Golang Viper 2022-07-22-
main.go
:package main import ( "fmt" "os" "github.com/spf13/pflag" "github.com/spf13/viper" ) func main() { // 1. 设置 Viper 配置 viper.SetConfigName("config") // 配置文件名(不带后缀) viper.AddConfigPath(".") // 配置文件路径 viper.SetConfigType("yaml") // 配置文件类型 viper.AutomaticEnv() // 自动读取环境变量 // 2. 设置命令行参数 pflag.String("name", "", "project name") pflag.String("host", "", "host address") pflag.String("port", "", "port number") pflag.String("config", "./config.yaml", "config file") // 配置文件参数 pflag.Parse() viper.BindPFlags(pflag.CommandLine) // 将命令行参数绑定到 Viper // 3. 读取配置文件 if configFile := viper.GetString("config"); configFile != "" { fmt.Println(configFile) if err := viper.ReadInConfig(); err != nil { fmt.Fprintf(os.Stderr, "读取配置文件失败:%v\n", err) os.Exit(1) } } // 4. 读取配置项 projectName := viper.GetString("name") port := viper.GetInt("port") fmt.Printf("ProjectName: %s, Port: %d\n", projectName, port) }
-
config.yaml
:name: hello host: 10.10.0.172 port: 9090
-
支持环境变量、命令行参数、配置文件。
- 支持多种配置文件,包括 JSON,YAML,TOML,INI 等。
- 支持监控配置文件的变化,会自动加载新的配置。
-
支持从远程加载配置,比如 etcd、zk、consul、redis 等,
也可以通过 RemoteProvider 接口自定义远程数据源:type RemoteProvider interface { Set(key string, value []byte) error Watch(key string) (chan *RemoteResponse, chan error) Get(key string) ([]byte, error) }
-
支持默认值。