1. بررسی اجمالی
در این کد، ما در مورد پروژه Spring Native، ساختن برنامه ای که از آن استفاده می کند، و استقرار آن در Google Cloud یاد می گیریم.
ما به اجزای آن، تاریخچه اخیر پروژه، برخی موارد استفاده و البته مراحل مورد نیاز برای استفاده از آن در پروژه های خود خواهیم پرداخت.
پروژه Spring Native در حال حاضر در مرحله آزمایشی است، بنابراین برای شروع به پیکربندی خاصی نیاز دارد. با این حال، همانطور که در SpringOne 2021 اعلام شد، Spring Native قرار است در Spring Framework 6.0 و Spring Boot 3.0 با پشتیبانی درجه یک ادغام شود، بنابراین این زمان عالی برای نگاه دقیقتر به پروژه چند ماه قبل از انتشار آن است.
در حالی که کامپایل به موقع برای مواردی مانند فرآیندهای طولانی مدت به خوبی بهینه شده است، موارد استفاده خاصی وجود دارد که در آنها برنامه های کامپایل شده زودتر حتی بهتر عمل می کنند، که ما در طول برنامه کد در مورد آنها صحبت خواهیم کرد.
شما یاد خواهید گرفت که چگونه
- از Cloud Shell استفاده کنید
- Cloud Run API را فعال کنید
- یک برنامه Spring Native ایجاد و استقرار کنید
- چنین برنامه ای را در Cloud Run مستقر کنید
آنچه شما نیاز دارید
- یک پروژه Google Cloud Platform با یک حساب صورتحساب GCP فعال
- gcloud cli نصب شده یا به Cloud Shell دسترسی داشته باشید
- مهارت های پایه جاوا + XML
- دانش کاری دستورات رایج لینوکس
نظرسنجی
چگونه از این آموزش استفاده خواهید کرد؟
تجربه خود را با جاوا چگونه ارزیابی می کنید؟
تجربه خود را در استفاده از خدمات Google Cloud چگونه ارزیابی می کنید؟
2. پس زمینه
پروژه Spring Native از چندین فناوری برای ارائه عملکرد برنامه بومی به توسعه دهندگان استفاده می کند.
برای درک کامل Spring Native، درک چند مورد از این فناوریهای مؤلفه، آنچه که آنها برای ما فعال میکنند و نحوه کار آنها در اینجا مفید است.
تدوین AOT
هنگامی که توسعه دهندگان جاوا را به طور معمول در زمان کامپایل اجرا می کنند، کد منبع .java ما در فایل های .class که در بایت کد نوشته شده اند، کامپایل می شود. این بایت کد فقط برای ماشین مجازی جاوا قابل درک است، بنابراین JVM باید این کد را در ماشین های دیگر تفسیر کند تا ما بتوانیم کد خود را اجرا کنیم.
این فرآیند چیزی است که به ما قابلیت حمل امضای جاوا را می دهد - به ما امکان می دهد "یک بار بنویسیم و همه جا اجرا کنیم"، اما در مقایسه با اجرای کد بومی گران است.
خوشبختانه، اکثر پیاده سازی های JVM از کامپایل به موقع برای کاهش این هزینه تفسیر استفاده می کنند. این با شمارش فراخوانیهای یک تابع به دست میآید، و اگر به اندازه کافی فراخوانی شود تا یک آستانه ( به طور پیشفرض 10000 ) فراخوانی شود، در زمان اجرا به کد اصلی کامپایل میشود تا از تفسیر گرانتر بیشتر جلوگیری شود.
کامپایل پیش از زمان رویکردی مخالف دارد، با کامپایل کردن تمام کدهای قابل دسترسی در یک فایل اجرایی بومی در زمان کامپایل. این قابلیت حمل را با بهره وری حافظه و سایر دستاوردهای عملکرد در زمان اجرا معامله می کند.
این البته یک معامله است و همیشه ارزش آن را ندارد. با این حال، کامپایل AOT می تواند در موارد استفاده خاص مانند:
- برنامه های کاربردی کوتاه مدت که زمان راه اندازی آنها مهم است
- محیط هایی با محدودیت حافظه بالا که در آن JIT ممکن است بسیار پرهزینه باشد
به عنوان یک واقعیت سرگرم کننده، کامپایل AOT به عنوان یک ویژگی آزمایشی در JDK 9 معرفی شد، اگرچه نگهداری این پیاده سازی گران بود، و هرگز کاملاً مورد توجه قرار نگرفت، بنابراین بی سر و صدا در جاوا 17 به نفع توسعه دهندگانی که فقط از GraalVM استفاده می کردند حذف شد.
GraalVM
GraalVM یک توزیع منبع باز JDK بسیار بهینه شده است که دارای زمان راه اندازی بسیار سریع، کامپایل تصویر بومی AOT و قابلیت های چند زبانه است که به توسعه دهندگان اجازه می دهد چندین زبان را در یک برنامه واحد ترکیب کنند.
GraalVM در حال توسعه فعال است، قابلیتهای جدید به دست میآورد و قابلیتهای موجود را همیشه بهبود میبخشد، بنابراین من توسعهدهندگان را تشویق میکنم که در ارتباط باشند.
برخی از نقاط عطف اخیر عبارتند از:
- یک خروجی ساخت تصویر بومی جدید و کاربرپسند ( 18-01-2021 )
- پشتیبانی از جاوا 17 ( 18-01-2022 )
- فعال کردن کامپایل چند لایه به طور پیش فرض برای بهبود زمان کامپایل چند زبانه ( 2021-04-20 )
بومی بهار
به زبان ساده - Spring Native استفاده از کامپایلر تصویری مادری GraalVM را برای تبدیل برنامههای کاربردی Spring به فایلهای اجرایی بومی فعال میکند.
این فرآیند شامل انجام یک تجزیه و تحلیل استاتیک از برنامه شما در زمان کامپایل برای یافتن همه روشهایی در برنامه شما است که از نقطه ورودی قابل دسترسی هستند.
این اساساً یک مفهوم "دنیای بسته" از برنامه شما ایجاد می کند، که در آن فرض می شود همه کدها در زمان کامپایل شناخته شده هستند و هیچ کد جدیدی مجاز به بارگذاری در زمان اجرا نیست.
توجه به این نکته مهم است که تولید تصویر بومی یک فرآیند فشرده حافظه است که بیشتر از کامپایل یک برنامه معمولی طول می کشد و محدودیت هایی را بر جنبه های خاصی از جاوا تحمیل می کند.
در برخی موارد، هیچ تغییر کدی برای کار با Spring Native مورد نیاز نیست. با این حال، برخی از موقعیتها به پیکربندی بومی خاصی نیاز دارند تا به درستی کار کنند. در آن شرایط، Spring Native اغلب Native Hints را برای سادهسازی این فرآیند ارائه میکند.
3. راه اندازی/پیش کار
قبل از شروع اجرای Spring Native، باید برنامه خود را ایجاد و اجرا کنیم تا یک خط پایه عملکرد ایجاد کنیم که بتوانیم بعداً آن را با نسخه بومی مقایسه کنیم.
1. ایجاد پروژه
ما با دریافت برنامه خود از start.spring.io شروع می کنیم:
curl https://start.spring.io/starter.zip -d dependencies=web \ -d javaVersion=11 \ -d bootVersion=2.6.4 -o io-native-starter.zip
این برنامه شروع کننده از Spring Boot 2.6.4 استفاده می کند که آخرین نسخه ای است که پروژه Spring-native در زمان نوشتن از آن پشتیبانی می کند .
توجه داشته باشید که از زمان انتشار GraalVM 21.0.3 ، می توانید از جاوا 17 برای این نمونه نیز استفاده کنید. ما همچنان از جاوا 11 برای این آموزش استفاده خواهیم کرد تا پیکربندی مربوطه را به حداقل برسانیم.
هنگامی که zip خود را در خط فرمان قرار دادیم، می توانیم یک زیر شاخه برای پروژه خود ایجاد کنیم و پوشه را در آنجا از حالت فشرده خارج کنیم:
mkdir spring-native cd spring-native unzip ../io-native-starter.zip
2. تغییر کد
هنگامی که پروژه را باز کردیم، به سرعت نشانه ای از زندگی اضافه می کنیم و پس از اجرای آن، عملکرد Spring Native را به نمایش می گذاریم.
DemoApplication.java را برای مطابقت با این ویرایش کنید:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
import java.time.Instant;
@RestController
@SpringBootApplication
public class DemoApplication {
private static Instant startTime;
private static Instant readyTime;
public static void main(String[] args) {
startTime = Instant.now();
SpringApplication.run(DemoApplication.class, args);
}
@GetMapping("/")
public String index() {
return "Time between start and ApplicationReadyEvent: "
+ Duration.between(startTime, readyTime).toMillis()
+ "ms";
}
@EventListener(ApplicationReadyEvent.class)
public void ready() {
readyTime = Instant.now();
}
}
در این مرحله برنامه پایه ما آماده کار است، بنابراین با خیال راحت یک تصویر بسازید و آن را به صورت محلی اجرا کنید تا قبل از تبدیل آن به یک برنامه بومی، ایده ای از زمان راه اندازی داشته باشید.
برای ساختن تصویر ما:
mvn spring-boot:build-image
همچنین میتوانید docker images demo
برای دریافت ایده از اندازه تصویر پایه استفاده کنید:
برای اجرای برنامه ما:
docker run --rm -p 8080:8080 demo:0.0.1-SNAPSHOT
3. برنامه پایه را مستقر کنید
اکنون که برنامه خود را داریم، آن را اجرا می کنیم و زمان ها را یادداشت می کنیم، که بعداً آن را با زمان راه اندازی برنامه بومی خود مقایسه خواهیم کرد.
بسته به نوع برنامهای که میسازید، میزبانیهای مختلفی از موارد شما وجود دارد.
با این حال، از آنجایی که مثال ما یک برنامه وب بسیار ساده و سرراست است، میتوانیم همه چیز را ساده نگه داریم و به Cloud Run تکیه کنیم.
اگر در دستگاه خود دنبال میکنید، مطمئن شوید که ابزار gcloud CLI را نصب و بهروزرسانی کردهاید.
اگر در Cloud Shell هستید، به همه موارد رسیدگی می شود و می توانید به سادگی موارد زیر را در فهرست منبع اجرا کنید:
gcloud run deploy
4. پیکربندی برنامه
1. پیکربندی مخازن Maven ما
از آنجایی که این پروژه هنوز در مرحله آزمایشی است، باید برنامه خود را طوری پیکربندی کنیم که بتوانیم مصنوعات آزمایشی را که در مخزن مرکزی maven در دسترس نیستند، پیدا کنیم.
این شامل افزودن عناصر زیر به pom.xml ما است که می توانید در ویرایشگر انتخابی خود انجام دهید.
مخازن و بخشهای پلاگین مخازن زیر را به pom ما اضافه کنید:
<repositories>
<repository>
<id>spring-release</id>
<name>Spring release</name>
<url>https://repo.spring.io/release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-release</id>
<name>Spring release</name>
<url>https://repo.spring.io/release</url>
</pluginRepository>
</pluginRepositories>
2. افزودن وابستگی های ما
سپس، وابستگی Spring-native را اضافه کنید، که برای اجرای یک برنامه Spring به عنوان یک تصویر بومی لازم است. توجه: اگر از Gradle استفاده می کنید این مرحله ضروری نیست
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>0.11.2</version>
</dependency>
</dependencies>
3. افزودن/فعال کردن پلاگین های ما
اکنون افزونه AOT را برای بهبود سازگاری و ردپای تصویر بومی اضافه کنید ( بیشتر بخوانید ):
<plugins>
<!-- ... -->
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<version>0.11.2</version>
<executions>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
اکنون ما افزونه Spring-boot-maven را بهروزرسانی میکنیم تا پشتیبانی تصویر بومی را فعال کرده و از سازنده paketo برای ساخت تصویر اصلی خود استفاده کنیم:
<plugins>
<!-- ... -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder:tiny</builder>
<env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
</env>
</image>
</configuration>
</plugin>
</plugins>
توجه داشته باشید که تصویر سازنده کوچک تنها یکی از چندین گزینه است. این یک انتخاب خوب برای مورد استفاده ما است زیرا کتابخانه ها و ابزارهای اضافی بسیار کمی دارد که به حداقل رساندن سطح حمله ما کمک می کند.
برای مثال اگر در حال ساختن برنامهای بودید که نیاز به دسترسی به برخی از کتابخانههای رایج C داشت، یا هنوز از الزامات برنامه خود مطمئن نبودید، ممکن است سازنده کامل مناسبتر باشد.
5. برنامه بومی را بسازید و اجرا کنید
وقتی همه چیز درست شد، باید بتوانیم تصویر خود را بسازیم و برنامه بومی و کامپایل شده خود را اجرا کنیم.
قبل از اجرای بیلد، چند نکته را باید در نظر داشته باشید:
- این کار بیشتر از یک ساخت معمولی زمان می برد (چند دقیقه)
- این فرآیند ساخت میتواند حافظه زیادی را به خود اختصاص دهد (چند گیگابایت)
- این فرآیند ساخت مستلزم دسترسی به دایمون Docker است
- در حالی که در این مثال ما این فرآیند را به صورت دستی انجام میدهیم، شما همچنین میتوانید مراحل ساخت خود را به گونهای پیکربندی کنید که به طور خودکار یک نمایه ساخت بومی فعال شود .
برای ساختن تصویر ما:
mvn spring-boot:build-image
هنگامی که ساخته شد، ما آماده هستیم تا برنامه بومی را در عمل ببینیم!
برای اجرای برنامه ما:
docker run --rm -p 8080:8080 demo:0.0.1-SNAPSHOT
در این مرحله ما در موقعیت بسیار خوبی برای دیدن هر دو طرف معادله برنامه اصلی هستیم.
ما مقداری از زمان و استفاده از حافظه اضافی را در زمان کامپایل رها کردهایم، اما در ازای آن برنامهای دریافت میکنیم که میتواند بسیار سریعتر راهاندازی شود و حافظه بسیار کمتری مصرف کند (بسته به حجم کاری).
اگر docker images demo
برای مقایسه اندازه تصویر اصلی با تصویر اصلی اجرا کنیم، میتوانیم شاهد کاهش چشمگیر باشیم:
همچنین باید توجه داشته باشیم که در موارد استفاده پیچیده تر، اصلاحات اضافی مورد نیاز است تا به کامپایلر AOT اطلاع دهد که برنامه شما در زمان اجرا چه کاری انجام می دهد. به همین دلیل، برخی از حجمهای کاری قابل پیشبینی (مانند کارهای دستهای) ممکن است برای این کار بسیار مناسب باشند، در حالی که برخی دیگر ممکن است افزایش بیشتری داشته باشند.
6. استقرار برنامه بومی ما
برای استقرار برنامه خود در Cloud Run، باید تصویر اصلی خود را در یک مدیر بسته مانند Artifact Registry دریافت کنیم.
1. آماده سازی مخزن داکر ما
ما می توانیم این فرآیند را با ایجاد یک مخزن شروع کنیم:
gcloud artifacts repositories create native-image-docker-repo --repository-format=docker \
--location=us-central1 --description="Repository for our native images"
در مرحله بعد، میخواهیم مطمئن شویم که برای ورود به رجیستری جدید خود احراز هویت شدهایم.
Gcloud CLI می تواند این فرآیند را تا حدودی ساده کند:
gcloud auth configure-docker us-central1-docker.pkg.dev
2. فشار دادن تصویر ما به آرتیفکت رجیستری
در ادامه تصویر خود را تگ می کنیم:
export PROJECT_ID=$(gcloud config list --format 'value(core.project)')
docker tag demo:0.0.1-SNAPSHOT \
us-central1-docker.pkg.dev/$PROJECT_ID/native-image-docker-repo/quickstart-image:tag2
و سپس می توانیم از docker push
برای ارسال آن به آرتیفکت رجیستری استفاده کنیم:
docker push us-central1-docker.pkg.dev/$PROJECT_ID/native-image-docker-repo/quickstart-image:tag2
3. استقرار در Cloud Run
ما اکنون آماده ایم تا تصویری را که در آرتیفکت رجیستری ذخیره کرده ایم در Cloud Run اجرا کنیم:
gcloud run deploy --image us-central1-docker.pkg.dev/$PROJECT_ID/native-image-docker-repo/quickstart-image:tag2
از آنجایی که ما برنامه خود را به عنوان یک تصویر بومی ساخته و اجرا کرده ایم، می توانیم مطمئن باشیم که برنامه ما در حین اجرا از هزینه های زیرساخت ما به خوبی استفاده می کند.
می توانید زمان راه اندازی برنامه پایه ما را با این برنامه بومی جدید برای خودتان مقایسه کنید!
7. خلاصه/پاکسازی
بابت ساخت و استقرار یک برنامه Spring Native در Google Cloud تبریک میگوییم!
امیدواریم این آموزش شما را تشویق کند تا با پروژه Spring Native بیشتر آشنا شوید و اگر در آینده نیازهای شما را برآورده کند، آن را به خاطر بسپارید.
اختیاری: سرویس را تمیز و/یا غیرفعال کنید
چه پروژه Google Cloud را برای این کد لبه ایجاد کرده باشید و چه در حال استفاده مجدد از یک پروژه موجود هستید، مراقب باشید از هزینههای غیرضروری از منابعی که ما استفاده کردهایم جلوگیری کنید.
میتوانید سرویسهای Cloud Run را که ایجاد کردهایم حذف یا غیرفعال کنید ، تصویری را که میزبانی کردیم حذف کنید یا کل پروژه را خاموش کنید .
8. منابع اضافی
در حالی که پروژه Spring Native در حال حاضر یک پروژه جدید و آزمایشی است، در حال حاضر منابع خوب زیادی برای کمک به پذیرندگان اولیه وجود دارد که مشکلات را حل کنند و درگیر شوند:
منابع اضافی
در زیر منابع آنلاینی وجود دارد که ممکن است برای این آموزش مرتبط باشد:
- درباره نکات بومی بیشتر بیاموزید
- درباره GraalVM بیشتر بدانید
- چگونه درگیر شویم
- خطای کمبود حافظه هنگام ساخت تصاویر بومی
- خطای برنامه شروع نشد
مجوز
این اثر تحت مجوز Creative Commons Attribution 2.0 Generic مجوز دارد.