[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!

댓글

이 블로그의 인기 게시물

PYTHONPATH, Python 모듈 환경설정

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

git 명령어

[Ubuntu] Apache2.4.x 설치

Create topic on Kafka with partition count, 카프카 토픽 생성하기

리눅스의 부팅과정 (프로세스, 서비스 관리)

Auto-populate a calendar in an MUI (Material-UI) TextField component

The pierce selector in Puppeteer