gRPC Java、Go、PHP使用例子

文章目录

本文例子是在Window平台测试,Java编写的gRPC服务器端,同时使用Java、Go、PHP编写客户端调用。

1、Protocol Buffers定义接口

1.1、编写接口服务

protobuf 复制代码
// 定义protocol buffers版本(proto3)
syntax = "proto3";

// 定义包名,避免协议消息类型之间的命名冲突。
package helloworld;

// 定义java格式
option java_multiple_files = true;
option java_package = "com.penngo.grpc";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

// 服务接口定义
service Greeter { 
  // 方法1定义
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // 方法2定义
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}

// HelloRequest消息类型格式定义,
message HelloRequest {
  // name为定义字段名,1为消息传输中的字段编号,使用后,不应该改编号。
  string name = 1;
}

// HelloReply消息类型格式定义,
message HelloReply {
  // message为定义字段名,1为消息传输中的字段编号,使用后,不应该改编号。
  string message = 1;
}

1.2、Protobuf基础数据类型

    <th>C++</th>
    <th>Java/Kotlin</th>
    <th>Python</th>
    <th>Go</th>
    <th>Ruby</th>
    <th>C#</th>
    <th>PHP</th>
    <th>Dart</th>
</tr>
<tr>
    <td>double</td>
    
    <td>double</td>
    <td>double</td>
    <td>float</td>
    <td>float64</td>
    <td>Float</td>
    <td>double</td>
    <td>float</td>
    <td>double</td>
</tr>
<tr>
    <td>float</td>
   
    <td>float</td>
    <td>float</td>
    <td>float</td>
    <td>float32</td>
    <td>Float</td>
    <td>float</td>
    <td>float</td>
    <td>double</td>
</tr>
<tr>
    <td>int32</td>
  
    <td>int32</td>
    <td>int</td>
    <td>int</td>
    <td>int32</td>
    <td>Fixnum or Bignum (as required)</td>
    <td>int</td>
    <td>integer</td>
    <td>int</td>
</tr>
<tr>
    <td>int64</td>

    <td>int64</td>
    <td>long</td>
    <td>int/long<sup>[4]</sup></td>
    <td>int64</td>
    <td>Bignum</td>
    <td>long</td>
    <td>integer/string<sup>[6]</sup></td>
    <td>Int64</td>
</tr>
<tr>
    <td>uint32</td>
   
    <td>uint32</td>
    <td>int<sup>[2]</sup></td>
    <td>int/long<sup>[4]</sup></td>
    <td>uint32</td>
    <td>Fixnum or Bignum (as required)</td>
    <td>uint</td>
    <td>integer</td>
    <td>int</td>
</tr>
<tr>
    <td>uint64</td>
 
    <td>uint64</td>
    <td>long<sup>[2]</sup></td>
    <td>int/long<sup>[4]</sup></td>
    <td>uint64</td>
    <td>Bignum</td>
    <td>ulong</td>
    <td>integer/string<sup>[6]</sup></td>
    <td>Int64</td>
</tr>
<tr>
    <td>sint32</td>
   
    <td>int32</td>
    <td>int</td>
    <td>int</td>
    <td>int32</td>
    <td>Fixnum or Bignum (as required)</td>
    <td>int</td>
    <td>integer</td>
    <td>int</td>
</tr>
<tr>
    <td>sint64</td>
    
    <td>int64</td>
    <td>long</td>
    <td>int/long<sup>[4]</sup></td>
    <td>int64</td>
    <td>Bignum</td>
    <td>long</td>
    <td>integer/string<sup>[6]</sup></td>
    <td>Int64</td>
</tr>
<tr>
    <td>fixed32</td>
  
    <td>uint32</td>
    <td>int<sup>[2]</sup></td>
    <td>int/long<sup>[4]</sup></td>
    <td>uint32</td>
    <td>Fixnum or Bignum (as required)</td>
    <td>uint</td>
    <td>integer</td>
    <td>int</td>
</tr>
<tr>
    <td>fixed64</td>
  
    <td>uint64</td>
    <td>long<sup>[2]</sup></td>
    <td>int/long<sup>[4]</sup></td>
    <td>uint64</td>
    <td>Bignum</td>
    <td>ulong</td>
    <td>integer/string<sup>[6]</sup></td>
    <td>Int64</td>
</tr>
<tr>
    <td>sfixed32</td>
   
    <td>int32</td>
    <td>int</td>
    <td>int</td>
    <td>int32</td>
    <td>Fixnum or Bignum (as required)</td>
    <td>int</td>
    <td>integer</td>
    <td>int</td>
</tr>
<tr>
    <td>sfixed64</td>
   
    <td>int64</td>
    <td>long</td>
    <td>int/long<sup>[4]</sup></td>
    <td>int64</td>
    <td>Bignum</td>
    <td>long</td>
    <td>integer/string<sup>[6]</sup></td>
    <td>Int64</td>
</tr>
<tr>
    <td>bool</td>
   
    <td>bool</td>
    <td>boolean</td>
    <td>bool</td>
    <td>bool</td>
    <td>TrueClass/FalseClass</td>
    <td>bool</td>
    <td>boolean</td>
    <td>bool</td>
</tr>
<tr>
    <td>string</td>
    
    <td>string</td>
    <td>String</td>
    <td>str/unicode<sup>[5]</sup></td>
    <td>string</td>
    <td>String (UTF-8)</td>
    <td>string</td>
    <td>string</td>
    <td>String</td>
</tr>
<tr>
    <td>bytes</td>
    
    <td>string</td>
    <td>ByteString</td>
    <td>str (Python 2)<br>bytes (Python 3)</td>
    <td>[]byte</td>
    <td>String (ASCII-8BIT)</td>
    <td>ByteString</td>
    <td>string</td>
    <td>List
        <int></int>
    </td>
</tr>
</tbody>
.proto

https://protobuf.dev/programming-guides/proto3/

2、服务器端实现

服务器端实例使用java编写

2.1、生成gRPC服务类

pom.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.penngo</groupId>
    <artifactId>grpc-helloworld</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>1.59.0</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.59.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.59.0</version>
        </dependency>
        <dependency> <!-- necessary for Java 9+ -->
            <groupId>org.apache.tomcat</groupId>
            <artifactId>annotations-api</artifactId>
            <version>6.0.53</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.7.1</version>
            </extension>
        </extensions>
        <plugins>
            <!-- grpc代码生成插件 -->
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.24.0:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.59.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>alimaven</id>
            <name>Maven Aliyun Mirror</name>
            <url>https://maven.aliyun.com/repository/central</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>https://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>

命令行下执行gRPC的java代码

bash 复制代码
mvn compile

自动扫描代码目中src/main/proto/helloworld.proto下的proto文件,自动生成gRPC相关代码到target/generated-sources/protobuf目录下。

2.2、Java服务器端实现

HelloServer.java

java 复制代码
package com.penngo;

import com.penngo.grpc.GreeterGrpc;
import com.penngo.grpc.HelloReply;
import com.penngo.grpc.HelloRequest;
import io.grpc.Grpc;
import io.grpc.InsecureServerCredentials;
import io.grpc.Server;
import io.grpc.stub.StreamObserver;
import java.io.IOException;

public class HelloServer {
    public static void main(String[] args) throws IOException, InterruptedException {
        int port = 50051;
        Server server = Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create())
                .addService(new GreeterImpl())
                .build()
                .start();

        Runtime.getRuntime().addShutdownHook(new Thread(()->{
            System.err.println("*** shutting down gRPC server since JVM is shutting down");
            stopServer(server);
            System.err.println("*** server shut down");
        }));
        server.awaitTermination();
    }

    private static void stopServer(Server server) {
        if (server != null) {
            server.shutdown();
        }
    }

    static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
        @Override
        public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
            System.out.println("收到客户端消息:" + req.getName());
            String msg = "我是Java Server";
            System.out.println("回复客户端消息:" + msg);
            HelloReply reply = HelloReply.newBuilder().setMessage("你好!" + msg).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }

        @Override
        public void sayHelloAgain(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
            System.out.println("再次收到客户端消息:" + req.getName());
            String msg = "我是Java Server2";
            System.out.println("再次回复客户端消息:" + msg);
            HelloReply reply = HelloReply.newBuilder().setMessage(msg).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    }
}

https://github.com/protocolbuffers/protobuf/releases

3、java、go、php客户端实现

3.1、Java客户端实现

HelloClient.java

java 复制代码
package com.penngo;

import com.penngo.grpc.GreeterGrpc;
import com.penngo.grpc.HelloReply;
import com.penngo.grpc.HelloRequest;
import io.grpc.Grpc;
import io.grpc.InsecureChannelCredentials;
import io.grpc.ManagedChannel;

import java.util.concurrent.TimeUnit;

public class HelloClient {
    public static void main(String[] args) throws InterruptedException {
        String host = "localhost";
        int port = 50051;
        ManagedChannel managedChannel = Grpc.newChannelBuilderForAddress(host, port, InsecureChannelCredentials.create()).build();
        GreeterGrpc.GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(managedChannel);
        String msg1 = "我是java client";
        System.out.println("向服务器端发送消息:" + msg1);
        HelloRequest helloRequest1 = HelloRequest.newBuilder().setName(msg1).build();
        HelloReply reply1 = blockingStub.sayHello(helloRequest1);
        System.out.println("收到服务器端消息:" + reply1.getMessage());

        String msg2 = "我是java client2";
        System.out.println("再次向服务器端发送消息:" + msg2);
        HelloRequest helloRequest2 = HelloRequest.newBuilder().setName(msg2).build();
        HelloReply reply2 = blockingStub.sayHelloAgain(helloRequest2);
        System.out.println("再次收到服务器端消息:" + reply2.getMessage());


        managedChannel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
    }

}

3.2、Go客户端实现

Go的代码生成需要使用protoc.exe来编译helloworld.proto服务文件,生成对应的服务调用代码

下载地址:https://github.com/protocolbuffers/protobuf/releases,当前最新版本为protoc-25.1-win64.zip

解压到目录D:\Program Files\protoc-25.1-win64\bin,需要把这个目录添加到环境变量PATH当中。

安装protocol编译器的Go插件

bash 复制代码
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2

执行下边命令生成Go的gRPC代码

bash 复制代码
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative helloworld/helloworld.proto

编写客户端实现代码

go 复制代码
package main

import (
	"context"
	"flag"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	pb "grpctest/helloworld"
	"log"
	"time"
)

var (
	addr = flag.String("addr", "localhost:50051", "the address to connect to")
)

func main() {
	flag.Parse()

	// Set up a connection to the server.
	conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)

	// Contact the server and print out its response.
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	msg1 := "我是go client"
	log.Printf("向服务器端发送消息: %s", msg1)
	r1, err1 := c.SayHello(ctx, &pb.HelloRequest{Name: msg1})
	if err1 != nil {
		log.Fatalf("could not greet: %v", err1)
	}
	log.Printf("收到服务器端消息:%s", r1.GetMessage())

	msg2 := "我是go client2"
	log.Printf("再次向服务器端发送消息: %s", msg1)
	r2, err2 := c.SayHelloAgain(ctx, &pb.HelloRequest{Name: msg2})

	if err2 != nil {
		log.Fatalf("could not greet: %v", err2)
	}
	log.Printf("再次收到服务器端消息: %s", r2.GetMessage())
}

3.3、PHP客户端实现

安装gRPC的PHP扩展https://pecl.php.net/package/gRPC

当前测试php版本7.3,下载php_grpc-1.42.0-7.3-nts-vc15-x64.zip

php.ini这个文件加入

extension=php_grpc.dll

新建composer.json

json 复制代码
{
  "name": "xxs/grpc",
  "require": {
    "grpc/grpc": "^v1.4.0",
    "google/protobuf": "^v3.3.0"
  },
  "autoload":{
    "psr-4":{
      "GPBMetadata\\":"GPBMetadata/",
      "Helloworld\\":"Helloworld/"
    }
  }
}

在composer.json相同目录下执行命令下载依赖库

bash 复制代码
composer install

安装grpc的php插件,https://github.com/lifenglsf/grpc_for_windows

解压复制到项目下grpc_for_windows/x64/grpc_php_plugin.exe

执行命令生成gRPC代码

bash 复制代码
protoc --proto_path=. --php_out=. --grpc_out=. --plugin=protoc-gen-grpc=grpc_for_windows/x64/grpc_php_plugin.exe ./helloworld.proto

client.php实现

php 复制代码
<?php
require dirname(__FILE__) . '/vendor/autoload.php';
//echo dirname(__FILE__) . '/vendor/autoload.php';
function greet($hostname)
{

    $client = new Helloworld\GreeterClient($hostname, [
        'credentials' => Grpc\ChannelCredentials::createInsecure(),
    ]);
    $request = new Helloworld\HelloRequest();

    $msg1 = "我是PHP client";
    $request->setName($msg1);
    echo "向服务器端发送消息: $msg1". PHP_EOL;
    list($response, $status) = $client->SayHello($request)->wait();
    if ($status->code !== Grpc\STATUS_OK) {
        echo "ERROR: " . $status->code . ", " . $status->details . PHP_EOL;
        exit(1);
    }
    echo "收到服务器端消息:".$response->getMessage() . PHP_EOL;

    $msg2 = "我是PHP client2";
    $request->setName($msg2);
    echo "再次向服务器端发送消息: $msg2". PHP_EOL;
    list($response, $status) = $client->SayHelloAgain($request)->wait();
    if ($status->code !== Grpc\STATUS_OK) {
        echo "ERROR: " . $status->code . ", " . $status->details . PHP_EOL;
        exit(1);
    }
    echo "再次收到服务器端发送消息:".$response->getMessage() . PHP_EOL;

    $client->close();
}

$hostname = !empty($argv[2]) ? $argv[2] : 'localhost:50051';
greet($hostname);

附件源码

相关推荐
Abladol-aj1 小时前
并发和并行的基础知识
java·linux·windows
清水白石0081 小时前
从一个“支付状态不一致“的bug,看大型分布式系统的“隐藏杀机“
java·数据库·bug
Elihuss2 小时前
ONVIF协议操作摄像头方法
开发语言·php
吾日三省吾码6 小时前
JVM 性能调优
java
弗拉唐7 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi778 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
少说多做3438 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀8 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20209 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea