grpc 入门(二)-- context

本节是继上一章节Hello world的进一步深入挖掘;

一、grpc 服务接口类型

  在godoc的网站上对grpc的端口类型进行了简单的介绍,总共有下面4种类型[1]:

gRPC lets you define four kinds of service method:

    Unary RPCs where the client sends a single request to the server and gets a single response back, just like a normal function call.

  rpc SayHello(HelloRequest) returns (HelloResponse){
  }

    Server streaming RPCs where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages.

  rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
  }

    Client streaming RPCs where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them and return its response.

  rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
  }

    Bidirectional streaming RPCs where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved.

  rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
  }

  We’ll look at the different types of RPC in more detail in the RPC life cycle section below.

  上面是从官网摘抄过来的,简单的来讲客户端和服务发送数据有两种形式:Unary和Streaming。Unary (一元)一次只发送一个包; Streaming(流)一次可以发送多个包。两种方式组合一下就形成了4种类型:

  服务端 客户端
1 Unary Unary
2 Streaming Streaming
3 Unary Streaming
4 Streaming Unary

  开发者需要根据业务需求来考虑使用不同的服务接口类型,最近作者在做一个项目采用的就是第一种接口类型,然后就引发了本片文章后面的一系列问题.... 。

  在上一篇Hello world文章里面的示例就是第一种类型接口,它最终声明了一个需要开发者去实习具体业务逻辑的接口:

// Server API for Greeter service

type GreeterServer interface {
    // Sends a greeting
    SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}

  在Hello world的示例中只对HelloRequest参数进行了使用,而没有使用conext参数,那这个参数有什么用?首先作者介绍一下在golang中context类型的作用。

二、Golang context

2.1、简介

  作者所讲的context的包名称是: "golang.org/x/net/context" ,希望读者不要引用错误了。

  在godoc中对context的介绍如下:

Package context defines the Context type, which carries deadlines, cancelation signals, and other request-scoped values across API boundaries and between processes. As of Go 1.7 this package is available in the standard library under the name context. https://golang.org/pkg/context. 

Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context. The chain of function calls between must propagate the Context, optionally replacing it with a modified copy created using WithDeadline, WithTimeout, WithCancel, or WithValue.


Programs that use Contexts should follow these rules to keep interfaces consistent across packages and enable static analysis tools to check context propagation:


Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named ctx:


func DoSomething(ctx context.Context, arg Arg) error {
	// ... use ctx ...
}

Do not pass a nil Context, even if a function permits it. Pass context.TODO if you are unsure about which Context to use.


Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions.


The same Context may be passed to functions running in different goroutines; Contexts are safe for simultaneous use by multiple goroutines.


See http://blog.golang.org/context for example code for a server that uses Contexts.

 

  鉴于作者英文水平有限,在这里不进行对照翻译,以免误导读者。它的第一句已经介绍了它的作用了:一个贯穿API的边界和进程之间的context 类型,可以携带deadlines、cancel signals和其他信息。就如同它的中文翻译一样:上下文。在一个应用服务中会并行运行很多的goroutines或进程, 它们彼此之间或者是从属关系、竞争关系、互斥关系,不同的goroutines和进程进行交互的时候需要进行状态的切换和数据的同步,而这就是context包要支持的功能。

2.2、解析

  context的接口定义如下:

// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.Context‘s methods may be called by multiple goroutines simultaneously.
type Context interface {
   // Deadline returns the time when work done on behalf of this context
   // should be canceled.  Deadline returns ok==false when no deadline is
   // set.  Successive calls to Deadline return the same results.
   Deadline() (deadline time.Time, ok bool)
   // Done returns a channel that‘s closed when work done on behalf of this
   // context should be canceled.  Done may return nil if this context can
   // never be canceled.  Successive calls to Done return the same value.
   Done() <-chan struct{}
   // Err returns a non-nil error value after Done is closed.  Err returns
   // Canceled if the context was canceled or DeadlineExceeded if the
   // context‘s deadline passed.  No other values for Err are defined.
   // After Done is closed, successive calls to Err return the same value.
   Err() error
   // Value returns the value associated with this context for key, or nil
   // if no value is associated with key.  Successive calls to Value with
   // the same key returns the same result.
   Value(key interface{}) interface{}
}

  每一个接口都有详细的注释,这里就不重复了。 在context的源码中有以下几个结构体实现了ContextInterface:

empty context

// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int

func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
    return
}

func (*emptyCtx) Done() <-chan struct{} {
    return nil
}

func (*emptyCtx) Err() error {
    return nil
}

func (*emptyCtx) Value(key interface{}) interface{} {
    return nil
}

func (e *emptyCtx) String() string {
    switch e {
    case background:
        return "context.Background"
    case todo:
        return "context.TODO"
    }
    return "unknown empty Context"
}

  这是一个空的ctx类型,每一个返回值都为空,它什么都功能都不具备,主要的作用是作为所有的context类型的起始点,context.Background()函数返回的就是这中类型的Context:

var (
    background = new(emptyCtx)
    todo       = new(emptyCtx)
)

// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
    return background
}

  empty context的作用作者下面会阐述。 

cancle context

// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
    Context

    mu       sync.Mutex            // protects following fields
    done     chan struct{}         // created lazily, closed by first cancel call
    children map[canceler]struct{} // set to nil by the first cancel call
    err      error                 // set to non-nil by the first cancel call
}

func (c *cancelCtx) Done() <-chan struct{} {
    c.mu.Lock()
    if c.done == nil {
        c.done = make(chan struct{})
    }
    d := c.done
    c.mu.Unlock()
    return d
}

func (c *cancelCtx) Err() error {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.err
}

func (c *cancelCtx) String() string {
    return fmt.Sprintf("%v.WithCancel", c.Context)
}

// cancel closes c.done, cancels each of c‘s children, and, if
// removeFromParent is true, removes c from its parent‘s children.
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
    if err == nil {
        panic("context: internal error: missing cancel error")
    }
    c.mu.Lock()
    if c.err != nil {
        c.mu.Unlock()
        return // already canceled
    }
    c.err = err
    if c.done == nil {
        c.done = closedchan
    } else {
        close(c.done)
    }
    for child := range c.children {
        // NOTE: acquiring the child‘s lock while holding parent‘s lock.
        child.cancel(false, err)
    }
    c.children = nil
    c.mu.Unlock()

    if removeFromParent {
        removeChild(c.Context, c)
    }
}

timer context

value context

  

  作者接下来会说明context的使用方式,首先来看示例一:

  

p { margin-bottom: 0.25cm; direction: ltr; color: rgb(0, 0, 10); line-height: 120%; text-align: left }
p.western { font-family: "Liberation Serif", serif; font-size: 12pt }
p.cjk { font-family: "Noto Sans CJK SC Regular"; font-size: 12pt }
p.ctl { font-family: "Noto Sans CJK SC Regular"; font-size: 12pt }
a:link { }

   

参考网址

[1] https://grpc.io/docs/guides/concepts.html#service-definition

原文地址:https://www.cnblogs.com/cnblogs-wangzhipeng/p/9112751.html

时间: 2024-11-15 00:46:15

grpc 入门(二)-- context的相关文章

Go GRPC 入门(二)

前言 最近较忙,其实准备一篇搞定的 中途有事,只能隔了一天再写 正文 pb.go 需要注意的是,在本个 demo 中,客户端与服务端都是 Golang,所以在客户端与服务端都公用一个 pb.go 模板文件(如果是不同的语言生成的pb是对应语言),可以将 pb.go 文件放置在云上由双方引用,也可以生成两个副本放在两端项目中,本次就使用 COPY 两份的方式 由于 Golang 一个文件夹只有一个 package,而生成的 pb.go 文件 package 为创建 proto 的名字(示例为 sp

[WebGL入门]二,开始WebGL之前,先了解一下canvas

注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的额外说明,我会加上[lufy:],另外,鄙人webgl研究还不够深入,一些专业词语,如果翻译有误,欢迎大家指正. HTML5和canvas标签 现在(2012年2月)HTML5依然处于草案阶段. HTML5支持网页端的多媒体功能和画布功能,追加了很多全新的更合理的Tag标签,各个浏览器也都在逐渐的完善这些新的特性. Canvas对象表示一个 HTML画布元素,如它的名字一样,它定义了一个API支持脚本化客

[WebGL入门]二十,绘制立体模型(圆环体)

注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的额外说明,我会加上[lufy:],另外,鄙人webgl研究还不够深入,一些专业词语,如果翻译有误,欢迎大家指正. 本次的demo的运行结果 立体的模型 这次稍微喘口气,开始绘制立体模型.这里说的[喘口气]是指本次的文章中没有出现任何新的技术知识点.只是利用到现在为止所介绍过的内容,来绘制一个立体的圆环体.到现在为止,只绘制了三角形和四边形,当然,在三维空间中绘制简单的多边形也没什么不对,但是缺点儿说服力.

kafka入门二:Kafka的设计思想、理念

本节主要从整体角度介绍Kafka的设计思想,其中的每个理念都可以深入研究,以后我可能会发专题文章做深入介绍,在这里只做较概括的描述以便大家更好的理解Kafka的独特之处.本节主要涉及到如下主要内容: Kafka设计基本思想 Kafka中的数据压缩 Kafka消息转运过程中的可靠性 Kafka集群镜像复制 Kafka 备份机制 一.kafka由来 由于对JMS日常管理的过度开支和传统JMS可扩展性方面的局限,LinkedIn(www.linkedin.com)开发了Kafka以满足他们对实时数据流

Netty入门二:开发第一个Netty应用程序

    既然是入门,那我们就在这里写一个简单的Demo,客户端发送一个字符串到服务器端,服务器端接收字符串后再发送回客户端. 2.1.配置开发环境 1.安装JDK 2.去官网下载jar包 (或者通过pom构建) 2.2.认识下Netty的Client和Server 一个Netty应用模型,如下图所示,但需要明白一点的是,我们写的Server会自动处理多客户端请求,理论上讲,处理并发的能力决定于我们的系统配置及JDK的极限. Client连接到Server端 建立链接发送/接收数据 Server端

Thinkphp入门 二 —空操作、空模块、模块分组、前置操作、后置操作、跨模块调用(46)

原文:Thinkphp入门 二 -空操作.空模块.模块分组.前置操作.后置操作.跨模块调用(46) [空操作处理] 看下列图: 实际情况:我们的User控制器没有hello()这个方法 一个对象去访问这个类不存在的方法,那么它会去访问”魔术方法__call()” 用户访问一个不存在的操作—>解决:给每个控制器都定义个_empty()方法来处理 第二个解决方法:定义一个空操作 [空模块处理] 我们使用一个类,但是现在这个类还没有被include进来. 我们可以通过自动加载机制处理__autoloa

转 Python爬虫入门二之爬虫基础了解

静觅 » Python爬虫入门二之爬虫基础了解 2.浏览网页的过程 在用户浏览网页的过程中,我们可能会看到许多好看的图片,比如 http://image.baidu.com/ ,我们会看到几张的图片以及百度搜索框,这个过程其实就是用户输入网址之后,经过DNS服务器,找到服务器主机,向服务器发出一个请求,服务器经过解析之后,发送给用户的浏览器 HTML.JS.CSS 等文件,浏览器解析出来,用户便可以看到形形色色的图片了. 因此,用户看到的网页实质是由 HTML 代码构成的,爬虫爬来的便是这些内容

Redbean:入门(二) - Find

<?php require_once 'rb.php'; $tableName = 'link'; //连接数据库 R::setup('mysql:host=localhost;dbname=hwibs_model','root',''); //链接表 R::dispense($tableName); //1.获取对象记录句柄,如果不存在id的情况下就无法使用load,那么可以使用find方法进行查找 $result = R::find($tableName,'id > 4');//普通使用

DevExpress XtraReports 入门二 创建 data-aware(数据感知) 报表

原文:DevExpress XtraReports 入门二 创建 data-aware(数据感知) 报表 本文只是为了帮助初次接触或是需要DevExpress XtraReports报表的人群使用的,为了帮助更多的人不会像我这样浪费时间才写的这篇文章,高手不想的看请路过 本文内容来DevExpress XtraReports帮助文档,如看过类似的请略过. 废话少说 开始正事 一.创建应用程序并添加报表 启动 MS Visual Studio (2005.2008.或 2010). 在 Visua

MongooooooooooooooooooooDB入门二:基本概念介绍

前言 工欲善其事必先利其器.在学习MongoDB之前,需要对MongoDB的一些基本概念有系统的了解. 所以,本篇文章主要介绍MongoDB的一些基本概念,这些概念的定义均来自<MongoDB权威指南>,关于此书想要了解更多,请点击此处. 我尽量使用最简洁的语言来尽可能完整地描述这些基本概念,如有遗漏或不妥之处欢迎指正. 文档 文档是MongoDB的核心概念之一.多个键值对有序地放在一起便是文档.例如: {"name":"Jerry","sco