Pic-a-day: ذخیره و تجزیه و تحلیل تصاویر با کتابخانه های Google Native Client Java

1. بررسی اجمالی

در اولین آزمایشگاه کد، تصاویر را در یک سطل ذخیره خواهید کرد. این یک رویداد ایجاد فایل ایجاد می کند که توسط یک سرویس مستقر در Cloud Run مدیریت می شود. این سرویس با Vision API تماس می گیرد تا تجزیه و تحلیل تصویر را انجام دهد و نتایج را در یک دیتا استور ذخیره کند.

427de3100de3a61e.png

چیزی که یاد خواهید گرفت

  • فضای ذخیره سازی ابری
  • Cloud Run
  • Cloud Vision API
  • Cloud Firestore

2. راه اندازی و الزامات

تنظیم محیط خود به خود

  1. به Google Cloud Console وارد شوید و یک پروژه جدید ایجاد کنید یا از یک موجود استفاده مجدد کنید. اگر قبلاً یک حساب Gmail یا Google Workspace ندارید، باید یک حساب ایجاد کنید .

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • نام پروژه نام نمایشی برای شرکت کنندگان این پروژه است. این یک رشته کاراکتری است که توسط API های Google استفاده نمی شود. شما می توانید آن را در هر زمان به روز کنید.
  • شناسه پروژه باید در تمام پروژه‌های Google Cloud منحصربه‌فرد باشد و تغییرناپذیر باشد (پس از تنظیم نمی‌توان آن را تغییر داد). Cloud Console به طور خودکار یک رشته منحصر به فرد تولید می کند. معمولاً برای شما مهم نیست که چیست. در اکثر کدها، باید به شناسه پروژه ارجاع دهید (معمولاً به عنوان PROJECT_ID شناخته می شود). اگر شناسه تولید شده را دوست ندارید، ممکن است یک شناسه تصادفی دیگر ایجاد کنید. از طرف دیگر، می‌توانید خودتان را امتحان کنید و ببینید آیا در دسترس است یا خیر. پس از این مرحله نمی توان آن را تغییر داد و در طول مدت پروژه باقی می ماند.
  • برای اطلاع شما، یک مقدار سوم وجود دارد، یک شماره پروژه که برخی از API ها از آن استفاده می کنند. در مورد هر سه این مقادیر در مستندات بیشتر بیاموزید.
  1. در مرحله بعد، برای استفاده از منابع Cloud/APIها باید صورتحساب را در کنسول Cloud فعال کنید . اجرا کردن از طریق این کد لبه نباید هزینه زیادی داشته باشد، اگر اصلاً باشد. برای اینکه منابع را خاموش کنید تا بیش از این آموزش متحمل صورتحساب نشوید، می توانید منابعی را که ایجاد کرده اید حذف کنید یا کل پروژه را حذف کنید. کاربران جدید Google Cloud واجد شرایط برنامه آزمایشی رایگان 300 دلاری هستند.

Cloud Shell را راه اندازی کنید

در حالی که Google Cloud را می توان از راه دور از لپ تاپ شما کار کرد، در این کد لبه از Google Cloud Shell استفاده خواهید کرد، یک محیط خط فرمان که در Cloud اجرا می شود.

از Google Cloud Console ، روی نماد Cloud Shell در نوار ابزار بالا سمت راست کلیک کنید:

55efc1aaa7a4d3ad.png

تهیه و اتصال به محیط فقط چند لحظه طول می کشد. وقتی تمام شد، باید چیزی شبیه به این را ببینید:

7ffe5cbb04455448.png

این ماشین مجازی با تمام ابزارهای توسعه که شما نیاز دارید بارگذاری شده است. این یک فهرست اصلی 5 گیگابایتی دائمی را ارائه می دهد و در Google Cloud اجرا می شود و عملکرد و احراز هویت شبکه را تا حد زیادی افزایش می دهد. تمام کارهای شما در این کد لبه را می توان در یک مرورگر انجام داد. شما نیازی به نصب چیزی ندارید.

3. API ها را فعال کنید

برای این آزمایشگاه، از Cloud Functions و Vision API استفاده خواهید کرد، اما ابتدا باید آنها را یا در Cloud Console یا با gcloud فعال کنید.

برای فعال کردن Vision API در Cloud Console، Cloud Vision API را در نوار جستجو جستجو کنید:

cf48b1747ba6a6fb.png

شما در صفحه Cloud Vision API قرار خواهید گرفت:

ba4af419e6086fbb.png

روی دکمه ENABLE کلیک کنید.

همچنین می‌توانید با استفاده از ابزار خط فرمان gcloud آن را فعال کنید.

در داخل Cloud Shell، دستور زیر را اجرا کنید:

gcloud services enable vision.googleapis.com

برای اتمام موفقیت آمیز باید عملیات را مشاهده کنید:

Operation "operations/acf.12dba18b-106f-4fd2-942d-fea80ecc5c1c" finished successfully.

Cloud Run و Cloud Build را نیز فعال کنید:

gcloud services enable cloudbuild.googleapis.com \
  run.googleapis.com

4. ایجاد سطل (کنسول)

یک سطل ذخیره سازی برای تصاویر ایجاد کنید. می توانید این کار را از کنسول Google Cloud Platform ( consol.cloud.google.com ) یا با ابزار خط فرمان gsutil از Cloud Shell یا محیط توسعه محلی خود انجام دهید.

از منوی "همبرگر" (☰)، به صفحه Storage بروید.

1930e055d138150a.png

سطل خود را نام ببرید

روی دکمه CREATE BUCKET کلیک کنید.

34147939358517f8.png

CONTINUE کلیک کنید.

مکان را انتخاب کنید

197817f20be07678.png

یک سطل چند منطقه ای در منطقه مورد نظر خود ایجاد کنید (اینجا Europe ).

CONTINUE کلیک کنید.

کلاس ذخیره سازی پیش فرض را انتخاب کنید

53cd91441c8caf0e.png

کلاس ذخیره سازی Standard را برای داده های خود انتخاب کنید.

CONTINUE کلیک کنید.

کنترل دسترسی را تنظیم کنید

8c2b3b459d934a51.png

از آنجایی که با تصاویر در دسترس عموم کار می کنید، می خواهید همه تصاویر ذخیره شده ما در این سطل کنترل دسترسی یکسانی داشته باشند.

گزینه Uniform Access Control را انتخاب کنید.

CONTINUE کلیک کنید.

حفاظت/رمزگذاری را تنظیم کنید

d931c24c3e705a68.png

پیش فرض ( Google-managed key) را حفظ کنید، زیرا از کلیدهای رمزگذاری خود استفاده نخواهید کرد.

روی CREATE کلیک کنید تا در نهایت ایجاد سطل ما نهایی شود.

allUsers را به عنوان نمایشگر ذخیره سازی اضافه کنید

به برگه Permissions بروید:

d0ecfdcff730ea51.png

یک عضو allUsers را با نقش Storage > Storage Object Viewer به سطل اضافه کنید:

e9f25ec1ea0b6cc6.png

روی SAVE کلیک کنید.

5. سطل (gsutil) را ایجاد کنید

همچنین می توانید از ابزار خط فرمان gsutil در Cloud Shell برای ایجاد سطل استفاده کنید.

در Cloud Shell، یک متغیر برای نام سطل یکتا تنظیم کنید. Cloud Shell قبلاً GOOGLE_CLOUD_PROJECT را روی شناسه پروژه منحصر به فرد شما تنظیم کرده است. می توانید آن را به نام سطل اضافه کنید.

به عنوان مثال:

export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}

ایجاد یک منطقه استاندارد چند منطقه ای در اروپا:

gsutil mb -l EU gs://${BUCKET_PICTURES}

از دسترسی یکنواخت به سطح سطل اطمینان حاصل کنید:

gsutil uniformbucketlevelaccess set on gs://${BUCKET_PICTURES}

سطل را عمومی کنید:

gsutil iam ch allUsers:objectViewer gs://${BUCKET_PICTURES}

اگر به بخش Cloud Storage کنسول بروید، باید یک سطل uploaded-pictures عمومی داشته باشید:

a98ed4ba17873e40.png

تست کنید که می توانید تصاویر را در سطل آپلود کنید و تصاویر آپلود شده برای عموم در دسترس هستند، همانطور که در مرحله قبل توضیح داده شد.

6. دسترسی عمومی به سطل را آزمایش کنید

با بازگشت به مرورگر ذخیره سازی، سطل خود را با دسترسی «عمومی» (شامل یک علامت هشدار که به شما یادآوری می کند که هر کسی به محتوای آن سطل دسترسی دارد) را در لیست می بینید.

89e7a4d2c80a0319.png

اکنون سطل شما برای دریافت تصاویر آماده است.

اگر روی نام سطل کلیک کنید، جزئیات سطل را مشاهده خواهید کرد.

131387f12d3eb2d3.png

در آنجا، می‌توانید دکمه Upload files را امتحان کنید تا آزمایش کنید که می‌توانید تصویری را به سطل اضافه کنید. یک پنجره انتخابگر فایل از شما می خواهد که یک فایل را انتخاب کنید. پس از انتخاب، در سطل شما آپلود می شود و دوباره دسترسی public را که به طور خودکار به این فایل جدید نسبت داده شده است، خواهید دید.

e87584471a6e9c6d.png

در کنار برچسب دسترسی Public ، نماد پیوند کوچکی را نیز مشاهده خواهید کرد. وقتی روی آن کلیک می کنید، مرورگر شما به آدرس عمومی آن تصویر می رود که به شکل زیر خواهد بود:

https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png

با BUCKET_NAME نام منحصربه‌فرد جهانی است که برای سطل خود انتخاب کرده‌اید، و سپس نام فایل تصویرتان.

با کلیک بر روی کادر کنار نام تصویر، دکمه DELETE فعال می شود و می توانید این تصویر اول را حذف کنید.

7. پایگاه داده را آماده کنید

شما اطلاعات مربوط به تصویر ارائه شده توسط Vision API را در پایگاه داده Cloud Firestore ، یک پایگاه داده اسناد NoSQL سریع، کاملا مدیریت شده، بدون سرور و بومی ابری ذخیره خواهید کرد. با رفتن به بخش Firestore در Cloud Console پایگاه داده خود را آماده کنید:

9e4708d2257de058.png

دو گزینه ارائه می شود: Native mode یا Datastore mode . از حالت بومی استفاده کنید، که ویژگی‌های اضافی مانند پشتیبانی آفلاین و همگام‌سازی بلادرنگ را ارائه می‌دهد.

روی SELECT NATIVE MODE کلیک کنید.

9449ace8cc84de43.png

یک منطقه چندگانه (اینجا در اروپا، اما در حالت ایده آل حداقل همان منطقه ای که عملکرد و سطل ذخیره سازی شما هستند) انتخاب کنید.

روی دکمه CREATE DATABASE کلیک کنید.

پس از ایجاد پایگاه داده، باید موارد زیر را مشاهده کنید:

56265949a124819e.png

با کلیک بر روی دکمه + START COLLECTION یک مجموعه جدید ایجاد کنید.

pictures مجموعه نام .

75806ee24c4e13a7.png

شما نیازی به ایجاد سند ندارید. وقتی تصاویر جدید در Cloud Storage ذخیره می شوند و توسط Vision API تجزیه و تحلیل می شوند، آنها را به صورت برنامه نویسی اضافه خواهید کرد.

روی Save کلیک کنید.

Firestore اولین سند پیش فرض را در مجموعه جدید ایجاد می کند، می توانید با خیال راحت آن سند را حذف کنید زیرا حاوی اطلاعات مفیدی نیست:

5c2f1e17ea47f48f.png

اسنادی که به صورت برنامه نویسی در مجموعه ما ایجاد می شوند دارای 4 فیلد خواهند بود:

  • نام (رشته): نام فایل تصویر آپلود شده که کلید سند نیز می باشد
  • برچسب ها (آرایه رشته ها): برچسب های موارد شناسایی شده توسط Vision API
  • رنگ (رشته): کد رنگ هگزادسیمال رنگ غالب (یعنی #ab12ef)
  • ایجاد (تاریخ): مُهر زمانی ذخیره شدن فراداده این تصویر
  • تصویر بندانگشتی (بولی): یک فیلد اختیاری که در صورت ایجاد یک تصویر کوچک برای این تصویر وجود دارد و درست است.

از آنجایی که در Firestore برای یافتن تصاویری که دارای ریز عکس‌ها هستند جستجو می‌کنیم و بر اساس تاریخ ایجاد مرتب می‌کنیم، باید یک فهرست جستجو ایجاد کنیم.

می توانید با دستور زیر در Cloud Shell ایندکس را ایجاد کنید:

gcloud firestore indexes composite create \
  --collection-group=pictures \
  --field-config field-path=thumbnail,order=descending \
  --field-config field-path=created,order=descending

یا می‌توانید این کار را از کنسول Cloud انجام دهید، با کلیک بر روی Indexes ، در ستون پیمایش در سمت چپ، و سپس ایجاد یک نمایه ترکیبی مانند شکل زیر:

ecb8b95e3c791272.png

روی Create کلیک کنید. ایجاد فهرست ممکن است چند دقیقه طول بکشد.

8. کد را شبیه سازی کنید

اگر قبلاً در آزمایشگاه کد قبلی نبوده اید، کد را شبیه سازی کنید:

git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop

سپس می توانید برای شروع ساخت آزمایشگاه به دایرکتوری حاوی سرویس بروید:

cd serverless-photosharing-workshop/services/image-analysis/java

شما طرح بندی فایل زیر را برای سرویس خواهید داشت:

f79613aff479d8ad.png

9. کد سرویس را کاوش کنید

شما با نگاهی به نحوه فعال شدن کتابخانه های کلاینت جاوا در pom.xml با استفاده از یک BOM شروع می کنید:

ابتدا فایل pom.xml را ویرایش کنید که وابستگی های تابع جاوا را فهرست می کند. برای افزودن وابستگی به Cloud Vision API Maven، کد را به‌روزرسانی کنید:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>cloudfunctions</groupId>
  <artifactId>gcs-function</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <maven.compiler.target>11</maven.compiler.target>
    <maven.compiler.source>11</maven.compiler.source>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.google.cloud</groupId>
        <artifactId>libraries-bom</artifactId>
        <version>26.1.1</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>com.google.cloud.functions</groupId>
      <artifactId>functions-framework-api</artifactId>
      <version>1.0.4</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-firestore</artifactId>
    </dependency>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-vision</artifactId>
    </dependency>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-storage</artifactId>
    </dependency>
  </dependencies>

این عملکرد در کلاس EventController پیاده سازی شده است. هر بار که یک تصویر جدید در سطل آپلود می شود، سرویس اعلانی برای پردازش دریافت می کند:

@RestController
public class EventController {
  private static final Logger logger = Logger.getLogger(EventController.class.getName());
    
  private static final List<String> requiredFields = Arrays.asList("ce-id", "ce-source", "ce-type", "ce-specversion");

  @RequestMapping(value = "/", method = RequestMethod.POST)
  public ResponseEntity<String> receiveMessage(
    @RequestBody Map<String, Object> body, @RequestHeader Map<String, String> headers) throws IOException, InterruptedException, ExecutionException {
...
}

کد به اعتبار سنجی سرصفحه های Cloud Events ادامه می دهد:

System.out.println("Header elements");
for (String field : requiredFields) {
    if (headers.get(field) == null) {
    String msg = String.format("Missing expected header: %s.", field);
    System.out.println(msg);
    return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
    } else {
    System.out.println(field + " : " + headers.get(field));
    }
}

System.out.println("Body elements");
for (String bodyField : body.keySet()) {
    System.out.println(bodyField + " : " + body.get(bodyField));
}

if (headers.get("ce-subject") == null) {
    String msg = "Missing expected header: ce-subject.";
    System.out.println(msg);
    return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
} 

اکنون می توان یک درخواست ایجاد کرد و کد یکی از این درخواست ها را برای ارسال به Vision API آماده می کند:

try (ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {
    List<AnnotateImageRequest> requests = new ArrayList<>();
    
    ImageSource imageSource = ImageSource.newBuilder()
        .setGcsImageUri("gs://" + bucketName + "/" + fileName)
        .build();

    Image image = Image.newBuilder()
        .setSource(imageSource)
        .build();

    Feature featureLabel = Feature.newBuilder()
        .setType(Type.LABEL_DETECTION)
        .build();
    Feature featureImageProps = Feature.newBuilder()
        .setType(Type.IMAGE_PROPERTIES)
        .build();
    Feature featureSafeSearch = Feature.newBuilder()
        .setType(Type.SAFE_SEARCH_DETECTION)
        .build();
        
    AnnotateImageRequest request = AnnotateImageRequest.newBuilder()
        .addFeatures(featureLabel)
        .addFeatures(featureImageProps)
        .addFeatures(featureSafeSearch)
        .setImage(image)
        .build();
    
    requests.add(request);

ما 3 قابلیت کلیدی Vision API را درخواست می کنیم:

  • تشخیص برچسب : برای درک آنچه در آن تصاویر وجود دارد
  • ویژگی های تصویر : برای دادن ویژگی های جالب به تصویر (ما به رنگ غالب تصویر علاقه مند هستیم)
  • جستجوی ایمن : برای اطلاع از اینکه آیا تصویر برای نمایش بی خطر است (این تصویر نباید حاوی محتوای بزرگسالان / پزشکی / نژادی / خشونت آمیز باشد)

در این مرحله، می‌توانیم با Vision API تماس بگیریم:

...
logger.info("Calling the Vision API...");
BatchAnnotateImagesResponse result = vision.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = result.getResponsesList();
...

برای مرجع، در اینجا پاسخ از Vision API به نظر می رسد:

{
  "faceAnnotations": [],
  "landmarkAnnotations": [],
  "logoAnnotations": [],
  "labelAnnotations": [
    {
      "locations": [],
      "properties": [],
      "mid": "/m/01yrx",
      "locale": "",
      "description": "Cat",
      "score": 0.9959855675697327,
      "confidence": 0,
      "topicality": 0.9959855675697327,
      "boundingPoly": null
    },
    ✄ - - - ✄
  ],
  "textAnnotations": [],
  "localizedObjectAnnotations": [],
  "safeSearchAnnotation": {
    "adult": "VERY_UNLIKELY",
    "spoof": "UNLIKELY",
    "medical": "VERY_UNLIKELY",
    "violence": "VERY_UNLIKELY",
    "racy": "VERY_UNLIKELY",
    "adultConfidence": 0,
    "spoofConfidence": 0,
    "medicalConfidence": 0,
    "violenceConfidence": 0,
    "racyConfidence": 0,
    "nsfwConfidence": 0
  },
  "imagePropertiesAnnotation": {
    "dominantColors": {
      "colors": [
        {
          "color": {
            "red": 203,
            "green": 201,
            "blue": 201,
            "alpha": null
          },
          "score": 0.4175916016101837,
          "pixelFraction": 0.44456374645233154
        },
        ✄ - - - ✄
      ]
    }
  },
  "error": null,
  "cropHintsAnnotation": {
    "cropHints": [
      {
        "boundingPoly": {
          "vertices": [
            { "x": 0, "y": 118 },
            { "x": 1177, "y": 118 },
            { "x": 1177, "y": 783 },
            { "x": 0, "y": 783 }
          ],
          "normalizedVertices": []
        },
        "confidence": 0.41695669293403625,
        "importanceFraction": 1
      }
    ]
  },
  "fullTextAnnotation": null,
  "webDetection": null,
  "productSearchResults": null,
  "context": null
}

اگر خطایی برگردانده نشد، می‌توانیم ادامه دهیم، به همین دلیل این بلوک را داریم:

if (responses.size() == 0) {
    logger.info("No response received from Vision API.");
    return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
}

AnnotateImageResponse response = responses.get(0);
if (response.hasError()) {
    logger.info("Error: " + response.getError().getMessage());
    return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
}

ما می‌خواهیم برچسب‌های چیزها، دسته‌ها یا مضامین شناسایی شده در تصویر را دریافت کنیم:

List<String> labels = response.getLabelAnnotationsList().stream()
    .map(annotation -> annotation.getDescription())
    .collect(Collectors.toList());
logger.info("Annotations found:");
for (String label: labels) {
    logger.info("- " + label);
}

ما علاقه مندیم که رنگ غالب تصویر را بدانیم:

String mainColor = "#FFFFFF";
ImageProperties imgProps = response.getImagePropertiesAnnotation();
if (imgProps.hasDominantColors()) {
    DominantColorsAnnotation colorsAnn = imgProps.getDominantColors();
    ColorInfo colorInfo = colorsAnn.getColors(0);

    mainColor = rgbHex(
        colorInfo.getColor().getRed(), 
        colorInfo.getColor().getGreen(), 
        colorInfo.getColor().getBlue());

    logger.info("Color: " + mainColor);
}

بیایید بررسی کنیم که آیا تصویر برای نمایش امن است یا خیر:

boolean isSafe = false;
if (response.hasSafeSearchAnnotation()) {
    SafeSearchAnnotation safeSearch = response.getSafeSearchAnnotation();

    isSafe = Stream.of(
        safeSearch.getAdult(), safeSearch.getMedical(), safeSearch.getRacy(),
        safeSearch.getSpoof(), safeSearch.getViolence())
    .allMatch( likelihood -> 
        likelihood != Likelihood.LIKELY && likelihood != Likelihood.VERY_LIKELY
    );

    logger.info("Safe? " + isSafe);
}

ما در حال بررسی ویژگی های بزرگسالان / جعل / پزشکی / خشونت / نژادپرستانه هستیم تا ببینیم که آیا محتمل یا بسیار محتمل هستند .

اگر نتیجه جستجوی ایمن درست باشد، می‌توانیم متادیتا را در Firestore ذخیره کنیم:

// Saving result to Firestore
if (isSafe) {
    FirestoreOptions firestoreOptions = FirestoreOptions.getDefaultInstance();
    Firestore pictureStore = firestoreOptions.getService();

    DocumentReference doc = pictureStore.collection("pictures").document(fileName);

    Map<String, Object> data = new HashMap<>();
    data.put("labels", labels);
    data.put("color", mainColor);
    data.put("created", new Date());

    ApiFuture<WriteResult> writeResult = doc.set(data, SetOptions.merge());

    logger.info("Picture metadata saved in Firestore at " + writeResult.get().getUpdateTime());
}

10. ساخت تصاویر برنامه با GraalVM (اختیاری)

در این مرحله اختیاری، با استفاده از GraalVM، یک JIT(JVM) based app image و سپس یک AOT(Native) Java app image خواهید ساخت.

برای اجرای بیلد، باید مطمئن شوید که یک JDK مناسب و سازنده تصویر native نصب و پیکربندی شده است. چندین گزینه در دسترس است.

To start ، GraalVM 22.2.x Community Edition را دانلود کنید و دستورالعمل های صفحه نصب GraalVM را دنبال کنید.

این فرآیند را می توان تا حد زیادی با کمک SDKMAN ساده کرد!

برای نصب توزیع JDK مناسب با SDKman ، با استفاده از دستور install شروع کنید:

sdk install java 22.2.r17-grl

به SDKman دستور دهید از این نسخه برای ساخت JIT و AOT استفاده کند:

sdk use java 22.2.0.r17-grl

native-image utility برای GraalVM نصب کنید:

gu install native-image

در Cloudshell ، برای راحتی شما، می توانید GraalVM و ابزار native-image را با این دستورات ساده نصب کنید:

# install GraalVM in your home directory
cd ~

# download GraalVM
wget https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.2.0/graalvm-ce-java17-linux-amd64-22.2.0.tar.gz
ls
tar -xzvf graalvm-ce-java17-linux-amd64-22.2.0.tar.gz

# configure Java 17 and GraalVM 22.2
echo Existing JVM: $JAVA_HOME
cd graalvm-ce-java17-22.2.0
export JAVA_HOME=$PWD
cd bin
export PATH=$PWD:$PATH

echo JAVA HOME: $JAVA_HOME
echo PATH: $PATH

# install the native image utility
java -version
gu install native-image

cd ../..

ابتدا متغیرهای محیط پروژه GCP را تنظیم کنید:

export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)

سپس می توانید برای شروع ساخت آزمایشگاه به دایرکتوری حاوی سرویس بروید:

cd serverless-photosharing-workshop/services/image-analysis/java

تصویر برنامه JIT (JVM) را بسازید:

./mvnw package -Pjvm

گزارش ساخت را در ترمینال مشاهده کنید:

...
[INFO] --- spring-boot-maven-plugin:2.7.3:repackage (repackage) @ image-analysis ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  24.009 s
[INFO] Finished at: 2022-09-26T22:17:32-04:00
[INFO] ------------------------------------------------------------------------

تصویر AOT (Native) را بسازید:.

./mvnw package -Pnative -DskipTests

گزارش ساخت را در ترمینال مشاهده کنید، از جمله گزارش‌های ساخت تصویر بومی:

توجه داشته باشید که ساخت، بسته به دستگاهی که روی آن آزمایش می کنید، کمی بیشتر طول می کشد.

...
[2/7] Performing analysis...  [**********]                                                              (95.4s @ 3.57GB)
  23,346 (94.42%) of 24,725 classes reachable
  44,625 (68.71%) of 64,945 fields reachable
 163,759 (70.79%) of 231,322 methods reachable
     989 classes, 1,402 fields, and 11,032 methods registered for reflection
      63 classes,    69 fields, and    55 methods registered for JNI access
       5 native libraries: -framework CoreServices, -framework Foundation, dl, pthread, z
[3/7] Building universe...                                                                              (10.0s @ 5.35GB)
[4/7] Parsing methods...      [***]                                                                      (9.7s @ 3.13GB)
[5/7] Inlining methods...     [***]                                                                      (4.5s @ 3.29GB)
[6/7] Compiling methods...    [[6/7] Compiling methods...    [********]                                                                (67.6s @ 5.72GB)
[7/7] Creating image...                                                                                  (8.7s @ 4.59GB)
  62.21MB (54.80%) for code area:   100,371 compilation units
  50.98MB (44.91%) for image heap:  465,035 objects and 365 resources
 337.09KB ( 0.29%) for other data
 113.52MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 packages in code area:                               Top 10 object types in image heap:
   2.36MB com.google.protobuf                                 12.70MB byte[] for code metadata
   1.90MB i.g.xds.shaded.io.envoyproxy.envoy.config.core.v3    6.66MB java.lang.Class
   1.73MB i.g.x.shaded.io.envoyproxy.envoy.config.route.v3     6.47MB byte[] for embedded resources
   1.67MB sun.security.ssl                                     4.61MB byte[] for java.lang.String
   1.54MB com.google.cloud.vision.v1                           4.37MB java.lang.String
   1.46MB com.google.firestore.v1                              3.38MB byte[] for general heap data
   1.37MB io.grpc.xds.shaded.io.envoyproxy.envoy.api.v2.core   1.96MB com.oracle.svm.core.hub.DynamicHubCompanion
   1.32MB i.g.xds.shaded.io.envoyproxy.envoy.api.v2.route      1.80MB byte[] for reflection metadata
   1.09MB java.util                                          911.80KB java.lang.String[]
   1.08MB com.google.re2j                                    826.48KB c.o.svm.core.hub.DynamicHub$ReflectionMetadata
  45.91MB for 772 more packages                                6.45MB for 3913 more object types
------------------------------------------------------------------------------------------------------------------------
                        15.1s (6.8% of total time) in 56 GCs | Peak RSS: 7.72GB | CPU load: 4.37
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
 /Users/ddobrin/work/dan/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis (executable)
 /Users/ddobrin/work/dan/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis.build_artifacts.txt (txt)
========================================================================================================================
Finished generating 'image-analysis' in 3m 41s.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  03:56 min
[INFO] Finished at: 2022-09-26T22:22:29-04:00
[INFO] ------------------------------------------------------------------------

11. ساخت و انتشار تصاویر کانتینر

بیایید یک تصویر ظرف در دو نسخه مختلف بسازیم: یکی به عنوان یک JIT(JVM) image و دیگری به عنوان یک AOT(Native) Java image .

ابتدا متغیرهای محیط پروژه GCP را تنظیم کنید:

export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)

تصویر JIT (JVM) را بسازید:.

./mvnw package -Pjvm-image

گزارش ساخت را در ترمینال مشاهده کنید:

[INFO]     [creator]     Adding layer 'process-types'
[INFO]     [creator]     Adding label 'io.buildpacks.lifecycle.metadata'
[INFO]     [creator]     Adding label 'io.buildpacks.build.metadata'
[INFO]     [creator]     Adding label 'io.buildpacks.project.metadata'
[INFO]     [creator]     Adding label 'org.opencontainers.image.title'
[INFO]     [creator]     Adding label 'org.opencontainers.image.version'
[INFO]     [creator]     Adding label 'org.springframework.boot.version'
[INFO]     [creator]     Setting default process type 'web'
[INFO]     [creator]     Saving docker.io/library/image-analysis-jvm:r17...
[INFO]     [creator]     *** Images (03a44112456e):
[INFO]     [creator]           docker.io/library/image-analysis-jvm:r17
[INFO]     [creator]     Adding cache layer 'paketo-buildpacks/syft:syft'
[INFO]     [creator]     Adding cache layer 'cache.sbom'
[INFO] 
[INFO] Successfully built image 'docker.io/library/image-analysis-jvm:r17'
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  02:11 min
[INFO] Finished at: 2022-09-26T13:09:34-04:00
[INFO] ------------------------------------------------------------------------

تصویر AOT (Native) را بسازید:.

./mvnw package -Pnative-image

گزارش ساخت را در ترمینال مشاهده کنید، از جمله گزارش‌های ساخت تصویر بومی و فشرده‌سازی تصویر با استفاده از UPX.

توجه داشته باشید که ساخت، بسته به دستگاهی که روی آن آزمایش می کنید، کمی بیشتر طول می کشد

...
[INFO]     [creator]     [2/7] Performing analysis...  [***********]                    (147.6s @ 3.10GB)
[INFO]     [creator]       23,362 (94.34%) of 24,763 classes reachable
[INFO]     [creator]       44,657 (68.67%) of 65,029 fields reachable
[INFO]     [creator]      163,926 (70.76%) of 231,656 methods reachable
[INFO]     [creator]          981 classes, 1,402 fields, and 11,026 methods registered for reflection
[INFO]     [creator]           63 classes,    68 fields, and    55 methods registered for JNI access
[INFO]     [creator]            4 native libraries: dl, pthread, rt, z
[INFO]     [creator]     [3/7] Building universe...                                      (21.1s @ 2.66GB)
[INFO]     [creator]     [4/7] Parsing methods...      [****]                            (13.7s @ 4.16GB)
[INFO]     [creator]     [5/7] Inlining methods...     [***]                              (9.6s @ 4.20GB)
[INFO]     [creator]     [6/7] Compiling methods...    [**********]                     (107.6s @ 3.36GB)
[INFO]     [creator]     [7/7] Creating image...                                         (14.7s @ 4.87GB)
[INFO]     [creator]       62.24MB (51.35%) for code area:   100,499 compilation units
[INFO]     [creator]       51.99MB (42.89%) for image heap:  473,948 objects and 473 resources
[INFO]     [creator]        6.98MB ( 5.76%) for other data
[INFO]     [creator]      121.21MB in total
[INFO]     [creator]     --------------------------------------------------------------------------------
[INFO]     [creator]     Top 10 packages in code area:           Top 10 object types in image heap:
[INFO]     [creator]        2.36MB com.google.protobuf             12.71MB byte[] for code metadata
[INFO]     [creator]        1.90MB i.g.x.s.i.e.e.config.core.v3     7.59MB byte[] for embedded resources
[INFO]     [creator]        1.73MB i.g.x.s.i.e.e.config.route.v3    6.66MB java.lang.Class
[INFO]     [creator]        1.67MB sun.security.ssl                 4.62MB byte[] for java.lang.String
[INFO]     [creator]        1.54MB com.google.cloud.vision.v1       4.39MB java.lang.String
[INFO]     [creator]        1.46MB com.google.firestore.v1          3.66MB byte[] for general heap data
[INFO]     [creator]        1.37MB i.g.x.s.i.e.envoy.api.v2.core    1.96MB c.o.s.c.h.DynamicHubCompanion
[INFO]     [creator]        1.32MB i.g.x.s.i.e.e.api.v2.route       1.80MB byte[] for reflection metadata
[INFO]     [creator]        1.09MB java.util                      910.41KB java.lang.String[]
[INFO]     [creator]        1.08MB com.google.re2j                826.95KB c.o.s.c.h.DynamicHu~onMetadata
[INFO]     [creator]       45.94MB for 776 more packages            6.69MB for 3916 more object types
[INFO]     [creator]     --------------------------------------------------------------------------------
[INFO]     [creator]         20.4s (5.6% of total time) in 81 GCs | Peak RSS: 6.75GB | CPU load: 4.53
[INFO]     [creator]     --------------------------------------------------------------------------------
[INFO]     [creator]     Produced artifacts:
[INFO]     [creator]      /layers/paketo-buildpacks_native-image/native-image/services.ImageAnalysisApplication (executable)
[INFO]     [creator]      /layers/paketo-buildpacks_native-image/native-image/services.ImageAnalysisApplication.build_artifacts.txt (txt)
[INFO]     [creator]     ================================================================================
[INFO]     [creator]     Finished generating '/layers/paketo-buildpacks_native-image/native-image/services.ImageAnalysisApplication' in 5m 59s.
[INFO]     [creator]         Executing upx to compress native image
[INFO]     [creator]                            Ultimate Packer for eXecutables
[INFO]     [creator]                               Copyright (C) 1996 - 2020
[INFO]     [creator]     UPX 3.96        Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 23rd 2020
[INFO]     [creator]     
[INFO]     [creator]             File size         Ratio      Format      Name
[INFO]     [creator]        --------------------   ------   -----------   -----------
 127099880 ->  32416676   25.50%   linux/amd64   services.ImageAnalysisApplication
...
[INFO]     [creator]     ===> EXPORTING
...
[INFO]     [creator]     Adding cache layer 'paketo-buildpacks/native-image:native-image'
[INFO]     [creator]     Adding cache layer 'cache.sbom'
[INFO] 
[INFO] Successfully built image 'docker.io/library/image-analysis-native:r17'
------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  05:28 min
[INFO] Finished at: 2022-09-26T13:19:53-04:00
[INFO] ------------------------------------------------------------------------

تأیید کنید که تصاویر ساخته شده اند:

docker images | grep image-analysis

دو تصویر را تگ کنید و به GCR فشار دهید:

# JIT(JVM) image
docker tag image-analysis-jvm:r17 gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-jvm:r17
docker push gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-jvm:r17

# AOT(Native) image
docker tag image-analysis-native:r17 gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-native:r17
docker push  gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-native:r17

12. در Cloud Run مستقر شوید

زمان استقرار سرویس است.

شما دو بار سرویس را مستقر خواهید کرد، یک بار با استفاده از تصویر JIT(JVM) و بار دوم با استفاده از تصویر AOT(Native). هر دو استقرار سرویس، برای مقاصد مقایسه، تصویر یکسانی را از سطل به صورت موازی پردازش خواهند کرد.

ابتدا متغیرهای محیط پروژه GCP را تنظیم کنید:

export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
gcloud config set project ${GOOGLE_CLOUD_PROJECT}
gcloud config set run/region 
gcloud config set run/platform managed
gcloud config set eventarc/location europe-west1

تصویر JIT (JVM) را اجرا کنید و گزارش استقرار را در کنسول مشاهده کنید:

gcloud run deploy image-analysis-jvm \
     --image gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-jvm:r17 \
     --region europe-west1 \
     --memory 2Gi --allow-unauthenticated

...
Deploying container to Cloud Run service [image-analysis-jvm] in project [...] region [europe-west1]
✓ Deploying... Done.                                                                                                                                                               
  ✓ Creating Revision...                                                                                                                                                           
  ✓ Routing traffic...                                                                                                                                                             
  ✓ Setting IAM Policy...                                                                                                                                                          
Done.                                                                                                                                                                              
Service [image-analysis-jvm] revision [image-analysis-jvm-00009-huc] has been deployed and is serving 100 percent of traffic.
Service URL: https://image-analysis-jvm-...-ew.a.run.app

تصویر AOT (Native) را اجرا کنید و گزارش استقرار را در کنسول مشاهده کنید:

gcloud run deploy image-analysis-native \
     --image gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-native:r17 \
     --region europe-west1 \
     --memory 2Gi --allow-unauthenticated 
...
Deploying container to Cloud Run service [image-analysis-native] in project [...] region [europe-west1]
✓ Deploying... Done.                                                                                                                                                               
  ✓ Creating Revision...                                                                                                                                                           
  ✓ Routing traffic...                                                                                                                                                             
  ✓ Setting IAM Policy...                                                                                                                                                          
Done.                                                                                                                                                                              
Service [image-analysis-native] revision [image-analysis-native-00005-ben] has been deployed and is serving 100 percent of traffic.
Service URL: https://image-analysis-native-...-ew.a.run.app

13. Eventarc Triggers را راه اندازی کنید

Eventarc یک راه حل استاندارد برای مدیریت جریان تغییرات حالت، به نام رویدادها، بین میکروسرویس های جدا شده ارائه می دهد. هنگامی که فعال می‌شود، Eventarc این رویدادها را از طریق اشتراک‌های Pub/Sub به مقصدهای مختلف هدایت می‌کند (در این سند، به مقصد رویداد مراجعه کنید) در حالی که تحویل، امنیت، مجوز، قابلیت مشاهده و مدیریت خطا را برای شما مدیریت می‌کند.

می‌توانید یک راه‌انداز Eventarc ایجاد کنید تا سرویس Cloud Run شما اعلان‌های یک رویداد یا مجموعه‌ای از رویدادها را دریافت کند. با تعیین فیلترها برای تریگر، می‌توانید مسیریابی رویداد، از جمله منبع رویداد و سرویس Cloud Run را پیکربندی کنید.

ابتدا متغیرهای محیط پروژه GCP را تنظیم کنید:

export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
gcloud config set project ${GOOGLE_CLOUD_PROJECT}
gcloud config set run/region 
gcloud config set run/platform managed
gcloud config set eventarc/location europe-west1

اعطای pubsub.publisher به حساب سرویس Cloud Storage:

SERVICE_ACCOUNT="$(gsutil kms serviceaccount -p ${GOOGLE_CLOUD_PROJECT})"

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
    --member="serviceAccount:${SERVICE_ACCOUNT}" \
    --role='roles/pubsub.publisher'

راه‌اندازهای Eventarc را برای تصاویر سرویس JVM(JIT) و AOT(Native) برای پردازش تصویر تنظیم کنید:

gcloud eventarc triggers list --location=eu

gcloud eventarc triggers create image-analysis-jvm-trigger \
     --destination-run-service=image-analysis-jvm \
     --destination-run-region=europe-west1 \
     --location=eu \
     --event-filters="type=google.cloud.storage.object.v1.finalized" \
     --event-filters="bucket=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}" \
     --service-account=${PROJECT_NUMBER}-compute@developer.gserviceaccount.com

gcloud eventarc triggers create image-analysis-native-trigger \
     --destination-run-service=image-analysis-native \
     --destination-run-region=europe-west1 \
     --location=eu \
     --event-filters="type=google.cloud.storage.object.v1.finalized" \
     --event-filters="bucket=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}" \
     --service-account=${PROJECT_NUMBER}-compute@developer.gserviceaccount.com    

توجه داشته باشید که دو محرک ایجاد شده اند:

gcloud eventarc triggers list --location=eu

14. نسخه های سرویس تست

پس از اجرای موفقیت آمیز سرویس، تصویری را در Cloud Storage پست می‌کنید، ببینید آیا خدمات ما فراخوانی شده‌اند، Vision API چه چیزی را برمی‌گرداند، و آیا ابرداده در Firestore ذخیره می‌شود.

به Cloud Storage برگردید و روی سطلی که در ابتدای آزمایشگاه ایجاد کردیم کلیک کنید:

ff8a6567afc76235.png

پس از ورود به صفحه جزئیات سطل، روی دکمه Upload files کلیک کنید تا یک عکس آپلود شود.

به عنوان مثال، یک تصویر GeekHour.jpeg به همراه پایگاه کد شما در زیر /services/image-analysis/java ارائه می شود. یک تصویر را انتخاب کنید و Open button فشار دهید:

347b76e8b775f2f5.png

اکنون می‌توانید اجرای سرویس را بررسی کنید، با image-analysis-jvm شروع و سپس image-analysis-native .

از منوی "همبرگر" (☰)، به سرویس Cloud Run > image-analysis-jvm بروید.

روی Logs کلیک کنید و خروجی را مشاهده کنید:

810a8684414ceafa.png

و در واقع، در لیست گزارش‌ها، می‌توانم ببینم که سرویس JIT(JVM) image-analysis-jvm فراخوانی شده است.

گزارش ها شروع و پایان اجرای سرویس را نشان می دهد. و در این بین، ما می‌توانیم گزارش‌هایی را که در تابع خود قرار می‌دهیم با دستورات log در سطح INFO ببینیم. می بینیم:

  • جزئیات رویدادی که عملکرد ما را تحریک می کند،
  • نتایج خام از فراخوانی Vision API،
  • برچسب هایی که در تصویری که آپلود کردیم یافت شد،
  • اطلاعات رنگ های غالب،
  • اینکه آیا تصویر برای نشان دادن بی خطر است،
  • و در نهایت آن ابرداده های مربوط به تصویر در Firestore ذخیره شده است.

شما این فرآیند را برای سرویس image-analysis-native تکرار خواهید کرد.

از منوی "همبرگر" (☰)، به Cloud Run > image-analysis-native سرویس بروید.

روی Logs کلیک کنید و خروجی را مشاهده کنید:

b80308c7d0f55a3.png

اکنون می خواهید مشاهده کنید که آیا فراداده تصویر در Fiorestore ذخیره شده است یا خیر.

دوباره از منوی "همبرگر" (☰)، به بخش Firestore بروید. در زیر بخش Data (به طور پیش فرض نشان داده شده است)، باید مجموعه pictures را با یک سند جدید اضافه کنید، مطابق با تصویری که اخیراً آپلود کرده اید:

933a20a9709cb006.png

15. تمیز کردن (اختیاری)

اگر قصد ندارید با دیگر آزمایشگاه‌های این سری ادامه دهید، می‌توانید منابع را پاکسازی کنید تا در هزینه‌ها صرفه‌جویی کنید و در کل شهروند ابری خوبی باشید. می توانید منابع را به صورت جداگانه به صورت زیر پاک کنید.

سطل را حذف کنید:

gsutil rb gs://${BUCKET_PICTURES}

حذف تابع:

gcloud functions delete picture-uploaded --region europe-west1 -q

با انتخاب حذف مجموعه از مجموعه، مجموعه Firestore را حذف کنید:

410b551c3264f70a.png

یا می توانید کل پروژه را حذف کنید:

gcloud projects delete ${GOOGLE_CLOUD_PROJECT} 

16. تبریک می گویم!

تبریک می گویم! شما اولین سرویس کلیدی پروژه را با موفقیت اجرا کردید!

آنچه را پوشش داده ایم

  • فضای ذخیره سازی ابری
  • Cloud Run
  • Cloud Vision API
  • Cloud Firestore
  • تصاویر بومی جاوا

مراحل بعدی