- window上先配置代理
set GOPROXY=https://goproxy.cn
https://www.v2ex.com/t/564346
set GO111MODULE=on
- Linux上配置代理
export GOPROXY=https://mirrors.aliyun.com/goproxy/
export GO111MODULE=on
- github地址:https://github.com/gin-gonic/gin
- gin的网站:https://gin-gonic.com/
- golang100Day:https://github.com/rubyhan1314/Golang-100-Days
- 千峰golang平台:https://www.qfgolang.com/
Engine引擎
- Engine被定义成结构体
- 可以用engine.Default() 和 engine.New()
- 区别:Default()也是调用了New() 但是加了两个中间件 Logger Recovery
- Logger负责进行打印并输出日志的中间件
- Recovery中间件是当程序panic会恢复执行并返回500错误
- 通常使用Default创建
Handler
- 里面有个context函数,很重要,上下文机制
GET POST
- engine也给提供了get post的方法可以进行分类直接请求
go基础
-
简短定义方式,不能定义全局变量。
-
变量定义了就要使用,否则无法通过编译。
-
显式类型定义: const b string = “abc”
-
隐式类型定义: const b = “abc”
-
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大
-
切片是一种方便、灵活且强大的包装器。切片本身没有任何数据。它们只是对现有数组的引用。
-
切片与数组相比,不需要设定长度,在[]中不用设定值,相对来说比较自由
切片
- 从概念上面来说slice像一个结构体,这个结构体包含了三个元素:
- 指针,指向数组中slice指定的开始位置
- 长度,即slice的长度
- 最大长度,也就是slice开始位置到数组的最后位置的长度
集合MAP
- 使用map的注意事项
- map是无序的,每次打印出来的map都不一样,也不能通过index获取,必须通过key获取
- map的长度是不固定的,也就是和slice一样是一种引用类型
- 内置的len函数同样适用于map,map拥有的key的数量
- map的key可以是所有可比较的类型,如布尔型,整数型,浮点型,复杂型,字符串型
-
delete(map, key) 函数用于删除集合的元素, 参数为 map 和其对应的 key。删除函数不返回任何值。
-
使用指针传递函数的参数
package main
import (
"fmt"
)
func change(val *int) {
*val = 55
}
func main() {
a := 58
fmt.Println("value of a before function call is",a)
b := &a
change(b)
fmt.Println("value of a after function call is", a)
}
结构体访问
type Books struct {
title string
author string
subject string
book_id int
}
var Book1 Books
Book1.title = "Go 语言"
- 结构体指针
var struct_pointer *Books
struct_pointer = &Book1;
struct_pointer.title;
package main
import (
"fmt"
)
type Address struct {
city, state string
}
type Person struct {
name string
age int
address Address
}
func main() {
var p Person
p.name = "Naveen"
p.age = 50
p.address = Address {
city: "Chicago",
state: "Illinois",
}
fmt.Println("Name:", p.name)
fmt.Println("Age:",p.age)
fmt.Println("City:",p.address.city)
fmt.Println("State:",p.address.state)
}
make用于内建类型(map、slice 和channel)的内存分配。new用于各种类型的内存分配 内建函数new本质上说跟其它语言中的同名函数功能一样:new(T)分配了零值填充的T类型的内存空间,并且返回其地址,即一个*T类型的值。用Go的术语说,它返回了一个指针,指向新分配的类型T的零值。有一点非常重要:new返回指针
内建函数make(T, args)与new(T)有着不同的功能,make只能创建slice、map和channel,并且返回一个有初始值(非零)的T类型,而不是*T。本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。例如,一个slice,是一个包含指向数据(内部array)的指针、长度和容量的三项描述符;在这些项目被初始化之前,slice为nil。对于slice、map和channel来说,make初始化了内部的数据结构,填充适当的值。
make返回初始化后的(非零)值。
- 一个方法就是一个包含了接受者的函数
文件操作
package main
import (
"fmt"
"os"
)
func main() {
/*
FileInfo:文件信息
interface
Name(),文件名
Size(),文件大小,字节为单位
IsDir(),是否是目录
ModTime(),修改时间
Mode(),权限
*/
fileInfo,err := os.Stat("D:/project/src/gindemo1/demo2/cui.txt")
if err != nil{
fmt.Println("err :",err)
return
}
fmt.Printf("%T\n",fileInfo)
//文件名
fmt.Println(fileInfo.Name())
//文件大小
fmt.Println(fileInfo.Size())
//是否是目录
fmt.Println(fileInfo.IsDir()) //IsDirectory
//修改时间
fmt.Println(fileInfo.ModTime())
//权限
fmt.Println(fileInfo.Mode()) //-rw-r--r--
//file3, err := os.Open("D:/project/src/gindemo1/demo2/cui.txt")
//if err != nil{
// fmt.Println("err:", err)
// return
//}
//fmt.Println(file3)
err1 := os.Remove("D:/project/src/gindemo1/demo2/cui.txt")
if err1 != nil {
fmt.Println("err:", err1)
return
}
fmt.Println("删除成功")
}
文件copy三种方式
- io/ioutil
package main
import (
"fmt"
"io/ioutil"
)
func CopyFile(srcFile, destFile string)(int, error){
input, err := ioutil.ReadFile(srcFile)
if err != nil{
fmt.Println(err)
return 0, err
}
err = ioutil.WriteFile(destFile, input, 0644)
if err != nil{
fmt.Println("操作失败")
fmt.Println(err)
return 0, err
}
return len(input), nil
}
func main(){
srcFile := "D:/3a008e5e10460bb18e0cb0255a4c1d9.jpg"
destFile := "D:/project/src/gindemo1/copyfile/3a008e5e10460bb18e0cb0255a4c1d9.jpg"
res, err := CopyFile(srcFile, destFile)
fmt.Println(res, err)
}
- Write Read需要自己创建byte切片
package main
import (
"fmt"
"io"
"os"
)
func copyFile1(srcFile, destFile string)(int, error){
file1, err := os.Open(srcFile)
if err != nil{
return 0,err
}
file2,err:=os.OpenFile(destFile,os.O_WRONLY|os.O_CREATE,os.ModePerm)
if err != nil{
return 0,err
}
defer file1.Close()
defer file2.Close()
bs := make([]byte,1024,1024)
n := -1
total := 0
for {
n,err = file1.Read(bs)
if err == io.EOF || n == 0{
fmt.Println("拷贝完毕。。")
break
}else if err !=nil{
fmt.Println("报错了。。。")
return total,err
}
total += n
_, _ = file2.Write(bs[:n])
}
return total,nil
}
func main(){
srcFile := "D:/project/src/gindemo1/copyfile/3a008e5e10460bb18e0cb0255a4c1d9.jpg"
destFile := "D:/project/src/gindemo1/copyfile02/3a008e5e10460bb18e0cb0255a4c1d9.jpg"
res, _ := copyFile1(srcFile, destFile)
fmt.Println(res)
}
- copy包来实现
package main
import (
"fmt"
"io"
"os"
)
func run(srcFile, destFile string)(int64, error){
file1, err := os.Open(srcFile)
if err != nil{
fmt.Println("err:", err)
}
file2, err := os.OpenFile(destFile,os.O_WRONLY|os.O_CREATE,os.ModePerm)
if err != nil{
fmt.Println("err:", err)
}
defer file1.Close()
defer file2.Close()
return io.Copy(file2, file1)
}
func main(){
a := "D:/project/src/gindemo1/copyfile/3a008e5e10460bb18e0cb0255a4c1d9.jpg"
b := "D:/project/src/gindemo1/copyfile03/3a008e5e10460bb18e0cb0255a4c1d9.jpg"
res,_ := run(a, b)
fmt.Println(res)
}
结论:copy和ioutil性能都不错
io包下Read()和Write()直接读写:我们自己创建读取数据的切片的大小,直接影响性能
- for循环和while循环的区别,while循环后面可以跟条件控制语句
断点续传:
- seeker包
断点续传思路,想实现断点续传就是记住上一次传递了多少的数据,可以创建一个临时文件,记录已经传递的数据量,当恢复传递时,先从临时文件中读取已经传递的数据量,然后通过seek方法,设置该读和写的位置,再继续传递数据。
package main
import (
"fmt"
"os"
"strconv"
"io"
)
func main() {
/*
断点续传:
文件传递:文件复制
/Users/ruby/Documents/pro/a/guliang.jpeg
复制到
guliang4.jpeg
思路:
边复制,边记录复制的总量
*/
srcFile:="/Users/ruby/Documents/pro/a/guliang.jpeg"
destFile:="guliang4.jpeg"
tempFile:=destFile+"temp.txt"
//fmt.Println(tempFile)
file1,_:=os.Open(srcFile)
file2,_:=os.OpenFile(destFile,os.O_CREATE|os.O_WRONLY,os.ModePerm)
file3,_:=os.OpenFile(tempFile,os.O_CREATE|os.O_RDWR,os.ModePerm)
defer file1.Close()
defer file2.Close()
//1.读取临时文件中的数据,根据seek
file3.Seek(0,io.SeekStart)
bs:=make([]byte,100,100)
n1,err:=file3.Read(bs)
fmt.Println(n1)
countStr:=string(bs[:n1])
fmt.Println(countStr)
//count,_:=strconv.Atoi(countStr)
count,_:=strconv.ParseInt(countStr,10,64)
fmt.Println(count)
//2. 设置读,写的偏移量
file1.Seek(count,0)
file2.Seek(count,0)
data:=make([]byte,1024,1024)
n2:=-1// 读取的数据量
n3:=-1//写出的数据量
total :=int(count)//读取的总量
for{
//3.读取数据
n2,err=file1.Read(data)
if err ==io.EOF{
fmt.Println("文件复制完毕。。")
file3.Close()
os.Remove(tempFile)
break
}
//将数据写入到目标文件
n3,_=file2.Write(data[:n2])
total += n3
//将复制总量,存储到临时文件中
file3.Seek(0,io.SeekStart)
file3.WriteString(strconv.Itoa(total))
//假装断电
//if total>8000{
// panic("假装断电了。。。,假装的。。。")
//}
}
}
bufio包
- bufio 是通过缓冲来提高效率。
io操作本身的效率并不低,低的是频繁的访问本地磁盘的文件。所以bufio就提供了缓冲区(分配一块内存),读和写都先在缓冲区中,最后再读写文件,来降低访问本地磁盘的次数,从而提高效率。
- bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。
ioutil
pass
channel
- go语言channel遍历的方法
- https://blog.csdn.net/weixin_42117918/article/details/82055634
- 缓存通道:make(chan T ,size)
- 缓存通道,缓存区的数据满了,才会阻塞状态
- 通道,channel,是用于实现goroutine之间的通信的。一个goroutine可以向通道中发送数据,另一条goroutine可以从该通道中获取数据。截止到现在我们所学习的通道,都是既可以发送数据,也可以读取数据,我们又把这种通道叫做双向通道。
data := <- a // read from channel a
a <- data // write to channel a
- 单向通道(定向)
/*
双向:
chan T -->
chan <- data,写出数据,写
data <- chan,获取数据,读
单向:定向
chan <- T,
只支持写,
<- chan T,
只读
*/
select语句
- select 是 Go 中的一个控制结构。select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。
select {
case communication clause :
statement(s);
case communication clause :
statement(s);
/* 你可以定义任意数量的 case */
default : /* 可选 */
statement(s);
}
go语言的csp模型
go语言的最大两个亮点,一个是goroutine,一个就是chan了。二者合体的典型应用CSP,基本就是大家认可的并行开发神器,简化了并行程序的开发难度,我们来看一下CSP。
-
java并发模型:多线程,锁来解析共享内存模型,要使线程安全必须满足两个条件:内存可见性,原子性
-
JVM规定多个线程进行通信是通过共享变量进行的,java内存模型规定了有主内存是所有线程共享的,而各个线程又有自己的工作内存,线程只能访问自己的工作内存中的数据