Go基础学习笔记(二):错误处理和资源管理、Goroutine、Channel、迷宫的广度优先搜索、http及其他标准库

7、错误处理和资源管理

1)、defer调用

1)defer调用

  • 确保调用在函数结束时发生
  • 参数在defer语句时计算
  • defer列表为后进先出
func tryDefer() {
    
    
	defer fmt.Println(1)
	defer fmt.Println(2)
	panic("error occurred")
}

func main() {
    
    
	tryDefer() // 2 1
}
func writeFile(filename string) {
    
    
	file, err := os.Create(filename)
	if err != nil {
    
    
		panic(err)
	}
	defer file.Close()
	
	writer := bufio.NewWriter(file)
	defer writer.Flush()
	fmt.Fprintf(writer, "hello world")
}

func main() {
    
    
	writeFile("hello world.txt")
}
func tryDefer() {
    
    
	for i := 0; i < 100; i++ {
    
    
		defer fmt.Println(i) // 30 29 28 ...
		if i == 30 {
    
    
			panic("printed too many")
		}
	}
}

func main() {
    
    
	tryDefer()
}

2)何时使用defer调用

  • Open/Close
  • Lock/Unlock
  • PrintHeader/PrintFooter

2)、错误处理概念

func writeFile(filename string) {
    
    
	file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666) // 文件已存在时报错
	if err != nil {
    
    
		if pathError, ok := err.(*os.PathError); !ok {
    
    
			panic(err)
		} else {
    
    
			fmt.Println(pathError.Op, pathError.Path, pathError.Err) // open hello world.txt file exists
		}
		return
	}
	defer file.Close()

	writer := bufio.NewWriter(file)
	defer writer.Flush()
	fmt.Fprintf(writer, "hello world")
}

func main() {
    
    
	writeFile("hello world.txt")
}

3)、服务器统一出错处理

package filelisting

import (
	"io/ioutil"
	"net/http"
	"os"
)

func HandleFileListing(writer http.ResponseWriter, request *http.Request) error {
    
    
	path := request.URL.Path[len("/list/"):]
	file, err := os.Open(path)
	defer file.Close()
	if err != nil {
    
    
		return err
	}
	all, err := ioutil.ReadAll(file)
	if err != nil {
    
    
		return err
	}
	writer.Write(all)
	return nil
}
package main

import (
	"learngo/errhandling/filelistingserver/filelisting"
	"log"
	"net/http"
	"os"
)

type appHandler func(writer http.ResponseWriter, request *http.Request) error

func errWrapper(handler appHandler) func(writer http.ResponseWriter, request *http.Request) {
    
    
	return func(writer http.ResponseWriter, request *http.Request) {
    
    
		err := handler(writer, request)
		if err != nil {
    
    
			log.Printf("Error handling request:%s\n", err.Error())
			code := http.StatusOK
			switch {
    
    
			case os.IsNotExist(err):
				code = http.StatusNotFound
			case os.IsPermission(err):
				code = http.StatusForbidden
			default:
				code = http.StatusInternalServerError
			}
			http.Error(writer, http.StatusText(code), code)
		}
	}
}

func main() {
    
    
	http.HandleFunc("/list/", errWrapper(filelisting.HandleFileListing))
	err := http.ListenAndServe(":8888", nil)
	if err != nil {
    
    
		panic(err)
	}
}

项目根目录下有hello world.txt,访问http://localhost:8888/list/hello%20world.txt,返回hello world

访问http://localhost:8888/list/,返回Not Found

拷贝一个hello world2.txt文件,修改访问权限

sudo cp hello\ world.txt hello\ world2.txt 
sudo chmod 500 hello\ world2.txt

访问http://localhost:8888/list/hello%20world2.txt,返回Forbidden

4)、panic和recover

1)panic

  • 停止当前函数执行
  • 一直向上返回,执行每一层的defer
  • 如果没有遇见recover,程序退出

2)recover

  • 仅在defer调用中使用
  • 获取panic的值
  • 如果无法处理,可重新panic
func tryRecover() {
    
    
	defer func() {
    
    
		r := recover()
		if err, ok := r.(error); ok {
    
    
			fmt.Println("Error occurred:", err)
		} else {
    
    
			panic(r)
		}
	}()
	panic(errors.New("this is an error"))
}

func main() {
    
    
	tryRecover()
}

5)、服务器统一出错处理2

package filelisting

import (
	"io/ioutil"
	"net/http"
	"os"
	"strings"
)

const prefix = "/list/"

type userError string

func (e userError) Error() string {
    
    
	return e.Message()
}

func (e userError) Message() string {
    
    
	return string(e)
}

func HandleFileListing(writer http.ResponseWriter, request *http.Request) error {
    
    
	if strings.Index(request.URL.Path, prefix) != 0 {
    
    
		return userError("Path must start with " + prefix)
	}
	path := request.URL.Path[len(prefix):]
	file, err := os.Open(path)
	defer file.Close()
	if err != nil {
    
    
		return err
	}
	all, err := ioutil.ReadAll(file)
	if err != nil {
    
    
		return err
	}
	writer.Write(all)
	return nil
}
package main

import (
	"learngo/errhandling/filelistingserver/filelisting"
	"log"
	"net/http"
	"os"
)

type appHandler func(writer http.ResponseWriter, request *http.Request) error

func errWrapper(handler appHandler) func(writer http.ResponseWriter, request *http.Request) {
    
    
	return func(writer http.ResponseWriter, request *http.Request) {
    
    
		defer func() {
    
    
			if r := recover(); r != nil {
    
    
				log.Printf("Panic: %v", r)
				http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
			}
		}()

		err := handler(writer, request)
		if err != nil {
    
    
			log.Printf("Error handling request:%s\n", err.Error())
			if userErr, ok := err.(userError); ok {
    
    
				http.Error(writer, userErr.Message(), http.StatusBadRequest)
				return
			}
			code := http.StatusOK
			switch {
    
    
			case os.IsNotExist(err):
				code = http.StatusNotFound
			case os.IsPermission(err):
				code = http.StatusForbidden
			default:
				code = http.StatusInternalServerError
			}
			http.Error(writer, http.StatusText(code), code)
		}
	}
}

type userError interface {
    
    
	error
	Message() string
}

func main() {
    
    
	http.HandleFunc("/", errWrapper(filelisting.HandleFileListing))
	err := http.ListenAndServe(":8888", nil)
	if err != nil {
    
    
		panic(err)
	}
}

访问http://localhost:8888/,返回Path must start with /list/

error vs panic:

  • 意料之中的:使用error。如:文件打不开
  • 意料之外的:使用panic。如:数组越界

8、Goroutine

1)、goroutine

  • 轻量级线程
  • 非抢占式多任务处理,由协程主动交出控制权
  • 编译器/解释器/虚拟机层面的多任务
  • 多个协程可能在一个或多个线程上运行
func main() {
    
    
	for i := 0; i < 10; i++ {
    
    
		go func(i int) {
    
    
			for {
    
    
				fmt.Printf("Hello from goroutine %d\n", i)
			}
		}(i)
	}
	time.Sleep(time.Millisecond)
}
func main() {
    
    
	var a [10]int
	for i := 0; i < 10; i++ {
    
    
		go func() {
    
    
			for {
    
    
				a[i]++
				runtime.Gosched()
			}
		}()
	}
	time.Sleep(time.Millisecond)
}

执行报错:

panic: runtime error: index out of range [10] with length 10

检测数据访问冲突:

go run -race goroutine.go

2)、Go语言的调度器

1)协程coroutine

子程序是协程的一个特例

2)goroutine

3)goroutine的定义

  • 任何函数只需加上go就能送给调度器运行
  • 不需要在定义时区分是否是异步函数
  • 调度器在合适点进行切换
  • 使用-race来检测数据访问冲突

4)goroutine可能的切换点

  • I/O,select
  • channel
  • 等待锁
  • 函数调用(有时)
  • runtime.Gosched()

只是参考,不能保证切换,不能保证在其他地方不切换

10、Channel

1)、channel

func channelDemo() {
    
    
	c := make(chan int)
	go func() {
    
    
		for {
    
    
			n := <-c
			fmt.Println(n)
		}
	}()
	c <- 1
	c <- 2
	time.Sleep(time.Second)
}

func main() {
    
    
	channelDemo()
}

channel数组:

func worker(id int, c chan int) {
    
    
	for {
    
    
		fmt.Printf("Worker %d received %c\n", id, <-c)
	}
}

func channelDemo() {
    
    
	var channels [10]chan int
	for i := 0; i < 10; i++ {
    
    
		channels[i] = make(chan int)
		go worker(i, channels[i])
	}
	for i := 0; i < 10; i++ {
    
    
		channels[i] <- 'a' + i
	}
	for i := 0; i < 10; i++ {
    
    
		channels[i] <- 'A' + i
	}
	time.Sleep(time.Second)
}

func main() {
    
    
	channelDemo()
}

channel作为返回值:

func createWorker(id int) chan<- int {
    
    
	c := make(chan int)
	go func() {
    
    
		for {
    
    
			fmt.Printf("Worker %d received %c\n", id, <-c)
		}
	}()
	return c
}

func channelDemo() {
    
    
	var channels [10]chan<- int
	for i := 0; i < 10; i++ {
    
    
		channels[i] = createWorker(i)
	}
	for i := 0; i < 10; i++ {
    
    
		channels[i] <- 'a' + i
	}
	for i := 0; i < 10; i++ {
    
    
		channels[i] <- 'A' + i
	}
	time.Sleep(time.Second)
}

func main() {
    
    
	channelDemo()
}

channel缓冲区:

func worker(id int, c chan int) {
    
    
	for {
    
    
		fmt.Printf("Worker %d received %c\n", id, <-c)
	}
}

func bufferedChannel() {
    
    
	c := make(chan int, 3)
	go worker(0, c)
	c <- 'a'
	c <- 'b'
	c <- 'c'
	time.Sleep(time.Second)
}

func main() {
    
    
	bufferedChannel()
}

关闭channel:

func worker(id int, c chan int) {
    
    
	for n := range c {
    
    
		fmt.Printf("Worker %d received %c\n", id, n)
	}
}

func channelClose() {
    
    
	c := make(chan int, 3)
	go worker(0, c)
	c <- 'a'
	c <- 'b'
	c <- 'c'
	close(c)
	time.Sleep(time.Second)
}

func main() {
    
    
	channelClose()
}
  • channel
  • buffered channel
  • range

2)、使用Channel等待任务结束

type worker struct {
    
    
	in   chan int
	done chan bool
}

func doWorker(id int, c chan int, done chan bool) {
    
    
	for n := range c {
    
    
		fmt.Printf("Worker %d received %c\n", id, n)
		done <- true
	}
}

func createWorker(id int) worker {
    
    
	w := worker{
    
    
		in:   make(chan int),
		done: make(chan bool),
	}
	go doWorker(id, w.in, w.done)
	return w
}

func channelDemo() {
    
    
	var workers [10]worker
	for i := 0; i < 10; i++ {
    
    
		workers[i] = createWorker(i)
	}
	for i, worker := range workers {
    
    
		worker.in <- 'a' + i
	}
	for _, worker := range workers {
    
    
		<-worker.done
	}
	for i, worker := range workers {
    
    
		worker.in <- 'A' + i
	}
	for _, worker := range workers {
    
    
		<-worker.done
	}
}

func main() {
    
    
	channelDemo()
}

使用WaitGroup实现:

type worker struct {
    
    
	in   chan int
	done func()
}

func doWorker(id int, w worker) {
    
    
	for n := range w.in {
    
    
		fmt.Printf("Worker %d received %c\n", id, n)
		w.done()
	}
}

func createWorker(id int, wg *sync.WaitGroup) worker {
    
    
	w := worker{
    
    
		in: make(chan int),
		done: func() {
    
    
			wg.Done()
		},
	}
	go doWorker(id, w)
	return w
}

func channelDemo() {
    
    
	var wg sync.WaitGroup
	wg.Add(20)
	var workers [10]worker
	for i := 0; i < 10; i++ {
    
    
		workers[i] = createWorker(i, &wg)
	}
	for i, worker := range workers {
    
    
		worker.in <- 'a' + i
	}
	for i, worker := range workers {
    
    
		worker.in <- 'A' + i
	}
	wg.Wait()
}

func main() {
    
    
	channelDemo()
}

3)、使用Channel进行树的遍历

package node

import "fmt"

type TreeNode struct {
    
    
	Value       int
	Left, Right *TreeNode
}

func (node TreeNode) Print() {
    
    
	fmt.Print(node.Value, " ")
}

func CreateNode(value int) *TreeNode {
    
    
	return &TreeNode{
    
    Value: value}
}
package node

import (
	"fmt"
)

func (node *TreeNode) Traverse() {
    
    
	node.TraverseFunc(func(node *TreeNode) {
    
    
		node.Print()
	})
	fmt.Println()
}

// 中序遍历
func (node *TreeNode) TraverseFunc(f func(*TreeNode)) {
    
    
	if node == nil {
    
    
		return
	}
	node.Left.TraverseFunc(f)
	f(node)
	node.Right.TraverseFunc(f)
}

func (node *TreeNode) TraverseWithChannel() chan *TreeNode {
    
    
	out := make(chan *TreeNode)
	go func() {
    
    
		node.TraverseFunc(func(node *TreeNode) {
    
    
			out <- node
		})
		close(out)
	}()
	return out
}
package main

import (
	"fmt"
	"learngo/tree/node"
)

func main() {
    
    
	root := node.CreateNode(3)
	root.Left = node.CreateNode(0)
	root.Right = node.CreateNode(5)
	root.Right.Left = node.CreateNode(4)
	root.Left.Right = node.CreateNode(2)
	root.Traverse() // 0 2 3 4 5
	nodeCount := 0
	root.TraverseFunc(func(node *node.TreeNode) {
    
    
		nodeCount++
	})
	fmt.Println("node count:", nodeCount) // node count: 5

	c := root.TraverseWithChannel()
	maxNode := 0
	for node := range c {
    
    
		if node.Value > maxNode {
    
    
			maxNode = node.Value
		}
	}
	fmt.Println("max node value:", maxNode) // max node value: 5
}

4)、用select进行调度

func generator() chan int {
    
    
	out := make(chan int)
	go func() {
    
    
		i := 0
		for {
    
    
			time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
			out <- i
			i++
		}
	}()
	return out
}

func worker(id int, c chan int) {
    
    
	for n := range c {
    
    
		time.Sleep(time.Second)
		fmt.Printf("Worker %d received %d\n", id, n)
	}
}

func createWorker(id int) chan<- int {
    
    
	c := make(chan int)
	go worker(id, c)
	return c
}

func main() {
    
    
	var c1, c2 = generator(), generator()
	var worker = createWorker(0)
	var values []int
	tm := time.After(10 * time.Second)
	tick := time.Tick(time.Second)
	for {
    
    
		var activeWorker chan<- int
		var activeValue int
		if len(values) > 0 {
    
    
			activeWorker = worker
			activeValue = values[0]
		}
		select {
    
    
		case n := <-c1:
			values = append(values, n)
		case n := <-c2:
			values = append(values, n)
		case activeWorker <- activeValue:
			values = values[1:]
		// 200毫秒没有数据打印timeout
		case <-time.After(200 * time.Millisecond):
			fmt.Println("timeout")
		// 每秒打印队列长度
		case <-tick:
			fmt.Println("queue len =", len(values))
		// 程序运行10秒后结束
		case <-tm:
			fmt.Println("end")
			return
		}
	}
}
  • select的使用
  • 定时器的使用
  • 在select中使用nil channel

5)、传统同步机制

  • WaitGroup
  • Mutex
  • Cond

实现atomicInt:

package main

import (
	"fmt"
	"sync"
	"time"
)

type atomicInt struct {
    
    
	value int
	lock  sync.Mutex
}

func (a *atomicInt) increment() {
    
    
	a.lock.Lock()
	defer a.lock.Unlock()
	a.value++
}

func (a *atomicInt) get() int {
    
    
	a.lock.Lock()
	defer a.lock.Unlock()
	return a.value
}

func main() {
    
    
	var a atomicInt
	for i := 0; i < 1000; i++ {
    
    
		go func() {
    
    
			a.increment()
		}()
	}
	time.Sleep(5 * time.Second)
	fmt.Println(a.get())
}

6)、并发模式(上)

  • 生成器
  • 服务/任务
  • 同时等待多个服务
func msgGen(name string) <-chan string {
    
    
	c := make(chan string)
	go func() {
    
    
		i := 0
		for {
    
    
			time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
			c <- fmt.Sprintf("service %s: message %d", name, i)
			i++
		}
	}()
	return c
}

func fanIn(c1, c2 <-chan string) chan string {
    
    
	c := make(chan string)
	go func() {
    
    
		for {
    
    
			c <- <-c1
		}
	}()
	go func() {
    
    
		for {
    
    
			c <- <-c2
		}
	}()
	return c
}

func main() {
    
    
	m1, m2 := msgGen("service1"), msgGen("service2")
	m := fanIn(m1, m2)
	for {
    
    
		fmt.Println(<-m)
	}
}

7)、并发模式(下)

func msgGen(name string) <-chan string {
    
    
	c := make(chan string)
	go func() {
    
    
		i := 0
		for {
    
    
			time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
			c <- fmt.Sprintf("service %s: message %d", name, i)
			i++
		}
	}()
	return c
}

func fanInBySelect(c1, c2 <-chan string) chan string {
    
    
	c := make(chan string)
	go func() {
    
    
		for {
    
    
			select {
    
    
			case m := <-c1:
				c <- m
			case m := <-c2:
				c <- m
			}
		}
	}()
	return c
}

func main() {
    
    
	m1, m2 := msgGen("service1"), msgGen("service2")
	m := fanInBySelect(m1, m2)
	for {
    
    
		fmt.Println(<-m)
	}
}
func msgGen(name string) <-chan string {
    
    
	c := make(chan string)
	go func() {
    
    
		i := 0
		for {
    
    
			time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
			c <- fmt.Sprintf("service %s: message %d", name, i)
			i++
		}
	}()
	return c
}

func fanIn(chs ...<-chan string) chan string {
    
    
	c := make(chan string)
	for _, ch := range chs {
    
    
		go func(in <-chan string) {
    
    
			for {
    
    
				c <- <-in
			}
		}(ch)
	}
	return c
}

func main() {
    
    
	m1, m2 := msgGen("service1"), msgGen("service2")
	m := fanIn(m1, m2)
	for {
    
    
		fmt.Println(<-m)
	}
}

8)、并发任务的控制

非阻塞等待:

func msgGen(name string) <-chan string {
    
    
	c := make(chan string)
	go func() {
    
    
		i := 0
		for {
    
    
			time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
			c <- fmt.Sprintf("service %s: message %d", name, i)
			i++
		}
	}()
	return c
}

func nonBlockingWait(c <-chan string) (string, bool) {
    
    
	select {
    
    
	case m := <-c:
		return m, true
	default:
		return "", false
	}
}

func main() {
    
    
	m1, m2 := msgGen("service1"), msgGen("service2")
	for {
    
    
		fmt.Println(<-m1)
		if m, ok := nonBlockingWait(m2); ok {
    
    
			fmt.Println(m)
		} else {
    
    
			fmt.Println("no message from service2")
		}
	}
}

超时机制:

func msgGen(name string) <-chan string {
    
    
	c := make(chan string)
	go func() {
    
    
		i := 0
		for {
    
    
			time.Sleep(time.Duration(rand.Intn(5000)) * time.Millisecond)
			c <- fmt.Sprintf("service %s: message %d", name, i)
			i++
		}
	}()
	return c
}

func timeoutWait(c <-chan string, timeout time.Duration) (string, bool) {
    
    
	select {
    
    
	case m := <-c:
		return m, true
	case <-time.After(timeout):
		return "", false
	}
}

func main() {
    
    
	m1 := msgGen("service1")
	for {
    
    
		if m, ok := timeoutWait(m1, 2*time.Second); ok {
    
    
			fmt.Println(m)
		} else {
    
    
			fmt.Println("timeout")
		}
	}
}

任务中断/退出:

func msgGen(name string, done chan struct{
    
    }) <-chan string {
    
    
	c := make(chan string)
	go func() {
    
    
		i := 0
		for {
    
    
			select {
    
    
			case <-time.After(time.Duration(rand.Intn(5000)) * time.Millisecond):
				c <- fmt.Sprintf("service %s: message %d", name, i)
			case <-done:
				fmt.Println("cleaning up")
				return
			}
			i++
		}
	}()
	return c
}

func timeoutWait(c <-chan string, timeout time.Duration) (string, bool) {
    
    
	select {
    
    
	case m := <-c:
		return m, true
	case <-time.After(timeout):
		return "", false
	}
}

func main() {
    
    
	done := make(chan struct{
    
    })
	m1 := msgGen("service1", done)
	for i := 0; i < 5; i++ {
    
    
		if m, ok := timeoutWait(m1, time.Second); ok {
    
    
			fmt.Println(m)
		} else {
    
    
			fmt.Println("timeout")
		}
	}
	done <- struct{
    
    }{
    
    }
	time.Sleep(time.Second)
}

优雅退出:

func msgGen(name string, done chan struct{
    
    }) <-chan string {
    
    
	c := make(chan string)
	go func() {
    
    
		i := 0
		for {
    
    
			select {
    
    
			case <-time.After(time.Duration(rand.Intn(5000)) * time.Millisecond):
				c <- fmt.Sprintf("service %s: message %d", name, i)
			case <-done:
				fmt.Println("cleaning up")
				time.Sleep(2 * time.Second)
				fmt.Println("cleanup done")
				done <- struct{
    
    }{
    
    }
				return
			}
			i++
		}
	}()
	return c
}

func timeoutWait(c <-chan string, timeout time.Duration) (string, bool) {
    
    
	select {
    
    
	case m := <-c:
		return m, true
	case <-time.After(timeout):
		return "", false
	}
}

func main() {
    
    
	done := make(chan struct{
    
    })
	m1 := msgGen("service1", done)
	for i := 0; i < 5; i++ {
    
    
		if m, ok := timeoutWait(m1, time.Second); ok {
    
    
			fmt.Println(m)
		} else {
    
    
			fmt.Println("timeout")
		}
	}
	done <- struct{
    
    }{
    
    }
	<-done
}

10、迷宫的广度优先搜索

迷宫文件maze.in

6 5
0 1 0 0 0
0 0 0 1 0
0 1 0 1 0
1 1 1 0 0
0 1 0 0 1
0 1 0 0 0

代码实现:

func readMaze(filename string) [][]int {
    
    
	file, err := os.Open(filename)
	if err != nil {
    
    
		panic(err)
	}
	var row, col int
	fmt.Fscanf(file, "%d %d", &row, &col)
	maze := make([][]int, row)
	for i := range maze {
    
    
		maze[i] = make([]int, col)
		for j := range maze[i] {
    
    
			fmt.Fscanf(file, "%d", &maze[i][j])
		}
	}
	return maze
}

type point struct {
    
    
	i, j int
}

var dirs = [4]point{
    
    {
    
    -1, 0}, {
    
    0, -1}, {
    
    1, 0}, {
    
    0, 1}}

func (p point) add(r point) point {
    
    
	return point{
    
    p.i + r.i, p.j + r.j}
}

func (p point) at(grid [][]int) (int, bool) {
    
    
	if p.i < 0 || p.i >= len(grid) {
    
    
		return 0, false
	}
	if p.j < 0 || p.j >= len(grid[p.i]) {
    
    
		return 0, false
	}
	return grid[p.i][p.j], true
}

func walk(maze [][]int, start, end point) [][]int {
    
    
	steps := make([][]int, len(maze))
	for i := range steps {
    
    
		steps[i] = make([]int, len(maze[i]))
	}
	Q := []point{
    
    start}
	for len(Q) > 0 {
    
    
		cur := Q[0]
		Q = Q[1:]
		if cur == end {
    
    
			break
		}
		for _, dir := range dirs {
    
    
			next := cur.add(dir)
			// maze at next is 0
			// and steps at next is 0
			// and next != start
			val, ok := next.at(maze)
			if !ok || val == 1 {
    
    
				continue
			}
			val, ok = next.at(steps)
			if !ok || val != 0 {
    
    
				continue
			}
			if next == start {
    
    
				continue
			}
			curSteps, _ := cur.at(steps)
			steps[next.i][next.j] = curSteps + 1
			Q = append(Q, next)
		}
	}
	return steps
}

func main() {
    
    
	maze := readMaze("maze/maze.in")
	steps := walk(maze, point{
    
    0, 0}, point{
    
    len(maze) - 1, len(maze[0]) - 1})
	for _, row := range steps {
    
    
		for _, val := range row {
    
    
			fmt.Printf("%3d", val)
		}
		fmt.Println()
	}
}

11、http及其他标准库

1)、http标准库

1)使用http客户端发送请求

func main() {
    
    
	resp, err := http.Get("https://www.imooc.com")
	if err != nil {
    
    
		panic(err)
	}
	defer resp.Body.Close()
	s, err := httputil.DumpResponse(resp, true)
	if err != nil {
    
    
		panic(err)
	}
	fmt.Printf("%s\n", s)
}

2)使用http.Client控制请求头部等

func main() {
    
    
	request, err := http.NewRequest(http.MethodGet, "https://www.imooc.com", nil)
	request.Header.Add("User-Agent",
		"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1")
	client := http.Client{
    
    
		CheckRedirect: func(
			req *http.Request,
			via []*http.Request) error {
    
    
			fmt.Println("Redirect:", req)
			return nil
		},
	}
	resp, err := client.Do(request)
	if err != nil {
    
    
		panic(err)
	}
	defer resp.Body.Close()
	s, err := httputil.DumpResponse(resp, true)
	if err != nil {
    
    
		panic(err)
	}
	fmt.Printf("%s\n", s)
}

2)、json数据格式的处理

type Order struct {
    
    
	Id         string  `json:"id"`
	Name       string  `json:"name,omitempty"` // omitempty:省略空
	Quantity   int     `json:"quantity"`
	TotalPrice float64 `json:"total_price"`
}

func main() {
    
    
	o := Order{
    
    
		Id:         "1234",
		Name:       "learn go",
		Quantity:   3,
		TotalPrice: 30,
	}
	s, _ := json.Marshal(o)
	fmt.Printf("%s\n", s) // {"id":"1234","name":"learn go","quantity":3,"total_price":30}

	o2 := Order{
    
    
		Id:         "1234",
		Name:       "",
		Quantity:   3,
		TotalPrice: 30,
	}
	s2, _ := json.Marshal(o2)
	fmt.Printf("%s\n", s2) // {"id":"1234","quantity":3,"total_price":30}
}
type OrderItem struct {
    
    
	Id    string  `json:"id"`
	Name  string  `json:"name"`
	Price float64 `json:"price"`
}

type Order struct {
    
    
	Id         string     `json:"id"`
	Item       *OrderItem `json:"item"`
	Quantity   int        `json:"quantity"`
	TotalPrice float64    `json:"total_price"`
}

func main() {
    
    
	o := Order{
    
    
		Id: "1234",
		Item: &OrderItem{
    
    
			Id:    "item_1",
			Name:  "learn go",
			Price: 15,
		},
		Quantity:   3,
		TotalPrice: 30,
	}
	s, _ := json.Marshal(o)
	fmt.Printf("%s\n", s) // {"id":"1234","item":{"id":"item_1","name":"learn go","price":15},"quantity":3,"total_price":30}
}
type OrderItem struct {
    
    
	Id    string  `json:"id"`
	Name  string  `json:"name"`
	Price float64 `json:"price"`
}

type Order struct {
    
    
	Id         string      `json:"id"`
	Items      []OrderItem `json:"items"`
	TotalPrice float64     `json:"total_price"`
}

func main() {
    
    
	marshal()
	unmarshal()
}

func marshal() {
    
    
	o := Order{
    
    
		Id: "1234",
		Items: []OrderItem{
    
    
			{
    
    
				Id:    "item_1",
				Name:  "learn go",
				Price: 15,
			},
			{
    
    
				Id:    "item_2",
				Name:  "interview",
				Price: 10,
			},
		},
		TotalPrice: 20,
	}
	s, _ := json.Marshal(o)
	fmt.Printf("%s\n", s)
}

func unmarshal() {
    
    
	s := `{"id":"1234","items":[{"id":"item_1","name":"learn go","price":15},{"id":"item_2","name":"interview","price":10}],"total_price":20}`
	var o Order
	json.Unmarshal([]byte(s), &o)
	fmt.Printf("%+v\n", o)
}

3)、第三方API数据格式的解析技巧

func main() {
    
    
	parseNLP()
}

func parseNLP() {
    
    
	res := `{
"data": [
    {
        "synonym":"",
        "weight":"0.6",
        "word": "真丝",
        "tag":"材质"
    },
    {
        "synonym":"",
        "weight":"0.8",
        "word": "韩都衣舍",
        "tag":"品牌"
    },
    {
        "synonym":"连身裙;联衣裙",
        "weight":"1.0",
        "word": "连衣裙",
        "tag":"品类"
    }
]
}`
	m := struct {
    
    
		Data []struct {
    
    
			Synonym string `json:"synonym"`
			Tag     string `json:"tag"`
		} `json:"data"`
	}{
    
    }
	err := json.Unmarshal([]byte(res), &m)
	if err != nil {
    
    
		panic(err)
	}
	fmt.Printf("%+v, %+v\n", m.Data[2].Synonym, m.Data[2].Tag)
}

4)、gin框架介绍

package main

import (
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"math/rand"
	"time"
)

const keyRequestId = "requestId"

func main() {
    
    
	r := gin.Default()
	logger, err := zap.NewProduction()
	if err != nil {
    
    
		panic(err)
	}
	r.Use(func(c *gin.Context) {
    
    
		s := time.Now()
		c.Next()
		// path, response code, log latency
		logger.Info("incoming request",
			zap.String("path", c.Request.URL.Path),
			zap.Int("status", c.Writer.Status()),
			zap.Duration("elapsed", time.Now().Sub(s)))
	}, func(c *gin.Context) {
    
    
		c.Set(keyRequestId, rand.Int())
		c.Next()
	})
	r.GET("/ping", func(c *gin.Context) {
    
    
		h := gin.H{
    
    
			"message": "pong",
		}
		if rid, exists := c.Get(keyRequestId); exists {
    
    
			h[keyRequestId] = rid
		}
		c.JSON(200, h)
	})
	r.GET("/hello", func(c *gin.Context) {
    
    
		c.String(200, "hello")
	})
	r.Run()
}

对应课程

Google资深工程师深度讲解Go语言

猜你喜欢

转载自blog.csdn.net/qq_40378034/article/details/125570420