gRPC Error Handling

Dokumentasi gRPC error code dapat dilihat di https://grpc.io/docs/guides/error/, namun dokumentasi tersebut agak kurang lengkap.

Anda dapat lihat dokumentasi implementasi yg lebih lengkap di https://avi.im/grpc-errors/

Pada modul ini kami coba jelaskan implementasi error handling untuk unary grpc.

Buka file protos/greet.proto, tambahkan rpc dan message baru

syntax = "proto3";

package greet;

message Greeting {
    string first_name = 1;
    string last_name = 2;
}

message GreetRequest{
    Greeting greeting = 1;
}

message GreetResponse{
    string result = 1;
}

//streaming request
message GreetManyRequest{
    Greeting greeting = 1;
}

//streaming response
message GreetManyResponse{
    string result = 1;
}

//client streaming msg request
message LongGreetRequest {
    Greeting greet = 1;
}
message LongGreetResponse {
    string result = 1;
}

//bi directional streaming
message GreetEveryoneRequest {
    Greeting greet = 1;
}
message GreetEveryoneResponse {
    string result = 1;
}

service GreetService {
    //unary
    rpc Greet (GreetRequest) returns (GreetResponse){};

    //server streaming
    rpc GreetManyTimes (GreetManyRequest) returns (stream GreetManyResponse){};

    //client streaming
    rpc LongGreet (stream LongGreetRequest) returns (LongGreetResponse){};

    //bidi streaming
    rpc GreetEveryone (stream GreetEveryoneRequest) returns (stream GreetEveryoneResponse){};    

    //contoh implementasi error handling unary
    rpc SquareRoot (SquareRootRequest) returns (SquareRootResponse);
}

//message untuk contoh implementasi handling erro unary
message SquareRootRequest {
    int32 number = 1;
}
//message untuk contoh implementasi handling erro unary
message SquareRootResponse {
    double number_root = 1;
}

Kemudian kita compile, buka command prompt dan jalankan perintah berikut:

protoc-gen-grpc --proto_path=protos --js_out=import_style=commonjs,binary:server --grpc_out=server protos/greet.proto

File greet_pb.js dan greet_grpc_pb.js akan diupdate.

Kemudian kita buka file server/index.js, kita tambahkan implementasi error handling untuk service squareRoot

var grpc = require('grpc');

var greets = require('./greet_pb');
var service = require('./greet_grpc_pb');

function greet (call, callback){
    var greeting = new greets.GreetResponse();

    greeting.setResult(        
        "Hello, " + call.request.getGreeting().getFirstName() + " " + call.request.getGreeting().getLastName()
    );

    callback(null, greeting);
}

//implementasi streaming API
function greetManyTimes (call, callback){
    var firstName = call.request.getGreeting().getFirstName();

    //kita simulasikan streaming.
    let count = 0, intervalID = setInterval(function(){

        var greetManyResponse = new greets.GreetManyResponse();
        greetManyResponse.setResult(firstName);
    
        //streaming goes here    
        call.write(greetManyResponse);
        if(++count > 9){
            clearInterval(intervalID);
            call.end() //server selesai mengirim message.
        }
    }, 1000);


}

//client streaming
function longGreet (call, callback){
    call.on('data', request=>{
        var fullname = request.getGreet().getFirstName() + ' ' + request.getGreet().getLastName();
        console.log('hello, ' + fullname);
    });   

    call.on('error', (error)=>{
        console.error(error);
    });

    call.on('end', ()=>{
        var response = new greets.LongGreetResponse();
        response.setResult('Long greet client streaming..');

        callback(null, response);
    });

}

//bidi streaming
async function sleep(interval){
    return new Promise((resolve)=>{
        setTimeout(()=> resolve(), interval)
    });
}

async function greetEveryone(call, callback){
    call.on('data', request =>{
        var fullname = request.getGreet().getFirstName() + " " + request.getGreet().getLastName();
        console.log('Data from: ' + fullname);
    });

    call.on('error', error =>{
        log.error(error);
    });

    call.on('end', ()=>{
        console.log('End bidi streaming...');
    });

    //simulasi streaming menggunakan for loop

    for(var i=0; i<10; i++){
        var response = new greets.GreetEveryoneResponse();
        response.setResult('Data ke: ' +  i);

        call.write(response);

        await sleep(1000);
    }

    call.end();
}

//contoh implementasi error unary
function squareRoot(call, callback){
    var number = call.request.getNumber();
    if (number >=0){
        var numberRoot = Math.sqrt(number);
        var response = new greets.SquareRootResponse();
        response.setNumberRoot(numberRoot);
        callback(null, response);
    }else{
        //error handling
        return callback({
            code: grpc.status.INVALID_ARGUMENT,
            message: 'Number is negatif : ' + number,
        });
    }
}

function main(){
    var Server = new grpc.Server();
    
    Server.addService(service.GreetServiceService, {
        greet: greet, 
        greetManyTimes: greetManyTimes, 
        longGreet:longGreet, 
        greetEveryone:greetEveryone,
        squareRoot: squareRoot, //implementasi error handling
    });
    Server.bindAsync("127.0.0.1:50051", grpc.ServerCredentials.createInsecure(), ()=>{
        Server.start();
    });
    

    console.log("Server running..");
}

main()

Buka file client, tambahkan code untuk melakukan error call dengan passing data negatif.

var grpc = require('grpc');

var greets = require('../server/greet_pb');
var service = require('../server/greet_grpc_pb');

// function unary(){
//     var client = new service.GreetServiceClient('localhost:50051', grpc.credentials.createInsecure());

//     var request = new greets.GreetRequest();
//     var greeting = new greets.Greeting();
//     greeting.setFirstName("Test1");
//     greeting.setLastName("Test2");

//     request.setGreeting(greeting);

//     client.greet(request, (error, response)=>{
//         if (!error){
//             console.log("Response: ", response.getResult());
//         }else{
//             console.error(error);
//         }
//     });

// }

// function callGreetMany(){
//     var client = new service.GreetServiceClient(
//         'localhost:50051', grpc.credentials.createInsecure()
//     );

//     var request = new greets.GreetManyRequest();
//     var greeting = new greets.Greeting();
//     greeting.setFirstName("Streaming");
//     greeting.setLastName("API");

//     request.setGreeting(greeting);

//     var call = client.greetManyTimes(request, ()=>{});

//     call.on("data", (response)=>{
//         console.log('Client streaming response: ', response.getResult());
//     });

//     call.on("status", (status)=>{
//         console.log(status.details);
//     });

//     call.on("error", (error)=>{
//         console.error(error.details);
//     });

//     call.on("end", ()=>{
//         console.log("streaming end..");
//     });


// }


// function callLongGreeting(){
//     var client = new service.GreetServiceClient(
//         'localhost:50051', grpc.credentials.createInsecure()
//     );

//     var request = new greets.LongGreetRequest();

//     var call = client.longGreet(request, (error, response)=>{
//         if(!error){
//             console.log('server response: ', response.getResult());
//         }else{
//             console.error(error);
//         }
//     });

//     let count = 0, intervalID = setInterval(function(){
//         console.log('Sending message ' + count);

//         //request 1
//         var request = new greets.LongGreetRequest();
//         var greeting = new greets.Greeting();
//         greeting.setFirstName('Client');
//         greeting.setLastName('Streaming');

//         request.setGreet(greeting);

//         //request 2
//         var request2 = new greets.LongGreetRequest();
//         var greeting2 = new greets.Greeting();
//         greeting2.setFirstName('Client2');
//         greeting2.setLastName('Streaming2');

//         request2.setGreet(greeting2);
        

//         call.write(request);
//         call.write(request2);

//         if(++count > 3){
//             clearInterval(intervalID);
//             call.end();
//         }

//     }, 1000);
// }

// //bidi streaming
// async function sleep(interval){
//     return new Promise((resolve)=>{
//         setTimeout(()=> resolve(), interval)
//     });
// }

// async function callBiDirect(){
//     var client = new service.GreetServiceClient(
//         'localhost:50051', grpc.credentials.createInsecure()
//     );

//     var call =  client.greetEveryone(request, (error, response)=>{
//         console.log('Server response: ' + response);
//     });

//     call.on('data', response=>{
//         console.log('Response from server:  ' + response.getResult());
//     });

//     call.on('error', error =>{
//         console.error(error);
//     });

//     call.on('end', ()=>{
//         console.log('End bidi...');
//     });

//     //simulasi streaming data menggunakan for loop
//     for(var i=0; i<10; i++){
//         var greeting = new greets.Greeting();
//         greeting.setFirstName('Bidi');
//         greeting.setLastName('Streaming');

//         var request = new greets.GreetEveryoneRequest();
//         request.setGreet(greeting);

//         call.write(request);
//         await sleep(1500);
//     }

//     call.end();

// }

function doErrorCall(){
    console.log("Implementasi RPC error handling");
    var client = new service.GreetServiceClient(
        'localhost:50051', grpc.credentials.createInsecure()
    );


    var number = -4;
    var squareRootRequest = new greets.SquareRootRequest();
    
    squareRootRequest.setNumber(number);
    
    client.squareRoot(squareRootRequest, (error, response)=>{
       if(!error) {
            console.log("Square root : " + response.getNumberRoot());
       }else{
            console.error(error.message);
       }
    });
}

doErrorCall();
//callBiDirect();
// callLongGreeting();
//callGreetMany();
//unary()

Buka command prompt, masuk ke direktori sever, kemudian jalankan service

$ node index.js

Buka command prompt kedua, masuk ke direktori client, kemudian jalankan code client.

$ node client.js

Implementasi RPC error handling
3 INVALID_ARGUMENT: Number is negatif : -4

Sesuai ekspektasi, server akan mengembalikan error code dan details.

Dapat diambil kesimpulan, cara error handling, sama dengan kita membuat program pada umumnya. Hanya yang perlu kita ikuti adalah aturan error code, yang dapat diakses melalui object grpc.status

Sharing is caring:

Leave a Comment