gRPC, Nest.js kép méretező

February 7, 2024

A NestJS és gRPC Integrációjának Bemutatása Egy Képvágó Alkalmazáson Keresztül

Ebben a projektben egy egyszerű, mégis hatékony képvágó alkalmazást hozunk létre, amely a NestJS és a gRPC technológiákra épül. A NestJS egy modern, teljes értékű Node.js keretrendszer a szerveroldali alkalmazásokhoz, míg a gRPC egy magas teljesítményű, nyílt forráskódú univerzális RPC keretrendszer, amely lehetővé teszi a szolgáltatások közötti gyors és hatékony kommunikációt.

Instalálás

Telepítjük a nest.js-t

POWERSHELL
npm i -g @nestjs/cli

Ezután hozzuk létre magát a projektet, amely az API szerverünket fogja szolgálni. A projekt létrehozásához használjuk a nest new parancsot, és adjuk meg a projekt nevét, például grpc-picture-resize. Ez a parancs létrehoz egy új mappát a megadott névvel, és inicializál egy új NestJS projektet benne:

POWERSHELL
nest new grpc-picture-resize

A képek átméretezésére szolgáló logikát egy külön mikroszolgáltatásban fogjuk implementálni, ami lehetővé teszi számunkra, hogy izoláltan kezeljük ezt a funkcionalitást, és szükség esetén könnyen skálázhatóvá tegyük. Hozzuk létre ezt a mikroszolgáltatást a nest g app parancs segítségével, és adjuk hozzá a projektünkhöz mint egy új alkalmazást. A mikroszolgáltatás neve legyen például resize:

POWERSHELL
nest g app resize

A rendszerünk működéséhez még telepítjük a következő könyvtárakat:

POWERSHELL
npm install @nestjs/microservices
POWERSHELL
npm install @grpc/proto-loader @grpc/grpc-js ts-proto

A kép levágáshoz a következő javascript könyvtárat telapítjük

POWERSHELL
npm install sharp

A nest-cli fájl módosítása a monoreponknak megfelelően

JSON
//nest-cli.json
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "apps/grpc-picture-resize/src",
"compilerOptions": {
"deleteOutDir": true,
"webpack": true,
"tsConfigPath": "apps/grpc-picture-resize/tsconfig.app.json"
},
"monorepo": true,
"root": "apps/grpc-picture-resize",
"projects": {
"grpc-picture-resize": {
"type": "application",
"root": "apps/grpc-picture-resize",
"entryFile": "src/main",
"sourceRoot": "./",
"compilerOptions": {
"tsConfigPath": "apps/grpc-picture-resize/tsconfig.app.json",
"assets": [
"proto/*.proto"
],
"watchAssets": true
}
},
"resize": {
"type": "application",
"root": "apps/resize",
"entryFile": "src/main",
"sourceRoot": "./",
"compilerOptions": {
"tsConfigPath": "apps/resize/tsconfig.app.json",
"assets": [
"proto/*.proto"
],
"watchAssets": true
}
}
}
}

A resize.proto fájl elkészítése és typescriptté alakítása

A package resize sor definiálja a protobuf üzenetek és szolgáltatások névterét. Ez segít elkerülni a névütközéseket, amikor különböző protobuf definíciókat használunk együtt.

A service ImageResizer blokk definiál egy szolgáltatást, amely tartalmaz egy ResizeImage RPC-t (Remote Procedure Call). Ez az eljárás vár egy ResizeRequest kérést, és egy ResizeResponse választ ad vissza.

ResizeImage RPC: Lehetővé teszi a kliensek számára, hogy képet küldjenek átméretezésre. A kérés tartalmazza a képet mint byte tömböt (bytes image), valamint a kívánt szélességet (int32 width) és magasságot (int32 height).

ResizeRequest: Ez az üzenet tartalmazza a kép átméretezéséhez szükséges adatokat. Az image mező a kép bináris tartalmát tárolja, míg a width és height mezők az új méreteket adják meg.

ResizeResponse: A válaszüzenet, amely tartalmazza az átméretezett kép adatait. A fileName a létrehozott képfájl nevét tartalmazza, míg a resizedWidth és resizedHeight mezők az átméretezett kép szélességét és magasságát adják meg.

PLAIN TEXT
//resize.prote
syntax = "proto3";
package resize;
service ImageResizer {
rpc ResizeImage (ResizeRequest) returns (ResizeResponse) {}
}
message ResizeRequest {
bytes image = 1;
int32 width = 2;
int32 height = 3;
}
message ResizeResponse {
string fileName = 1;
int32 resizedWidth = 2;
int32 resizedHeight = 3;
}

A proto fájl elkészítése után protoc-vel typescript fájlt hozunk belőle létre

Windows-nál a következő lépésekkel tudod alkalmazni a protoc-t:

  • Töltsd le a protoc legújabb verzióját.
  • Telepítsd choco install protoc
  • És végül add a fájl bin mappákát a környezeti változókhoz.
  • A programuk főkönyvtárából futtatjuk a következő kódot:

    POWERSHELL
    protoc --plugin=protoc-gen-ts_proto="C:\path\to\your\project\folder\node_modules\.bin\protoc-gen-ts_proto.cmd" --ts_proto_out=./ --ts_proto_opt=nestJs=true ./proto/min.proto

    —plugin-nél megadjuk a használt plugint és a helyét

    —ts_proto_out-nál megadjuk, hogy nestJs-re optimalizálunk és hogy hova másolunk és hogy hol van a felhasznált proto fájlunk

    Api kezelése

    Az AppController osztályban definiálunk egy POST végpontot ('/resize'), amely lehetővé teszi a felhasználók számára, hogy egy kép URL-jét, valamint a kívánt szélességet és magasságot megadva kérjenek képátméretezést. A metódus letölti a képet a megadott URL-ről, létrehoz egy átméretezési kérést a letöltött képből, majd a resizeImage szolgáltatási hívással átméretezi a képet.

    TYPESCRIPT
    //grpc-picture-resize/src/app.controller.ts
    import { Body, Controller, Post } from '@nestjs/common';
    import { AppService } from './app.service';
    import fetch from 'node-fetch';
    @Controller()
    export class AppController {
    constructor(private readonly appService: AppService) {}
    @Post('/resize')
    async resizeImageByUrl(
    @Body() body: { imageUrl: string; width: number; height: number },
    ) {
    // Downloading the image from the provided URL
    const response = await fetch(body.imageUrl);
    if (!response.ok) {
    throw new Error('Failed to download the image');
    }
    const imageBuffer = await response.buffer();
    // Creating a resize request with the downloaded image
    const resizeRequest = {
    image: imageBuffer,
    width: body.width,
    height: body.height,
    };
    // Resizing the image using the gRPC service
    return this.appService.resizeImage(resizeRequest);
    }
    }

    Az AppModule fájlban konfiguráljuk a gRPC klienst a ClientsModule.register metódussal. Itt adunk meg egy szolgáltatásnevet ('resize'), amely megegyezik a proto fájlban definiált csomagnévvel, és beállítjuk a kommunikáció módját (Transport.GRPC). A protoPath opcióval megadjuk a gRPC definíciós fájl elérési útját, amely leírja a képek átméretezésére szolgáló interfészt.

    TYPESCRIPT
    //grpc-picture-resize/src/app.module.ts
    import { Module } from '@nestjs/common';
    import { ClientsModule, Transport } from '@nestjs/microservices';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { join } from 'path';
    @Module({
    imports: [
    ClientsModule.register([
    {
    name: 'resize',
    transport: Transport.GRPC,
    options: {
    package: 'resize',
    protoPath: join(__dirname, '../resize.proto'),
    },
    },
    ]),
    ],
    controllers: [AppController],
    providers: [AppService],
    })
    export class AppModule {}

    Az AppService osztály felelős a gRPC szolgáltatás fogyasztásáért. A konstruktorban injektáljuk a gRPC klienst, és az onModuleInit metódusban inicializáljuk a képátméretező kliens-szolgáltatást a getService<ImageResizerClient> hívással. A resizeImage metódus végrehajtja a képátméretezési kérést, és visszaadja az átméretezett kép fájlnevét.

    TYPESCRIPT
    //grpc-picture-resize/src/app.service.ts
    import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
    import { ClientGrpc } from '@nestjs/microservices';
    import {
    ResizeRequest,
    IMAGE_RESIZER_SERVICE_NAME,
    ImageResizerClient,
    } from 'proto/resize';
    @Injectable()
    export class AppService implements OnModuleInit {
    private imageResizerClient: ImageResizerClient;
    constructor(@Inject('resize') private clientGrpc: ClientGrpc) {}
    onModuleInit() {
    this.imageResizerClient = this.clientGrpc.getService<ImageResizerClient>(
    IMAGE_RESIZER_SERVICE_NAME,
    );
    }
    async resizeImage(resizeRequest: ResizeRequest) {
    const resizedImage = await this.imageResizerClient
    .resizeImage(resizeRequest)
    .toPromise();
    return resizedImage.fileName;
    }
    }

    Végül a main.ts fájlban létrehozzuk és indítjuk a NestJS alkalmazást. Az await app.listen(3000); sorral az alkalmazás a 3000-es porton figyel, lehetővé téve az API-hoz való hozzáférést.

    TYPESCRIPT
    //grpc-picture-resize/src/main.ts
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    async function bootstrap() {
    const app = await NestFactory.create(AppModule);
    await app.listen(3000);
    }
    bootstrap();

    Kép vágás megoldása

    A ResizeController felelős a gRPC hívások fogadásáért és kezeléséért. A @GrpcMethod() dekorátorral jelölt resizeImage metódus a gRPC kliensektől érkező ResizeImage hívásokat fogadja. Ebben a metódusban hívódik meg a ResizeService által nyújtott képátméretezési funkció, amely egy ResizeRequest objektumot vár paraméterként, és egy ResizeResponse objektummal tér vissza.

    TYPESCRIPT
    //resize/src/resize.controller.ts
    import { Controller } from '@nestjs/common';
    import { GrpcMethod } from '@nestjs/microservices';
    import { ResizeService } from './resize.service';
    import { ResizeRequest, ResizeResponse } from 'proto/resize';
    @Controller()
    export class ResizeController {
    constructor(private readonly resizeService: ResizeService) {}
    @GrpcMethod('ImageResizer', 'ResizeImage')
    async resizeImage(resizeRequest: ResizeRequest): Promise<ResizeResponse> {
    return this.resizeService.resizeImage(resizeRequest);
    }
    }

    A ResizeModule a kontroller és a szolgáltatás NestJS modulba való csomagolásáért felelős. Ez a modul biztosítja, hogy a kontroller és a szolgáltatás elérhető és használható legyen a mikroszolgáltatásban.

    TYPESCRIPT
    //resize/src/resize.module.ts
    import { Module } from '@nestjs/common';
    import { ResizeService } from './resize.service';
    import { ResizeController } from './resize.controller';
    @Module({
    controllers: [ResizeController],
    providers: [ResizeService],
    })
    export class ResizeModule {}

    A ResizeService implementálja a képek tényleges átméretezését használva a sharp könyvtárat. A resizeImage metódus fogad egy ResizeRequest objektumot, amely tartalmazza a kép adatát (a Buffer formájában), valamint a kívánt szélességet és magasságot. A sharp segítségével átméretezi a képet, majd a lemezre menti a generált képet egy előre meghatározott könyvtárba. Végül visszatér egy ResizeResponse objektummal, amely tartalmazza a generált fájl nevét, valamint az átméretezett kép szélességét és magasságát.

    TYPESCRIPT
    //resize/src/resize.service.ts
    import { Injectable } from '@nestjs/common';
    import * as sharp from 'sharp';
    import { ResizeRequest, ResizeResponse } from 'proto/resize';
    import * as fs from 'fs';
    import * as path from 'path';
    @Injectable()
    export class ResizeService {
    private saveDirectory = path.join(__dirname, '../');
    async resizeImage(resizeRequest: ResizeRequest): Promise<ResizeResponse> {
    try {
    const fileName = `resized-${Date.now()}.jpg`;
    const outputPath = path.join(this.saveDirectory, fileName);
    const { data: resizedImage, info } = await sharp(
    Buffer.from(resizeRequest.image),
    )
    .resize(resizeRequest.width, resizeRequest.height)
    .toBuffer({ resolveWithObject: true });
    // Save to disk
    fs.writeFileSync(outputPath, resizedImage);
    return {
    fileName: fileName,
    resizedWidth: info.width,
    resizedHeight: info.height,
    };
    } catch (error) {
    console.error('Image resizing and saving failed: ', error.message);
    throw new Error(error.message);
    }
    }
    }

    A bootstrap függvény indítja el a mikroszolgáltatást. Ebben az esetben a NestFactory.createMicroservice metódust használjuk a gRPC szerver létrehozására, konfigurálására és indítására. A transport beállítása Transport.GRPC-re van állítva, ami lehetővé teszi a NestJS számára, hogy gRPC protokollt használjon a kommunikációhoz. Az options objektumban megadjuk a proto fájl elérési útját és a csomag nevét, amely leírja a kommunikációra használt interfészt és üzenetformátumokat.

    TYPESCRIPT
    //resize/src/main.ts
    import { NestFactory } from '@nestjs/core';
    import { MicroserviceOptions, Transport } from '@nestjs/microservices';
    import { ResizeModule } from './resize.module';
    import { join } from 'path';
    async function bootstrap() {
    const app = await NestFactory.createMicroservice<MicroserviceOptions>(
    ResizeModule,
    {
    transport: Transport.GRPC,
    options: {
    package: 'resize',
    protoPath: join(__dirname, '../resize.proto'),
    },
    },
    );
    await app.listen();
    }
    bootstrap();

    Tesztelés

    A kód egyszerűsége érdekében csak az image fájl nevét küldjük vissza. A kész módosított képhez pedig mentjük a localhost-on, ahogy a fentebbi kódban már megadtuk

    Mikroszolgáltatás és kliens alkalmazás indítása: Először is indítsuk el a képátméretező mikroszolgáltatást és a kliens alkalmazást a következő parancsokkal:

  • A képátméretező mikroszolgáltatás elindítása:
  • POWERSHELL
    npm run start:dev resize
  • A kliens alkalmazás elindítása:
  • POWERSHELL
    npm run start:dev grpc-picture-resize

    Tesztelés Postman vagy curl segítségével:

    A rendszer teszteléséhez használhatjuk a Postman alkalmazást vagy a curl parancssori eszközt. A teszteléshez küldjünk egy POST kérést a kliens alkalmazás /resize végpontjára, megadva a kép URL-jét, a kívánt szélességet és magasságot a kérés törzsében.

    Például, curl használatával:

    POWERSHELL
    curl -X POST

    A sikeres képátméretezés után a megadott paraméterekkel átméretezett kép a resize alkalmazás által konfigurált helyi mappában lesz mentve. A kép fájlneve, amely tartalmazza az átméretezés időpontját, visszaküldésre kerül a kérés indítójának, lehetővé téve az eredmények ellenőrzését.

    Hogyan tovább

    A gRPC használata képátméretezésre több előnnyel is jár, különösen mikroszolgáltatások esetén, vagy amikor a rendszer több különböző komponensből áll, amelyek közötti kommunikáció fontos szempont. A kód fejlesztésének egyik lehetősége, hogy a feltöltt képeket feltöltjük aws s3-ra és annak az url-jét küldjük vissza a felhasználónak

    Code

    A teljes kód megtalálható:

    https://github.com/balazsfaragodev/grpc-nestjs-tutorial-beginner

    Oszd meg ezt a cikket

    Merülj el az izgalmas tudásban, amíg a buszra vársz!

    Indítsd a napod a legújabb technológiai áttörésekkel. Csatlakozz most, és merülj el az innovációban!

    Kapcsolódó Cikkek