terminus icon indicating copy to clipboard operation
terminus copied to clipboard

Example code for gRPC healthcheck does not work

Open Baroshem opened this issue 4 years ago • 9 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Current behavior

Code from examples/grpc does not work as expected returning status 500 and message 'null', while console prints out the Exception Error that it cannot connect to all connections.

Minimum reproduction code

Code from examples/grpc

Steps to reproduce

  1. copy code from exaples/grpc
  2. access route to trigger a health controller
  3. See error in browser/Postman and error in the console.

Expected behavior

It should correctly connect to the microservice and return the actual state of that microservice. The client folder of examples/grpc is missing the part about registering proto for this certain health check. I have found a solution so I can create a pull request with missing part.

Package version

7.2.0

NestJS version

7.6.18

Node.js version

14.17.0

In which operating systems have you tested?

  • [X] macOS
  • [ ] Windows
  • [ ] Linux

Other

I can prepare a pull request with required changes so that the example code could work as expected.

Baroshem avatar Oct 04 '21 11:10 Baroshem

Hi @Baroshem, you mean the one in the sample/004-grpc-app?

Tony133 avatar Oct 04 '21 13:10 Tony133

Hi @Tony133

Yes exactly, I copy pasted the code from the example and it was not able to connect to the microservice and return its state. What I have found was missing in my case were the options for grpcHealthIndicator with transport and proto declaration. When I added these options to my healthcheck service it was returning corectly.

So basically what I think is missing from the example is similar code that you have in the server folder but for client.

{
    transport: Transport.GRPC,
    options: {
      package: 'grpc.health.v1',
      protoPath: join(__dirname, '../protos/health.proto'),
    },
  }

This part is added to the server but not to the client so I think that client cannot connect to grpc microservice because of that. At least adding this to the client worked for my case.

I can create a pull request with my changes so that it will be easier to see.

Baroshem avatar Oct 04 '21 15:10 Baroshem

I tried the example (sample/004-grpc-app) and it returned an empty object, so there is something wrong, I used version 8 of NestJS, as the examples are all updated to the latest version. What kind of error does he give? However, if you want to contribute, feel free to pull for this problem. 👍

Tony133 avatar Oct 04 '21 16:10 Tony133

@Tony133

I checked it once again. It does indeed work in your example but it does not work if you are using microservices and trying to connect to check the state of the microservice.

So in my case what I needed to add was something like this:

  check() {
    return this.health.check([
        async () =>
          this.grpc.checkService<GrpcOptions>('hero_service', 'grpc.health.v1', {
            timeout: 2000,
            package: 'grpc.health.v1',
            url: 'localhost:3001'
            protoPath: [join(__dirname, '../../protos/health.proto')],
          }),
    ]);
  }

Without it, I was not able to connect to the microservice and return the appriopriate state. Let me know if you would like me to add this to the example. I tested it and it does not break the current test (if I add this part) and can help people who are struggling with microservices just like me :)

Baroshem avatar Oct 05 '21 09:10 Baroshem

Hi @Baroshem,

I did this, taking an example from what you said.

in src/client/health/health.controller.ts in I did it like this:

//src/client/health/health.controller.ts
import { Controller, Get } from '@nestjs/common';
import { GrpcOptions } from '@nestjs/microservices';
import { join } from 'path';
import {
  HealthCheck,
  HealthCheckService,
  GRPCHealthIndicator,
} from '@nestjs/terminus';

@Controller('health')
export class HealthController {
  constructor(
    private health: HealthCheckService,
    private grpc: GRPCHealthIndicator,
  ) {}

  @Get()
  @HealthCheck()
  check() {
    return this.health.check([
      async () =>
        this.grpc.checkService<GrpcOptions>('hero_service', 'grpc.health.v1', {
          timeout: 2000,
          package: 'grpc.health.v1',
          url: 'localhost:3001',
          protoPath: join(__dirname, '../../protos/health.proto'),
        }),
    ]);
  }
}

while in src/server/main.ts in I did it like this:

//src/server/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { join } from 'path';
import { Transport } from '@nestjs/microservices';

async function bootstrap() {
  const app = await NestFactory.createMicroservice(AppModule, {
    transport: Transport.GRPC,
    options: {
      package: 'grpc.health.v1',
      url: 'localhost:3001',
      protoPath: join(__dirname, '../protos/health.proto'),
    },
  });
  app.listen();
  console.log('Microservice is listening');
}
bootstrap();

from browser or curl, it gives me this(see screenshot):

screenshot

gives you the same json back to you too?

Tony133 avatar Oct 05 '21 10:10 Tony133

@Tony133

Yes but only when I wrap health controller with try catch and return value like this

try {
      const check = await this.healthService.check();

      res.send(check?.details);
    } catch (error) {
      res.status(error?.status).send(error?.response?.details);
    }

If I won't do that it will then return empty page in the browser but request will return correct data.

Baroshem avatar Oct 05 '21 10:10 Baroshem

@Baroshem strange as i explained above to me it works, but are you using version 7.x or version 8.x of NestJS?

Tony133 avatar Oct 05 '21 11:10 Tony133

@Baroshem I tried deleting the node_modules folder and reinstalling everything and now it works with the same code that is inside samples/004-grpc-app. Very strange

However if you find some time create a minimal reproduction in a clonable git repository in order to better evaluate the problem.

Tony133 avatar Oct 05 '21 15:10 Tony133

@Baroshem Thanks for pointing out those extra options to insert that were missing in the sample.

For me it works the same way as @Tony133 shows, without the need to wrap with try, catch

Edit: NestJS v8

Royserg avatar Oct 20 '21 12:10 Royserg

Somewhat related question. What about microservices that don't open a TCP port by default, like Kafka? Is it possible to make terminus write to /tmp so we could do health check with exec for example?

https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes

alko89 avatar Sep 29 '22 13:09 alko89

I've been given the task to write a healthcheck with terminus. We have a hybrid app that has grpc. This stuff really confuses me. What is the 'hero_service' and how do you know it is OK?

roppa avatar Mar 06 '24 14:03 roppa