Java के साथ InsideLoop डेवलपमेंट - SpringBoot

1. खास जानकारी

इस लैब में, ऐसी सुविधाओं और क्षमताओं के बारे में बताया गया है जिन्हें कंटेनर वाले एनवायरमेंट में Java ऐप्लिकेशन डेवलप करने का काम करने वाले सॉफ़्टवेयर इंजीनियर के लिए, डेवलपमेंट वर्कफ़्लो को बेहतर बनाने के लिए डिज़ाइन किया गया है. कंटेनर को डेवलप करने के लिए, उपयोगकर्ता को कंटेनर और कंटेनर बनाने की प्रोसेस के बारे में जानकारी होनी चाहिए. इसके अलावा, डेवलपर को आम तौर पर अपने फ़्लो को रोकना पड़ता है. साथ ही, रिमोट एनवायरमेंट में अपने ऐप्लिकेशन की जांच करने और उन्हें डीबग करने के लिए, उन्हें अपने आईडीई से बाहर निकलना पड़ता है. इस ट्यूटोरियल में बताए गए टूल और टेक्नोलॉजी की मदद से, डेवलपर अपने आईडीई को छोड़े बिना, कंटेनर वाले ऐप्लिकेशन पर असरदार तरीके से काम कर सकते हैं.

आपको क्या सीखने को मिलेगा

इस लैब में, आपको GCP में कंटेनर का इस्तेमाल करके डेवलपमेंट करने के तरीकों के बारे में जानकारी मिलेगी. जैसे:

  • सेटअप और ज़रूरी शर्तें
  • नया Java स्टार्टर ऐप्लिकेशन बनाना
  • डेवलपमेंट प्रोसेस के बारे में जानकारी
  • सामान्य CRUD रेस्ट सर्विस डेवलप करना
  • साफ़-सफ़ाई सेवा

2. सेटअप और ज़रूरी शर्तें

अपनी स्पीड से एनवायरमेंट सेट अप करना

  1. Google Cloud Console में साइन इन करें और नया प्रोजेक्ट बनाएं या किसी मौजूदा प्रोजेक्ट का फिर से इस्तेमाल करें. अगर आपके पास पहले से कोई Gmail या Google Workspace खाता नहीं है, तो आपको एक खाता बनाना होगा.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • प्रोजेक्ट का नाम, इस प्रोजेक्ट में हिस्सा लेने वाले लोगों के लिए डिसप्ले नेम होता है. यह एक कैरेक्टर स्ट्रिंग है, जिसका इस्तेमाल Google API नहीं करते. इसे किसी भी समय अपडेट किया जा सकता है.
  • प्रोजेक्ट आईडी, सभी Google Cloud प्रोजेक्ट के लिए यूनीक होना चाहिए. साथ ही, इसे बदला नहीं जा सकता. Cloud Console, एक यूनीक स्ट्रिंग अपने-आप जनरेट करता है. आम तौर पर, आपको इससे कोई फ़र्क़ नहीं पड़ता कि यह क्या है. ज़्यादातर कोडलैब में, आपको प्रोजेक्ट आईडी का रेफ़रंस देना होगा. इसे आम तौर पर PROJECT_ID के तौर पर पहचाना जाता है. इसलिए, अगर आपको यह पसंद नहीं है, तो कोई दूसरा रैंडम आईडी जनरेट करें. इसके अलावा, अपने हिसाब से कोई आईडी बनाकर देखें कि वह उपलब्ध है या नहीं. इसके बाद, प्रोजेक्ट बनाने के बाद इसे "फ़्रीज़" कर दिया जाता है.
  • तीसरी वैल्यू, प्रोजेक्ट नंबर होती है. इसका इस्तेमाल कुछ एपीआई करते हैं. इन तीनों वैल्यू के बारे में ज़्यादा जानने के लिए, दस्तावेज़ देखें.
  1. इसके बाद, आपको Cloud Console में बिलिंग चालू करनी होगी, ताकि Cloud संसाधनों/एपीआई का इस्तेमाल किया जा सके. इस कोडलैब को पूरा करने में ज़्यादा खर्च नहीं आएगा. संसाधन बंद करने के लिए, कोडलैब के आखिर में दिए गए "बंद करें" निर्देशों का पालन करें, ताकि इस ट्यूटोरियल के बाद आपको बिलिंग न करनी पड़े. Google Cloud के नए उपयोगकर्ताओं को, मुफ़्त में आज़माने के लिए 300 डॉलर का क्रेडिट मिलता है.

Cloud Shell Editor शुरू करना

इस लैब को Google Cloud Shell Editor के साथ इस्तेमाल करने के लिए डिज़ाइन और टेस्ट किया गया है. एडिटर को ऐक्सेस करने के लिए,

  1. https://console.cloud.google.com पर जाकर, अपने Google प्रोजेक्ट को ऐक्सेस करें.
  2. सबसे ऊपर दाएं कोने में मौजूद, Cloud Shell एडिटर आइकॉन पर क्लिक करें

8560cc8d45e8c112.png

  1. आपकी विंडो में सबसे नीचे एक नया पैनल खुलेगा
  2. 'एडिटर खोलें' बटन पर क्लिक करें

9e504cb98a6a8005.png

  1. एडिटर खुलेगा. इसमें दाईं ओर एक्सप्लोरर और बीच में एडिटर दिखेगा
  2. स्क्रीन पर सबसे नीचे एक टर्मिनल पैन भी उपलब्ध होना चाहिए
  3. अगर टर्मिनल खुला नहीं है, तो नई टर्मिनल विंडो खोलने के लिए `ctrl+`` कीबोर्ड शॉर्टकट का इस्तेमाल करें

gcloud सेट अप करना

Cloud Shell में, अपना प्रोजेक्ट आईडी और वह क्षेत्र सेट करें जहां आपको अपना ऐप्लिकेशन डिप्लॉय करना है. इन्हें PROJECT_ID और REGION वैरिएबल के तौर पर सेव करें.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')

सोर्स कोड पाना

इस लैब का सोर्स कोड, GitHub पर GoogleCloudPlatform में container-developer-workshop में मौजूद है. नीचे दिए गए निर्देश का इस्तेमाल करके इसे क्लोन करें. इसके बाद, डायरेक्ट्री में बदलाव करें.

git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot

इस लैब में इस्तेमाल किए गए इंफ़्रास्ट्रक्चर को उपलब्ध कराएं

इस लैब में, आपको GKE पर कोड डिप्लॉय करना होगा. साथ ही, CloudSQL डेटाबेस में सेव किए गए डेटा को ऐक्सेस करना होगा. नीचे दी गई सेटअप स्क्रिप्ट, आपके लिए इस इन्फ़्रास्ट्रक्चर को तैयार करती है. इस कॉन्फ़िगरेशन को जोड़ने में 10 मिनट से ज़्यादा समय लगेगा. सेटअप की प्रोसेस के दौरान, अगले कुछ चरण पूरे किए जा सकते हैं.

./setup.sh

3. नया Java स्टार्टर ऐप्लिकेशन बनाना

इस सेक्शन में, आपको Java Spring Boot का नया ऐप्लिकेशन बनाना होगा. इसके लिए, spring.io से मिले सैंपल ऐप्लिकेशन का इस्तेमाल करें

सैंपल ऐप्लिकेशन का क्लोन बनाना

  1. स्टार्टर ऐप्लिकेशन बनाना
curl  https://start.spring.io/starter.zip -d dependencies=web -d type=maven-project -d javaVersion=11 -d packageName=com.example.springboot -o sample-app.zip
  1. ऐप्लिकेशन को अनज़िप करना
unzip sample-app.zip -d sample-app
  1. sample-app डायरेक्ट्री में जाएं और फ़ोल्डर को Cloud Shell IDE वर्कस्पेस में खोलें
cd sample-app && cloudshell workspace .

spring-boot-devtools और Jib जोड़ना

Spring Boot DevTools को चालू करने के लिए, अपने एडिटर में एक्सप्लोरर से pom.xml ढूंढें और खोलें. इसके बाद, <description>Demo project for Spring Boot</description> वाली जानकारी की लाइन के बाद, यहां दिया गया कोड चिपकाएं

  1. pom.xml में spring-boot-devtools जोड़ें

प्रोजेक्ट की रूट डायरेक्ट्री में pom.xml फ़ाइल खोलें. Description एंट्री के बाद, यह कॉन्फ़िगरेशन जोड़ें.

pom.xml

  <!--  Spring profiles-->
  <profiles>
    <profile>
      <id>sync</id>
      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-devtools</artifactId>
        </dependency>
      </dependencies>
    </profile>
  </profiles>
  1. pom.xml में jib-maven-plugin चालू करें

Jib, Google का एक ओपन-सोर्स Java कंटेनर बनाने वाला टूल है. इसकी मदद से, Java डेवलपर उन Java टूल का इस्तेमाल करके कंटेनर बना सकते हैं जिनके बारे में उन्हें जानकारी है. Jib, कंटेनर इमेज बनाने वाला एक तेज़ और आसान टूल है. यह आपके ऐप्लिकेशन को कंटेनर इमेज में पैकेज करने के सभी चरणों को मैनेज करता है. इसके लिए, आपको Dockerfile लिखने या Docker इंस्टॉल करने की ज़रूरत नहीं होती. साथ ही, यह Maven और Gradle में सीधे तौर पर इंटिग्रेट हो जाता है.

pom.xml फ़ाइल में नीचे की ओर स्क्रोल करें और Jib प्लगिन को शामिल करने के लिए, Build सेक्शन को अपडेट करें. बिल्ड सेक्शन पूरा होने के बाद, इससे मेल खाना चाहिए.

pom.xml

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
      <!--  Jib Plugin-->
      <plugin>
        <groupId>com.google.cloud.tools</groupId>
        <artifactId>jib-maven-plugin</artifactId>
        <version>3.2.0</version>
      </plugin>
       <!--  Maven Resources Plugin-->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.1.0</version>
      </plugin>
    </plugins>
  </build>

अगर बिल्ड फ़ाइल में बदलाव करने के बारे में कहा जाए, तो Always चुनें.

447a90338f51931f.png

मेनिफ़ेस्ट जनरेट करना

Skaffold, कंटेनर डेवलपमेंट को आसान बनाने के लिए इंटिग्रेटेड टूल उपलब्ध कराता है. इस चरण में, आपको skaffold को शुरू करना होगा. इससे Kubernetes की बुनियादी YAML फ़ाइलें अपने-आप बन जाएंगी. इस प्रोसेस में, कंटेनर इमेज की परिभाषाओं वाली डायरेक्ट्री की पहचान करने की कोशिश की जाती है. जैसे, Dockerfile. इसके बाद, हर डायरेक्ट्री के लिए डिप्लॉयमेंट और सेवा मेनिफ़ेस्ट बनाया जाता है.

प्रोसेस शुरू करने के लिए, नीचे दिए गए निर्देश को चलाएं.

  1. टर्मिनल में यह कमांड चलाएं
skaffold init --generate-manifests
  1. जब कहा जाए, तब:
  • अपने कर्सर को Jib Maven Plugin पर ले जाने के लिए, ऐरो बटन का इस्तेमाल करें
  • विकल्प चुनने के लिए, स्पेसबार दबाएं.
  • जारी रखने के लिए Enter दबाएं
  1. पोर्ट के लिए 8080 डालें
  2. कॉन्फ़िगरेशन सेव करने के लिए, y डालें

फ़ाइल फ़ोल्डर में दो फ़ाइलें जोड़ी गई हैं. जैसे, skaffold.yaml और deployment.yaml

ऐप्लिकेशन का नाम अपडेट करना

कॉन्फ़िगरेशन में शामिल डिफ़ॉल्ट वैल्यू, फ़िलहाल आपके ऐप्लिकेशन के नाम से मेल नहीं खाती हैं. फ़ाइलों को अपडेट करें, ताकि वे डिफ़ॉल्ट वैल्यू के बजाय आपके ऐप्लिकेशन के नाम का रेफ़रंस दें.

  1. Skaffold कॉन्फ़िगरेशन में एंट्री बदलना
  • skaffold.yaml खोलें
  • फ़िलहाल pom-xml-image के तौर पर सेट की गई इमेज का नाम चुनें
  • राइट क्लिक करें और 'सभी बदलावों को बदलें' चुनें
  • demo-app के तौर पर नया नाम डालें
  1. Kubernetes कॉन्फ़िगरेशन में एंट्री बदलना
  • deployment.yaml फ़ाइल खोलो
  • फ़िलहाल pom-xml-image के तौर पर सेट की गई इमेज का नाम चुनें
  • राइट क्लिक करें और 'सभी बदलावों को बदलें' चुनें
  • demo-app के तौर पर नया नाम डालें

हॉट सिंक की सुविधा चालू करना

बेहतर हॉट रिलोड अनुभव के लिए, आपको Jib की सिंक करने की सुविधा का इस्तेमाल करना होगा. इस चरण में, Skaffold को कॉन्फ़िगर किया जाएगा, ताकि वह बिल्ड प्रोसेस में इस सुविधा का इस्तेमाल कर सके.

ध्यान दें कि skaffold कॉन्फ़िगरेशन में कॉन्फ़िगर की जा रही "sync" प्रोफ़ाइल, Spring की उस "sync" प्रोफ़ाइल का इस्तेमाल करती है जिसे आपने पिछले चरण में कॉन्फ़िगर किया था. इसमें आपने spring-dev-tools के लिए सहायता चालू की थी.

  1. skaffold कॉन्फ़िगरेशन अपडेट करें

skaffold.yaml फ़ाइल में, फ़ाइल के पूरे बिल्ड सेक्शन को इस स्पेसिफ़िकेशन से बदलें. फ़ाइल के अन्य सेक्शन में बदलाव न करें.

skaffold.yaml

build:
  artifacts:
  - image: demo-app
    jib:
      project: com.example:demo
      type: maven
      args: 
      - --no-transfer-progress
      - -Psync
      fromImage: gcr.io/distroless/java:debug
    sync:
      auto: true

डिफ़ॉल्ट रूट जोड़ना

/src/main/java/com/example/springboot/ पर HelloController.java नाम की फ़ाइल बनाएं.

डिफ़ॉल्ट एचटीटीपी रूट बनाने के लिए, फ़ाइल में यह कॉन्टेंट चिपकाएं

HelloController.java

package com.example.springboot;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;

@RestController
public class HelloController {

    @Value("${target:local}")
    String target;

    @GetMapping("/") 
    public String hello()
    {
        return String.format("Hello from your %s environment!", target);
    }
}

4. डेवलपमेंट प्रोसेस के बारे में जानकारी

इस सेक्शन में, Cloud Code प्लगिन का इस्तेमाल करके कुछ चरणों को पूरा करने का तरीका बताया गया है. इससे आपको बुनियादी प्रोसेस के बारे में जानने में मदद मिलेगी. साथ ही, आपको अपने स्टार्टर ऐप्लिकेशन के कॉन्फ़िगरेशन और सेटअप की पुष्टि करने का तरीका भी पता चलेगा.

Cloud Code, Skaffold के साथ इंटिग्रेट होता है, ताकि आपकी डेवलपमेंट प्रोसेस को आसान बनाया जा सके. नीचे दिए गए चरणों में GKE पर डिप्लॉय करने पर, Cloud Code और Skaffold आपकी कंटेनर इमेज को अपने-आप बना देंगे. इसके बाद, वे इसे Container Registry में पुश कर देंगे. इसके बाद, वे आपके ऐप्लिकेशन को GKE पर डिप्लॉय कर देंगे. यह प्रोसेस बैकग्राउंड में होती है. इससे डेवलपर को जानकारी नहीं मिलती. Cloud Code, कंटेनर पर आधारित डेवलपमेंट के लिए, डीबग करने और हॉटसिंक करने की सुविधाएं उपलब्ध कराता है. इससे डेवलपमेंट की प्रोसेस को बेहतर बनाया जा सकता है.

Kubernetes पर डिप्लॉय करें

  1. Cloud Shell Editor में सबसे नीचे मौजूद पैनल में, Cloud Code  चुनें

fdc797a769040839.png

  1. सबसे ऊपर दिखने वाले पैनल में, Kubernetes पर डीबग करें को चुनें. अगर कहा जाए, तो मौजूदा Kubernetes कॉन्टेक्स्ट का इस्तेमाल करने के लिए, 'हां' चुनें.

cfce0d11ef307087.png

  1. पहली बार कमांड चलाने पर, स्क्रीन के सबसे ऊपर एक प्रॉम्प्ट दिखेगा. इसमें पूछा जाएगा कि क्या आपको मौजूदा Kubernetes कॉन्टेक्स्ट चाहिए. इसे स्वीकार करने और मौजूदा कॉन्टेक्स्ट का इस्तेमाल करने के लिए, "हाँ" चुनें.

817ee33b5b412ff8.png

  1. इसके बाद, एक प्रॉम्प्ट दिखेगा, जिसमें आपसे पूछा जाएगा कि किस कंटेनर रजिस्ट्री का इस्तेमाल करना है. दी गई डिफ़ॉल्ट वैल्यू को स्वीकार करने के लिए, Enter दबाएं

eb4469aed97a25f6.png

  1. प्रोग्रेस और सूचनाएं देखने के लिए, नीचे वाले पैन में मौजूद आउटपुट टैब चुनें

f95b620569ba96c5.png

  1. ज़्यादा जानकारी और कंटेनर से लाइव स्ट्रीम किए जा रहे लॉग देखने के लिए, दाईं ओर मौजूद चैनल ड्रॉप-डाउन में "Kubernetes: Run/Debug - Detailed" चुनें

94acdcdda6d2108.png

  1. ड्रॉपडाउन से "Kubernetes: Run/Debug" चुनकर, सरल बनाए गए व्यू पर वापस जाएं
  2. बिल्ड और टेस्ट पूरे होने पर, आउटपुट टैब में यह दिखता है: Resource deployment/demo-app status completed successfully. साथ ही, एक यूआरएल दिखता है: "Forwarded URL from service demo-app: http://localhost:8080"
  3. क्लाउड कोड टर्मिनल में, आउटपुट (http://localhost:8080) में मौजूद यूआरएल पर कर्सर घुमाएं. इसके बाद, दिखने वाले टूलटिप में, वेब प्रीव्यू खोलें को चुनें.

जवाब यह होगा:

Hello from your local environment!

ब्रेकपॉइंट का इस्तेमाल करना

  1. /src/main/java/com/example/springboot/HelloController.java पर मौजूद HelloController.java ऐप्लिकेशन खोलें
  2. रूट पाथ के लिए, return String.format("Hello from your %s environment!", target); वाला रिटर्न स्टेटमेंट ढूंढें
  3. लाइन नंबर के बाईं ओर मौजूद खाली जगह पर क्लिक करके, उस लाइन में ब्रेकपॉइंट जोड़ें. ब्रेकपॉइंट सेट होने पर, लाल रंग का इंडिकेटर दिखेगा
  4. अपने ब्राउज़र को फिर से लोड करें. ध्यान दें कि डीबगर, प्रोसेस को ब्रेकपॉइंट पर रोक देता है. साथ ही, आपको ऐप्लिकेशन के वैरिएबल और सैंडबॉक्स की स्थिति की जांच करने की अनुमति देता है. यह ऐप्लिकेशन, GKE में रिमोट तरीके से चल रहा है
  5. वैरिएबल सेक्शन में नीचे की ओर तब तक क्लिक करें, जब तक आपको "Target" वैरिएबल न मिल जाए.
  6. मौजूदा वैल्यू को "स्थानीय" के तौर पर देखें
  7. वैरिएबल के नाम "target" पर दो बार क्लिक करें. इसके बाद, पॉप-अप में वैल्यू को "Cloud" जैसी किसी दूसरी वैल्यू में बदलें
  8. डीबग कंट्रोल पैनल में मौजूद, जारी रखें बटन पर क्लिक करें
  9. अपने ब्राउज़र में जवाब देखें. इसमें अब वह अपडेट की गई वैल्यू दिखेगी जिसे आपने अभी डाला है.

हॉट रीलोड

  1. स्टेटमेंट को बदलकर, कोई दूसरी वैल्यू दिखाएं. जैसे, "Hello from %s Code"
  2. यह फ़ाइल, GKE में मौजूद रिमोट कंटेनर में अपने-आप सेव हो जाती है और सिंक हो जाती है
  3. अपडेट किए गए नतीजे देखने के लिए, अपने ब्राउज़र को रीफ़्रेश करें.
  4. डीबग टूलबार में मौजूद लाल रंग के स्क्वेयर पर क्लिक करके, डीबग सेशन बंद करें a13d42d726213e6c.png

5. सामान्य CRUD रेस्ट सर्विस डेवलप करना

इस समय, आपका ऐप्लिकेशन कंटेनर में डेवलपमेंट के लिए पूरी तरह से कॉन्फ़िगर हो गया है. साथ ही, आपने Cloud Code की मदद से डेवलपमेंट के बुनियादी वर्कफ़्लो को समझ लिया है. यहां दिए गए सेक्शन में, आपको सीखी गई बातों को आज़माने का मौका मिलेगा. इसके लिए, Google Cloud में मैनेज किए जा रहे डेटाबेस से कनेक्ट होने वाले REST सेवा के एंडपॉइंट जोड़े जाएंगे.

डिपेंडेंसी कॉन्फ़िगर करना

ऐप्लिकेशन कोड, बाकी सेवा से जुड़े डेटा को सेव करने के लिए डेटाबेस का इस्तेमाल करता है. पक्का करें कि डिपेंडेंसी उपलब्ध हैं. इसके लिए, pom.xl में यह कोड जोड़ें

  1. pom.xml फ़ाइल खोलें और कॉन्फ़िगरेशन के dependencies सेक्शन में यह जोड़ें

pom.xml

    <!--  Database dependencies-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.flywaydb</groupId>
      <artifactId>flyway-core</artifactId>
    </dependency>

बाकी की सेवा को कोड करना

Quote.java

/src/main/java/com/example/springboot/ में Quote.java नाम की फ़ाइल बनाएं और यहां दिया गया कोड कॉपी करें. इससे ऐप्लिकेशन में इस्तेमाल किए गए Quote ऑब्जेक्ट के लिए Entity मॉडल तय होता है.

package com.example.springboot;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Objects;

@Entity
@Table(name = "quotes")
public class Quote
{
    @Id
    @Column(name = "id")
    private Integer id;

    @Column(name="quote")
    private String quote;

    @Column(name="author")
    private String author;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getQuote() {
        return quote;
    }

    public void setQuote(String quote) {
        this.quote = quote;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) {
        return true;
      }
      if (o == null || getClass() != o.getClass()) {
        return false;
      }
        Quote quote1 = (Quote) o;
        return Objects.equals(id, quote1.id) &&
                Objects.equals(quote, quote1.quote) &&
                Objects.equals(author, quote1.author);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, quote, author);
    }
}

QuoteRepository.java

src/main/java/com/example/springboot पर QuoteRepository.java नाम की फ़ाइल बनाएं और उसमें यह कोड कॉपी करें

package com.example.springboot;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface QuoteRepository extends JpaRepository<Quote,Integer> {

    @Query( nativeQuery = true, value =
            "SELECT id,quote,author FROM quotes ORDER BY RANDOM() LIMIT 1")
    Quote findRandomQuote();
}

यह कोड, डेटा को सेव करने के लिए JPA का इस्तेमाल करता है. यह क्लास, Spring JPARepository इंटरफ़ेस को बढ़ाती है और कस्टम कोड बनाने की अनुमति देती है. आपने जोड़े गए कोड में findRandomQuote कस्टम तरीका जोड़ा हो.

QuoteController.java

सर्विस के लिए एंडपॉइंट को दिखाने के लिए, QuoteController क्लास यह सुविधा देगी.

src/main/java/com/example/springboot पर QuoteController.java नाम की फ़ाइल बनाएं और इसमें यह कॉन्टेंट कॉपी करें

package com.example.springboot;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class QuoteController {

    private final QuoteRepository quoteRepository;

    public QuoteController(QuoteRepository quoteRepository) {
        this.quoteRepository = quoteRepository;
    }

    @GetMapping("/random-quote") 
    public Quote randomQuote()
    {
        return quoteRepository.findRandomQuote();  
    }

    @GetMapping("/quotes") 
    public ResponseEntity<List<Quote>> allQuotes()
    {
        try {
            List<Quote> quotes = new ArrayList<Quote>();
            
            quoteRepository.findAll().forEach(quotes::add);

            if (quotes.size()==0 || quotes.isEmpty()) 
                return new ResponseEntity<List<Quote>>(HttpStatus.NO_CONTENT);
                
            return new ResponseEntity<List<Quote>>(quotes, HttpStatus.OK);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return new ResponseEntity<List<Quote>>(HttpStatus.INTERNAL_SERVER_ERROR);
        }        
    }

    @PostMapping("/quotes")
    public ResponseEntity<Quote> createQuote(@RequestBody Quote quote) {
        try {
            Quote saved = quoteRepository.save(quote);
            return new ResponseEntity<Quote>(saved, HttpStatus.CREATED);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }     

    @PutMapping("/quotes/{id}")
    public ResponseEntity<Quote> updateQuote(@PathVariable("id") Integer id, @RequestBody Quote quote) {
        try {
            Optional<Quote> existingQuote = quoteRepository.findById(id);
            
            if(existingQuote.isPresent()){
                Quote updatedQuote = existingQuote.get();
                updatedQuote.setAuthor(quote.getAuthor());
                updatedQuote.setQuote(quote.getQuote());

                return new ResponseEntity<Quote>(updatedQuote, HttpStatus.OK);
            } else {
                return new ResponseEntity<Quote>(HttpStatus.NOT_FOUND);
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }     

    @DeleteMapping("/quotes/{id}")
    public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
        try {
            quoteRepository.deleteById(id);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        } catch (RuntimeException e) {
            System.out.println(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }    
}

डेटाबेस कॉन्फ़िगरेशन जोड़ना

application.yaml

सेवा से ऐक्सेस किए गए बैकएंड डेटाबेस के लिए कॉन्फ़िगरेशन जोड़ें. src/main/resources में मौजूद application.yaml फ़ाइल में बदलाव करें. अगर यह फ़ाइल मौजूद नहीं है, तो इसे बनाएं. इसके बाद, बैकएंड के लिए पैरामीटर वाला Spring कॉन्फ़िगरेशन जोड़ें.

target: local

spring:
  config:
    activate:
      on-profile: cloud-dev
  datasource:
    url: 'jdbc:postgresql://${DB_HOST:127.0.0.1}/${DB_NAME:quote_db}'
    username: '${DB_USER:user}'
    password: '${DB_PASS:password}'
  jpa:
    properties:
      hibernate:
        jdbc:
          lob:
            non_contextual_creation: true
        dialect: org.hibernate.dialect.PostgreSQLDialect
    hibernate:
      ddl-auto: update

Database Migration जोड़ें

src/main/resources/db/migration/ में एक फ़ोल्डर बनाएं

एसक्यूएल फ़ाइल बनाएं: V1__create_quotes_table.sql

नीचे दिए गए कॉन्टेंट को फ़ाइल में चिपकाएं

V1__create_quotes_table.sql

CREATE TABLE quotes(
   id INTEGER PRIMARY KEY,
   quote VARCHAR(1024),
   author VARCHAR(256)
);

INSERT INTO quotes (id,quote,author) VALUES (1,'Never, never, never give up','Winston Churchill');
INSERT INTO quotes (id,quote,author) VALUES (2,'While there''s life, there''s hope','Marcus Tullius Cicero');
INSERT INTO quotes (id,quote,author) VALUES (3,'Failure is success in progress','Anonymous');
INSERT INTO quotes (id,quote,author) VALUES (4,'Success demands singleness of purpose','Vincent Lombardi');
INSERT INTO quotes (id,quote,author) VALUES (5,'The shortest answer is doing','Lord Herbert');

Kubernetes कॉन्फ़िगरेशन

deployment.yaml फ़ाइल में ये बदलाव करने से, ऐप्लिकेशन को CloudSQL इंस्टेंस से कनेक्ट करने की अनुमति मिलती है.

  • TARGET - यह वैरिएबल को कॉन्फ़िगर करता है, ताकि यह पता चल सके कि ऐप्लिकेशन किस एनवायरमेंट में एक्ज़ीक्यूट किया गया है
  • SPRING_PROFILES_ACTIVE - shows the active Spring profile, which will be configured to cloud-dev
  • DB_HOST - यह डेटाबेस का निजी आईपी है. इसे डेटाबेस इंस्टेंस बनाते समय नोट किया गया था. इसके अलावा, इसे Google Cloud Console के नेविगेशन मेन्यू में मौजूद SQL पर क्लिक करके भी देखा जा सकता है - कृपया वैल्यू बदलें !
  • DB_USER और DB_PASS - CloudSQL इंस्टेंस कॉन्फ़िगरेशन में सेट किए गए हैं. इन्हें GCP में सीक्रेट के तौर पर सेव किया जाता है

नीचे दिए गए कॉन्टेंट के साथ, deployment.yaml फ़ाइल को अपडेट करें.

deployment.yaml

apiVersion: v1
kind: Service
metadata:
  name: demo-app
  labels:
    app: demo-app
spec:
  ports:
  - port: 8080
    protocol: TCP
  clusterIP: None
  selector:
    app: demo-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-app
  labels:
    app: demo-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo-app
  template:
    metadata:
      labels:
        app: demo-app
    spec:
      containers:
      - name: demo-app
        image: demo-app
        env:
          - name: PORT
            value: "8080"
          - name: TARGET
            value: "Local Dev - CloudSQL Database - K8s Cluster"
          - name: SPRING_PROFILES_ACTIVE
            value: cloud-dev
          - name: DB_HOST
            value: ${DB_INSTANCE_IP}   
          - name: DB_PORT
            value: "5432"  
          - name: DB_USER
            valueFrom:
              secretKeyRef:
                name: gke-cloud-sql-secrets
                key: username
          - name: DB_PASS
            valueFrom:
              secretKeyRef:
                name: gke-cloud-sql-secrets
                key: password
          - name: DB_NAME
            valueFrom:
              secretKeyRef:
                name: gke-cloud-sql-secrets
                key: database 

DB_HOST वैल्यू को अपने डेटाबेस के पते से बदलें

export DB_INSTANCE_IP=$(gcloud sql instances describe quote-db-instance \
    --format=json | jq \
    --raw-output ".ipAddresses[].ipAddress")

envsubst < deployment.yaml > deployment.new && mv deployment.new deployment.yaml

ऐप्लिकेशन डिप्लॉय करना और उसकी पुष्टि करना

  1. Cloud Shell Editor में सबसे नीचे मौजूद पैनल में, Cloud Code चुनें. इसके बाद, स्क्रीन पर सबसे ऊपर मौजूद, Kubernetes पर डिबग करें चुनें.
  2. बिल्ड और टेस्ट पूरे होने पर, आउटपुट टैब में यह दिखता है: Resource deployment/demo-app status completed successfully. साथ ही, एक यूआरएल दिखता है: "Forwarded URL from service demo-app: http://localhost:8080"
  3. कोई भी कोटेशन देखें

Cloud Shell टर्मिनल से, रैंडम कोट एंडपॉइंट के ख़िलाफ़ नीचे दी गई कमांड को कई बार चलाएं. बार-बार कॉल करने पर अलग-अलग कोटेशन दिखना

curl -v 127.0.0.1:8080/random-quote
  1. कोटेशन जोड़ना

नीचे दी गई कमांड का इस्तेमाल करके, id=6 वाला नया कोटेशन बनाएं. साथ ही, देखें कि अनुरोध वापस कैसे भेजा जाता है

curl -v -H 'Content-Type: application/json' -d '{"id":"6","author":"Henry David Thoreau","quote":"Go confidently in the direction of your dreams! Live the life you have imagined"}' -X POST 127.0.0.1:8080/quotes
  1. कोटेशन मिटाना

अब मिटाने के तरीके का इस्तेमाल करके, उस कोटेशन को मिटाएं जिसे आपने अभी-अभी जोड़ा है. साथ ही, HTTP/1.1 204 रिस्पॉन्स कोड देखें.

curl -v -X DELETE 127.0.0.1:8080/quotes/6
  1. सर्वर गड़बड़ी

एंट्री मिटाए जाने के बाद, पिछले अनुरोध को फिर से चलाने पर गड़बड़ी की स्थिति का सामना करना

curl -v -X DELETE 127.0.0.1:8080/quotes/6

ध्यान दें कि जवाब में HTTP:500 Internal Server Error दिखता है.

ऐप्लिकेशन को डीबग करना

पिछले सेक्शन में, आपने देखा कि डेटाबेस में मौजूद नहीं है. इस सेक्शन में, समस्या का पता लगाने के लिए एक ब्रेकपॉइंट सेट किया जाएगा. गड़बड़ी DELETE ऑपरेशन में हुई है. इसलिए, आपको QuoteController क्लास के साथ काम करना होगा.

  1. src.main.java.com.example.springboot.QuoteController.java खोलें
  2. deleteQuote() तरीका ढूंढना
  3. उस लाइन को ढूंढें जहां डेटाबेस से किसी आइटम को मिटाने का तरीका बताया गया है: quoteRepository.deleteById(id);
  4. लाइन नंबर के बाईं ओर मौजूद खाली जगह पर क्लिक करके, उस लाइन पर ब्रेकपॉइंट सेट करें.
  5. आपको एक लाल इंडिकेटर दिखेगा. इससे पता चलेगा कि ब्रेकपॉइंट सेट हो गया है
  6. delete निर्देश को फिर से चलाएं
curl -v -X DELETE 127.0.0.1:8080/quotes/6
  1. बाईं ओर मौजूद कॉलम में मौजूद आइकॉन पर क्लिक करके, डीबग व्यू पर वापस जाएं
  2. QuoteController क्लास में रोकी गई डीबग लाइन देखें.
  3. डीबगर में, step over आइकॉन b814d39b2e5f3d9e.png पर क्लिक करें और देखें कि कोई अपवाद थ्रो किया गया है
  4. ध्यान दें कि एक सामान्य RuntimeException was caught. यह क्लाइंट को इंटरनल सर्वर की गड़बड़ी वाला एचटीटीपी 500 कोड दिखाता है, जो सही नहीं है.
   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> DELETE /quotes/6 HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 500
< Content-Length: 0
< Date: 
<
* Connection #0 to host 127.0.0.1 left intact

कोड अपडेट करना

कोड गलत है. इसलिए, EmptyResultDataAccessException अपवाद को पकड़ने और एचटीटीपी 404 'नहीं मिला' स्टेटस कोड वापस भेजने के लिए, अपवाद ब्लॉक को फिर से बनाया जाना चाहिए.

गड़बड़ी ठीक करें.

  1. डिबग सेशन चालू रहने के दौरान, डिबग कंट्रोल पैनल में "जारी रखें" बटन दबाकर अनुरोध पूरा करें.
  2. इसके बाद, कोड में यह ब्लॉक जोड़ें:
       } catch (EmptyResultDataAccessException e){
            return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
        }

यह तरीका कुछ ऐसा दिखना चाहिए

    public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
        try {
            quoteRepository.deleteById(id);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        } catch(EmptyResultDataAccessException e){
            return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
        } catch (RuntimeException e) {
            System.out.println(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
  1. मिटाने के निर्देश को फिर से चलाएं
curl -v -X DELETE 127.0.0.1:8080/quotes/6
  1. डीबगर की मदद से, EmptyResultDataAccessException को पकड़ें और देखें कि कॉलर को एचटीटीपी 404 Not Found का मैसेज मिला है.
   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> DELETE /quotes/6 HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 404
< Content-Length: 0
< Date: 
<
* Connection #0 to host 127.0.0.1 left intact
  1. डीबग टूलबार में मौजूद लाल रंग के स्क्वेयर पर क्लिक करके, डीबग सेशन बंद करें a13d42d726213e6c.png

6. साफ़-सफ़ाई सेवा

बधाई हो! इस लैब में, आपने स्क्रैच से एक नया Java ऐप्लिकेशन बनाया है. साथ ही, इसे कंटेनर के साथ असरदार तरीके से काम करने के लिए कॉन्फ़िगर किया है. इसके बाद, आपने अपने ऐप्लिकेशन को रिमोट GKE क्लस्टर पर डिप्लॉय किया और उसे डीबग किया. इसके लिए, आपने डेवलपर के उसी फ़्लो का इस्तेमाल किया जो ऐप्लिकेशन के ट्रेडिशनल स्टैक में मिलता है.

लैब पूरा होने के बाद, साफ़-सफ़ाई करने के लिए:

  1. लैब में इस्तेमाल की गई फ़ाइलें मिटाएं
cd ~ && rm -rf container-developer-workshop
  1. प्रोजेक्ट मिटाकर, उससे जुड़े सभी इन्फ़्रास्ट्रक्चर और संसाधन हटाएं