spring boot+grpc+Jersey整合集成

spring boot+grpc+Jersey整合集成

最近接触的新的项目中用到了grpc,所以就学习了一下,但是在学习的过程中在网上基本上没有找到一篇完整的教程从无到有的一步一步的讲解集成的步骤,所以就写了这篇文章,记录下自己的集成步骤,以帮忙像我这样的初学者。

1、项目结构

就是一个普通的maven项目
spring boot+grpc+Jersey整合集成

2、集成步骤

1、创建grpc-complete-demo项目

pom.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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
    </parent>

    <groupId>com.liao</groupId>
    <artifactId>grpc-complete-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <modules>
        <module>proto</module>
        <module>grpc-server</module>
        <module>grpc-client</module>
    </modules>
    <packaging>pom</packaging>

    <name>grpc-complete-demo</name>
    <description>Demo project for Spring Boot</description>


</project>

2.创建proto项目

1、配置pom.xml文件

依赖了proto文件编译相关的jar及插件,还有几个是打包的插件【把生成的class打包供其他工程使用】,另外我用到了jersey相关的几个jar

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an
  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  KIND, either express or implied.  See the License for the
  specific language governing permissions and limitations
  under the License.
-->
<!-- $Id: pom.xml 642118 2008-03-28 08:04:16Z reinhard $ -->
<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/maven-v4_0_0.xsd">
    <parent>
        <artifactId>grpc-complete-demo</artifactId>
        <groupId>com.liao</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <name>proto</name>
    <artifactId>proto</artifactId>
    <version>0.0.1</version>

    <properties>
        <protobuf.version>3.5.1</protobuf.version>
        <grpc.version>1.10.0</grpc.version>
        <grpc-jersey.version>0.3.2</grpc-jersey.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>${protobuf.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-core</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>com.xorlev.grpc-jersey</groupId>
            <artifactId>jersey-rpc-support</artifactId>
            <version>${grpc-jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
            <version>2.25.1</version>
        </dependency>
    </dependencies>

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

        <plugins>

            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.0</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:${protobuf.version}-1:exe:${os.detected.classifier}
                    </protocArtifact>
                </configuration>
                <executions>
                    <execution>
                        <id>1</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>2</id>
                        <goals>
                            <goal>compile-custom</goal>
                        </goals>
                        <configuration>
                            <pluginId>grpc-java</pluginId>
                            <pluginArtifact>
                                io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
                            </pluginArtifact>
                        </configuration>
                    </execution>
                    <execution>
                        <id>3</id>
                        <goals>
                            <goal>compile-custom</goal>
                        </goals>
                        <configuration>
                            <pluginId>grpc-jersey</pluginId>
                            <pluginArtifact>
                                com.xorlev.grpc-jersey:protoc-gen-jersey:${grpc-jersey.version}:exe:${os.detected.classifier}
                            </pluginArtifact>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-site-plugin</artifactId>
            </plugin>

            <!-- 编译jar包的jdk版本 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2、编写proto文件

spring boot+grpc+Jersey整合集成

User.proto,文件比较简单在这里就不说明了,如果不清楚了自行百度

syntax = "proto3";

package com.liao;
import "google/api/annotations.proto";

option java_package = "com.liao.grpc";
option java_outer_classname = "UserProto";
option java_multiple_files = true;

service User {
    rpc getUser (UserRequest) returns (UserResponse) {
        option (google.api.http) = {
            get: "/get/{id}"
        };

    }
}
message UserRequest {
    int32 id = 1;
}

message UserResponse {
    int32 id = 1;
    string name = 2;
    int32 age = 3;
}

编译proto文件
spring boot+grpc+Jersey整合集成
编译后即可看到生成的class
spring boot+grpc+Jersey整合集成

2.创建grpc-server项目

1.项目就是一个纯粹的spring boot项目,pom.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/maven-v4_0_0.xsd">
    <parent>
        <artifactId>grpc-complete-demo</artifactId>
        <groupId>com.liao</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>

    <name>grpc-server</name>
    <artifactId>grpc-server</artifactId>
    <properties>
        <grpc.version>1.10.0</grpc.version>
        <grpc-jersey.version>0.3.2</grpc-jersey.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jersey</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </dependency>
        <dependency>
            <groupId>com.xorlev.grpc-jersey</groupId>
            <artifactId>jersey-rpc-support</artifactId>
            <version>${grpc-jersey.version}</version>
        </dependency>

        <dependency>
            <groupId>com.liao</groupId>
            <artifactId>proto</artifactId>
            <version>0.0.1</version>
        </dependency>

        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</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-netty</artifactId>
            <version>${grpc.version}</version>
        </dependency>

        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-services</artifactId>
            <version>${grpc.version}</version>
        </dependency>


        <dependency>
            <groupId>org.lognet</groupId>
            <artifactId>grpc-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>

        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>javax.ws.rs-api</artifactId>
            <version>2.1</version>
        </dependency>


    </dependencies>

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

</project>

2、ServerApp springboot 项目入口

package com.liao;

import io.grpc.ServerBuilder;
import io.grpc.internal.GrpcUtil;
import io.grpc.netty.NettyServerBuilder;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
import org.lognet.springboot.grpc.GRpcServerBuilderConfigurer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * @author liao
 * @date 2018/10/18
 */
@SpringBootApplication
public class ServerApp {
    public static void main(String[] args) {
        SpringApplication.run(ServerApp.class, args);
    }

    /**
     * 自定义gRPC服务器配置,可以使用默认配置也行
     */
     @Component
    public class MyGRpcServerBuilderConfigurer extends GRpcServerBuilderConfigurer {
        @Override
        public void configure(ServerBuilder<?> serverBuilder) {

            if (serverBuilder instanceof NettyServerBuilder) {
                NettyServerBuilder nettyServerBuilder = (NettyServerBuilder) serverBuilder;
                nettyServerBuilder
                    .keepAliveTime(30, TimeUnit.SECONDS)
                    .keepAliveTimeout(30, TimeUnit.SECONDS)
                    .permitKeepAliveTime(30, TimeUnit.SECONDS);
            }

            int threads = Runtime.getRuntime().availableProcessors() * 20;

            serverBuilder.executor(
                new ThreadPoolExecutor(
                    threads,
                    threads,
                    0L,
                    TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(),
                    GrpcUtil.getThreadFactory("grpc-worker-%d", true)));
            // . compressorRegistry(YOURCOMPRESSIONREGISTRY)
            //. decompressorRegistry(YOURDECOMPRESSIONREGISTRY)
            //. useTransportSecurity(YOURTRANSPORTSECURITYSETTINGS);
        }
    }
}

3、grpc服务实现

UserGrpcImpl.java

package com.liao.grpcimpl;

import com.liao.grpc.UserGrpc;
import com.liao.grpc.UserRequest;
import com.liao.grpc.UserResponse;
import io.grpc.stub.StreamObserver;
import org.lognet.springboot.grpc.GRpcService;

/**
 * @author liao
 * @date 2018/10/18
 * 代码本身没有任何意义,只是学习使用
 */
@GRpcService
public class UserGrpcImpl extends UserGrpc.UserImplBase {

    @Override
    public void getUser(UserRequest request, StreamObserver<UserResponse> responseObserver) {

        UserResponse ur = null;
        switch (request.getId()) {
            case 1:
                ur = UserResponse.newBuilder().setId(1).setAge(10).setName("liao").build();
                break;
            case 2:
                ur = UserResponse.newBuilder().setId(2).setAge(20).setName("测试账号").build();
                break;
            default:
                ur = UserResponse.newBuilder().setId(-1).setAge(-1).setName("默认账号").build();
        }
        //固定套路不用纠结
        responseObserver.onNext(ur);
        responseObserver.onCompleted();
    }
}

4、application.properties配置文件

server.port=8081
spring.application.name=grpc-server
#grpc 端口
grpc.port=12345
#是否启用grpc server,默认值是true
grpc.enabled=true
#这个地方是个坑,感觉系统starter类有毛病如果配置了这个属性会启动连个grpc服务,一个端口是自己指定的,一个端口是-1,不明白是为什么,下边会贴出源码
grpc.inProcessServerName=testuser

5、grpc-srping-boot-starter源码

spring boot+grpc+Jersey整合集成

6、grpc+jersey整合配置【不配置也不影响grpc服务的使用,按需配置】

配置这个的目的是让项目既提供restful服务又提供grpc服务
GrpcJerseyConfig.java

package com.liao.config;

import com.fullcontact.rpc.jersey.GrpcJerseyErrorHandler;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.liao.grpc.UserGrpc;
import com.liao.grpc.UserGrpcJerseyResource;
import io.grpc.Channel;
import io.grpc.inprocess.InProcessChannelBuilder;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Response;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author liao
 * @date 2018/10/19
 */
@Configuration
public class GrpcJerseyConfig {
    @Component
    @ApplicationPath("/user")
    public class JerseyConfig extends ResourceConfig {
        public JerseyConfig() {
             ErrorHandler.setErrorHandler(new MyGrpcJerseyErrorHandler());
            register(new UserGrpcJerseyResource(UserGrpc.newStub(channel())));
        }
    }

    private class MyGrpcJerseyErrorHandler extends GrpcJerseyErrorHandler.Default {

        /**
         * 自定义异常处理
         *随便从别的地方抄过来的不用关注内容
         *
         * @param t
         * @param responseHeaders
         */
        @Override
        public Optional<Response> handleUnaryError(Throwable t, ImmutableMultimap<String, String> responseHeaders) {

            int code = 500;

            Response.ResponseBuilder ok = Response.ok();

            if (!responseHeaders.isEmpty()) {
                for (Map.Entry<String, String> entry : responseHeaders.entries()) {
                    ok.header(entry.getKey(), entry.getValue());
                }
            }

            ok.entity("MyGrpcJerseyErrorHandler");
            return Optional.of(ok.build());
        }
    }


    
    public Channel channel() {
        int threads = Runtime.getRuntime().availableProcessors() * 10;

        return InProcessChannelBuilder.forName("testuser")
            .usePlaintext(true)
            .executor(
                new ThreadPoolExecutor(
                    threads,
                    threads,
                    0L,
                    TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(),
                    new ThreadFactoryBuilder().setNameFormat("grpc-jersey-executor-%d").setDaemon(true).build()))
            .build();
    }
}

这一步有一个细节需要注意
这个地方的testuser要与配置文件的grpc.inProcessServerName=testuser一致,否则通过rest方式无法访问到grpc服务

 InProcessChannelBuilder.forName("testuser")
grpc.inProcessServerName=testuser

7、restful方式测试

url的拼接方法看图应该就明白了
spring boot+grpc+Jersey整合集成

创建grpc-client项目

1、pom.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/maven-v4_0_0.xsd">
    <parent>
        <artifactId>grpc-complete-demo</artifactId>
        <groupId>com.liao</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <packaging>war</packaging>

    <name>grpc-client</name>
    <artifactId>grpc-client</artifactId>

    <properties>
        <grpc.version>1.10.0</grpc.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-client-spring-boot-starter</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>21.0</version>
        </dependency>


        <dependency>
            <groupId>com.liao</groupId>
            <artifactId>proto</artifactId>
            <version>0.0.1</version>
        </dependency>
    </dependencies>

</project>

2、application.properties

server.port=8082
spring.application.name=grpc-client
#grpc-server1 与server里@GrpcClient("grpc-server1")要一致
grpc.client.grpc-server1.host=127.0.0.1
grpc.client.grpc-server1.port=12345
grpc.client.grpc-server1.enableKeepAlive=true
grpc.client.grpc-server1.keepAliveWithoutCalls=true


3、配置service

UserService.java

package com.liao.service;

import com.liao.grpc.UserGrpc;
import com.liao.grpc.UserRequest;
import com.liao.grpc.UserResponse;
import io.grpc.Channel;
import net.devh.springboot.autoconfigure.grpc.client.GrpcClient;
import org.springframework.stereotype.Service;

/**
 * @author liao
 * @date 2018/10/19
 */
@Service
public class UserService {
    @GrpcClient("grpc-server1")
    private Channel serverChannel;

    public Object get(int id) {
        UserGrpc.UserBlockingStub stub = UserGrpc.newBlockingStub(serverChannel);
        UserResponse response = stub.getUser(UserRequest.newBuilder().setId(id).build());
        return response.getName();
    }
}

4、UserController.java

package com.liao.controller;

import com.liao.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author liao
 * @date 2018/10/19
 */
@RestController
@RequestMapping("/user/")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("get/{id}")
    public Object get(@PathVariable(name = "id") int id) {
        return userService.get(id);
    }
}

5、测试结果

spring boot+grpc+Jersey整合集成