Giter VIP home page Giter VIP logo

grpcall's Introduction

grpcall

grpcall is a client library easy request GRPC Server with reflect mode, then it make grpc proxy mode middleware, like grpc-gateway.

use GRPC's reflect mode to request GRPC Server. grpcall support FileDescriptorSet and grpc/reflection mode.

some code refer: protoreflect, grpcurl

Feature

  • support event hook
  • dynamic request GRPC Server with reflect mode.
  • dynamic convert protobuf to json and json to protobuf
  • grpc client manager
  • support stream
  • recv system signal to update new descriptor.
  • ...

Usage

grpcall.SetProtoSetFiles("helloworld.protoset")
grpcall.InitDescSource()

var handler = DefaultEventHandler{}
var sendBody string

grpcEnter, err := grpcall.New(
    grpcall.SetHookHandler(&handler),
)
grpcEnter.Init()

sendBody = `{"name": "xiaorui.cc"}`
res, err := grpcEnter.Call("127.0.0.1:50051", "helloworld.Greeter", "SayHello", sendBody)
fmt.Printf("%+v \n", res)
fmt.Println("req/reply return err: ", err)

sendBody = `{"msg": "hehe world"}`
res, err = grpcEnter.Call("127.0.0.1:50051", "helloworld.SimpleService", "SimpleRPC", sendBody)
fmt.Printf("%+v \n", res)
fmt.Println("stream return err: ", err)

wg := sync.WaitGroup{}
wg.Add(2)

go func() {
    defer wg.Done()
    for {
        body := fmt.Sprintf(`{"msg": "hehe world %s"}`, time.Now().String())
        select {
        case res.SendChan <- []byte(body):
            // fmt.Println("send", body)
            time.Sleep(3 * time.Second)

        case <-res.DoneChan:
            return

        default:
            return
        }
    }
}()

go func() {
    defer wg.Done()

    for {
        select {
        case msg, ok := <-res.ResultChan:
            fmt.Println("recv data:", msg, ok)
        case err := <-res.DoneChan:
            fmt.Println("done chan: ", err)
            return
        }
    }
}()

wg.Wait()

type DefaultEventHandler struct {
    sendChan chan []byte
}

func (h *DefaultEventHandler) OnReceiveData(md metadata.MD, resp string, respErr error) {
}

func (h *DefaultEventHandler) OnReceiveTrailers(stat *status.Status, md metadata.MD) {
}

Test Example

  1. start grpc server
cd testing; go run test_server.go
  1. start grpcall client
cd testing; go run test_grpcall_client.go

Protoset Files

Protoset files contain binary encoded google.protobuf.FileDescriptorSet protos. To create a protoset file, invoke protoc with the *.proto files that define the service:

protoc --proto_path=. \
    --descriptor_set_out=myservice.protoset \
    --include_imports \
    my/custom/server/service.proto

grpcall's People

Contributors

fridrichchile avatar karldoenitz avatar rfyiamcool avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

grpcall's Issues

ctx bug

func (e *EngineHandler) invokeCall(ctx context.Context, gclient *grpc.ClientConn, target, serviceName, methodName, data string) (*ResultModel, error) {
	if serviceName == "" || methodName == "" {
		return nil, errors.New("serverName or methodName is null")
	}

	if gclient == nil && target == "" {
		return nil, errors.New("target addr is null")
	}

	if ctx == nil {
		ctx = e.ctx
	}

	var (
		err       error
		cc        *grpc.ClientConn
		connErr   error
		refClient *grpcreflect.Client

		addlHeaders multiString
		rpcHeaders  multiString
		reflHeaders multiString

		descSource DescriptorSource
	)

	descSource = descSourceController.descSource

	// parse proto by grpc reflet api
	if descSourceController.descMode == ProtoReflectMode {
		md := MetadataFromHeaders(append(addlHeaders, reflHeaders...))
		refCtx := metadata.NewOutgoingContext(e.ctx, md)
		cc, connErr = e.DoConnect(target)
		if connErr != nil {
			return nil, connErr
		}

		refClient = grpcreflect.NewClient(refCtx, reflectpb.NewServerReflectionClient(cc))
		descSource = DescriptorSourceFromServer(e.ctx, refClient)
	}

	if gclient == nil {
		cc, connErr = e.DoConnect(target)
		if connErr != nil {
			return nil, connErr
		}
	} else {
		cc = gclient
	}

	var inData io.Reader
	inData = strings.NewReader(data)
	rf, err := RequestParserFor(descSource, inData)
	if err != nil {
		return nil, errors.New("request parse and format failed")
	}

	result, err := e.invokeCtl.InvokeRPC(e.ctx, descSource, cc, serviceName, methodName,
		append(addlHeaders, rpcHeaders...),
		rf.Next,
	)
	return result, err
}

这一段代码如果ctx是空则会把e.ctx给ctx,然后invokeRPC的时候用的e.ctx。
如果ctx不为空,则会造成上游请求的ctx在这一层丢失

grpcall.SetProtoFiles()不生效

grpcall.SetProtoFiles("本地绝对路径", "proto文件名")
grpcall.SetMode(grpcall.ProtoFilesMode)
err := grpcall.InitDescSource()
if err != nil {
	panic(err.Error())
}
grpcEnter.Init()

报错:panic: runtime error: invalid memory address or nil pointer dereference
代码执行到core.go的第61行崩溃了。
看了一下,传入的DescriptorSource是个nil,跟进看了一下代码,问题出在enter.go的158行,用一个空值覆盖了原先已经解析出来的值,把这一行删掉就行了,ext:

// enter.go 158行
func (d *DescSourceEntry) InitDescSource() error {
	var err error
	var desc DescriptorSource

	switch descSourceController.descMode {
	case ProtoSetMode:
		// parse proto by protoset

		if descSourceController.protoset.IsEmpty() {
			return errors.New("protoset null")
		}

		for _, f := range descSourceController.protoset {
			ok := pathExists(f)
			if !ok {
				return errors.New("protoset file not exist")
			}
		}

		desc, err = DescriptorSourceFromProtoSets(descSourceController.protoset...)
		if err != nil {
			return errors.New("Failed to process proto descriptor sets")
		}

		descSourceController.descSource = desc

	case ProtoFilesMode:
		// parse proto by protoFiles
		descSourceController.descSource, err = DescriptorSourceFromProtoFiles(
			descSourceController.importPaths,
			descSourceController.protoFiles...,
		)
		if err != nil {
			return errors.New("Failed to process proto source files")
		}

		// descSourceController.descSource = desc 删掉这一行就好了

	default:
		return errors.New("only eq ProtoSetMode and ProtoFilesMode")
	}

	return nil
}

grpc服务重启,返回的结果Data是空

服务启动后,如果依赖的grpc服务重启后,再次调用call方法,得到的resultModel的Data是空的,必须也重启项目服务才行,这个问题怎么解决?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.