golang使用grpc+go-kit模拟oauth认证的操作

我们使用grpc对外的接口,进行服务,模拟对外认证的接口

首先我们要了解oauth的基本认证过程

第三方的服务端,在oauth2.0中作为一个客户端的身份,进行请求数据。

用户进行选择第三方的登陆,比如选择到某一个第三方的平台进行登陆,则会跳转到第三方登陆平台

用户输入用户名密码,在第三方平台进行登陆,,如果登陆成功,则返回code。

客户端,也就是我们想要登陆的网站,将会读取code,并且将会携带这个code,和第三方网站所颁发的密码,进行请求token,如果code和注册时所得到的密码,都验证成功,此时,第三方客户端会返回一个token。

我们登陆的网站会携带这个token去请求用户身份资源的服务器,如果token比对成功,则返回用户的信息所以我们需要一些服务

codeserver,作用,分发code,验证code的准确性

tokenserver,作用分发token,验证token的准确性

loginserver,作用,登陆成功后,调用codeserver得到code

userdetailserver,作用调用tokenserver的token验证,验证token是否合法,如果合法,进行返回用户的基本信息继续,我们大概看一下这些功能具体怎样实现。

实现

codeserver

type Codeserver struc (
	GetCode ()
	ValidCode ()
)
//函数的具体传参今不写了

其实我们的code和token,主要是使用redis数据库进行实现,并且给申请的code和token设置过期时间, 也就是说,在数据库中实现一个定时的作用,如果,申请完code,长时间不申请token则这个code会过期,就会让用户重新进行登陆,重新获取code

func (s ServicesA) GetCode(c context.Context, req *codeserver.GetCodeReuqest) (*codeserver.RCodeResponse, error) {
	con , err := UseRedis()//加载redis,用于操作redis
	if err != nil {
		return nil , errors.New("the redis databases is not work")
	}
	randstr :=  GetRandomString(10)//随机生成一个字符串作为code
	_ , err = con.Do("hset" , req.UserId , "code" , randstr)//插入数据库,用于获取token时进行验证
	con.Do("set" , randstr , req.UserId , "EX" , 120)
	con.Do("EXPIRE" , req.UserId , 20)//设置code的过期时间
	if err != nil {
		return nil , errors.New("data is not insert")
	}
	return &codeserver.RCodeResponse{Code: randstr} , nil
}
//检查code是否合法
func (s ServicesA) Isvalid(c context.Context, req *codeserver.ValidRequest) (*codeserver.ValidResponse, error) {
	con , err := UseRedis()//加载redis
	if err != nil {
		return nil , errors.New("the databses is not work")
	}
	r , err := con.Do("get" , req.Code)//找到code,如果能找到code,则合法,找不到则不合法
	if err != nil {
		return nil , err
	}
	if r == nil {
		return &codeserver.ValidResponse{IsValid: false} , nil
	} else {
		return &codeserver.ValidResponse{IsValid: true} , nil
	}
}

至于其他的endpoint层和transport层等等,就先不写了,我们就这篇文章主要是看怎样模拟实现oauth

tokenserver

func Isvalid (request *codeserver.ValidRequest) bool {
	lis , err := grpc.Dial("127.0.0.1:8081" , grpc.WithInsecure())
	if err != nil {
		log.Println(err)
		return false
	}
	client := codeserver.NewCodeServerClient(lis)
	rep , err := client.Isvalid(context.Background() , request)
	if err != nil {
		log.Println(err)
		return false
	}
	if rep.IsValid {
		return true
	} else {
		return false
	}
}
func (s ServiceAI) GetToken(ctx context.Context, req *tokenservice.ReqGetToken) (*tokenservice.RepGetToken, error) {
//判断code是否合法
	if !Isvalid(&codeserver.ValidRequest{UserId: req.UserId , Code: req.Code}) {
		return nil , errors.New("code is not valid ")
	}
	con , err := UseRedis()
	if err != nil {
		return nil , errors.New("connet database default")
	}
	//通过code获取clientid
	User := GetUserId(req.Code)
	mysql , err := UseMysql()
	if err != nil {
		log.Println("get secrete default")
	}
	var c Client
	mysql.Table("client").Where("id = ?",req.ClientId).Find(&c)
//在mysql数据库中进行查找,请求所携带的密码,是否与第三方注册时给的密码是否相同,如果不相同,则不返回token。
	if c.Secret !=req.Secret {
		fmt.Println(c.Secret , " " , req.Secret)
		return nil , errors.New("not pi pei")
	}
	str := GetRandomString(11)
	_ , err = con.Do("hset" , User , "token" ,  str)
	con.Do("EXPIRE" , User , 120)
	//将生成的token进行插入数据库,并设置过期时间,如果避免token被多次利用
	con.Do("set" , str , User , "EX" , 120)
	//设置userid和token的对应关系,避免没有对应上,客户端拿到token之后随便拿取其他人的用户滤数据
	if err != nil {
		return nil , err
	}
	return &tokenservice.RepGetToken{Toen: str} , nil
}
//判断token是都合法,给userdetailserver用,当服务器接到token后,需要调用这个接口,查看token是否合法,如果合法返回用户数据
func (s ServiceAI) IsValidToken(ctx context.Context, req *tokenservice.IsValidTokenReq) (*tokenservice.IsValidToeknRep, error) {
	con , err := UseRedis()
	if err != nil {
		log.Println(err)
		return nil , err
	}
	r , err := con.Do("get" ,req.Token)
	if err != nil {
		return nil , err
	}
	if r == nil {
		return &tokenservice.IsValidToeknRep{IsValid: false} , nil
	}
	rep := string(r.([]uint8))
	return &tokenservice.IsValidToeknRep{IsValid: true , Userid: rep} , nil
}

useroauthserver

type User struct {
	Id int
	Name string
	Password string
	Al string
	UId string
}
func usemysql () (*gorm.DB , error) {
	return gorm.Open("mysql" , "root:123456@/oauth?charset=utf8&parseTime=True&loc=Local")
}
//调用codeserver接口,进行拿取code
func getcode (userid string) string {
	con , err := grpc.Dial(":8081" , grpc.WithInsecure())
	if err != nil {
		log.Println(err , errors.New("get code default"))
	}
	client := codeserver.NewCodeServerClient(con)
	rep , err := client.GetCode(context.Background() , &codeserver.GetCodeReuqest{UserId: userid})
	if err != nil || rep == nil{
		log.Println(err)
		return ""
	}
	return rep.Code
}
//认证用户,将上传的用户名和密码进行比对。
func (a AuthServicesA) AuthT(ctx context.Context, req *userauth.AuthRequest) (*userauth.AuthResponse, error) {
	con , err := usemysql()
	if err != nil {
		log.Println(err)
		return nil , errors.New("the database is connect default")
	}
	var u User
	con.Table("user").Where("uid =?" , req.Id).Find(&u)
	//在数据库中进行查找,如果没找到该用户,说明该用户不存在,或者用户输入错误
	if &u == nil {
		return nil , errors.New("the id is wrong ")
	}
	if req.Password != u.Password {
		return nil , errors.New("the user password is wrong")
	}
//如果认证成功,则进行调用codeserver接口,返回code
	code :=getcode(req.Id)
	if code == "" {
		return &userauth.AuthResponse{IsTrue: false} , nil
	}
	return &userauth.AuthResponse{Code: code , IsTrue: true} , nil
}

基本原理就是这样,但是我们还是差一个userdetail的服务端

这个服务端,主要作用就是拿到请求的token,并进行检验,如果检验成功,返回用户数据,至于怎样检验,就是调用tokenserver中的检验接口。

这里就不写了,留给读者完成。

我写的这三个接口在gitee上有源码,是基于golang写的,使用的框架有grpc,go-kit的服务框架。

具体地址gitee.com/silves-xiang

补充:go-kit实践之2:go-kit 实现注册发现与负载均衡

一、介绍

grpc提供了简单的负载均衡,需要自己实现服务发现resolve。我们既然要使用go-kit来治理微服务,那么我们就使用go-kit的注册发现、负载均衡机制。

go-kit官方【stringsvc3】例子中使用的负载均衡方案是通过服务端转发进行,翻找下源码go-kit的服务注册发现、负载均衡在【sd】包中。下面我们介绍怎么通过go-kit进行客户端负载均衡。

go-kit提供的注册中心

1、 etcd

2、 consul

3、 eureka

4、 zookeeper

go-kit提供的负载均衡

1、 random[随机]

2、 roundRobin[轮询]

只需实现Balancer接口,我们可以很容易的增加其它负载均衡机制

type Balancer interface {
   Endpoint() (endpoint.Endpoint, error)
}

etcd注册发现

etcd和zookeeper类似是一个高可用、强一致性的存储仓库,拥有服务发现功能。 我们就通过go-kit提供的etcd包来实现服务注册发现

二、示例

1、protobuf文件及生成对应的go文件

syntax = "proto3";

// 请求书详情的参数结构  book_id 32位整形
message BookInfoParams {
    int32 book_id = 1;
} 

// 书详情信息的结构   book_name字符串类型
message BookInfo {
    int32 book_id = 1;
    string  book_name = 2;
}

// 请求书列表的参数结构  page、limit   32位整形
message BookListParams {
    int32 page = 1;
    int32 limit = 2;
}

// 书列表的结构    BookInfo结构数组
message BookList {
    repeated BookInfo book_list = 1;
}
// 定义 获取书详情  和 书列表服务   入参出参分别为上面所定义的结构
service BookService {
    rpc GetBookInfo (BookInfoParams) returns (BookInfo) {}
    rpc GetBookList (BookListParams) returns (BookList) {}
}

生成对应的go语言代码文件:protoc --go_out=plugins=grpc:. book.proto (其中:protobuf文件名为:book.proto)

2、Server端代码

package main
import (
	"MyKit"
	"context"
	"fmt"
	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/sd/etcdv3"
	grpc_transport "github.com/go-kit/kit/transport/grpc"
	"google.golang.org/grpc"
	"net"
	"time"
)

type BookServer struct {
	bookListHandler grpc_transport.Handler
	bookInfoHandler grpc_transport.Handler
}

//一下两个方法实现了 protoc生成go文件对应的接口:
/*
// BookServiceServer is the server API for BookService service.
type BookServiceServer interface {
	GetBookInfo(context.Context, *BookInfoParams) (*BookInfo, error)
	GetBookList(context.Context, *BookListParams) (*BookList, error)
}
*/
//通过grpc调用GetBookInfo时,GetBookInfo只做数据透传, 调用BookServer中对应Handler.ServeGRPC转交给go-kit处理
func (s *BookServer) GetBookInfo(ctx context.Context, in *book.BookInfoParams) (*book.BookInfo, error) {

	_, rsp, err := s.bookInfoHandler.ServeGRPC(ctx, in)
	if err != nil {
		return nil, err

	}
	/*
	if info,ok:=rsp.(*book.BookInfo);ok {
		return info,nil
	}
	return nil,errors.New("rsp.(*book.BookInfo)断言出错")
	*/
	return rsp.(*book.BookInfo), err //直接返回断言的结果
}

//通过grpc调用GetBookList时,GetBookList只做数据透传, 调用BookServer中对应Handler.ServeGRPC转交给go-kit处理
func (s *BookServer) GetBookList(ctx context.Context, in *book.BookListParams) (*book.BookList, error) {
	_, rsp, err := s.bookListHandler.ServeGRPC(ctx, in)
	if err != nil {
		return nil, err
	}
	return rsp.(*book.BookList), err
}

//创建bookList的EndPoint
func makeGetBookListEndpoint()endpoint.Endpoint  {
	return func(ctx context.Context, request interface{}) (response interface{}, err error) {
		b:=new(book.BookList)
		b.BookList=append(b.BookList,&book.BookInfo{BookId:1,BookName:"Go语言入门到精通"})
		b.BookList=append(b.BookList,&book.BookInfo{BookId:2,BookName:"微服务入门到精通"})
		b.BookList=append(b.BookList,&book.BookInfo{BookId:2,BookName:"区块链入门到精通"})
		return b,nil
	}
}

//创建bookInfo的EndPoint
func makeGetBookInfoEndpoint() endpoint.Endpoint {
	return func(ctx context.Context, request interface{}) (interface{}, error) {
		//请求详情时返回 书籍信息
		req := request.(*book.BookInfoParams)
		b := new(book.BookInfo)
		b.BookId = req.BookId
		b.BookName = "Go入门到精通"
		return b, nil
	}
}

func decodeRequest(_ context.Context, req interface{}) (interface{}, error) {
	return req, nil
}

func encodeResponse(_ context.Context, rsp interface{}) (interface{}, error) {
	return rsp, nil
}

func main() {
	var (
		etcdServer     = "127.0.0.1:2379"        //etcd服务的IP地址
		prefix         = "/services/book/"       //服务的目录
		ServerInstance = "127.0.0.1:50052"       //当前实例Server的地址
		key            = prefix + ServerInstance //服务实例注册的路径
		value          = ServerInstance
		ctx            = context.Background()
		//服务监听地址
		serviceAddress = ":50052"
	)
	//etcd连接参数
	option := etcdv3.ClientOptions{DialTimeout: time.Second * 3, DialKeepAlive: time.Second * 3}
	//创建连接
	client, err := etcdv3.NewClient(ctx, []string{etcdServer}, option)
	if err != nil {
		panic(err)
	}
	//创建注册
	registrar := etcdv3.NewRegistrar(client, etcdv3.Service{Key: key, Value: value}, log.NewNopLogger())
	registrar.Register() //启动注册服务
	bookServer := new(BookServer)
	bookListHandler := grpc_transport.NewServer(
		makeGetBookListEndpoint(),
		decodeRequest,
		encodeResponse,
	)
	bookServer.bookListHandler = bookListHandler

	bookInfoHandler := grpc_transport.NewServer(
		makeGetBookInfoEndpoint(),
		decodeRequest,
		encodeResponse,
	)
	bookServer.bookInfoHandler = bookInfoHandler

	listener, err := net.Listen("tcp", serviceAddress) //网络监听,注意对应的包为:"net"
	if err != nil {
		fmt.Println(err)
		return
	}
	gs := grpc.NewServer(grpc.UnaryInterceptor(grpc_transport.Interceptor))
	book.RegisterBookServiceServer(gs, bookServer) //调用protoc生成的代码对应的注册方法
	gs.Serve(listener)                             //启动Server

}

3、Client端代码

package main
import (
	"MyKit"
	"context"
	"fmt"
	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/sd"
	"github.com/go-kit/kit/sd/etcdv3"
	"github.com/go-kit/kit/sd/lb"
	"google.golang.org/grpc"
	"io"
	"time"
)

func main() {

	var (
		//注册中心地址
		etcdServer = "127.0.0.1:2379"
		//监听的服务前缀
		prefix = "/services/book/"
		ctx    = context.Background()
	)
	options := etcdv3.ClientOptions{
		DialTimeout:   time.Second * 3,
		DialKeepAlive: time.Second * 3,
	}
	//连接注册中心
	client, err := etcdv3.NewClient(ctx, []string{etcdServer}, options)
	if err != nil {
		panic(err)
	}
	logger := log.NewNopLogger()
	//创建实例管理器, 此管理器会Watch监听etc中prefix的目录变化更新缓存的服务实例数据
	instancer, err := etcdv3.NewInstancer(client, prefix, logger)
	if err != nil {
		panic(err)
	}
	//创建端点管理器, 此管理器根据Factory和监听的到实例创建endPoint并订阅instancer的变化动态更新Factory创建的endPoint
	endpointer := sd.NewEndpointer(instancer, reqFactory, logger) //reqFactory自定义的函数,主要用于端点层(endpoint)接受并显示数据
	//创建负载均衡器
	balancer := lb.NewRoundRobin(endpointer)

	/**
	我们可以通过负载均衡器直接获取请求的endPoint,发起请求
	reqEndPoint,_ := balancer.Endpoint()
	*/

	/**
	也可以通过retry定义尝试次数进行请求
	*/
	reqEndPoint := lb.Retry(3, 3*time.Second, balancer)

	//现在我们可以通过 endPoint 发起请求了
	req := struct{}{}
	if _, err = reqEndPoint(ctx, req); err != nil {
		panic(err)
	}
}

//通过传入的 实例地址  创建对应的请求endPoint
func reqFactory(instanceAddr string) (endpoint.Endpoint, io.Closer, error) {
	return func(ctx context.Context, request interface{}) (interface{}, error) {
		fmt.Println("请求服务: ", instanceAddr)
		conn, err := grpc.Dial(instanceAddr, grpc.WithInsecure())
		if err != nil {
			fmt.Println(err)
			panic("connect error")
		}
		defer conn.Close()
		bookClient := book.NewBookServiceClient(conn)
		bi, _ := bookClient.GetBookInfo(context.Background(), &book.BookInfoParams{BookId: 1})
		fmt.Println("获取书籍详情")
		fmt.Println("bookId: 1", " => ", "bookName:", bi.BookName)

		bl, _ := bookClient.GetBookList(context.Background(), &book.BookListParams{Page: 1, Limit: 10})
		fmt.Println("获取书籍列表")
		for _, b := range bl.BookList {
			fmt.Println("bookId:", b.BookId, " => ", "bookName:", b.BookName)
		}
		return nil, nil
	}, nil, nil
}

4、运行

(1)安装etcd并启动

由于本实例服务发现采用了etcd,因此在运行之前需要先安装etcd并运行。

(2)etcd是一个分布式一致性键值存储,其主要用于分布式系统的共享配置和服务发现。etcd由Go语言编写.

下载地址: https://github.com/coreos/etcd/releases

将压缩文件解压到指定文件夹,解压后的目录如下:

其中etcd.exe是服务端,etcdctl.exe是客户端。点击etcd.exe运行etcd服务。(注:设置环境变量自由决定,此实例也可以不用设置)

(2)实例运行

先运行Server端,在运行Client端,效果如下:

5、问题汇总

如果运行时,提示一下错误:

panic: /debug/requests is already registered. You may have two independent copies of golang.org/x/net/trace in your binary, trying to maintain separate state. This may involve a vendored copy of golang.org/
x/net/trace.

goroutine 1 [running]:
go.etcd.io/etcd/vendor/golang.org/x/net/trace.init.0()
        D:/GoSrc/src/go.etcd.io/etcd/vendor/golang.org/x/net/trace/trace.go:116 +0x1ab
exit status 2

说明golang.org/x/net/包下的 trace 与go.etcd.io/etcd/vendor/golang.org/x/net/ 包下trace有冲突,解决方法:找到go.etcd.io\etcd\vendor目录:

由于已经在src目录下存在golang.org 与google.golang.org两个包

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • golang grpc 负载均衡的方法

    微服务架构里面,每个服务都会有很多节点,如果流量分配不均匀,会造成资源的浪费,甚至将一些机器压垮,这个时候就需要负载均衡,最简单的一种策略就是轮询,顺序依次选择不同的节点访问. grpc 在客户端提供了负载均衡的实现,并提供了服务地址解析和更新的接口(默认提供了 DNS 域名解析的支持),方便不同服务的集成 使用示例 conn, err := grpc.Dial( "", grpc.WithInsecure(), // 负载均衡,使用 consul 作服务发现 grpc.WithBal

  • 详解golang consul-grpc 服务注册与发现

    在微服务架构里面,每个小服务都是由很多节点组成,节点的添加删除故障希望能对下游透明,因此有必要引入一种服务的自动注册和发现机制,而 consul 提供了完整的解决方案,并且内置了对 GRPC 以及 HTTP 服务的支持 总体架构 服务调用: client 直连 server 调用服务 服务注册: 服务端将服务的信息注册到 consul 里 服务发现: 客户端从 consul 里发现服务信息,主要是服务的地址 健康检查: consul 检查服务器的健康状态 服务注册 服务端将服务信息注册到 con

  • golang 微服务之gRPC与Protobuf的使用

    RPC是什么? 所谓RPC(remote procedure call 远程过程调用)框架实际是提供了一套机制,使得应用程序之间可以进行通信,而且也遵从server/client模型.使用的时候客户端调用server端提供的接口就像是调用本地的函数一样. gRPC是什么? 与许多RPC系统一样,gRPC基于定义服务的思想,指定可以使用其参数和返回类型远程调用的方法.默认情况下,gRPC使用协议缓冲区作为接口定义语言(IDL)来描述服务接口和有效负载消息的结构. gRPC有什么好处以及在什么场景下

  • golang使用grpc+go-kit模拟oauth认证的操作

    我们使用grpc对外的接口,进行服务,模拟对外认证的接口 首先我们要了解oauth的基本认证过程 第三方的服务端,在oauth2.0中作为一个客户端的身份,进行请求数据. 用户进行选择第三方的登陆,比如选择到某一个第三方的平台进行登陆,则会跳转到第三方登陆平台 用户输入用户名密码,在第三方平台进行登陆,,如果登陆成功,则返回code. 客户端,也就是我们想要登陆的网站,将会读取code,并且将会携带这个code,和第三方网站所颁发的密码,进行请求token,如果code和注册时所得到的密码,都验

  • golang在GRPC中设置client的超时时间

    超时 建立连接 主要就2函数Dail和DialContext. // Dial creates a client connection to the given target. func Dial(target string, opts ...DialOption) (*ClientConn, error) { return DialContext(context.Background(), target, opts...) } func DialContext(ctx context.Cont

  • OAuth认证协议中的HMACSHA1加密算法(实例)

    实例如下: <?php function hmacsha1($key,$data) { $blocksize=64; $hashfunc='sha1'; if (strlen($key)>$blocksize) $key=pack('H*', $hashfunc($key)); $key=str_pad($key,$blocksize,chr(0x00)); $ipad=str_repeat(chr(0x36),$blocksize); $opad=str_repeat(chr(0x5c),$

  • 使用新浪微博API的OAuth认证发布微博实例

    继续前面的文章<新浪微博OAuth认证和储存的主要过程详解>,现在我们就使用它来发布微博. 我们已经将用户新浪微博的oauth_token和oauth_secret保存到 $_SESSION['oauth_token']=$result['oauth_token']; $_SESSION['oauth_secret']=$result['oauth_secret']; 里面,现在要做的就很简单了··就是调用sinaOauth的类进行发布.. 代码如下: //Statuses/update $c

  • 新浪微博OAuth认证和储存的主要过程详解

    网上很多关于OAuth的文章,但是包括sina本身都都没有详细的的介绍,包括验证过程和验证后数据的储存,所以参考了Twitter的认证过程写下一些详细的注释代码. 在我们开始前,我们先建立一张数据库来保存用户信息,下面是一个基本的 Mysql 的例子: CREATE TABLE `oauth_users` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `oauth_provider` VARCHAR(10), `oauth_uid` text,

  • 使用PHP模拟HTTP认证

    如果你希望在每个脚本的基础上实现口令保护功能,那么你可以通过结合header()函数和$PHP_AUTH_USER.$PHP_AUTH_PW全局变量的方法来创建一个基本认证机制.通常基于服务器的认证请求/响应过程如下: 1. 用户向一台Web服务器请求一个文件.如果文件在一个受到保护的区域以内,服务器就在响应数据的头部内加上401(非法用户)字符串作为回应. 2.浏览器看见该响应之后弹出用户名/口令对话框. 3.用户在对话框中输入用户名和口令,然后单击"OK"把这些信息送回到服务器进行

  • python golang中grpc 使用示例代码详解

    python 1.使用前准备,安装这三个库 pip install grpcio pip install protobuf pip install grpcio_tools 2.建立一个proto文件hello.proto // [python quickstart](https://grpc.io/docs/quickstart/python.html#run-a-grpc-application) // python -m grpc_tools.protoc --python_out=. -

  • Golang实现gRPC的Proxy的原理解析

    背景 gRPC是Google开始的一个RPC服务框架, 是英文全名为Google Remote Procedure Call的简称. 广泛的应用在有RPC场景的业务系统中,一些架构中将gRPC请求都经过一个gRPC服务代理节点或网关,进行服务的权限现在,限流,服务调用简化,增加请求统计等等诸多功能. 如下以Golang和gRPC为例,解析gRPC的转发原理. gRPC Proxy原理 基本原理如下 基于TCP启动一个gRPC代理服务 拦截gRPC框架的服务映射,能将gRPC请求的服务拦截到转发代

  • golang下grpc框架的使用编写示例

    目录 1. 什么是grpc和protobuf 1.1 grpc 1.2 protobuf 2.go下grpc 2.1官网下载protobuf工具 2.2 下载go的依赖包 2.3 编写proto文件 2.4 生成hello.pb.proto文件 2.5 编写server端代码 2.6 编写client端代码 2.7 python和go相互调用实践(跨语言调用) 1. 什么是grpc和protobuf 1.1 grpc gRPC是一个高性能.开源和通用的RPC框架,面向移动和HTTP/2设计. 目

  • Golang开发gRPC服务入门介绍

    目录 1.安装protoc 2.安装protoc的Golang gRPC插件 3.编写proto文件 4.生成gRPC代理代码 5.编写gRPC服务端程序 6.编写gRPC客户端程序 7.运行程序 gRPC是Google发起的一个开源RPC框架,使用HTTP/2传输协议,使用Protocol Buffers编码协议,相比RESTful框架的程序性能提高不少,而且当前流行的编程语言基本都已经支持. Golang开发gRPC应用程序的套路也已经很清晰,这篇文章就来做一个简单的介绍,算是入门. 1.安

随机推荐