golang 使用去并发下载图片资源来源:HTTP://my.oschina.net/qbit/blog/189928

使用去并发下载图片资源来源:HTTP://my.oschina.net/qbit/blog/189928

gistfile1.go
package img

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
)

const (
	DataRoot     = "./tmp/" // 存放封面图的根目录
	TimeoutLimit = 5        // 设置超时时间
)

// 表示章节ID和封面URL的对应关系
type VolumeCover struct {
	VolumeID int
	Url      string
	Lock     sync.Mutex
	Msg      chan string
}

// 将图片下载并保存到本地
func SaveImage(vc *VolumeCover) {
	res, err := http.Get(vc.Url)
	defer res.Body.Close()
	if err != nil {
		vc.Msg <- (strconv.Itoa(vc.VolumeID) + " HTTP_ERROR")
	}
	// 创建文件
	dst, err := os.Create(DataRoot + strconv.Itoa(vc.VolumeID) + ".jpg")
	if err != nil {
		vc.Msg <- (strconv.Itoa(vc.VolumeID) + " OS_ERROR")
	}
	// 生成文件
	_, err := io.Copy(dst, res.Body)
	if err != nil {
		vc.Msg <- (strconv.Itoa(vc.VolumeID) + " COPY_ERROR")
	}
	// goroutine通信
	vc.Lock.Lock()
	vc.Msg <- "in"
	vc.Lock.Unlock()
}

func Start(name string, password string, limit int) error {
	runtime.GOMAXPROCS(4)
	sl, err := sql.Open("mysql", name+":"+password+"@/xxx?charset=utf8")
	defer sl.Close()
	if err != nil {
		return err
	}
	// 构造SELECT语句并检索
	queryStr := "SELECT VolumeID, ImageUrl FROM volume "
	if limit > 0 {
		queryStr += "limit " + strconv.Itoa(limit)
	}
	rows, err := sl.Query(queryStr)
	defer rows.Close()
	if err != nil {
		return err
	}

	// 构建列表
	i := 0
	vclist := make([]*VolumeCover, limit)
	for rows.Next() {
		vc := &VolumeCover{}
		if err := rows.Scan(&vc.VolumeID, &vc.Url); err != nil {
			return err
		}
		vc.Msg = make(chan string, 1)
		vc.Lock = *new(sync.Mutex)
		vclist[i] = vc
		i++
	}

	// start goroutines
	for i := 0; i < len(vclist); i++ {
		go SaveImage(vclist[i])
		// set timeout
		go func(i int) {
			time.Sleep(TimeoutLimit * time.Second)
			vclist[i].Lock.Lock()
			vclist[i].Msg <- "out"
			vclist[i].Lock.Unlock()
		}(i)
	}

	// 阻塞地获取结果
	for i := 0; i < len(vclist); i++ {
		func(c *VolumeCover) {
			select {
			case <-c.Msg:
				if m, ok := <-c.Msg; ok {
					fmt.Println(m)
				}
				close(c.Msg)
			}
		}(vclist[i])
	}
	return nil
}

golang GoLang使用AES加密将字符串加密到base64,反之亦然。

GoLang使用AES加密将字符串加密到base64,反之亦然。

main.go
package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
)

func main() {
	originalText := "encrypt this golang"
	fmt.Println(originalText)

	key := []byte("example key 1234")

	// encrypt value to base64
	cryptoText := encrypt(key, originalText)
	fmt.Println(cryptoText)

	// encrypt base64 crypto to original value
	text := decrypt(key, cryptoText)
	fmt.Printf(text)
}

// encrypt string to base64 crypto using AES
func encrypt(key []byte, text string) string {
	// key := []byte(keyText)
	plaintext := []byte(text)

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}

	stream := cipher.NewCFBEncrypter(block, iv)
	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

	// convert to base64
	return base64.URLEncoding.EncodeToString(ciphertext)
}

// decrypt from base64 to decrypted string
func decrypt(key []byte, cryptoText string) string {
	ciphertext, _ := base64.URLEncoding.DecodeString(cryptoText)

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	if len(ciphertext) < aes.BlockSize {
		panic("ciphertext too short")
	}
	iv := ciphertext[:aes.BlockSize]
	ciphertext = ciphertext[aes.BlockSize:]

	stream := cipher.NewCFBDecrypter(block, iv)

	// XORKeyStream can work in-place if the two arguments are the same.
	stream.XORKeyStream(ciphertext, ciphertext)

	return fmt.Sprintf("%s", ciphertext)
}

golang GoLang使用AES加密将字符串加密到base64,反之亦然。

GoLang使用AES加密将字符串加密到base64,反之亦然。

main.go
package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
)

func main() {
	originalText := "encrypt this golang"
	fmt.Println(originalText)

	key := []byte("example key 1234")

	// encrypt value to base64
	cryptoText := encrypt(key, originalText)
	fmt.Println(cryptoText)

	// encrypt base64 crypto to original value
	text := decrypt(key, cryptoText)
	fmt.Printf(text)
}

// encrypt string to base64 crypto using AES
func encrypt(key []byte, text string) string {
	// key := []byte(keyText)
	plaintext := []byte(text)

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}

	stream := cipher.NewCFBEncrypter(block, iv)
	stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

	// convert to base64
	return base64.URLEncoding.EncodeToString(ciphertext)
}

// decrypt from base64 to decrypted string
func decrypt(key []byte, cryptoText string) string {
	ciphertext, _ := base64.URLEncoding.DecodeString(cryptoText)

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	if len(ciphertext) < aes.BlockSize {
		panic("ciphertext too short")
	}
	iv := ciphertext[:aes.BlockSize]
	ciphertext = ciphertext[aes.BlockSize:]

	stream := cipher.NewCFBDecrypter(block, iv)

	// XORKeyStream can work in-place if the two arguments are the same.
	stream.XORKeyStream(ciphertext, ciphertext)

	return fmt.Sprintf("%s", ciphertext)
}

golang 表明我们可以在不访问源代码的情况下获取调用者的函数名称。 go build -o test show_caller_name.go; rm show_caller_name.go

表明我们可以在不访问源代码的情况下获取调用者的函数名称。 go build -o test show_caller_name.go; rm show_caller_name.go; 。/测试

show_caller_name.go
package main

import (
	"bytes"
	"fmt"
	"runtime"
)

var (
	dunno     = []byte("???")
	centerDot = []byte("·")
	dot       = []byte(".")
)

func function(pc uintptr) []byte {
	fn := runtime.FuncForPC(pc)
	if fn == nil {
		return dunno
	}
	name := []byte(fn.Name())
	// The name includes the path name to the package, which is unnecessary
	// since the file name is already included.  Plus, it has center dots.
	// That is, we see
	//      runtime/debug.*T·ptrmethod
	// and want
	//      *T.ptrmethod
	if period := bytes.Index(name, dot); period >= 0 {
		name = name[period+1:]
	}
	name = bytes.Replace(name, centerDot, dot, -1)
	return name
}

func showCallerName() {
	pc, _, _, ok := runtime.Caller(1)
	if !ok {
		fmt.Printf("runtime.Caller() failed\n")
		return
	}
	name := function(pc)
	fmt.Printf("Caller name: %s\n", name)
}

func myCaller() {
	showCallerName()
}

func main() {
	myCaller()
}

golang 显示无法将数据附加到gob文件 - 当尝试解码写入的数据时,解码器将使用“缓冲区中的额外数据”错误输出

显示一个人无法将数据附加到gob文件 - 当尝试解码由append写入的数据时,解码器将使用“缓冲区中的额外数据”错误输出。

test_gob.go
package main

import (
	"encoding/gob"
	"fmt"
	"io"
	"log"
	"math/rand"
	"os"
)

type Struct1 struct {
	IntVal    int
	StringVal string
}

func PathExists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

func readData(filePath string) {
	if !PathExists(filePath) {
		return
	}
	file, err := os.Open(filePath)
	defer file.Close()
	if err != nil {
		log.Fatalf("os.Open('%s') failed with '%s'\n", filePath, err.Error())
	}
	dec := gob.NewDecoder(file)
	var val Struct1
	for {
		err = dec.Decode(&val)
		if err != nil {
			if err == io.EOF {
				return
			}
			log.Fatalf("dec.Decode() failed with '%s'\n", err.Error())
		}
		fmt.Printf("Read: %#v\n", val)
	}
}

func addData(filePath string) {
	file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("os.OpenFile('%s') failed with '%s'\n", filePath, err.Error())
	}
	defer file.Close()
	enc := gob.NewEncoder(file)
	for i := 0; i < 1; i++ {
		n := int(rand.Intn(65536))
		v := Struct1{
			IntVal:    n,
			StringVal: fmt.Sprintf("%d", n),
		}
		err = enc.Encode(v)
		if err != nil {
			log.Fatalf("enc.Encode() failed with '%s'\n", err.Error())
		}
		fmt.Printf("Wrote: %#v\n", v)
	}
}

func main() {
	filePath := "test.gob"
	readData(filePath)
	addData(filePath)
	readData(filePath)
	addData(filePath)
	readData(filePath)
}

golang 使用github.com/cznic/ql进行测试

使用github.com/cznic/ql进行测试

test_ql.go
package main

import (
	"fmt"
	"github.com/cznic/ql"
	"log"
	"math/rand"
	"os"
	"sort"
)

var (
	stmCreateTables = ql.MustCompile(`
BEGIN TRANSACTION;
CREATE TABLE products (
	n int32,
	s string
);
COMMIT;`)
	stmInsertIntStr = ql.MustCompile(`INSERT INTO products VALUES($1, $2);`)
	stmCount        = ql.MustCompile(`SELECT count() FROM products;`)
	stmSelect       = ql.MustCompile(`SELECT n, s FROM products ORDER BY n;`)
)

func genRandomIntStr() (int32, string) {
	n := rand.Int31()
	return int32(n), fmt.Sprintf("s_%d", n)
}

func PathExists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

type IntStr struct {
	n int32
	s string
}

type IntStrByInt []IntStr

func (s IntStrByInt) Len() int {
	return len(s)
}
func (s IntStrByInt) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s IntStrByInt) Less(i, j int) bool {
	return s[i].n < s[j].n
}

var (
	insertedData []IntStr
)

func getCountFromRecordSet(rs []ql.Recordset) int {
	var count int64
	err := rs[0].Do(false, func(rec []interface{}) (bool, error) {
		//fmt.Printf("rec: %#v\n", rec)
		count = rec[0].(int64)
		return true, nil
	})
	if err != nil {
		log.Fatalf("rs[0].Do() failed with '%s'\n", err.Error())
	}
	fmt.Printf("count: %d\n", count)
	return int(count)
}

func verifyCount(db *ql.DB) {
	rs, _, err := db.Execute(ql.NewRWCtx(), stmCount)
	if err != nil {
		log.Fatalf("db.Execute('%s') failed with '%s'\n", stmCount, err.Error())
	}

	//fmt.Printf("len(rs) for stmCount: %d\n", len(rs))
	nRecords := getCountFromRecordSet(rs)
	if nRecords != 1024 {
		log.Fatalf("nRecords (%d) != 1024\n", nRecords)
	}
}

func readDatabase(db *ql.DB) []IntStr {
	verifyCount(db)
	rs, _, err := db.Execute(ql.NewRWCtx(), stmSelect)
	if err != nil {
		log.Fatalf("db.Execute('%s') failed with '%s'\n", stmSelect, err.Error())
	}
	var res []IntStr
	for _, rec := range rs {
		err := rec.Do(false, func(fields []interface{}) (bool, error) {
			n := fields[0].(int32)
			s := fields[1].(string)
			res = append(res, IntStr{n: n, s: s})
			return true, nil
		})
		if err != nil {
			log.Fatalf("rec.Do() failed with '%s'\n", err.Error())
		}
	}
	fmt.Printf("len(res) = %d\n", len(res))
	return res
}

func populateDatabase(db *ql.DB) {
	_, _, err := db.Execute(ql.NewRWCtx(), stmCreateTables)
	if err != nil {
		log.Fatalf("db.Execute('%s') failed with '%s'\n", stmCreateTables, err.Error())
	}
	rand.Seed(666) // ensure consistent numbers
	ctx := ql.NewRWCtx()
	_, _, err = db.Run(ctx, "BEGIN TRANSACTION;")
	if err != nil {
		log.Fatalf("db.Run('%s') failed with '%s'\n", "BEGIN TRANSACTION;", err.Error())
	}
	for i := 0; i < 1024; i++ {
		n, s := genRandomIntStr()
		_, _, err = db.Execute(ctx, stmInsertIntStr, n, s)
		if err != nil {
			log.Fatalf("db.Execute('%s') failed with '%s'\n", stmInsertIntStr, err.Error())
		}
		el := IntStr{n: n, s: s}
		insertedData = append(insertedData, el)
	}

	_, _, err = db.Run(ctx, "COMMIT;")
	if err != nil {
		log.Fatalf("db.Run('%s') failed with '%s'\n", "COMMIT;", err.Error())
	}
}

func generateInsertedData() {
	rand.Seed(666) // ensure consistent numbers
	for i := 0; i < 1024; i++ {
		n, s := genRandomIntStr()
		el := IntStr{n: n, s: s}
		insertedData = append(insertedData, el)
	}
}

func verifySelectResults(res []IntStr) {
	for i, el := range res {
		//fmt.Printf("i=%d\n", i)
		expected := insertedData[i]
		if el.n != expected.n {
			log.Fatalf("i=%d, el.n != expected.n (%d != %d)\n", i, el.n, expected.n)
		}
		if el.s != expected.s {
			log.Fatalf("i=%d, el.s != expected.s (%s != %s)\n", i, el.s, expected.s)
		}
	}
	fmt.Printf("select results verified!\n")
}

func dumpFirst(n int, arr []IntStr) {
	if n > len(arr) {
		n = len(arr)
	}
	for i := 0; i < n; i++ {
		el := arr[i]
		fmt.Printf("n=%d, s=%s\n", el.n, el.s)
	}
}

func main() {
	fmt.Printf("Testing ql database.\n")
	dbFileName := "test.db"
	exists := PathExists(dbFileName)
	opt := &ql.Options{CanCreate: true}
	db, err := ql.OpenFile(dbFileName, opt)
	if err != nil {
		log.Fatalf("ql.OpenFile() failed with '%s'\n", err.Error())
	}
	defer db.Close()
	if !exists {
		populateDatabase(db)
	} else {
		generateInsertedData()
	}

	res := readDatabase(db)

	//dumpFirst(10, res)
	//fmt.Printf("\n")

	//dumpFirst(10, insertedData)
	//fmt.Printf("\n")

	sort.Sort(IntStrByInt(insertedData))
	//dumpFirst(10, insertedData)
	//fmt.Printf("\n")

	verifySelectResults(res)
}

golang Goroutine安全模式使用通道来抽象使用频道。

Goroutine安全模式使用通道来抽象使用频道。

goroutine-channel-pattern.go
/*
  A Goroutine safe pattern using channels that abstracts away the channels

  This is a concept of creating a goroutine safe object that uses channels under the covers to communicate
  with the internal map[string]string structure.  I know that typically this kind of solution may done
  with mutexes but the excercise was in using channels on purpose although they are heavier.

  Note a couple of points:

  - When using channels, you can still build a public-facing api that nicely abstracts them away, therefore
    somemone using this api for example, doesn't have to understand the paradigm of communicating over channels
  - This example is just a prototype, as an example
  - Notice that all state is mutated internal to the Db's f function
  - Notice that in the Fetch method there is bi-directional communication a send/receive
*/

package main

import "fmt"
import "time"

type KeyValue struct {
	Key   string
	Value string
	Reply chan KeyValue
}

type Db struct {
	db           map[string]string
	storeChannel chan KeyValue
	fetchChannel chan KeyValue
}

func NewDb() *Db {
	d := &Db{}

	d.db = make(map[string]string)

	d.storeChannel = make(chan KeyValue)
	d.fetchChannel = make(chan KeyValue)

	go func() {

		for {
			select {
			case storeValue := <-d.storeChannel:
				d.internalStore(storeValue)

			case fetchKey := <-d.fetchChannel:
				fetchKey.Reply <- d.internalFetch(fetchKey)
			}
		}
	}()

	return d
}

func (d *Db) internalFetch(kv KeyValue) KeyValue {
	v, ok := d.db[kv.Key]
	if ok {
		return KeyValue{Key: kv.Key, Value: v}
	}
	return KeyValue{Key: kv.Key}
}

func (d *Db) internalStore(kv KeyValue) {
	d.db[kv.Key] = kv.Value
	fmt.Println("Just stored: ", kv)
}

func (d *Db) Fetch(key string) KeyValue {

	ch := make(chan KeyValue)
	d.fetchChannel <- KeyValue{Key: key, Reply: ch}

	return <-ch
}

func (d *Db) Store(key string, value string) {

	d.storeChannel <- KeyValue{Key: key, Value: value}
}

func main() {

	myDb := NewDb()

	//myDb can safely be used by many goroutines although in this example it's only used by the main goroutine.
	myDb.Store("id-3383", "John")
	myDb.Store("id-2218", "Ralph")
	myDb.Store("id-7741", "Amy")

	//simulate some time has gone by
	time.Sleep(time.Second * 1)

	fmt.Println(myDb.Fetch("id-3383"))
	fmt.Println(myDb.Fetch("id-7741"))

	//not found returns a KeyValue with an empty value
	fmt.Println(myDb.Fetch("id-9965"))


	var s string
	fmt.Scanln(&s)
}

golang hiromi.go

hiromi.go
package main
import "fmt"
func main () {
	// 見つめ合う
	fmt.Println("おっくせんまん!おっくせんまん!!")
	defer fmt.Println("ジャパン!!!");
}

golang test.go

test.go
package main

import (
	"fmt"
)

func main() {
	var a = new(myType)
	a.set(10)
	f(a)
}

type myInterface interface {
	get() int
	set(i int)
}

func  f(i myInterface) {
	fmt.Println(i.get())
}

type myType struct {
	i	int
}

func (p *myType) get() int {
	return p.i
}

func (p *myType) set(i int) {
	p.i = i
}

golang 去我的博客文章中间件样本。 http://justinas.org/writing-http-middleware-in-go/

去我的博客文章中间件样本。 http://justinas.org/writing-http-middleware-in-go/

4_modifier.go
package main

import (
	"net/http"
	"net/http/httptest"
)

type ModifierMiddleware struct {
	handler http.Handler
}

func (m *ModifierMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	rec := httptest.NewRecorder()
	// passing a ResponseRecorder instead of the original RW
	m.handler.ServeHTTP(rec, r)
	// after this finishes, we have the response recorded
	// and can modify it before copying it to the original RW

	// we copy the original headers first
	for k, v := range rec.Header() {
		w.Header()[k] = v
	}
	// and set an additional one
	w.Header().Set("X-We-Modified-This", "Yup")
	// only then the status code, as this call writes the headers as well 
	w.WriteHeader(418)
        // The body hasn't been written (to the real RW) yet,
        // so we can prepend some data.
        data := []byte("Middleware says hello again. ")

        // But the Content-Length might have been set already,
        // we should modify it by adding the length
        // of our own data.
        // Ignoring the error is fine here:
        // if Content-Length is empty or otherwise invalid,
        // Atoi() will return zero,
        // which is just what we'd want in that case.
        clen, _ := strconv.Atoi(r.Header.Get("Content-Length"))
        clen += len(data)
        w.Header.Set("Content-Length", strconv.Itoa(clen))

        // finally, write out our data
        w.Write(data)
	// then write out the original body
	w.Write(rec.Body.Bytes())
}

func myHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Success!"))
}

func main() {
	mid := &ModifierMiddleware{http.HandlerFunc(myHandler)}

	println("Listening on port 8080")
	http.ListenAndServe(":8080", mid)
}
3_append.go
package main

import (
	"net/http"
)

type AppendMiddleware struct {
	handler http.Handler
}

func (a *AppendMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.handler.ServeHTTP(w, r)
	w.Write([]byte("<!-- Middleware says hello! -->"))
}

func myHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Success!"))
}

func main() {
	mid := &AppendMiddleware{http.HandlerFunc(myHandler)}

	println("Listening on port 8080")
	http.ListenAndServe(":8080", mid)
}
2_singlehost_closure.go
package main

import (
	"net/http"
)

func SingleHost(handler http.Handler, allowedHost string) http.Handler {
	ourFunc := func(w http.ResponseWriter, r *http.Request) {
		host := r.Host
		if host == allowedHost {
			handler.ServeHTTP(w, r)
		} else {
			w.WriteHeader(403)
		}
	}
	return http.HandlerFunc(ourFunc)
}


func myHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Success!"))
}

func main() {
	single := SingleHost(http.HandlerFunc(myHandler), "example.com")

	println("Listening on port 8080")
	http.ListenAndServe(":8080", single)
}
1_singlehost.go
package main

import (
	"net/http"
)

type SingleHost struct {
	handler     http.Handler
	allowedHost string
}

func NewSingleHost(handler http.Handler, allowedHost string) *SingleHost {
	return &SingleHost{handler: handler, allowedHost: allowedHost}
}

func (s *SingleHost) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	host := r.Host
	if host == s.allowedHost {
		s.handler.ServeHTTP(w, r)
	} else {
		w.WriteHeader(403)
	}
}

func myHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Success!"))
}

func main() {
	single := NewSingleHost(http.HandlerFunc(myHandler), "example.com")

	println("Listening on port 8080")
	http.ListenAndServe(":8080", single)
}