grpc在go和java之间进行互相调用的过程

下面记录使用golang开发grpc的服务端程序,然后供java去调用grpc服务的过程,先看服务端proto文件内容

 

syntax = "proto3";
package proto;
option java_package = "com.test.rpc";
option java_multiple_files = false;

message SayHelloRequest{
    bytes name=1;
}

message SayHelloResponse{
    bytes result=1;
}

service SayHelloService{
    rpc SayHello(SayHelloRequest) returns (SayHelloResponse);
}

以上文件命名为service.proto文件保存,grpc要求每一个方法调用都要有一个请求参数,一个响应参数,这里面的SayHelloRequest就是请求参数,具体来讲grpc远程方法调用时就只需要传递一个name参数,bytes对应的是字符串类型。相对应的SayHelloResponse就是grpc方法调用的返回结果,返回的也是个字符串,参数名称叫做result,具体的grpc方法名称是SayHello。

go语言端通过以下命令生成go代码(在service.proto所在的目录执行):

protoc --go_out=plugins=grpc:. service.proto

命令执行以后会产生一个Service.pb.go文件,里面会自动生成如下内容(部分):

grpc在go和java之间进行互相调用的过程_第1张图片

上面自动生成的代码里面的interface就是等待我们去具体实现的grpc服务内容,具体来说就是去具体实现SayHello那个方法,随便找个地方去实现这个方法,例如:

package main

import (
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"log"
	"motorway/proto"
	"net"
	"runtime"
	"strconv"
)

const (
	port = "41005"
)

type Data struct{}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	//起服务
	lis, err := net.Listen("tcp", ":"+port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	data:=&Data{}
	proto.RegisterSayHelloServiceServer(s,data)
	log.Printf("grpc server in: %s", port)
	s.Serve(lis)

}

func (t *Data) SayHello(ctx context.Context,in *proto.SayHelloRequest) (result *proto.SayHelloResponse, err error){
	return &proto.SayHelloResponse{
		Result:[] byte("hello :"+string(in.Name)),
	},nil
}

在上面的代码中,Data对象的SayHello就是我们实现的接口方法(因为 go语言实现接口没有implements等关键字,只要写的方法名称,参数,返回值和interface里面的方法定义一样就认为是实现该方法了),上面的main方法里面实例化一个grpc的server,向server对象注册SayHello服务对象data,最后调用Serve方法就可以对外提供grpc服务了。

 

以上go语言端的grpc服务已经就绪,可以等待调用了,要是急于验证服务的可用性,go的客户端调用代码如下:

package main

import (
	"fmt"
	"motorway/proto"
	"log"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	"math/rand"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
)

var (
	wg sync.WaitGroup
)

const (
	networkType = "tcp"
	server      =  "127.0.0.1" //"172.0.16.111" "172.0.16.105"//"127.0.0.1"
	port        = "41005"
	parallel    = 50        //连接并行度
	times       = 1000    //每连接请求次数
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	currTime := time.Now()

	//并行请求
	for i := 0; i < int(parallel); i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			exe()
		}()
	}
	wg.Wait()

	log.Printf("time taken: %.2f s ", time.Now().Sub(currTime).Seconds())
}

func exe() {
	//建立连接
	conn, err := grpc.Dial(server + ":" + port,grpc.WithInsecure())
	if nil!=conn{
		defer conn.Close()
	}

	if (nil!=err){
		fmt.Printf("创建连接失败!%s\n",err)
		return
	}

	client2 := proto.NewSayHelloServiceClient(conn)

	for i := 0; i < int(times); i++ {
		testSayHello(client2)
	}
}



func testSayHello(client proto.SayHelloServiceClient){
	req:=&proto.SayHelloRequest{
		Name:[]byte("张三"),
	}

	result,_:=client.SayHello(context.Background(),req)
	if nil!=result{
		//fmt.Printf("%s \n",string(result.Result))
	}else{
		fmt.Println("response is nil")
	}
}

代码里面模拟了50个并发客户端,每个客户端请求调用1000次的情况,在我的i5 cpu机器上面差不多需要4.2s完成这5万次调用。

好了,言归正传,接着说java端怎么进行调用的问题,新建一个spring boot项目,在pom.xml里面增加maven依赖如下:


			io.grpc
			grpc-netty-shaded
			1.20.0
		
		
			io.grpc
			grpc-protobuf
			1.20.0
		
		
			io.grpc
			grpc-stub
			1.20.0
		

然后继续在pom.xml的build里面增加plugin等如下(不要完全照搬,就是org.xolstice.maven.plugins那个还有下面那个extensions需要复制):


		
			
				org.springframework.boot
				spring-boot-maven-plugin
			

			
				org.xolstice.maven.plugins
				protobuf-maven-plugin
				0.5.1
				
					com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}
					grpc-java
					io.grpc:protoc-gen-grpc-java:1.20.0:exe:${os.detected.classifier}
				
				
					
						
							compile
							compile-custom
						
					
				
			
		


		
			
				kr.motd.maven
				os-maven-plugin
				1.5.0.Final
			
		

	

 

然后在项目里面建立proto目录,将go语言那边的service.proto文件复制进去,如下图:

grpc在go和java之间进行互相调用的过程_第2张图片

然后打开maven工具,找到如下图所示的插件:

grpc在go和java之间进行互相调用的过程_第3张图片

先执行protobuf:compile一遍,再执行protobuf:copile-custom,会自动生成如下文件:

grpc在go和java之间进行互相调用的过程_第4张图片

正常情况下应该生成两个文件,一个是com.test.rpc.Service,另外一个是com.test.rpc.SayHelloServiceGrpc,回到spring boot application入口main方法,编写如下代码:

package com.grpc;

import com.google.protobuf.ByteString;
import com.test.rpc.SayHelloServiceGrpc;
import com.test.rpc.Service;
import io.grpc.netty.shaded.io.grpc.netty.NegotiationType;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;

@SpringBootApplication
public class DemoApplication {

	private static final String host="127.0.0.1";
	private static final int port=41005;

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);

		io.grpc.Channel channel = NettyChannelBuilder.forAddress(host, port)
				.negotiationType(NegotiationType.PLAINTEXT)
				.build();

		Service.SayHelloRequest req=Service.SayHelloRequest.newBuilder().setName(ByteString.copyFrom("测试",Charset.forName("utf-8"))).build();

		Service.SayHelloResponse result= SayHelloServiceGrpc.newBlockingStub(channel).sayHello(req);
		try {
			System.out.println(result.getResult().toString("utf-8"));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

	}

}

java这边请求参数是通过Service.XXXXX(请求对象).newBuilder().不断的set各个请求参数值(xxx).build()去构造出来的,然后是调用SayHelloServiceGrpc.newBlockingStub(channel).sayHello(req);  这种方式去调用的,grpc貌似有多种调用方式,还没有研究到那么深入,最后是调用结果的输出。

grpc在go和java之间进行互相调用的过程_第5张图片

golang服务端程序正确的返回了grpc调用结果。

 

你可能感兴趣的:(IT技术相关)