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();
        }

    }
}
相關文章
相關標籤/搜索