golang实战技巧

| 分类 技术  | 标签 Golang 

前言

本文总结工作过程中对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
}

Refs


上一篇     下一篇