golang 等待动画

wait.go
package main

import (
	"fmt"
	"time"
)

func main() {
	spinner(time.Second)
}

func spinner(delay time.Duration) {
	for {
		for _, r := range `-\|/` {
			fmt.Printf("\r%c", r)
			time.Sleep(delay)
		}
	}
}

golang 一旦

正式提供的Context包

Once.go
package sync

import (
	"sync"
	"sync/atomic"
)

type Once struct {
	m    sync.Mutex
	done uint32
}

func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 1 {
		return
	}
	o.m.Lock()
	defer o.m.Unlock()
	if o.done == 0 {
		defer atomic.StoreUint32(&o.done, 1)
		f()
	}
}

golang 互斥

互斥通道

Mutex.go
package main

import "time"

type Mutex struct {
	lock chan struct{}
}

func New() *Mutex {
	mu := &Mutex{lock: make(chan struct{}, 1)}
	mu.lock <- struct{}{}
	return mu
}

func (mu *Mutex) Lock() {
	<-mu.lock
}

func (mu *Mutex) Unlock() {
	select {
	case mu.lock <- struct{}{}:
	default:
		panic("unlock of unlocked mutex")
	}
}

func (mu *Mutex) TryLock() bool {
	select {
	case <-mu.lock:
		return true
	default:
	}
	return false
}

func (mu *Mutex) TryLockWithTimeout(timeout time.Duration) bool {
	select {
	case <-mu.lock:
		return true
	case <-time.After(timeout):
	}
	return false
}

func (mu *Mutex) IsLocked() bool {
	return len(mu.lock) == 0
}

func main() {

}

golang 上下文

正式提供的Context包

Context.go
package context

import (
	"errors"
	"reflect"
	"sync"
	"time"
)

// Context 四个方法
type Context interface {
	Deadline() (deadline time.Time, ok bool)
	Done() <-chan struct{}
	Err() error
	Value(key interface{}) interface{}
}

// 网络等其他领域的定义的 Error 对象
type deadlineExceededError struct{}
func (deadlineExceededError) Error() string   { return "context deadline exceeded" }
func (deadlineExceededError) Timeout() bool   { return true }
func (deadlineExceededError) Temporary() bool { return true }

var (
	Canceled         error = errors.New("context canceled")
	DeadlineExceeded error = deadlineExceededError{}
)

// 定义一个空的实现 Context 类型, 返回的Background TODO 基于此类型
type emptyCtx int

func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
	return
}

func (*emptyCtx) Done() <-chan struct{} {
	return nil
}

func (*emptyCtx) Err() error {
	return nil
}

func (*emptyCtx) Value(key interface{}) interface{} {
	return nil
}

var (
	background = new(emptyCtx)
	todo       = new(emptyCtx)
)

func Background() Context {
	return background
}

func TODO() Context {
	return todo
}

type CancelFunc func()

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	c := newCancelCtx(parent)
	propagateCancel(parent, &c)
	return &c, func() { c.cancel(true, Canceled) }
}

func newCancelCtx(parent Context) cancelCtx {
	return cancelCtx{Context: parent}
}

func propagateCancel(parent Context, child canceler) {
	if parent.Done() == nil {
		// TODO or Background
		return // parent is never canceled
	}
	if p, ok := parentCancelCtx(parent); ok {
		p.mu.Lock()
		if p.err != nil {
			// parent has already been canceled
			child.cancel(false, p.err)
		} else {
			if p.children == nil {
				p.children = make(map[canceler]struct{})
			}
			p.children[child] = struct{}{}
		}
		p.mu.Unlock()
	} else {
		go func() {
			select {
			case <-parent.Done():
				child.cancel(false, parent.Err())
			case <-child.Done():
			}
		}()
	}
}

func parentCancelCtx(parent Context) (*cancelCtx, bool) {
	for {
		switch c := parent.(type) {
		case *cancelCtx:
			return c, true
		case *timerCtx:
			return &c.cancelCtx, true
		case *valueCtx:
			parent = c.Context
		default:
			return nil, false
		}
	}
}

func removeChild(parent Context, child canceler) {
	p, ok := parentCancelCtx(parent)
	if !ok {
		return
	}
	p.mu.Lock()
	if p.children != nil {
		delete(p.children, child)
	}
	p.mu.Unlock()
}

type canceler interface {
	cancel(removeFromParent bool, err error)
	Done() <-chan struct{}
}

// closedchan is a reusable closed channel.
var closedchan = make(chan struct{})

func init() {
	close(closedchan)
}

type cancelCtx struct {
	Context

	mu       sync.Mutex            // protects following fields
	done     chan struct{}         // created lazily, closed by first cancel call
	children map[canceler]struct{} // set to nil by the first cancel call
	err      error                 // set to non-nil by the first cancel call
}

func (c *cancelCtx) Done() <-chan struct{} {
	c.mu.Lock()
	if c.done == nil {
		c.done = make(chan struct{})
	}
	d := c.done
	c.mu.Unlock()
	return d
}

func (c *cancelCtx) Err() error {
	c.mu.Lock()
	err := c.err
	c.mu.Unlock()
	return err
}

func (c *cancelCtx) cancel(removeFromParent bool, err error) {
	if err == nil {
		panic("context: internal error: missing cancel error")
	}
	c.mu.Lock()
	if c.err != nil {
		c.mu.Unlock()
		return // already canceled
	}
	c.err = err
	if c.done == nil {
		c.done = closedchan
	} else {
		close(c.done)
	}
	for child := range c.children {
		// NOTE: acquiring the child's lock while holding parent's lock.
		child.cancel(false, err)
	}
	c.children = nil
	c.mu.Unlock()

	if removeFromParent {
		removeChild(c.Context, c)
	}
}

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
	if cur, ok := parent.Deadline(); ok && cur.Before(d) {
		// The current deadline is already sooner than the new one.
		return WithCancel(parent)
	}
	c := &timerCtx{
		cancelCtx: newCancelCtx(parent),
		deadline:  d,
	}
	propagateCancel(parent, c)
	dur := time.Until(d)
	if dur <= 0 {
		c.cancel(true, DeadlineExceeded) // deadline has already passed
		return c, func() { c.cancel(false, Canceled) }
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err == nil {
		c.timer = time.AfterFunc(dur, func() {
			c.cancel(true, DeadlineExceeded)
		})
	}
	return c, func() { c.cancel(true, Canceled) }
}

// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
	cancelCtx
	timer *time.Timer // Under cancelCtx.mu.

	deadline time.Time
}

func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
	return c.deadline, true
}

func (c *timerCtx) cancel(removeFromParent bool, err error) {
	c.cancelCtx.cancel(false, err)
	if removeFromParent {
		// Remove this timerCtx from its parent cancelCtx's children.
		removeChild(c.cancelCtx.Context, c)
	}
	c.mu.Lock()
	if c.timer != nil {
		c.timer.Stop()
		c.timer = nil
	}
	c.mu.Unlock()
}

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
	return WithDeadline(parent, time.Now().Add(timeout))
}

func WithValue(parent Context, key, val interface{}) Context {
	if key == nil {
		panic("nil key")
	}
	if !reflect.TypeOf(key).Comparable() {
		panic("key is not comparable")
	}
	return &valueCtx{parent, key, val}
}

type valueCtx struct {
	Context
	key, val interface{}
}

func (c *valueCtx) Value(key interface{}) interface{} {
	if c.key == key {
		return c.val
	}
	return c.Context.Value(key)
}

golang 命令

命令工具

Command.go
package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strings"
)

const prompt = "$ "

var (
	noPathError = errors.New("path required.")
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	fmt.Print(prompt)

	for scanner.Scan() {
		fmt.Print(prompt)

		input := scanner.Text()
		if input == "" {
			continue
		}

		args := strings.Split(input, " ")
		command := args[0]
		args = args[1:]
		switch command {
		case "cd":
			if len(args) == 0 {
				panic(noPathError)
			}
			os.Chdir(args[0])
		case "exit":
			os.Exit(0)
		default:
			cmd := exec.Command(command, args...)
			cmd.Stderr = os.Stderr
			cmd.Stdout = os.Stdout

			cmd.Run()
		}
	}

}

golang 端口扫描工具

端口扫描工具

PortScanner.go
package main

import (
	"errors"
	"flag"
	"fmt"
	"net"
	"sync"
	"time"
)

// command param
var (
	host            string
	start           int
	end             int
	timeout         string
	timeoutDuration time.Duration
	pause           string
	pauseDuration   time.Duration
	log             bool
)

// port util
var (
	minPort = 80
	maxPort = 65535
	isPort  = func(port int) bool {
		return port >= minPort && port <= maxPort
	}
)

// custom error
var (
	startPortError                     = fmt.Errorf("starting port out of range (should be between %d and %d)", minPort, maxPort)
	endPortError                       = fmt.Errorf("ending port out of range (should be between %d and %d)", minPort, maxPort)
	endPortIsSmallerThanStartPortError = errors.New("ending port must be greater than starting port")
)

// custom type
type status string

func (s *status) init(isOpened bool) {
	if isOpened {
		*s = "OPENED"
	} else {
		*s = "CLOSED"
	}
}

func (s status) isOpened() bool {
	return s == "OPENED"
}

func (s status) isClosed() bool {
	return s == "CLOSED"
}

// global variable
var (
	openedPorts []int
	wg          sync.WaitGroup
	lock        sync.Mutex
)

func init() {
	flag.StringVar(&host, "host", "localhost", "the host to scan")
	flag.StringVar(&timeout, "timeout", "1000ms", "timeout (e.g. 50ms or 1s)")
	flag.StringVar(&pause, "pause", "1ms", "pause after each scanned port (e.g. 5ms)")
	flag.IntVar(&start, "start", minPort, "the lower end to scan")
	flag.IntVar(&end, "end", maxPort, "the upper end to scan")
	flag.BoolVar(&log, "log", true, "print result when scanning the port (true/false)")
	flag.Parse()

	if !isPort(start) {
		panic(startPortError)
	}
	if !isPort(end) {
		panic(endPortError)
	}
	if end < start {
		panic(endPortIsSmallerThanStartPortError)
	}
	var err error
	timeoutDuration, err = time.ParseDuration(timeout)
	if err != nil {
		panic(err)
	}

	pauseDuration, err = time.ParseDuration(pause)
	if err != nil {
		panic(err)
	}
}

func scan(port int) {
	defer wg.Done()

	conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), timeoutDuration)

	var s status
	s.init(err == nil)

	if log {
		fmt.Printf("scan: %d , status: %v\n", port, s)
	}
	if s.isOpened() {
		if conn != nil {
			conn.Close()
		}

		lock.Lock()
		openedPorts = append(openedPorts, port)
		lock.Unlock()
	}

}

func main() {
	startTime := time.Now()
	for port := start; port <= end; port++ {
		wg.Add(1)
		go scan(port)
		time.Sleep(pauseDuration)
	}
	wg.Wait()

	fmt.Printf("\nscan finished in %v\n", time.Since(startTime))

	if len(openedPorts) == 0 {
		fmt.Println("Ports are off.")
		return
	}

	fmt.Println("Openned ports: ")
	for _, port := range openedPorts {
		fmt.Println(port)
	}
}

golang 合并频道

合并频道

merge.go
package main

import "sync"

func merge(cs ...chan interface{}) <-chan interface{} {
	var wg sync.WaitGroup
	out := make(chan interface{})
	output := func(c <-chan interface{}) {
		for e := range c {
			out <- e
		}
		wg.Done()
	}
	wg.Add(len(cs))
	for _, c := range cs {
		go output(c)
	}
	go func() {
		wg.Wait()
		close(out)
	}()

	return out
}

golang Gen频道

发电机

gen.go
package main

func gen() <-chan int {
	out := make(chan int)
	go func() {
		i := 0
		for {
			out <- i
			i++
		}
	}()

	return out
}

golang 活动对象

设计模式 - 活动对象

ActiveObject.go
package main

import "fmt"

type MethodRequest int

const (
	Incr MethodRequest = iota
	Decr
)

type Service struct {
	queue chan MethodRequest
	v     int
}

func (s *Service) Incr() {
	s.queue <- Incr
}

func (s *Service) Decr() {
	s.queue <- Decr
}

func (s *Service) schedule() {
	for r := range s.queue {
		if r == Incr {
			s.v++
		} else if r == Decr {
			s.v--
		}
	}
}

func New(buffer int) *Service {
	s := &Service{
		queue: make(chan MethodRequest, buffer),
	}
	go s.schedule()
	return s
}

func main() {
	s := New(0)
	s.Incr()
	s.Decr()
	s.Decr()
	fmt.Println(s.v)
}

golang 证书

使用指定IAM角色的KEY

credentials.go
func main() {
	creds := credentials.NewStaticCredentials(
		"ID",
		"SECRET",
		"",
	)
	session, err := session.NewSession(&aws.Config{
		Region:      aws.String("region_name1"),
		Credentials: creds,
	})
	if err != nil {
		panic(err)
	}

	svc := sqs.New(session)
	fmt.Println(svc.ListQueues(nil))
}