go语言基础-gozero

发布于:2023-02-06 ⋅ 阅读:(671) ⋅ 点赞:(0)

go基础

go 文档
在这里插入图片描述
Go语言的并发是基于 goroutine 的,goroutine 类似于线程,但并非线程。可以将 goroutine 理解为一种虚拟线程。Go 语言运行时会参与调度 goroutine,并将 goroutine 合理地分配到每个 CPU 中,最大限度地使用CPU性能。开启一个goroutine的消耗非常小(大约2KB的内存),你可以轻松创建数百万个goroutine。

go语言特性

高效的性能
简洁的语法
广泛验证的工程效率
极致的部署体验
极低的服务端资源成本

go常用命令

go env 
go env -w GOPROXY=https://goproxy.cn,direct
go env -w GO111MODULE=on
再没有 GO111MODULE时, go编译程序的查找依赖的顺序 go path > goroot , 有了GO111MODULE后,会读取当前项目的go.mod.文件, 在go.mod文件中会记录有哪些依赖

goroutine 协程的特点:
1.goroutine具有可增长的分段堆栈。这意味着它们只在需要时才会使用更多内存。
2.goroutine的启动时间比线程快。
3.goroutine原生支持利用channel安全地进行通信。
4.goroutine共享数据结构时无需使用互斥锁。
代码风格统一 一套格式化工具——go fmt
天生支持并发,完美契合当下高并发的互联网生态

go下载地址

官网地址:https://golang.google.cn/dl/
中文地址:https://studygolang.com/dl
1.18

goland 下载地址

https://www.jetbrains.com/go/download/#section=mac
帮助文档
https://www.jetbrains.com/help/go/2022.2/getting-started.html

配置goproxy

设置

go env -w GOPROXY=https://goproxy.cn,direct
com + , -> go ->go Modules ->environment:
GOPROXY=https://goproxy.cn,direct

在这里插入图片描述

go教程

https://golang.google.cn/doc/
https://topgoer.com/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/%E7%AC%AC%E4%B8%80%E4%B8%AAgo%E7%A8%8B%E5%BA%8F.html

go 安装

建立Go的工作空间(workspace,也就是GOPATH环境变量指向的目录)
GO代码必须在工作空间内。工作空间是一个目录,其中包含三个子目录:
src ---- 里面每一个子目录,就是一个包。包内是Go的源码文件
pkg ---- 编译后生成的,包的目标文件
bin ---- 生成的可执行文件
添加PATH环境变量and设置GOPATH环境变量

vi /etc/profile
export GOROOT=/usr/local/go        ##Golang安装目录
export PATH=$GOROOT/bin:$PATH
export GOPATH=/home/go  ##Golang项目目录
go
go env

gopath :GOPATH是一个环境变量,用来表明你写的go项目的存放路径

GOPATH路径最好只设置一个,所有的项目代码都放到GOPATH的src目录下
https://topgoer.com/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/%E9%85%8D%E7%BD%AEgopath.html

在进行Go语言开发的时候,我们的代码总是会保存在GOPATH/src目录下。在工程经过go build、go install或go get等指令后,会将下载的第三方包源代码文件放在$GOPATH/src目录下, 产生的二进制可执行文件放在 $GOPATH/bin目录下,生成的中间缓存文件会被保存在 $GOPATH/pkg 下

安装
go get gocv.io/x/gocv

第一个go

package main  // 声明 main 包,表明当前是一个可执行程序

import "fmt"  // 导入内置 fmt 

func main(){  // main函数,是程序执行的入口
    fmt.Println("Hello World!")  // 在终端打印 Hello World!
}

go语法

声明
strName string
变量名   类型
函数
函数名大小写 表示包是否导出
函数多个形参 ... 三个点
func Test(a ...interface{}) (n int, err error)
任意类型 ...interface{}

构造函数 
func newFunc()	不需要被导出
func NewFunc()  需要被导出
映射
map[string]string
结构体

type hao struct {
	x,y float64
	z int
}
结构体定义方法
func (h hao) demo() float64 {
	return h.x+1.0
}

结构体初始化:
area := hao{1.0,2.0,5}

定义复合结构体: 可以分割独立可复用的结构当中
type  hao struct {
	hao1 hao1
	hao2 hao2
	hao3    //自动转发,不指定变量名, 会自动生成同名字段
}
h1 := hao1{}
h2 := hao2{}
h = hao{hao1:h1,hao2:h2, hao3:hao3{}}
h.hao3.value3
h.value3  //是相同字段

接口
类型必须满足一组方法的声明
type hao interface {
	demo() string
}
type hao1 int
func (h1 hao1) demo() string{
	return  strings.Repeat("oyes",int(h1))
}
hao = hao1(3)
fmt.Println(hoa.talk())
指针

指针存储的内存地址
&调用传参内存地址 *解引用函数定义具体值
空指针解引用

hao := 666
fmt.Println(&hao) //变量的内存地址  0x1010c101
解引用
hao1 = &hao
// go语言不允许类似于c语言的hao1++这样的内存操作
fmt.Println(*hao1)  //解引用

hao1 = "oyes"
var hao2 *string 
hao2 := &hao1
fmt.Println(*hao2)
// *星号放在类型前面表示声明指针类型,*星号放在变量前面表示解引用该变量指向的值
解引用改变其值
*hao2 = "hao oyes"
hao3 := *hao2 //解引用,是拷贝的数据副本

// 内部指针:指向结构内部字段的指针  地址操作符&
type hao1 struct {
	hao2
}
&hao1.hao2
func haof(h *hao2) string {}

指针没有赋值默认 为nil

空指针处理
func hoaf(h *hao) {
	if h == nil {
		return
	}
	h.page++
}
异常
demo,err := hao()
// 异常不等于空,有报错
if err != nil {
	fmt.Println(err)
	os.Exit(1)
}
for _,str := range demo{
}

func hao() []string,error{
	return err
	defer f.Close() //函数返回前执行
}

优雅的错误处理
type safeWriter struct {
	w io.Writer
	err Error
}
func (sw *safeWriter) writeln(s string){
	if sw.err != nil {
		return
	}
	_,sw.err = fmt.Fprintln(sw.w,s)
}
类型断言

err := g()
if err != nil {
	if errs,ok :=err.(SudokuError); ok {
		//具体的逻辑
	}
	os.Exit(1)
}
Go语言的错误值机制促使开发者考虑错误,而不是处理异常默认将其忽略。而且不需要用到特殊关键字,简单而灵活

recover() 被延迟的方法使用

defer func(){
	if e:=recover(); e!=nil {
		fmt.Pringln(e)
	}
}()
panic("o no !!!")
并发

通过 goroutine并发执行,并通过channel实现多个goroutine之间的通信和协同

go hao() //goroutine
c := make(chan int)  //channel 管道
c <- 99
r := c

func main(){
	c := make(chan int)
	for i := 0; i < 5; i++{
		go sleepyGoher(i,c)
	}
	for i :=0; i<5; i++{
		goherID := <-c
		fmt.Pringln("goherId:",goherId)
	}
}

func sleepyGoher(id int, c chan in ){
	time.Sleep(3*time.Second)
	fmt.Println("...",id,"snore ...")
	c<-id
}
当 多通道,或等待超时时自动中断 time.After
timeout := time.After(2*time.Second)
for i:=0;i<5;i++{
	select {
	case gopherId:=<=c:
		fmt.Println(gopherId)
	case <-timeout:  //等待耗尽
		fmt.Println("time out")
		return
	}
}

阻塞和死锁

阻塞:goroutine在等待通道的发送或者接收,其被阻塞了
死锁: 一个或多个goroutine因某些永远不会发生的事情而被阻塞时,称这种情况为死锁, 死锁并不消耗资源

func main(){
	c := make(chan int)
	<- c
}

装配流水线
func source( down chan string){
	for _,v :=range []string{"a","b","c"}
	{
		down <-v
	}
	down <- ""
	close(donw) // 用close 代替空字符表示通道关闭
}

func filter(up,down chan string){
	for {
		item := <- up
		v,ok := <- up // 获得状态 ok表示通道是否被关闭
		if !ok {
			close(down)
			return 
		}
		if item == ""{
			down <- ""
			return
		}
		if !strings.Contains(item,"bad"){
			down <-item
		}
	}
}
优化后:
func filter(up,down chan string){
	for item:= range up {
		if !string.Contains(item,"bad"){
			down<-item
		}
	}
	close(down)
}

func printG(up chan string){
	for{
		v := up
		if v != ""{
			return 
		}
		fmt.Println(v)
	}
}

func main(){
	up := make(chan string)
	down := make(chan string)
	go source(up)
	go fileter(up,down)
	printGopher(down)
}
并发状态 互斥锁

使用共享值为 竞态条件
互斥锁,组织多个goroutine操作同一值
死锁 :拥有互斥锁的goroutine,所动同一互斥锁,就会发生死锁
为了保证互斥锁的安全:
1、尽可能简化互斥锁的保护代码
2、对一份共享状态只使用一个互斥锁

var mu sync.Mutex   //声明互斥锁
mu.Lock()  //上锁
mu.Unlock() //解锁
defer mu.Unlock()  //确保退出前一定解锁

死锁

go高级

并发

进程和线程

    A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
    B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
    C.一个进程可以创建和撤销多个线程;同一个进程中的多个线程之间可以并发执行

并发和并行

    A. 多线程程序在一个核的cpu上运行,就是并发。
    B. 多线程程序在多个核的cpu上运行,就是并行。

协程和线程

协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。
线程:一个线程上可以跑多个协程,协程是轻量级的线程。

goroutine 只是由官方实现的超级"线程池"
每个实例4~5KB的栈内存占用和由于实现机制而大幅减少的创建和销毁开销是go高并发的根本原因。
并发主要由切换时间片来实现"同时"运行,并行则是直接利用多核实现多线程的运行,go可以设置使用核数,以发挥多核计算机的能力。
goroutine 奉行通过通信来共享内存,而不是共享内存来通信。

goroutine

https://topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html

goroutine原理

要实现并发编程的时候,我们通常需要自己维护一个线程池,并且需要自己去包装一个又一个的任务,同时需要自己去调度线程执行任务并维护上下文切换,这一切通常会耗费程序员大量的心智。那么能不能有一种机制,程序员只需要定义很多个任务,让系统去帮助我们把这些任务分配到CPU上实现并发执行呢?

Go语言中的goroutine就是这样一种机制,goroutine的概念类似于线程,但 goroutine是由Go的运行时(runtime)调度和管理的。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU。Go语言之所以被称为现代化的编程语言,就是因为它在语言层面已经内置了调度和上下文切换的机制。

在Go语言编程中你不需要去自己写进程、线程、协程,你的技能包里只有一个技能–goroutine,当你需要让某个任务并发执行的时候,你只需要把这个任务包装成一个函数,开启一个goroutine去执行这个函数就可以了,就是这么简单粗暴。
GPM是Go语言运行时(runtime)层面的实现,是go语言自己实现的一套调度系统。区别于操作系统调度OS线程

1.G很好理解,就是个goroutine的,里面除了存放本goroutine信息外 还有与所在P的绑定等信息。
2.P管理着一组goroutine队列,P里面会存储当前goroutine运行的上下文环境(函数指针,堆栈地址及地址边界),P会对自己管理的goroutine队列做一些调度(比如把占用CPU时间较长的goroutine暂停、运行后续的goroutine等等)当自己的队列消费完了就去全局队列里取,如果全局队列里也消费完了会去其他P的队列里抢任务。
3.M(machine)是Go运行时(runtime)对操作系统内核线程的虚拟, M与内核线程一般是一一映射的关系, 一个groutine最终是要放到M上执行的;
P与M一般也是一一对应的。他们关系是: P管理着一组G挂载在M上运行。当一个G长久阻塞在一个M上时,runtime会新建一个M,阻塞G所在的P会把其他的G 挂载在新建的M上。当旧的G阻塞完成或者认为其已经死掉时 回收旧的M。

P的个数是通过runtime.GOMAXPROCS设定(最大256),Go1.5版本之后默认为物理线程数。 在并发量大的时候会增加一些P和M,但不会太多,切换太频繁的话得不偿失。

单从线程调度讲,Go语言相比起其他语言的优势在于OS线程是由OS内核来调度的,goroutine则是由Go运行时(runtime)自己的调度器调度的,这个调度器使用一个称为m:n调度的技术(复用/调度m个goroutine到n个OS线程)。 其一大特点是goroutine的调度是在用户态下完成的, 不涉及内核态与用户态之间的频繁切换,包括内存的分配与释放,都是在用户态维护着一块大的内存池, 不直接调用系统的malloc函数(除非内存池需要改变),成本比调度OS线程低很多。 另一方面充分利用了多核的硬件资源,近似的把若干goroutine均分在物理线程上, 再加上本身goroutine的超轻量,以上种种保证了go调度方面的性能。

goroutine竞争状态

https://blog.csdn.net/qq_27449067/article/details/103882823
goroutine在没有相互同步状态的情况下同时访问某个资源,并且同时对这个资源进行读写的时候,对于这个资源就处于相互竞争状态(race candition)

var number int
var wait sync.WaitGroup
func main()  {
    wait.Add(2)
    go updateNumber(20000)//加20000
    go updateNumber(30000)//加30000
    wait.Wait()
    fmt.Println(number)
}
func updateNumber(addNumber int)  {
    for i:=0;i<addNumber ;i    {
        number   
    }
    wait.Done()
}

原子函数 atomic能够以很底层的加锁机制来同步访问整型变量和指针,我们可以使用原子函数来处理竞争问题

var number int32
var wait sync.WaitGroup
func main()  {
    wait.Add(2)
    go updateNumber(20000)
    go updateNumber(30000)
    wait.Wait()
    fmt.Println(number)
}
func updateNumber(addNumber int)  {
    defer wait.Done()
    for i:=0;i<addNumber ;i    {
        atomic.AddInt32(&number,1)
    }

}

这个函数会同步整型值的加法,方法是强制同一时刻只能有一个goroutine 运行并完成这个加法操作

互斥锁 mutex另一种方式是创建一个互斥锁来锁住一个区域,来保证同一个资源不会被同时修改或者使用。保证当前只有一个goroutine在执行当前区域的代码。

var (
    number int32
    wait sync.WaitGroup
    mutex sync.Mutex
    )
func main()  {
    wait.Add(2)
    go updateNumber(20000)
    go updateNumber(30000)
    wait.Wait()
    fmt.Println(number)
}
func updateNumber(addNumber int)  {
    defer wait.Done()
    for i:=0;i<addNumber ;i    {
        mutex.Lock() // 加锁
        number  ;
        mutex.Unlock() //释放锁
    }
}

在Number改变的前后对当前区域加锁,最后也能得到我们的目的,但是这样的会话,每次在number变更的时候,都会创建锁与释放锁,会对性能产生很大的影响。其实我们可以在for循环区域来加锁
后面这种形式的效率明显是要比第一种高很多的。所以我们程序有使用互斥锁的话,需要考虑加锁的粒度问题
虽然上面上面两种方式也可以解决竞争问题,但是在go中有一种更好的方式来解决这个问题,那就是goroutine的好兄弟channel

go汇总

八股文

https://www.topgoer.cn/docs/gomianshiti/gomianshiti-1dd225t6esqld

go 爬虫

goquery
https://www.bbsmax.com/A/gVdngBm8zW/

爬虫小例子
https://topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/%E7%88%AC%E8%99%AB%E5%B0%8F%E6%A1%88%E4%BE%8B.html

goquery 百度热搜
https://zhuanlan.zhihu.com/p/264823205

package main

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"log"
	"net/http"
	"os"
)

func BaiduHotSearch() {
	res, err := http.Get("http://www.baidu.com")
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
	}

	doc, err := goquery.NewDocumentFromReader(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	doc.Find(".s-hotsearch-content .hotsearch-item").Each(func(i int, s *goquery.Selection) {
		content := s.Find(".title-content-title").Text()
		fmt.Printf("%d: %s\n", i, content)
	})
}

func brickowl() {
	var url string
	//url = "https://www.brickowl.com/catalog/lego-sets"
	url = "https://www.baidu.com/sugrec?prod=pc_his&from=pc_web&json=1&sid=36558_36625_36255_36822_36973_36413_36954_36166_36917_36919_36746_26350_36931&hisdata=%5B%7B%22time%22%3A1659258458%2C%22kw%22%3A%22rsync%20%E8%BF%9C%E7%A8%8B%E5%90%8C%E6%AD%A5%E5%88%B0%E6%9C%AC%E5%9C%B0%22%7D%2C%7B%22time%22%3A1659258458%2C%22kw%22%3A%22python%20logging%20%E5%A4%9A%E6%96%87%E4%BB%B6%22%7D%2C%7B%22time%22%3A1659258458%2C%22kw%22%3A%22python%20%E4%B8%89%E5%85%83%E8%A1%A8%E8%BE%BE%E5%BC%8F%22%7D%2C%7B%22time%22%3A1659258458%2C%22kw%22%3A%22%E5%A5%A5%E7%89%B9%E6%9B%BC%22%7D%2C%7B%22time%22%3A1659258458%2C%22kw%22%3A%22%E8%B6%85%E6%97%B6%E7%A9%BA%E5%A4%A7%E5%86%B3%E6%88%98%22%7D%2C%7B%22time%22%3A1659258458%2C%22kw%22%3A%22request%20%E8%8E%B7%E5%8F%96%20https%22%7D%2C%7B%22time%22%3A1659258458%2C%22kw%22%3A%22request%20%E8%8E%B7%E5%8F%96%20443%22%7D%2C%7B%22time%22%3A1659258458%2C%22kw%22%3A%22python3%20%E4%B8%8B%E8%BD%BDhttps%22%7D%2C%7B%22time%22%3A1659258458%2C%22kw%22%3A%22%E6%9C%AC%E5%9C%B0%E6%9C%8D%E5%8A%A1app%20%E7%9F%A5%E4%B9%8E%22%7D%2C%7B%22time%22%3A1659258458%2C%22kw%22%3A%22%E4%B8%93%E4%B8%9A%E9%A2%86%E5%9F%9F%E7%9F%A5%E8%AF%86%E5%9B%BE%E8%B0%B1%22%7D%5D&_t=1659258457505&req=2&bs=goland%20%E5%88%87%E6%8D%A2%E7%AA%97%E5%8F%A3%E5%BF%AB%E6%8D%B7%E9%94%AE&csor=0"
	request, err := http.NewRequest("GET", url, nil)
	fmt.Println(err)
	request.AddCookie(&http.Cookie{Name: "SSESS96636da61f62e4e8dc28f1bac0edf597", Value: "EDilyyzHY_b5PNhdazL_d_mGIaCzAYcmdRChrKwyd9c"})
	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(0)
	}
	defer response.Body.Close()
	doc, err := goquery.NewDocumentFromReader(response.Body)
	fmt.Println(doc.Text(), err)
}

func main() {
	BaiduHotSearch()
}

思考

怎么理解 工具大于约定和文档?

怎么理解 约束做一件事只有一种方式?

怎么理解 面向故障编程?

链路跟踪?

持续

go官方文档

go 学习文档

八股文

每日一库

为什么说做好微服务很难

gozero项目示例

gitee文档
官网文档快速开始

gozero商城

gozero

教程视频

安装

go-zero
goctl 命令行工具
protoc protobuf 编辑器
安装文档参考
https://github.com/Mikaelemmmm/go-zero-looklook/blob/main/deploy/script/gencode/gen.sh

go install github.com/zeromicro/go-zero/tools/goctl@latest
查看gopath下
/Users/chenhaohao/go/bin/goctl

加入环境变量
vi ~/.bash_profile  # mac还需配置 vi ~/.zshrc 添加 source ~/.bash_profile 才会生效
export PATH=$PATH:/Users/chenhaohao/go/bin
source ~/.bash_profile

protoc
https://github.com/protocolbuffers/protobuf/releases
https://github.com/protocolbuffers/protobuf/releases/tag/v21.4
mac
protoc-21.4-osx-x86_64.zip

wget https://github.com/protocolbuffers/protobuf/releases/download/v21.4/protoc-21.4-osx-x86_64.zip
cd 
mv protoc  ~/go/bin/s
chmod 777 protoc 
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

快速开始

$ mkdir go-zero-demo
$ cd go-zero-demo
$ go mod init go-zero-demo
$ goctl api new greet
$ go mod tidy
vim greet/internal/logic/greetlogic.go 

func (l *GreetLogic) Greet(req *types.Request) (*types.Response, error) {
    return &types.Response{
        Message: "Hello go-zero",
    }, nil
}

$ cd greet
$ go run greet.go -f etc/greet-api.yaml

http://localhost:8888/from/you
本文含有隐藏内容,请 开通VIP 后查看