菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
0
0

gRPC-Go 和 Java 的一次 HelloWorld(十)

原创
05/13 14:22
阅读数 478

都说grpc是跨语言的一个rpc框架,当团队内部有多种流行编程语言时,那么grpc可以为他们提供通信,今天我们就通过一个Hello World来看看Java和Go是怎么通信的,一起实践吧,只有亲身实践才能更好的掌握,理解。

下文所有程序源代码地址如下

Java:https://github.com/sunpengwei1992/java_grpc
Go:https://github.com/sunpengwei1992/go_common/tree/master/grpc

我们以Go作为服务端,Java作为客户端

Go实现服务端

准备好Go版的proto文件

syntax = "proto3";
package proto;
//接口请求入参
message HelloRequest{
    string request = 1;
}
//接口返回出参
message HelloResponse{
    string response = 1;
}
//定义接口
service HelloService{
    //一个简单的rpc
    rpc HelloWorld(HelloRequest) returns (HelloResponse){}
    //一个服务器端流式rpc
    rpc HelloWorldServerStream(HelloRequest) returns (stream HelloResponse){}
    //一个客户端流式rpc
    rpc HelloWorldClientStream(stream HelloRequest) returns (HelloResponse){}
    //一个客户端和服务器端双向流式rpc
    rpc HelloWorldClientAndServerStream(stream HelloRequest) 
          returns (stream  HelloResponse){}
}

下载依赖的moudle,如下地址

github.com/golang/protobuf v1.3.2
google.golang.org/grpc v1.23.1

执行如下命令,生成proto.pb.go文件

protoc --goout=plugins=grpc:. helloworld.proto

服务端实现接口,提供服务

type HelloServiceServer struct {
}
func (*HelloServiceServer) HelloWorld(ctx context.Context, 
        req *pb.HelloRequest) (*pb.HelloResponse, error) {
   //打印入参
   log.Printf("%v", req.Request)
   //响应服务
   return &pb.HelloResponse{Response: "hello my is gRpcServer"}, nil
}

编写服务端代码,并启动服务端

func StartServer() {
   lis, err := net.Listen("tcp", "127.0.0.1:8090")
   if err != nil {
      log.Fatalf("failed to listen: %v", err)
   }
   //创建一个grpc服务器对象
   gRpcServer := grpc.NewServer()

   pb.RegisterHelloServiceServer(gRpcServer, &impl.HelloServiceServer{})
   //开启服务端
   gRpcServer.Serve(lis)
}

到次为止,Go服务端启动完成,接下来我们准备Java客户端代码

Java实现客户端

准备好Java版的proto文件,和Go版的区别是多了一些Java的选项,其余的不能改变

syntax = "proto3";
package proto; //包名和go中的必须一致
option java_generic_services = true;
option java_multiple_files = true;
option java_package = "com.spw.proto";
option java_outer_classname = "HelloWorldProto";
//接口请求入参
message HelloRequest{
    string request = 1;
}
//接口返回出参
message HelloResponse{
    string response = 1;
}
//定义接口
service HelloService{
    //一个简单的rpc
    rpc HelloWorld(HelloRequest) returns (HelloResponse){}
    //一个服务器端流式rpc
    rpc HelloWorldServerStream(HelloRequest) returns (stream HelloResponse){}
    //一个客户端流式rpc
    rpc HelloWorldClientStream(stream HelloRequest) returns (HelloResponse){}
    //一个客户端和服务器端双向流式rpc
    rpc HelloWorldClientAndServerStream(stream HelloRequest) returns (stream HelloResponse){}
}

新建一个maven项目,配置pom文件,依赖的jar包如下

<dependencies>
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-protobuf</artifactId>
        <version>1.23.1</version>
    </dependency>
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-stub</artifactId>
        <version>1.23.1</version>
    </dependency>
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-netty-shaded</artifactId>
        <version>1.23.1</version>
    </dependency>
</dependencies>

编译配置如下

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <plugin>
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
            <version>0.5.1</version>
            <configuration>
                <!--proto编译器  os.detected.classifier,获取操作系统,这个属性是由
                ${os.detected.name}-${os.detected.arch}一起得来的-->
                <protocArtifact>
                com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}
                </protocArtifact>
                <pluginId>grpc-java</pluginId>
                <!--grpc-java代码生成工具-->
                <pluginArtifact>
                io.grpc:protoc-gen-grpc-java:1.23.1:exe:${os.detected.classifier} 
                </pluginArtifact>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
    <!--用于根据.proto 文件生成 protobuf 文件-->
    <extensions>
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>1.5.0.Final</version>
        </extension>
    </extensions>
</build>

开始根据proto文件编译生成java文件,如下图所示,依次点击红色的插件

编写客户端文件,连接Go的服务端,发起请求

//通过netty创建通道
ManagedChannel channel = NettyChannelBuilder.forAddress("127.0.0.1", 8090)
        .negotiationType(NegotiationType.PLAINTEXT)
        .build();
//获取客户端存根对象
HelloServiceGrpc.HelloServiceBlockingStub blockingStub = HelloServiceGrpc.newBlockingStub(channel);
//创建入参
HelloRequest helloRequest = HelloRequest.newBuilder().setRequestBytes(ByteString.copyFromUtf8("hello grpc")).build();
//调用服务端
HelloResponse helloResponse = blockingStub.helloWorld(helloRequest);
//打印响应
System.out.println(helloResponse.getResponse());

我们以Java作为服务端,Go作为客户端

Java实现服务端

上面Java客户端生成的文件不要懂,另写一个服务的实现类,代码如下

public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {
    /**
     * <pre>
     *一个简单的rpc
     * </pre>
     */
    @Override
    public void helloWorld(HelloRequest request,
               io.grpc.stub.StreamObserver<HelloResponse>responseObserver) {

        responseObserver.onNext(HelloResponse.newBuilder()
                               .setResponse("hello my is java server").build());
        responseObserver.onCompleted();
    }
}

写一个main方法启动服务端,代码如下

public static void main(String[] args) {
    try {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        //启动服务端
        Server server = ServerBuilder.forPort(8090)
                    //添加自己的服务实现类
                    .addService(new HelloServiceImpl()).build().start();

        countDownLatch.await();
    } catch (IOException e) {
    } catch (InterruptedException e) {
    }
}
Go作为客户端

上面go服务端生成的proto文件依然不要变,实现自己的客户端代码,如下

func StartClient() {
   conn, err := grpc.Dial("127.0.0.1:8090", grpc.WithInsecure())
   if err != nil {
      fmt.Println(err)
      return
   }
   //创建客户端存根对象
   c := pb.NewHelloServiceClient(conn)
   //调用服务
   res, err := c.HelloWorld(context.Background(), 
                            new(pb.HelloRequest), grpc.EmptyCallOption{})
   fmt.Println(res, err)
   defer conn.Close()
}

总结

通过一个HelloWorld的案例带领大家实践多语言通过grpc通信,真实场景中,往往非常复杂,还需要大家多多研究,比如,负载均衡,限流,服务降级,protobuffer文件管理,版本升级等各种问题都需要考虑.

发表评论

0/200
0 点赞
0 评论
收藏
为你推荐 换一批