One of the practical battles of gRPC in java: generating code with proto

About the "java version of gRPC actual combat" series

  • The java version of gRPC actual combat is a gRPC development note for java programmers originally written by Xinchen. We will learn and master various knowledge points of gRPC through actual combat with readers;

Links to the full series of "java version gRPC actual combat"

  1. Generate code with proto
  2. Service publishing and invocation
  3. Server stream
  4. Client stream
  5. Bidirectional flow
  6. The client dynamically obtains the server address
  7. Registration discovery based on eureka

About gRPC

  1. gRPC is a high-performance, open source and general RPC framework designed for mobile and HTTP/2. At present, C, Java and Go language versions are available: gRPC, gRPC Java and gRPC Go. Among them, C version supports C, C++, Node.js, Python, Ruby, Objective-C, PHP and c# support
  2. gRPC is designed based on the HTTP/2 standard and brings features such as bidirectional flow, flow control, header compression, multiple multiplexing requests on a single TCP connection and so on. These features make it perform better on mobile devices, save power and save space.
  3. Each process can call each other through gRPC, as shown in the following figure:

core technology

  • In order to publish gRPC services in java, I use the open source library net. Devh: gRPC server spring boot starter
  • Net. Devh: gRPC client spring boot starter is used when calling other gRPC services
  • Thank Michael Dashen, the author of the open source library, for your wisdom in simplifying the gRPC development work of java programmers. Project address: https://github.com/yidongnan/grpc-spring-boot-starter

Overview of this article

As the beginning of this series of articles, the following things should be done in this article:

  1. Explicit dependency library and development environment
  2. Create a new parent project gRPC tutorials. In the future, all the source codes of the java version gRPC actual combat series will be in this project
  3. Automatic generation of java code with proto file in actual combat

Explicit dependency library and development environment

The dependency libraries and development environments covered in the whole series of articles are as follows:

  1. JDK: 1.8.0_281
  2. gradle: 6.7.1
  3. springboot: 2.3.8.RELEASE
  4. grpc: 1.35.0
  5. protobuf: 3.14.0
  6. grpc-server-spring-boot-starter: 2.11.0.RELEASE
  7. grpc-client-spring-boot-starter: 2.11.0.RELEASE
  8. Operating system: win10 Professional Edition
  9. IDEA: 2021.1 (Ultimate Edition)

Source download

  • The complete source code in this actual combat can be downloaded from GitHub. The address and link information are shown in the table below( https://github.com/zq2599/blog_demos):

name

link

remarks

Project Home

https://github.com/zq2599/blog_demos

The project is on the GitHub home page

git warehouse address (https)

https://github.com/zq2599/blog_demos.git

The warehouse address of the source code of the project, https protocol

git warehouse address (ssh)

git@github.com:zq2599/blog_demos.git

The project source code warehouse address, ssh protocol

  • There are multiple folders in the git project. The source code of the gRPC practical combat series for java is in the gRPC tutorials folder, as shown in the red box below:

Create the parent project of gRPC Practice Series in java

  • Create a new gradle project named grpc tutorials. The previously mentioned libraries and their versions are handled in this project. The content of build.gradle is as follows:
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter

buildscript {
    repositories {
        maven {
            url 'https://plugins.gradle.org/m2/'
        }
        // If there is a private server, configure it here. If not, please comment it out
        maven {
            url 'http://192.168.50.43:8081/repository/aliyun-proxy/'
        }
        // Alibaba cloud
        maven {
            url 'http://maven.aliyun.com/nexus/content/groups/public/'
        }

        mavenCentral()
    }
    ext {
        // Project version
        projectVersion = '1.0-SNAPSHOT'

        // Version of dependent Library
        grpcSpringBootStarterVersion = '2.11.0.RELEASE'

        // Grpc version https://github.com/grpc/grpc-java/releases
        grpcVersion = '1.35.0'
        // Protobuf version https://github.com/protocolbuffers/protobuf/releases
        protobufVersion = '3.14.0'
        // gradle plug-in version of protobuf
        protobufGradlePluginVersion = '0.8.12'

        // sprignboot version https://github.com/spring-projects/spring-boot/releases
        springBootVersion = '2.3.8.RELEASE'
        // Spring cloud version https://github.com/spring-cloud/spring-cloud-release/releases
        springCloudVersion = 'Hoxton.SR9'
        // nacos version https://github.com/alibaba/spring-cloud-alibaba/releases
        springCloudAlibabaNacosVersion = '2.2.3.RELEASE'
        // Security version https://github.com/spring-projects/spring-security-oauth/releases
        springSecurityOAuthVersion = '2.5.0.RELEASE'
    }
}

plugins {
    id 'java'
    id 'java-library'
    id 'org.springframework.boot' version "${springBootVersion}" apply false
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'net.nemerosa.versioning' version '2.14.0'
    id 'com.google.protobuf' version '0.8.14'
    id 'io.franzbecker.gradle-lombok' version '4.0.0' apply false
    id 'com.github.ben-manes.versions' version '0.36.0' // gradle dependencyUpdates
}

// If you attempt to build without the `--scan` parameter in `gradle 6.0+` it will cause a build error that it can't find
// a buildScan property to change. This avoids that problem.
if (hasProperty('buildScan')) {
    buildScan {
        termsOfServiceUrl = 'https://gradle.com/terms-of-service'
        termsOfServiceAgree = 'yes'
    }
}

wrapper {
    gradleVersion = '6.7.1'
}

def buildTimeAndDate = OffsetDateTime.now()

ext {
    // Get the current date and time at build time
    buildDate = DateTimeFormatter.ISO_LOCAL_DATE.format(buildTimeAndDate)
    buildTime = DateTimeFormatter.ofPattern('HH:mm:ss.SSSZ').format(buildTimeAndDate)
    buildRevision = versioning.info.commit
}

allprojects {
    apply plugin: 'java'
    apply plugin: 'idea'
    apply plugin: 'eclipse'
    apply plugin: 'io.spring.dependency-management'
    apply plugin: 'io.franzbecker.gradle-lombok'

    compileJava {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
        options.encoding = 'UTF-8'
    }

    compileJava.options*.compilerArgs = [
            '-Xlint:all', '-Xlint:-processing'
    ]

    // Copy LICENSE
    tasks.withType(Jar) {
        from(project.rootDir) {
            include 'LICENSE'
            into 'META-INF'
        }
    }

    // Content written to MANIFEST.MF
    jar {
        manifest {
            attributes(
                    'Created-By': "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']})".toString(),
                    'Built-By': 'travis',
                    'Build-Date': buildDate,
                    'Build-Time': buildTime,
                    'Built-OS': "${System.properties['os.name']}",
                    'Build-Revision': buildRevision,
                    'Specification-Title': project.name,
                    'Specification-Version': projectVersion,
                    'Specification-Vendor': 'Will Zhao',
                    'Implementation-Title': project.name,
                    'Implementation-Version': projectVersion,
                    'Implementation-Vendor': 'Will Zhao'
            )
        }
    }

    repositories {
        mavenCentral()

        // If there is a private server, configure it here. If not, please comment it out
        maven {
            url 'http://192.168.50.43:8081/repository/aliyun-proxy/'
        }

        // Alibaba cloud
        maven {
            url 'http://maven.aliyun.com/nexus/content/groups/public/'
        }

        jcenter()
    }

    buildscript {
        repositories {
            maven { url 'https://plugins.gradle.org/m2/' }
        }
    }
}

allprojects { project ->
    buildscript {
        dependencyManagement {
            imports {
                mavenBom "org.springframework.boot:spring-boot-starter-parent:${springBootVersion}"
                mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
                mavenBom "com.google.protobuf:protobuf-bom:${protobufVersion}"
                mavenBom "io.grpc:grpc-bom:${grpcVersion}"
                mavenBom "org.junit:junit-bom:5.7.0"
            }

            dependencies {
                dependency 'org.projectlombok:lombok:1.16.16'
                dependency 'org.apache.commons:commons-lang3:3.11'
                dependency 'commons-collections:commons-collections:3.2.2'
                dependency "net.devh:grpc-server-spring-boot-starter:${grpcSpringBootStarterVersion}"
                dependency "net.devh:grpc-client-spring-boot-starter:${grpcSpringBootStarterVersion}"
            }
        }

        ext {
            micrometerVersion = dependencyManagement.importedProperties['micrometer.version']
            springFrameworkVersion = dependencyManagement.importedProperties['spring-framework.version']
            springSecurityVersion = dependencyManagement.importedProperties['spring-security.version']
            springCloudCommonsVersion = dependencyManagement.importedProperties['spring-cloud-commons.version']
        }
    }
}

group = 'com.bolingcavalry'
version = projectVersion
  • The parent project used in the whole series has been completed, and then you can start coding;

Automatic generation of java code with proto file in actual combat

  • gRPC services can be written in different languages. The key is that the proto file defining the service can be generated into codes in various languages, including java. Let's experience it together next;
  • Create a new module named grpc lib under the parent project grpc tutorials. The content of its build.gradle is as follows. It can be seen that the protobuf plug-in is configured and how the generated java code can be added to the source path by the IDE tool:
// gradle plug-in for generating java code according to proto
plugins {
    id 'com.google.protobuf'
}

dependencies {
    implementation 'io.grpc:grpc-netty-shaded'
    implementation 'io.grpc:grpc-protobuf'
    implementation 'io.grpc:grpc-stub'
    if (JavaVersion.current().isJava9Compatible()) {
        // Workaround for @javax.annotation.Generated
        // see: https://github.com/grpc/grpc-java/issues/3633
        implementation 'jakarta.annotation:jakarta.annotation-api'
    }
}


protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:${protobufVersion}"
    }
    // The automatically generated code is here
    generatedFilesBaseDir = "$projectDir/src/generated"
    clean {
        delete generatedFilesBaseDir
    }
    // Plug in for generating java code
    plugins {
        grpc {
            artifact = 'io.grpc:protoc-gen-grpc-java'
        }
    }
    generateProtoTasks {
        all()*.plugins {
            grpc {}
        }
    }
}

// For eclipse, the generated code can be added to the source path through the following script, which will be used during compilation
eclipse {
    classpath {
        file.beforeMerged { cp ->
            def generatedGrpcFolder = new org.gradle.plugins.ide.eclipse.model.SourceFolder('src/generated/main/grpc', null);
            generatedGrpcFolder.entryAttributes['ignore_optional_problems'] = 'true';
            cp.entries.add( generatedGrpcFolder );
            def generatedJavaFolder = new org.gradle.plugins.ide.eclipse.model.SourceFolder('src/generated/main/java', null);
            generatedJavaFolder.entryAttributes['ignore_optional_problems'] = 'true';
            cp.entries.add( generatedJavaFolder );
        }
    }
}

// For idea, the generated code can be added to the source path through the following script, which will be used during compilation
idea {
    module {
        sourceDirs += file('src/generated/main/java')
        sourceDirs += file('src/generated/main/grpc')
        generatedSourceDirs += file('src/generated/main/java')
        generatedSourceDirs += file('src/generated/main/grpc')
    }
}
  • A new file named helloworld.proto is added in the src/main/proto directory of gRPC lib module, which defines a gRPC service, which contains an interface, and the definition of the input participation and return result of this interface:
syntax = "proto3";

option java_multiple_files = true;
// package for generating java code
option java_package = "com.bolingcavalry.grpctutorials.lib";
option java_outer_classname = "HelloWorldProto";

// gRPC service
service Simple {
    // Interface definition
    rpc SayHello (HelloRequest) returns (HelloReply) {
    }
}

// Data structure of input parameter
message HelloRequest {
    string name = 1;
}

// Return the data structure of the result
message HelloReply {
    string message = 1;
}
  • The proto file is ready. Next, generate java code according to this file. Execute the command gradle grpc lib: generateproto in the grpc tutorials directory to generate java code according to the helloworld.proto file. After successful execution, the contents in the red box below will be generated. These are Java Codes:
  • This article only talks about how to generate the above codes. The purpose of these codes will be left to the next article. Here, it is only briefly mentioned that there is an abstract class SimpleImplBase in SimpleGrpc, which needs to be inherited when making gRPC services. In addition, if you want to remotely call the sayHello interface of gRPC, you will use the simplestab class in SimpleGrpc class, and the rest hellopreply HelloRequest these are data structure definitions returned by input participants;
  • So far, the preparations for the actual combat of the Java version of gRPC have been completed, and the method of generating java code according to the proto file has been mastered. In the next chapter, let's try to publish and call the service together;

Added by Mouse on Tue, 07 Dec 2021 12:28:26 +0200