[gRPC] server of Java and client of Typescript

Great! To develop two applications – a gRPC client in TypeScript using Avro and a gRPC server in Java using Avro, we’ll follow these steps. I’ll break it down into the server and client components.


Plan Overview

  • Server Application: Java-based gRPC server with Avro serialization.

    • Exposes a gRPC service that sends/receives Avro-encoded data.
    • Uses Gradle to manage dependencies.
  • Client Application: TypeScript-based gRPC client.

    • Communicates with the gRPC server using Avro-encoded messages.
    • Uses grpc-web and a gRPC gateway to bridge between gRPC and the web frontend.

1. gRPC Server in Java with Avro

Project Setup for Server

  1. Create a new Gradle project.
  2. Add the necessary dependencies (gRPC, Avro, and protobuf).

Server Directory Structure

grpc-server-java/
├── build.gradle.kts
├── settings.gradle.kts
├── src/
│   ├── main/
│   │   ├── java/         # Java source files
│   │   │   └── com/example/service/  # Service implementation
│   │   └── proto/        # Protobuf and Avro schema files
└── └── resources/        # Application config files (if needed)

Step 1: Add Dependencies in build.gradle.kts

plugins {
    java
    id("com.google.protobuf") version "0.9.1" // gRPC plugin
    id("com.github.davidmc24.gradle.plugin.avro") version "1.6.0" // Avro plugin
    id("application")
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("io.grpc:grpc-netty-shaded:1.56.0")
    implementation("io.grpc:grpc-protobuf:1.56.0")
    implementation("io.grpc:grpc-stub:1.56.0")
    implementation("com.google.protobuf:protobuf-java:3.22.0")

    // Avro dependencies
    implementation("org.apache.avro:avro:1.11.1")

    // Protobuf compiler for code generation
    compileOnly("com.google.protobuf:protoc:3.22.0")
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.22.0"
    }
    plugins {
        create("grpc") {
            artifact = "io.grpc:protoc-gen-grpc-java:1.56.0"
        }
    }
    generateProtoTasks {
        all().forEach { task ->
            task.plugins {
                create("grpc")
            }
        }
    }
}

tasks.withType<JavaCompile> {
    options.encoding = "UTF-8"
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

application {
    mainClass.set("com.example.service.GrpcServer")
}

Step 2: Define .proto and Avro Schema Files

Place your .proto file in the src/main/proto/ folder:

// src/main/proto/user_service.proto
syntax = "proto3";

package example;

service UserService {
    rpc GetUser (UserRequest) returns (UserResponse);
}

message UserRequest {
    string userId = 1;
}

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

And define the Avro schema in a .avsc file:

// src/main/resources/avro/user.avsc
{
  "type": "record",
  "name": "User",
  "namespace": "com.example.avro",
  "fields": [
    { "name": "userId", "type": "string" },
    { "name": "name", "type": "string" },
    { "name": "age", "type": "int" }
  ]
}

Step 3: Implement the gRPC Service

Create the Java implementation of the gRPC service.

// src/main/java/com/example/service/UserServiceImpl.java
package com.example.service;

import com.example.proto.UserRequest;
import com.example.proto.UserResponse;
import com.example.proto.UserServiceGrpc;
import io.grpc.stub.StreamObserver;

public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {

    @Override
    public void getUser(UserRequest request, StreamObserver<UserResponse> responseObserver) {
        // Example: Return a UserResponse with dummy data
        UserResponse response = UserResponse.newBuilder()
                .setUserId(request.getUserId())
                .setName("John Doe")
                .setAge(25)
                .build();

        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

Step 4: Start the gRPC Server

Create the gRPC server main class.

// src/main/java/com/example/service/GrpcServer.java
package com.example.service;

import io.grpc.Server;
import io.grpc.ServerBuilder;

import java.io.IOException;

public class GrpcServer {

    public static void main(String[] args) throws IOException, InterruptedException {
        Server server = ServerBuilder.forPort(8080)
                .addService(new UserServiceImpl())
                .build();

        System.out.println("Server started on port 8080");
        server.start();
        server.awaitTermination();
    }
}

Step 5: Build and Run the Server

Run the following commands:

./gradlew clean build
./gradlew run

The server should start on port 8080.


2. gRPC Client in TypeScript with Avro

Project Setup for Client

  1. Create a new TypeScript project.
  2. Use grpc-web to communicate with the Java gRPC server.
  3. Use avsc to handle Avro encoding and decoding in TypeScript.

Step 1: Install Dependencies

Run the following commands to set up the client:

mkdir grpc-client-ts
cd grpc-client-ts
npm init -y
npm install grpc-web google-protobuf avsc @types/google-protobuf --save

Step 2: Generate TypeScript gRPC Stubs

Use protoc to generate TypeScript code for your gRPC service.

protoc -I=. user_service.proto \
  --js_out=import_style=commonjs:./src \
  --grpc-web_out=import_style=typescript,mode=grpcwebtext:./src

This will generate the necessary TypeScript files in the src/ directory.


Step 3: Implement the gRPC Client

Create a TypeScript client to call the gRPC service.

// src/client.ts
import { UserServiceClient } from './user_service_pb_service';
import { UserRequest, UserResponse } from './user_service_pb';
import * as avro from 'avsc';

// Define the Avro schema for decoding
const userSchema = avro.Type.forSchema({
  type: 'record',
  name: 'User',
  fields: [
    { name: 'userId', type: 'string' },
    { name: 'name', type: 'string' },
    { name: 'age', type: 'int' },
  ],
});

const client = new UserServiceClient('http://localhost:8080', null, null);

const request = new UserRequest();
request.setUserid('123');

client.getUser(request, {}, (err, response: UserResponse) => {
  if (err) {
    console.error('Error:', err);
    return;
  }

  // Decode the Avro-encoded response
  const user = userSchema.fromBuffer(response.serializeBinary());
  console.log('Received user:', user);
});

Step 4: Run the Client

Use the following command to run the client:

npx ts-node src/client.ts

Make sure the Java server is running before executing the client.


Conclusion

You now have a working gRPC server in Java with Avro serialization and a TypeScript gRPC client using grpc-web. This setup allows seamless communication between the two applications.

Let me know if you encounter any issues or need further help!

댓글

이 블로그의 인기 게시물

Install and run an FTP server using Docker

Using the MinIO API via curl

PYTHONPATH, Python 모듈 환경설정

Elasticsearch Ingest API

오늘의 문장2

How to checkout branch of remote git, 깃 리모트 브랜치 체크아웃

Fundamentals of English Grammar #1

To switch to a specific tag in a Git repository

You can use Sublime Text from the command line by utilizing the subl command

티베트-버마어파 와 한어파(중국어파)의 어순 비교