既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
if EnableTracing {
_, file, line, _ := runtime.Caller(1)
s.events = trace.NewEventLog(“grpc.Server”, fmt.Sprintf(“%s:%d”, file, line))
}
if s.opts.numServerWorkers > 0 { //协程池,如果设置协程数量,那么将会以指定协程数量去处理数据
s.initServerWorkers()
}
if channelz.IsOn() {
s.channelzID = channelz.RegisterServer(&channelzServer{s}, “”)
}
return s
}
* NewServer 需要传入选项,经常看框架源码的同学都知道干什么用了,相当于go 可以传入零个或者多个参数,并且都是回调函数,来设置grpc 的配置参数
* **设置拦截器配置**,拦截器其实相当于[中间件]( )
* 设置是否开启协程数量,如果设置了数量,那么将会初始化numServerWorkers goroutine 去循环处理数据
#### **1.2.2 grpc 配置**
type serverOptions struct {
creds credentials.TransportCredentials //证书,tls 有关
codec baseCodec //默认编解码器
cp Compressor //压缩器
dc Decompressor
unaryInt UnaryServerInterceptor //一元服务拦截器
streamInt StreamServerInterceptor//stream 服务拦截器
chainUnaryInts []UnaryServerInterceptor //拦截器数组
chainStreamInts []StreamServerInterceptor//stream拦截器数组
inTapHandle tap.ServerInHandle
statsHandler stats.Handler
maxConcurrentStreams uint32
maxReceiveMessageSize int
maxSendMessageSize int
unknownStreamDesc *StreamDesc //未知stream 处理
keepaliveParams keepalive.ServerParameters //连接保活参数
keepalivePolicy keepalive.EnforcementPolicy //连接保活策略
initialWindowSize int32 //初始滑动窗口大小
initialConnWindowSize int32 //初始连接滑动窗口大小
writeBufferSize int
readBufferSize int
connectionTimeout time.Duration//连接超时
maxHeaderListSize *uint32//http header 列表的最大限制
headerTableSize *uint32
numServerWorkers uint32 //work 处理协程数量
}
* 最后初始化的时候回将拦截器数组串成链,后面在细讲的时候再写
**默认配置**
var defaultServerOptions = serverOptions{
maxReceiveMessageSize: defaultServerMaxReceiveMessageSize,
maxSendMessageSize: defaultServerMaxSendMessageSize,
connectionTimeout: 120 * time.Second,
writeBufferSize: defaultWriteBufSize,
readBufferSize: defaultReadBufSize,
}
**如何使用配置?**
[https://github.com/grpc/grpc-go/blob/master/server.go]( ) +625
从这里开始有许多设置配置的选项,我们根据需要选择合适的配置,比如,我们要设置消息最大发送[字节]( )
s := grpc.NewServer(grpc.MaxSendMsgSize(1000))
### **1.3 grpc 和客户端通信流程**
#### **1.3.1创建连接**
[https://github.com/grpc/grpc-go/blob/master/server.go]( ) +739
* server 函数为每个连接创建 goroutine
* goroutines处理grpc 请求并调用注册的handler去响应请求
* Server 会返回在lis.Accept产生致命错误的时候,当方法返回后lis 将会被关闭
* server会返回错误,没有调用stop 或者GracefulStop
func (s *Server) Serve(lis net.Listener) error {
s.mu.Lock()//加锁
s.printf(“serving”)
s.serve = true //将服务器启动了设置为true
if s.lis == nil {//如果s.lis==nil,代表已经stop 或者已经GracefulStop,为什么这么说了,因为在创建服务器的时候已经make s.lis 了,不可能为nil,只有在停止的时候才会为nil
s.mu.Unlock() //解锁,关闭目前传入的新lis
lis.Close()
return ErrServerStopped//返回报错
}
s.serveWG.Add(1)
defer func() { //调用defer,这里会阻塞住,等待服务器停止
s.serveWG.Done()
if s.quit.HasFired() {
<-s.done.Done()
}
}()
ls := &listenSocket{Listener: lis} //创建封装的listenSocket
s.lis[ls] = true
if channelz.IsOn() {
ls.channelzID = channelz.RegisterListenSocket(ls, s.channelzID, lis.Addr().String())
}
s.mu.Unlock() //解锁
defer func() { //注册defer,在返回的时候,将lis 关闭,并从server 的map 里面删除
s.mu.Lock()
if s.lis != nil && s.lis[ls] {
ls.Close()
delete(s.lis, ls)
}
s.mu.Unlock()
}()
var tempDelay time.Duration // how long to sleep on accept failure
for {//正常tc 处理流程
rawConn, err := lis.Accept()
if err != nil {
if ne, ok := err.(interface {
Temporary() bool
}); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
s.mu.Lock()
s.printf(“Accept error: %v; retrying in %v”, err, tempDelay)
s.mu.Unlock()
timer := time.NewTimer(tempDelay)
select {
case <-timer.C:
case <-s.quit.Done():
timer.Stop()
return nil
}
continue
}
s.mu.Lock()
s.printf(“done serving; Accept = %v”, err)
s.mu.Unlock()
if s.quit.HasFired() {
return nil
}
return err
}
tempDelay = 0
s.serveWG.Add(1)
go func() { //新建goroutine 处理连接信息
s.handleRawConn(lis.Addr().String(), rawConn)
s.serveWG.Done() //调用wg.Done
}()
}
}
该函数作用主要是监听新连接,并分配goroutine来处理每个新连接信息,但是有几个问题我们一起来看看
1、**tempDelay及Temporary()**
当accept报错会返回这个错误,我们需要处理这个错误,这时候会启动一个[定时器]( ),多少秒后再去调用accept,同时监听服务器退出信号。
定时器时间是先5毫秒,然后不断\*2,直到最后稳定到1秒钟,之后一直睡眠1秒钟,等待lis 从错误中恢复过来,如果一直出错将会一直进到定时器。
2、**服务器是怎么阻塞和怎么退出的**
在第一个defer的时候,服务器将会调用done 等待s.quit.HasFired
s.serveWG.Add(1)
defer func() { //调用defer,这里会阻塞住,等待服务器停止
s.serveWG.Done()
if s.quit.HasFired() {
<-s.done.Done()
}
}()
HasFired,返回e.fired是否为1
func (e *Event) HasFired() bool {
return atomic.LoadInt32(&e.fired) == 1
}
在创建连接的时候也调用了serveWG
s.serveWG.Add(1)
go func() { //新建goroutine 处理连接信息
s.handleRawConn(lis.Addr().String(), rawConn)
s.serveWG.Done() //调用wg.Done
}()
但是我们并没有看到[wg.wait]( )(),或者e.fired 什么时候为1的?答案在Stop 以及GracefulStop()身上
* 在默认的for{}死循环中监听连接,服务器就被阻塞住了,在e.fired变为1(调用stop 系列函数),或者出错就会返回,返回就会调用defer,此时就会执行到<-s.done.Done()这个逻辑,再次阻塞