前言
本文总结工作过程中对golang使用上的一些技巧,不断补充……
remove duplicate values from slices
seen := make(map[string]struct{})
for _, item := range items {
    if _, ok := seen[item]; !ok {
        seen[item] = struct{}{}
        // TODO: unduplicated operations ...
    }
}
httpclient
client := &http.Client{
	Transport: &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
			DualStack: true,
		}).DialContext,
		MaxIdleConns:        100,
		MaxIdleConnsPerHost: 100,
		IdleConnTimeout:     90 * time.Second,
	},
}
// construct encoded endpoint
Url, err := url.Parse(fmt.Sprintf("http://%s:%d", addr, port))
if err != nil {
	return err
}
Url.Path += "/index"
endpoint := Url.String()
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
	return err
}
// use httpClient to send request
rsp, err := client.Do(req)
if err != nil {
	return err
}
// close the connection to reuse it
defer rsp.Body.Close()
// check status code
if rsp.StatusCode != http.StatusOK {
	return fmt.Errorf("get rsp error: %v", rsp)
}
// parse rsp body
err = json.NewDecoder(rsp.Body).Decode(&xxx)
if err != nil {
	return err
}
return err
Singleton Pattern
type singleton struct {
}
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
    once.Do(func() {
        instance = &singleton{}
    })
    return instance
}
goroutine pool
func goroutine_pool(number, taskNum int) {
	var wg sync.WaitGroup
	channels := make(chan int, taskNum)
	for i := 0; i < taskNum; i++ {
		channels <- i
	}
	close(channels)
	if taskNum < number {
		number = taskNum
	}
	for i := 0; i < number; i++ {
		wg.Add(1)
		go func(index int) {
			defer wg.Done()
			for ch := range channels {
				// TODO: goroutine process logic code
				fmt.Printf("task: %d in goroutine: %d ...\n", ch, index)
			}
		}(i)
	}
	wg.Wait()
}
func main() {
	goroutine_pool(5, 20)
}
参考kubernetes实现:
package workqueue
import (
	"context"
	"sync"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
type DoWorkPieceFunc func(piece int)
// ParallelizeUntil is a framework that allows for parallelizing N
// independent pieces of work until done or the context is canceled.
func ParallelizeUntil(ctx context.Context, workers, pieces int, doWorkPiece DoWorkPieceFunc) {
	var stop <-chan struct{}
	if ctx != nil {
		stop = ctx.Done()
	}
	toProcess := make(chan int, pieces)
	for i := 0; i < pieces; i++ {
		toProcess <- i
	}
	close(toProcess)
	if pieces < workers {
		workers = pieces
	}
	wg := sync.WaitGroup{}
	wg.Add(workers)
	for i := 0; i < workers; i++ {
		go func() {
			defer utilruntime.HandleCrash()
			defer wg.Done()
			for piece := range toProcess {
				select {
				case <-stop:
					return
				default:
					doWorkPiece(piece)
				}
			}
		}()
	}
	wg.Wait()
}
Sort(Refes to helm/helm kind_sorter.go)
...
// KindSortOrder is an ordering of Kinds.
type KindSortOrder []string
// InstallOrder is the order in which manifests should be installed (by Kind).
//
// Those occurring earlier in the list get installed before those occurring later in the list.
var InstallOrder KindSortOrder = []string{
	"Namespace",
	"NetworkPolicy",
	"ResourceQuota",
}
// UninstallOrder is the order in which manifests should be uninstalled (by Kind).
//
// Those occurring earlier in the list get uninstalled before those occurring later in the list.
var UninstallOrder KindSortOrder = []string{
	"APIService",
	"Ingress",
}
// sortByKind does an in-place sort of manifests by Kind.
//
// Results are sorted by 'ordering', keeping order of items with equal kind/priority
func sortByKind(manifests []Manifest, ordering KindSortOrder) []Manifest {
	ks := newKindSorter(manifests, ordering)
	sort.Stable(ks)
	return ks.manifests
}
type kindSorter struct {
	ordering  map[string]int
	manifests []Manifest
}
func newKindSorter(m []Manifest, s KindSortOrder) *kindSorter {
	o := make(map[string]int, len(s))
	for v, k := range s {
		o[k] = v
	}
	return &kindSorter{
		manifests: m,
		ordering:  o,
	}
}
func (k *kindSorter) Len() int { return len(k.manifests) }
func (k *kindSorter) Swap(i, j int) { k.manifests[i], k.manifests[j] = k.manifests[j], k.manifests[i] }
func (k *kindSorter) Less(i, j int) bool {
	a := k.manifests[i]
	b := k.manifests[j]
	first, aok := k.ordering[a.Head.Kind]
	second, bok := k.ordering[b.Head.Kind]
	if !aok && !bok {
		// if both are unknown then sort alphabetically by kind, keep original order if same kind
		if a.Head.Kind != b.Head.Kind {
			return a.Head.Kind < b.Head.Kind
		}
		return first < second
	}
	// unknown kind is last
	if !aok {
		return false
	}
	if !bok {
		return true
	}
	// sort different kinds, keep original order if same priority
	return first < second
}
Golang可变参数(Refes to grpc/grpc-go)
// dialOptions configure a Dial call. dialOptions are set by the DialOption
// values passed to Dial.
type dialOptions struct {
	unaryInt  UnaryClientInterceptor
	streamInt StreamClientInterceptor
    ...
}
// DialOption configures how we set up the connection.
type DialOption interface {
	apply(*dialOptions)
}
// funcDialOption wraps a function that modifies dialOptions into an
// implementation of the DialOption interface.
type funcDialOption struct {
	f func(*dialOptions)
}
func (fdo *funcDialOption) apply(do *dialOptions) {
	fdo.f(do)
}
func newFuncDialOption(f func(*dialOptions)) *funcDialOption {
	return &funcDialOption{
		f: f,
	}
}
// WithWriteBufferSize determines how much data can be batched before doing a
// write on the wire. The corresponding memory allocation for this buffer will
// be twice the size to keep syscalls low. The default value for this buffer is
// 32KB.
//
// Zero will disable the write buffer such that each write will be on underlying
// connection. Note: A Send call may not directly translate to a write.
func WithWriteBufferSize(s int) DialOption {
	return newFuncDialOption(func(o *dialOptions) {
		o.copts.WriteBufferSize = s
	})
}
// WithReadBufferSize lets you set the size of read buffer, this determines how
// much data can be read at most for each read syscall.
//
// The default value for this buffer is 32KB. Zero will disable read buffer for
// a connection so data framer can access the underlying conn directly.
func WithReadBufferSize(s int) DialOption {
	return newFuncDialOption(func(o *dialOptions) {
		o.copts.ReadBufferSize = s
	})
}
AppendIf
func appendIf(actions []action, a action, shouldAppend bool) []action {
	if shouldAppend {
		actions = append(actions, a)
	}
	return actions
}
...
// k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/installer.go:436
actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer, false}, isLister)
actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer, false}, isCreater)
actions = appendIf(actions, action{"DELETECOLLECTION", resourcePath, resourceParams, namer, false}
必须实现某个接口
// Implement ShortNamesProvider
var _ rest.ShortNamesProvider = &REST{}
append正确用法
- 错误使用
// len(dms) = 4(!= 2)
func main() {
    testSlice := []string{"a", "b"}
    dms := make([]string, len(testSlice))
    for _, item := range testSlice {
        dms = append(dms, item)
    }
}
- 正确使用1
// len(dms) = 2
func main() {
    testSlice := []string{"a", "b"}
    dms := make([]string, 0)
    for _, item := range testSlice {
       	dms = append(dms, item)
    }
}
- 正确使用2(推荐)
// len(dms) = 2
func main() {
    testSlice := []string{"a", "b"}
    var dms []string
    for _, item := range testSlice {
       	dms = append(dms, item)
    }
}
range临时变量踩坑
- 错误使用
type Union struct {
	Id string
	// ...
}
// map[2621754f-c80c-4bd7-a060-66850d2c0ab1:0xc001961d40 7f9e180c-e0bc-45c7-8d11-9507f9a4b087:0xc001961d40 a2997835-c915-477a-b23b-5dc5078a629b:0xc001961d40]
func convertUnion2Map(unionSlice []Union) map[string]Union {
    unionMap := make(map[string]*Union)
    for _, union := range unionSlice {
        unionMap[union.Id] = &union
    }
    return unionMap
}
这种情况map中每个key指向相同的地址,也即range中临时变量产生一次,每次覆盖值
- 正确使用
type Union struct {
	Id string
	// ...
}
func convertUnion2Map(unionSlice []Union) map[string]Union {
    unionMap := make(map[string]*Union)
    for index := range unionSlice {
        unionMap[unionSlice[index].Id] = &unionSlice[index]
    }
    return unionMap
}