grpc(一)grpc-java之helloworld

一、参考资料
  (1)grpc-java官网QuickStart: https://grpc.io/docs/quickstart/java.html
  (2)grpc-java的github: https://github.com/grpc/grpc-java
  (3)grpc-java的tutorial: https://grpc.io/docs/tutorials/basic/java.html
  (4)Protocol Buffers:https://developers.google.com/protocol-buffers/
 
二、grpc helloworld案例
  本demo保存地址:https://github.com/wenbinouyang/grpc007.git
  2.一、需求:我在项目a中UserServiceImpl中写了一个getUserById(Integer id)方法,而后想在项目b中使用,怎么办?
  2.二、定义想要调用的方法
  首先新建一个springboot项目(springboot版本2.1.3.RELEASE),项目名为 grpc007_server,按照传统作法,编写实体类、dao层和service层。为了方便,dao省略,service使用模拟数据。
  实体类:
package com.oy.model;
public class User {
    private Integer id;
    private String name;
    private Integer age;
    private Double salary;
}
package com.oy.model;
public class Book {
    private Integer id;
    private String name;
    private Double price;
}

  service接口:html

package com.oy.service;
import com.oy.model.User;
public interface UserService {
    User getUserById(Integer id);
}
package com.oy.service;
import com.oy.model.Book;
public interface BookService {
    int addBook(Book book);
}

  service实现类:java

package com.oy.service.impl;

import org.springframework.stereotype.Service;
import com.oy.model.User;
import com.oy.service.UserService;

@Service
public class UserServiceImpl implements UserService {
    @Override
    public User getUserById(Integer id) {
        User user = null;
        if (id.equals(1)) {
            user = new User();
            user.setAge(25);
            user.setId(1);
            user.setName("张无忌");
            user.setSalary(10000.0);
        } else {
            user = new User();
            user.setAge(20);
            user.setId(2);
            user.setName("周芷若");
            user.setSalary(10000.0);
        }
        return user;
    }
}
package com.oy.service.impl;

import org.springframework.stereotype.Service;
import com.oy.model.Book;
import com.oy.service.BookService;

@Service
public class BookServiceImpl implements BookService{
    @Override
    public int addBook(Book book) {
        return 1;
    }
}

  

  2.三、定义服务
  为了在另一个项目使用上面定义的两个service方法。咱们须要定义服务。新建一个新springboot项目,名为grpc007_proto,用来专门定义方法。在src/main/proto目录下,建立文件hello.proto,内容为:
 
syntax = "proto3";

option java_multiple_files = false;
option java_package = "com.oy.grpc";
option java_outer_classname = "GrpcLib";
//option objc_class_prefix = "HLW";

package grpc;

// The service definition.
service UserService {
    rpc getUserById (getUserByIdRequest) returns (GrpcReply) {}
}

service BookService {
    rpc addBook (addBookRequest) returns (GrpcReply) {}
}

/************************************************************/

message GrpcReply {
    int32 code = 1;
    string data = 2;
}

message getUserByIdRequest {
    int32 id = 1;
}

message addBookRequest {
    int32 id = 1;
    string name = 2;
    double price = 3;
}

 

  hello.proto定义了服务的方法名、入参和返回值。经过hello.proto能够生成咱们须要的代码,可是首先要在项目grpc007_proto的pom.xml中引入依赖和编译插件:git

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.oy</groupId>
    <artifactId>grpc007_proto</artifactId>
    <version>0.0.1</version>
    <name>grpc007_proto</name>
    <description>grpc007_proto for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <grpc.version>1.14.0</grpc.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.5.0.Final</version>
            </extension>
        </extensions>

        <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>
                    <protocArtifact>com.google.protobuf:protoc:3.5.1:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.14.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

 

  选中项目grpc007_proto,右键/Run As/Maven build...,输入clean compile命令进行编译。github

  

  选中项目,使用F5更新项目,能够看到生成的代码以下:spring

 

  2.四、发布服务shell

  尽管咱们定义了服务,可是客户端并不能直接使用,咱们还须要在服务端对服务进一步包装处理,而后才能发布服务。apache

 

  首先将项目grpc007_proto生成的3个类复制到 grpc007_server。而后要将咱们想要调用的方法进行包装。json

  pom.xml文件:c#

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.oy</groupId>
    <artifactId>grpc007_server</artifactId>
    <version>0.0.1</version>
    <name>grpc007_server</name>
    <description>grpc007_server for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <grpc.version>1.14.0</grpc.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>    
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

 

   UserGrpc类:(最好命名为UserServiceImpl)springboot

package com.oy.service.grpc;

import javax.annotation.Resource;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
import com.oy.grpc.UserServiceGrpc;
import com.oy.grpc.GrpcLib.GrpcReply;
import com.oy.grpc.GrpcLib.getUserByIdRequest;
import com.oy.model.User;
import com.oy.service.UserService;
import com.oy.utils.Utils;
import io.grpc.stub.StreamObserver;

@Component
public class UserGrpc extends UserServiceGrpc.UserServiceImplBase {

    @Resource
    private UserService userService;

    @Override
    public void getUserById(getUserByIdRequest request, StreamObserver<GrpcReply> responseObserver) {
        Utils.log.info("UserGrpc#getUserById, id:{}", request.getId());

        // 调用service层的方法
        User user = userService.getUserById(request.getId());

        String data = JSONObject.toJSONString(user);
        GrpcReply reply = GrpcReply.newBuilder().setCode(0).setData(data).build();
        responseObserver.onNext(reply);
        responseObserver.onCompleted();
    }
}

  BookGrpc类:(最好命名为BookServiceImpl)

package com.oy.service.grpc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
import com.oy.grpc.BookServiceGrpc;
import com.oy.grpc.GrpcLib.GrpcReply;
import com.oy.grpc.GrpcLib.addBookRequest;
import com.oy.model.Book;
import com.oy.service.BookService;
import com.oy.utils.Utils;

import io.grpc.stub.StreamObserver;

@Component
public class BookGrpc extends BookServiceGrpc.BookServiceImplBase {

    @Autowired
    private BookService bookService;

    @Override
    public void addBook(addBookRequest request, StreamObserver<GrpcReply> responseObserver) {
        Integer id = request.getId();
        String name = request.getName();
        Double price = request.getPrice();
        Utils.log.info("BookGrpc#addBook, id:{}, name:{}, price:{}", id, name, price);

        // 调用service层的方法
        Book book = new Book();
        book.setId(id);
        book.setName(name);
        book.setPrice(price);
        int result = bookService.addBook(book);

        JSONObject jsonObj = new JSONObject();

        if (result == 1) {
            jsonObj.put("msg", "add book succ");
        } else {
            jsonObj.put("msg", "add book failed");
        }

        String data = jsonObj.toJSONString();
        GrpcReply reply = GrpcReply.newBuilder().setCode(0).setData(data).build();
        responseObserver.onNext(reply);
        // onCompleted() method to specify that we’ve finished dealing with the RPC
        responseObserver.onCompleted();
    }
}

 

  定义grpc服务端,GrpcServer类:

package com.oy;

import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.oy.service.grpc.BookGrpc;
import com.oy.service.grpc.UserGrpc;
import com.oy.utils.Utils;
import io.grpc.Server;
import io.grpc.ServerBuilder;

@Component
public class GrpcServer {
    private int port = 23333;
    private Server server;

    @Autowired
    private UserGrpc userGrpc;
    
    @Autowired
    private BookGrpc bookGrpc;

    private void start() throws IOException {
        server = ServerBuilder.forPort(port)
                .addService(userGrpc)
                .addService(bookGrpc)
                .build().start();
        
        Utils.log.info(("grpc service start..."));

        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                Utils.log.error(("shutting down gRPC server since JVM is shutting down"));
                GrpcServer.this.stop();
                Utils.log.error("gRPC server shut down");
            }
        });
    }

    private void stop() {
        if (server != null) {
            server.shutdown();
        }
    }

    // block
    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    public void init(String[] args) throws IOException, InterruptedException {
        start();
        blockUntilShutdown();
    }
}

  

  springboot启动类,实现CommandLineRunner接口,重写run()方法。spring容器加载完毕后会调用run()方法,从而启动grpc服务。能够经过springboot启动类的main方法启动grpc007_server,也能够将项目grpc007_server打成可执行jar再运行。

package com.oy;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Grpc007ServerMainApplication implements CommandLineRunner {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Grpc007ServerMainApplication.class, args);
    }

    @Autowired
    private GrpcServer grpcServer;

    @Override
    public void run(String... args) throws Exception {
        grpcServer.init(args);
    }

}

 

  将项目grpc007_server打成可执行jar:选中项目 grpc007_proto,右键/Run As/Maven build...,输入clean package命令进行打包,进入 项目grpc007_proto的target目录,能够将jar包重命名为a.jar,shift+右键,在此处打开Powershell窗口。输入java -jar a.jar,回车。 输入clean package命令进行打包若是报错,多是由于src/test/java里面代码的问题,删除src/test/java里面的内容。
 
   2.五、客户端
  新建一个springboot项目,名为grpc007_client。
 

  客户端程序,GrpcClient类:

package com.oy.grpc.client;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

import com.oy.grpc.BookServiceGrpc;
import com.oy.grpc.GrpcClientPool;
import com.oy.grpc.GrpcLib.GrpcReply;
import com.oy.grpc.GrpcLib.addBookRequest;
import com.oy.grpc.GrpcLib.getUserByIdRequest;
import com.oy.grpc.UserServiceGrpc;
import com.oy.utils.UtilFunctions;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;

public class GrpcClient {
    public static String host = "localhost";
    private final ManagedChannel channel;
    private final UserServiceGrpc.UserServiceBlockingStub userBlockingStub;
    private final BookServiceGrpc.BookServiceBlockingStub bookBlockingStub;

    public GrpcClient(String host, int port) {
        channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
        userBlockingStub = UserServiceGrpc.newBlockingStub(channel);
        bookBlockingStub = BookServiceGrpc.newBlockingStub(channel);
    }

    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }

    @SuppressWarnings({ "rawtypes" })
    public static Object call(String rpcMethoddName, Object... args) throws Exception {
        UtilFunctions.log.info("=========== GrpcClient#call begin ===========");
        GrpcClient client = null;
        try {
            // client = GrpcClientPool.borrowObject();
            client = new GrpcClient(host, 23333);

            Class[] argsTypes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                UtilFunctions.log.info("args types: {}", args[i].getClass());
                argsTypes[i] = args[i].getClass();
            }
            Method method = client.getClass().getMethod(rpcMethoddName, argsTypes);
            Object result = method.invoke(client, args);
            return result;
        } catch (Exception e) {
            UtilFunctions.log.error("GrpcClient#call Exception: {}", e.toString());
            return null;
        } finally {
            if (client != null) {
                // GrpcClientPool.returnObject(client);
                client.shutdown();
            }
        }
    }

    // ============= User module =============
    public Object getUserById(Integer id) {
        UtilFunctions.log.info("=========== GrpcClient#getUserById begin ===========");
        getUserByIdRequest request = getUserByIdRequest.newBuilder().setId(id).build();
        GrpcReply response;
        try {
            response = userBlockingStub.getUserById(request);
            UtilFunctions.log.info("GrpcClient#getUserById response, code:{}, data:{}", response.getCode(),
                    response.getData());
        } catch (StatusRuntimeException e) {
            UtilFunctions.log.error("GrpcClient#getUserById error, StatusRuntimeException:{}", e);
            return null;
        }
        return response;
    }

    // ============= Book module =============
    public Object addBook(Integer id, String name, Double price) {
        UtilFunctions.log.info("=========== GrpcClient#addBook begin ===========");
        addBookRequest request = addBookRequest.newBuilder().setId(id).setName(name).setPrice(price).build();
        GrpcReply response;
        try {
            response = bookBlockingStub.addBook(request);
            UtilFunctions.log.info("GrpcClient#addBook response, code:{}, data:{}", response.getCode(),
                    response.getData());
        } catch (StatusRuntimeException e) {
            UtilFunctions.log.error("GrpcClient#addBook error, StatusRuntimeException:{}", e);
            return null;
        }
        return response;
    }

}

  测试类:

package com.oy.grpc.client;

import com.oy.grpc.GrpcLib.GrpcReply;
import com.oy.utils.UtilFunctions;

public class TestService {

    public static void main(String[] args) throws Exception {

        for (int i = 0; i < 1; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    GrpcReply result = null;
                    try {
                        //result = (GrpcReply) GrpcClient.call("getUserById", Integer.valueOf("1"));
                        //result = (GrpcReply) GrpcClient.call("getUserById", 2);
                        result = (GrpcReply) GrpcClient.call("addBook", 1, "thinking in java", 50.0);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    UtilFunctions.log.info("client call interface, get code:{}, data:{}", result.getCode(),
                            result.getData());

                }
            }).start();
        }

    }
}
相关文章
相关标签/搜索