当前仓库属于暂停状态,部分功能使用受限,详情请查阅 仓库状态说明
2 Star 0 Fork 1

JUMEI_ARCH/go-plugins
暂停

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
grpc.go 18.27 KB
一键复制 编辑 原始数据 按行查看 历史
miaolz123 提交于 2018-08-09 18:32 . feat: catch grpc status error
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
// Package grpc provides a grpc server
package grpc
import (
"context"
"crypto/tls"
"fmt"
"io"
"net"
"reflect"
"runtime/debug"
"sort"
"strconv"
"strings"
"sync"
"time"
"github.com/micro/go-log"
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/cmd"
"github.com/micro/go-micro/codec"
"github.com/micro/go-micro/errors"
meta "github.com/micro/go-micro/metadata"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/server"
"github.com/micro/util/go/lib/addr"
mgrpc "github.com/micro/util/go/lib/grpc"
"github.com/micro/grpc-go/codes"
"github.com/micro/grpc-go/credentials"
"github.com/micro/grpc-go/encoding"
"github.com/micro/grpc-go/metadata"
"github.com/micro/grpc-go/status"
"github.com/micro/grpc-go/transport"
)
const (
defaultMaxMsgSize = 1024 * 1024 * 4 // use 4MB as the default message size limit
defaultContentType = "application/grpc"
)
type grpcServer struct {
rpc *rServer
exit chan chan error
wg sync.WaitGroup
sync.RWMutex
opts server.Options
handlers map[string]server.Handler
subscribers map[*subscriber][]broker.Subscriber
// used for first registration
registered bool
}
func init() {
cmd.DefaultServers["grpc"] = NewServer
}
func newGRPCServer(opts ...server.Option) server.Server {
options := newOptions(opts...)
return &grpcServer{
opts: options,
rpc: &rServer{
serviceMap: make(map[string]*service),
},
handlers: make(map[string]server.Handler),
subscribers: make(map[*subscriber][]broker.Subscriber),
exit: make(chan chan error),
}
}
func (g *grpcServer) getCredentials() credentials.TransportCredentials {
if g.opts.Context != nil {
if v := g.opts.Context.Value(tlsAuth{}); v != nil {
tls := v.(*tls.Config)
return credentials.NewTLS(tls)
}
}
return nil
}
func (g *grpcServer) getHttp2TransportConfig() transport.ServerConfig {
if g.opts.Context != nil {
if v := g.opts.Context.Value(transportConfig{}); v != nil {
return *v.(*transport.ServerConfig)
}
}
return transport.ServerConfig{}
}
func (g *grpcServer) serve(l net.Listener) error {
defer l.Close()
var tempDelay time.Duration
for {
conn, err := l.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
}
select {
case <-time.After(tempDelay):
}
continue
}
return err
}
tempDelay = 0
go g.accept(conn)
}
}
func (g *grpcServer) useTransportAuthenticator(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
if creds := g.getCredentials(); creds != nil {
return creds.ServerHandshake(rawConn)
}
return rawConn, nil, nil
}
func (g *grpcServer) accept(rawConn net.Conn) {
conn, authInfo, err := g.useTransportAuthenticator(rawConn)
if err != nil {
rawConn.Close()
return
}
serverConfig := g.getHttp2TransportConfig()
serverConfig.AuthInfo = authInfo
st, err := transport.NewServerTransport("http2", conn, &serverConfig)
if err != nil {
conn.Close()
return
}
defer st.Close()
var wg sync.WaitGroup
st.HandleStreams(func(stream *transport.Stream) {
wg.Add(1)
g.wg.Add(1)
go func() {
defer func() {
wg.Done()
g.wg.Done()
if r := recover(); r != nil {
log.Log(r, string(debug.Stack()))
}
}()
g.serveStream(st, stream)
}()
}, func(ctx context.Context, method string) context.Context {
return ctx
})
wg.Wait()
}
func (g *grpcServer) serveStream(t transport.ServerTransport, stream *transport.Stream) {
// get Go method from stream method
serviceName, methodName, err := mgrpc.ServiceMethod(stream.Method())
if err != nil {
if gerr := t.WriteStatus(stream, status.New(codes.InvalidArgument, err.Error())); err != nil {
log.Logf("grpc: Server.serveStream failed to write status: %v", gerr)
}
return
}
g.rpc.mu.Lock()
service := g.rpc.serviceMap[serviceName]
g.rpc.mu.Unlock()
if service == nil {
if err := t.WriteStatus(stream, status.New(codes.Unimplemented, fmt.Sprintf("unknown service %v", service))); err != nil {
log.Logf("grpc: Server.serveStream failed to write status: %v", err)
}
return
}
mtype := service.method[methodName]
if mtype == nil {
if err := t.WriteStatus(stream, status.New(codes.Unimplemented, fmt.Sprintf("unknown service %v", service))); err != nil {
log.Logf("grpc: Server.serveStream failed to write status: %v", err)
}
return
}
// get grpc metadata
gmd, ok := metadata.FromIncomingContext(stream.Context())
if !ok {
gmd = metadata.MD{}
}
// copy the metadata to go-micro.metadata
md := meta.Metadata{}
for k, v := range gmd {
md[k] = strings.Join(v, ", ")
}
// get content type
ct := defaultContentType
if ctype, ok := md["x-content-type"]; ok {
ct = ctype
}
// get codec
codec, err := g.newGRPCCodec(ct)
if err != nil {
if errr := t.WriteStatus(stream, status.New(codes.Internal, err.Error())); errr != nil {
log.Logf("grpc: Server.serveStream failed to write status: %v", errr)
}
return
}
// timeout for server deadline
to := md["timeout"]
delete(md, "x-content-type")
delete(md, "timeout")
// create new context
ctx := meta.NewContext(stream.Context(), md)
// set the timeout if we have it
if len(to) > 0 {
if n, err := strconv.ParseUint(to, 10, 64); err == nil {
ctx, _ = context.WithTimeout(ctx, time.Duration(n))
}
}
// process unary
if !mtype.stream {
g.processRequest(t, stream, service, mtype, codec, ct, ctx)
return
}
// process strea
g.processStream(t, stream, service, mtype, codec, ct, ctx)
}
func (g *grpcServer) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, codec encoding.Codec, opts *transport.Options) error {
hd, p, err := encode(codec, msg, nil, nil, nil)
if err != nil {
log.Fatalf("grpc: Server failed to encode response %v", err)
}
return t.Write(stream, hd, p, opts)
}
func (g *grpcServer) processRequest(t transport.ServerTransport, stream *transport.Stream, service *service, mtype *methodType, codec encoding.Codec, ct string, ctx context.Context) (err error) {
p := &parser{r: stream}
for {
pf, req, err := p.recvMsg(defaultMaxMsgSize)
if err == io.EOF {
// The entire stream is done (for unary RPC only).
return err
}
if err == io.ErrUnexpectedEOF {
err = Errorf(codes.Internal, io.ErrUnexpectedEOF.Error())
}
if err != nil {
switch err := err.(type) {
case *rpcError:
if err := t.WriteStatus(stream, status.New(err.code, err.desc)); err != nil {
log.Logf("grpc: Server.processUnaryRPC failed to write status %v", err)
}
case transport.ConnectionError:
// Nothing to do here.
case interface{ GRPCStatus() *status.Status }:
if err := t.WriteStatus(stream, err.GRPCStatus()); err != nil {
log.Logf("grpc: Server.processUnaryRPC failed to write status %v", err)
}
default:
panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err))
}
return err
}
if err := checkRecvPayload(pf, stream.RecvCompress(), nil); err != nil {
switch err := err.(type) {
case *rpcError:
if err := t.WriteStatus(stream, status.New(err.code, err.desc)); err != nil {
log.Logf("grpc: Server.processUnaryRPC failed to write status %v", err)
}
default:
if err := t.WriteStatus(stream, status.New(codes.Internal, err.Error())); err != nil {
log.Logf("grpc: Server.processUnaryRPC failed to write status %v", err)
}
}
return err
}
// status code/desc
statusCode := codes.OK
statusDesc := ""
// exceeds max message size, bail early
if len(req) > defaultMaxMsgSize {
statusCode = codes.Internal
statusDesc = fmt.Sprintf("grpc: server received a message of %d bytes exceeding %d limit", len(req), defaultMaxMsgSize)
return t.WriteStatus(stream, status.New(statusCode, statusDesc))
}
var argv, replyv reflect.Value
// Decode the argument value.
argIsValue := false // if true, need to indirect before calling.
if mtype.ArgType.Kind() == reflect.Ptr {
argv = reflect.New(mtype.ArgType.Elem())
} else {
argv = reflect.New(mtype.ArgType)
argIsValue = true
}
// Unmarshal request
if err := codec.Unmarshal(req, argv.Interface()); err != nil {
statusCode = convertCode(err)
statusDesc = err.Error()
if err := t.WriteStatus(stream, status.New(statusCode, statusDesc)); err != nil {
log.Logf("grpc: Server.processUnaryRPC failed to write status: %v", err)
return err
}
return nil
}
if argIsValue {
argv = argv.Elem()
}
// reply value
replyv = reflect.New(mtype.ReplyType.Elem())
function := mtype.method.Func
var returnValues []reflect.Value
// create a client.Request
r := &rpcRequest{
service: g.opts.Name,
contentType: ct,
method: fmt.Sprintf("%s.%s", service.name, mtype.method.Name),
request: argv.Interface(),
}
// define the handler func
fn := func(ctx context.Context, req server.Request, rsp interface{}) error {
returnValues = function.Call([]reflect.Value{service.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(req.Request()), reflect.ValueOf(rsp)})
// The return value for the method is an error.
if err := returnValues[0].Interface(); err != nil {
return err.(error)
}
return nil
}
// wrap the handler func
for i := len(g.opts.HdlrWrappers); i > 0; i-- {
fn = g.opts.HdlrWrappers[i-1](fn)
}
// execute the handler
if appErr := fn(ctx, r, replyv.Interface()); appErr != nil {
if err, ok := appErr.(*rpcError); ok {
statusCode = err.code
statusDesc = err.desc
} else if err, ok := appErr.(*errors.Error); ok {
statusCode = microError(err)
statusDesc = appErr.Error()
} else {
statusCode = convertCode(appErr)
statusDesc = appErr.Error()
}
if err := t.WriteStatus(stream, status.New(statusCode, statusDesc)); err != nil {
log.Logf("grpc: Server.processUnaryRPC failed to write status: %v", err)
return err
}
return nil
}
opts := &transport.Options{
Last: true,
}
if err := g.sendResponse(t, stream, replyv.Interface(), codec, opts); err != nil {
switch err := err.(type) {
case transport.ConnectionError:
// Nothing to do here.
default:
statusCode = codes.Unknown
statusDesc = err.Error()
}
return err
}
return t.WriteStatus(stream, status.New(statusCode, statusDesc))
}
}
func (g *grpcServer) processStream(t transport.ServerTransport, stream *transport.Stream, service *service, mtype *methodType, codec encoding.Codec, ct string, ctx context.Context) (err error) {
opts := g.opts
r := &rpcRequest{
service: opts.Name,
contentType: ct,
method: fmt.Sprintf("%s.%s", service.name, mtype.method.Name),
stream: true,
}
ss := &rpcStream{
request: r,
t: t,
s: stream,
p: &parser{r: stream},
codec: codec,
maxMsgSize: defaultMaxMsgSize,
}
function := mtype.method.Func
var returnValues []reflect.Value
// Invoke the method, providing a new value for the reply.
fn := func(ctx context.Context, req server.Request, stream interface{}) error {
returnValues = function.Call([]reflect.Value{service.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(stream)})
if err := returnValues[0].Interface(); err != nil {
return err.(error)
}
return nil
}
for i := len(opts.HdlrWrappers); i > 0; i-- {
fn = opts.HdlrWrappers[i-1](fn)
}
appErr := fn(ctx, r, ss)
if appErr != nil {
if err, ok := appErr.(*rpcError); ok {
ss.statusCode = err.code
ss.statusDesc = err.desc
} else if err, ok := appErr.(*errors.Error); ok {
ss.statusCode = microError(err)
ss.statusDesc = appErr.Error()
} else {
ss.statusCode = convertCode(appErr)
ss.statusDesc = appErr.Error()
}
}
return t.WriteStatus(ss.s, status.New(ss.statusCode, ss.statusDesc))
}
func (g *grpcServer) newGRPCCodec(contentType string) (encoding.Codec, error) {
codecs := make(map[string]encoding.Codec)
if g.opts.Context != nil {
if v := g.opts.Context.Value(codecsKey{}); v != nil {
codecs = v.(map[string]encoding.Codec)
}
}
if c, ok := codecs[contentType]; ok {
return c, nil
}
if c, ok := defaultGRPCCodecs[contentType]; ok {
return c, nil
}
return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType)
}
func (g *grpcServer) newCodec(contentType string) (codec.NewCodec, error) {
if cf, ok := g.opts.Codecs[contentType]; ok {
return cf, nil
}
if cf, ok := defaultRPCCodecs[contentType]; ok {
return cf, nil
}
return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType)
}
func (g *grpcServer) Options() server.Options {
opts := g.opts
return opts
}
func (g *grpcServer) Init(opts ...server.Option) error {
for _, opt := range opts {
opt(&g.opts)
}
return nil
}
func (g *grpcServer) NewHandler(h interface{}, opts ...server.HandlerOption) server.Handler {
return newRpcHandler(h, opts...)
}
func (g *grpcServer) Handle(h server.Handler) error {
if err := g.rpc.register(h.Handler()); err != nil {
return err
}
g.handlers[h.Name()] = h
return nil
}
func (g *grpcServer) NewSubscriber(topic string, sb interface{}, opts ...server.SubscriberOption) server.Subscriber {
return newSubscriber(topic, sb, opts...)
}
func (g *grpcServer) Subscribe(sb server.Subscriber) error {
sub, ok := sb.(*subscriber)
if !ok {
return fmt.Errorf("invalid subscriber: expected *subscriber")
}
if len(sub.handlers) == 0 {
return fmt.Errorf("invalid subscriber: no handler functions")
}
if err := validateSubscriber(sb); err != nil {
return err
}
g.Lock()
_, ok = g.subscribers[sub]
if ok {
return fmt.Errorf("subscriber %v already exists", sub)
}
g.subscribers[sub] = nil
g.Unlock()
return nil
}
func (g *grpcServer) Register() error {
// parse address for host, port
config := g.opts
var advt, host string
var port int
// check the advertise address first
// if it exists then use it, otherwise
// use the address
if len(config.Advertise) > 0 {
advt = config.Advertise
} else {
advt = config.Address
}
parts := strings.Split(advt, ":")
if len(parts) > 1 {
host = strings.Join(parts[:len(parts)-1], ":")
port, _ = strconv.Atoi(parts[len(parts)-1])
} else {
host = parts[0]
}
addr, err := addr.Extract(host)
if err != nil {
return err
}
// register service
node := &registry.Node{
Id: config.Name + "-" + config.Id,
Address: addr,
Port: port,
Metadata: config.Metadata,
}
node.Metadata["broker"] = config.Broker.String()
node.Metadata["registry"] = config.Registry.String()
node.Metadata["server"] = g.String()
node.Metadata["transport"] = g.String()
// node.Metadata["transport"] = config.Transport.String()
g.RLock()
// Maps are ordered randomly, sort the keys for consistency
var handlerList []string
for n, e := range g.handlers {
// Only advertise non internal handlers
if !e.Options().Internal {
handlerList = append(handlerList, n)
}
}
sort.Strings(handlerList)
var subscriberList []*subscriber
for e := range g.subscribers {
// Only advertise non internal subscribers
if !e.Options().Internal {
subscriberList = append(subscriberList, e)
}
}
sort.Slice(subscriberList, func(i, j int) bool {
return subscriberList[i].topic > subscriberList[j].topic
})
var endpoints []*registry.Endpoint
for _, n := range handlerList {
endpoints = append(endpoints, g.handlers[n].Endpoints()...)
}
for _, e := range subscriberList {
endpoints = append(endpoints, e.Endpoints()...)
}
g.RUnlock()
service := &registry.Service{
Name: config.Name,
Version: config.Version,
Nodes: []*registry.Node{node},
Endpoints: endpoints,
}
g.Lock()
registered := g.registered
g.Unlock()
if !registered {
log.Logf("Registering node: %s", node.Id)
}
// create registry options
rOpts := []registry.RegisterOption{registry.RegisterTTL(config.RegisterTTL)}
if err := config.Registry.Register(service, rOpts...); err != nil {
return err
}
// already registered? don't need to register subscribers
if registered {
return nil
}
g.Lock()
defer g.Unlock()
g.registered = true
for sb, _ := range g.subscribers {
handler := g.createSubHandler(sb, g.opts)
var opts []broker.SubscribeOption
if queue := sb.Options().Queue; len(queue) > 0 {
opts = append(opts, broker.Queue(queue))
}
sub, err := config.Broker.Subscribe(sb.Topic(), handler, opts...)
if err != nil {
return err
}
g.subscribers[sb] = []broker.Subscriber{sub}
}
return nil
}
func (g *grpcServer) Deregister() error {
config := g.opts
var advt, host string
var port int
// check the advertise address first
// if it exists then use it, otherwise
// use the address
if len(config.Advertise) > 0 {
advt = config.Advertise
} else {
advt = config.Address
}
parts := strings.Split(advt, ":")
if len(parts) > 1 {
host = strings.Join(parts[:len(parts)-1], ":")
port, _ = strconv.Atoi(parts[len(parts)-1])
} else {
host = parts[0]
}
addr, err := addr.Extract(host)
if err != nil {
return err
}
node := &registry.Node{
Id: config.Name + "-" + config.Id,
Address: addr,
Port: port,
}
service := &registry.Service{
Name: config.Name,
Version: config.Version,
Nodes: []*registry.Node{node},
}
log.Logf("Deregistering node: %s", node.Id)
if err := config.Registry.Deregister(service); err != nil {
return err
}
g.Lock()
if !g.registered {
g.Unlock()
return nil
}
g.registered = false
for sb, subs := range g.subscribers {
for _, sub := range subs {
log.Logf("Unsubscribing from topic: %s", sub.Topic())
sub.Unsubscribe()
}
g.subscribers[sb] = nil
}
g.Unlock()
return nil
}
func (g *grpcServer) Start() error {
registerDebugHandler(g)
config := g.opts
// micro: config.Transport.Listen(config.Address)
ts, err := net.Listen("tcp", config.Address)
if err != nil {
return err
}
log.Logf("Listening on %s", ts.Addr().String())
g.Lock()
g.opts.Address = ts.Addr().String()
g.Unlock()
// micro: go ts.Accept(s.accept)
go g.serve(ts)
go func() {
// wait for exit
ch := <-g.exit
// wait for waitgroup
if wait(g.opts.Context) {
g.wg.Wait()
}
// close transport
ch <- ts.Close()
// disconnect broker
config.Broker.Disconnect()
}()
return config.Broker.Connect()
}
func (g *grpcServer) Stop() error {
ch := make(chan error)
g.exit <- ch
return <-ch
}
func (g *grpcServer) String() string {
return "grpc"
}
func NewServer(opts ...server.Option) server.Server {
return newGRPCServer(opts...)
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/JMArch/go-plugins.git
git@gitee.com:JMArch/go-plugins.git
JMArch
go-plugins
go-plugins
v0.14.0

搜索帮助

0d507c66 1850385 C8b1a773 1850385