docker,kubernetes,容器网络,容器存储,istio,dapr
- 短变量只能放在内部;;
fmt.printf("%v",str)
- iota 可被修改的常量,递增,可跳过;;;
const (
a1= iota
a2=iota
_=iota
a3=iota
)
fmt.Printf("%v %v %v",a1,a2,a3)
//0 1 3
- 0打头八进制%o,0x打头16进制%x
字符串连接
package main
import (
"bytes"
"fmt"
"strings"
)
func main(){
const (
a1= iota
a2=iota
a3=iota
)
fmt.Printf("%v %v %v\n",a1,a2,a3)
linestr:=`2312
23123
sadasd
`
fmt.Printf("%v",linestr)
name:="zzzy"
msg:=fmt.Sprintf("name:%s, str:%s",name,linestr)
fmt.Println(msg)
msg=strings.Join([]string{name,linestr},",")
fmt.Println(msg)
var buffer bytes.Buffer
buffer.WriteString("tom")
buffer.WriteString("zzy")
msg=buffer.String()
fmt.Println(msg)
}
package main
import (
"fmt"
"strings"
)
func main(){
b:=strings.Contains("sdads","q")
//strings.ToLower()
//strings.ToUpper()
s:="22221111"
fmt.Println(strings.HasSuffix(s,"1"))
fmt.Println(strings.HasPrefix(s,"2"))
// strings.Index()
fmt.Println(b)
a:=100
switch a {
case 100: fmt.Println("100")
case 200: fmt.Println("200")
}
x := [...]int{1,2,3,4,5}
for _,v :=range x{
fmt.Println(v)
}
var m = make(map[string]string)
m["name"]="zzy"
m["year"]="2001"
fmt.Println(m)
for i := 0; i < 10; i++ {
MYLABEL:
for j:=0 ;j<5;j++ {
fmt.Println(j)
if(j==2){
continue MYLABEL
}
}
}
}
切片
s1:=[]int(1,2,3)
s2:=make([]int,3)
copy(s2,s1)
map
v,ok:= m1[k2]
//通过ok判断map。m1 是否存在key k2
包管理
go mod init 项目名 ::: 进行初始化
相应包目录下 go build
import 项目名/包名
go get 。。。。
项目路径下 go mod tidy 下载mod文件中规定的依赖
struct
- go中的面向对象实现
- struct嵌套另一个匿名函数,即可访问匿名struct的字段和方法,实现继承。
- 当方法和字段名相同时,编辑器采用就近原则。
context
1. Deadline — 返回 context.Context 被取消的时间,也就是完成工作的截止日期;
2. Done — 返回一个 Channel,这个 Channel 会在当前工作完成或者上下文被取消后关闭,多次调用 Done
方法会返回同一个 Channel;
3. Err — 返回 context.Context 结束的原因,它只会在 Done 方法对应的 Channel 关闭时返回非空的值;
*如果 context.Context 被取消,会返回 Canceled 错误;
如果 context.Context 超时,会返回 DeadlineExceeded 错误;*
4. Value — 从 context.Context 中获取键对应的值,对于同一个上下文来说,多次调用 Value 并传入相同的 Key 会返回相同的结果,该方法可以用来传递请求特定的数据;
func main() {
//context.Background() 返回一个空的Context,这个空的Context一般用于整个Context树的根节点。然后我们使用context.WithCancel(parent)函数,创建一个可取消的子Context,然后当作参数传给goroutine使用,这样就可以使用这个子Context跟踪这个goroutine。
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context) {
for {
select {
//使用select调用<-ctx.Done()判断是否要结束,如果接受到值的话,就可以返回结束goroutine了;如果接收不到,就会继续进行监控。
case <-ctx.Done():
fmt.Println("监控退出,停止了...")
return
default:
fmt.Println("goroutine监控中...")
time.Sleep(2 * time.Second)
}
}
}(ctx)
//睡眠10秒钟
time.Sleep(10 * time.Second)
fmt.Println("可以了,通知监控停止")
//那么是如何发送结束指令的呢?这就是示例中的cancel函数啦,它是我们调用context.WithCancel(parent)函数生成子Context的时候返回的,第二个返回值就是这个取消函数,它是CancelFunc类型的。我们调用它就可以发出取消指令,然后我们的监控goroutine就会收到信号,就会返回结束。
cancel()
//为了检测监控过是否停止,如果没有监控输出,就表示停止了
time.Sleep(5 * time.Second)
}
并发
- map不是线程安全的,并发操作需要加锁;;;
-
可以将每个协程拉取的结果写入到一个临时对象,然后再将其汇聚到map,这样便可以不用使用锁。即独立处理,然后合并。
-
//有锁 并发写入map func WriteMapWithLock() map[int]int { m := make(map[int]int) var mu sync.Mutex var wg sync.WaitGroup //10个协程并发写入map wg.Add(10) for i := 0; i < 10; i++ { go func(i int) { defer wg.Done() mu.Lock() m[i] = i * i mu.Unlock() }(i) } wg.Wait() return m } //无锁 func WirteMapLockfree() map[int]int { m := make(map[int]int) values := make([]int, 10) var wg sync.WaitGroup wg.Add(10) for i := 0; i < 10; i++ { go func(i int) { defer wg.Done() values[i] = i * i }(i) } wg.Wait() for i, v := range values { m[i] = v } return m } func BenchmarkWriteMapWithLock_test(b *testing.B) { for n := 0; n < b.N; n++ { _ = WriteMapWithLock() } } func BenchmarkWriteMapWithfree_test(b *testing.B) { for n := 0; n < b.N; n++ { _ = WirteMapLockfree() } }
循环
for i := range c
会不断从信道接收值,直到它被关闭。 -
对于从无缓冲 Channel 进行的接收,发生在对该 Channel 进行的发送完成之前。因此,后台线程
<-done
接收操作完成之后,main
线程的done <- 1
发送操作才可能完成(从而退出 main、退出程序),而此时打印工作已经完成了。func main() { done := make(chan int) go func(){ fmt.Println("你好, 世界") <-done }() done <- 1 }
-
对于带缓冲的 Channel,对于 Channel 的第 K 个接收完成操作发生在第 K+C 个发送操作完成之前,其中 C 是 Channel 的缓存大小。虽然管道是带缓存的,
main
线程接收完成是在后台线程发送开始但还未完成的时刻,此时打印工作也是已经完成的。func main() { done := make(chan int, 10) // 带 10 个缓存 // 开 N 个后台打印线程 for i := 0; i < cap(done); i++ { go func(){ fmt.Println("你好, 世界") done <- 1 }() } // 等待 N 个后台线程完成 for i := 0; i < cap(done); i++ { <-done } }
-
安全退出,通过一个控制退出的广播管道控制多个线程退出(
close
关闭一个管道,所有从关闭管道接收的操作均会收到一个零值和一个可选的失败标志),并使用waitgroup等待各线程完成退出。
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func worker(num int, can chan bool) {
defer wg.Done()
for {
select {
default:
fmt.Println(num, " is working")
time.Sleep(time.Second)
case <-can:
return
}
}
}
func main() {
can := make(chan bool)
for i := 0; i < 10; i++ {
wg.Add(1)
go worker(i, can)
}
time.Sleep(time.Second * 5)
close(can)
wg.Wait()
}
Stringer接口
fmt
包(还有很多包)都通过此接口来打印值。;通过让 IPAddr
类型实现 fmt.Stringer
来打印点号分隔的地址。
type Stringer interface {
String() string
}
package main
import "fmt"
type IPAddr [4]byte
// TODO: 给 IPAddr 添加一个 "String() string" 方法
func (ip IPAddr) String() string {
return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3])
}
func main() {
hosts := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}
web编程
- 两个路由拥有一致的 http 方法 (指
GET
、POST
、PUT
、DELETE
) 和请求路径前缀,且在某个位置出现了 A 路由是 wildcard(指:id
这种形式)参数,B 路由则是普通字符串,那么就会发生路由冲突。路由冲突会在初始化阶段直接 panic
conflict:
GET /user/info/:name
GET /user/:id
no conflict:
GET /user/info/:name
POST /user/:id
- goland配置proxy,
Go
->Go Modules(vgo)
->设置proxy
为https://goproxy.io/
纯后端 API 模块划分
- Controller,与上述类似,服务入口,负责处理路由,参数校验,请求转发。
- Logic/Service,逻辑(服务)层,一般是业务逻辑的入口,可以认为从这里开始,所有的请求参数一定是合法的。业务逻辑和业务流程也都在这一层中。常见的设计中会将该层称为 Business Rules。
- DAO/Repository,这一层主要负责和数据、存储打交道。将下层存储以更简单的函数、接口形式暴露给 Logic 层来使用。负责数据的持久化工作。
reactor
单线程多路复用
通过reactor对多个连接进行监听,当有数据时进行调用,没有数据时处理其他连接;完整逻辑进行异步回调。
多线程多路复用
单线程reactor进行网络io处理,处理时使用线程池进行处理,完成后进行回调,最后返回客户端。io线程和逻辑线程分开处理。
多Reactor多线程模型
mainreactor监听,将新连接指定交付给subreactor
kratos
.proto文件中声明rpc时加上对应的api地址
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get: "/helloworld/{name}"
};
}
internal
- biz放业务逻辑,data处理数据库,conf用于解析config.yaml中的配置