1. खास जानकारी
इस कोडलैब में, हम Spring Native प्रोजेक्ट के बारे में जानेंगे. साथ ही, इसका इस्तेमाल करने वाला ऐप्लिकेशन बनाएंगे और उसे Google Cloud पर डिप्लॉय करेंगे.
हम इसके कॉम्पोनेंट, प्रोजेक्ट के हाल के इतिहास, इस्तेमाल के कुछ उदाहरणों, और अपने प्रोजेक्ट में इसका इस्तेमाल करने के लिए ज़रूरी चरणों के बारे में बताएंगे.
फ़िलहाल, Spring Native प्रोजेक्ट एक्सपेरिमेंट के तौर पर उपलब्ध है. इसलिए, इसे शुरू करने के लिए कुछ खास कॉन्फ़िगरेशन की ज़रूरत होगी. हालांकि, SpringOne 2021 में यह एलान किया गया था कि Spring Native को Spring Framework 6.0 और Spring Boot 3.0 में बेहतरीन सहायता के साथ इंटिग्रेट किया जाएगा. इसलिए, रिलीज़ होने से कुछ महीने पहले इस प्रोजेक्ट को अच्छी तरह से जानने का यह सही समय है.
JIT कंपाइलेशन को लंबे समय तक चलने वाली प्रोसेस जैसी चीज़ों के लिए बहुत अच्छी तरह से ऑप्टिमाइज़ किया गया है. हालांकि, कुछ ऐसे इस्तेमाल के उदाहरण हैं जिनमें पहले से कंपाइल किए गए ऐप्लिकेशन बेहतर परफ़ॉर्म करते हैं. इनके बारे में हम कोडलैब के दौरान बात करेंगे.
आपको इनके बारे में जानकारी मिलेगी
- Cloud Shell का इस्तेमाल करना
- Cloud Run API को चालू करना
- Spring Native ऐप्लिकेशन बनाना और उसे डिप्लॉय करना
- ऐसे ऐप्लिकेशन को Cloud Run पर डिप्लॉय करना
आपको इन चीज़ों की ज़रूरत होगी
- Google Cloud Platform प्रोजेक्ट, जिसमें चालू GCP बिलिंग खाता हो
- gcloud cli इंस्टॉल हो या Cloud Shell का ऐक्सेस हो
- Java और एक्सएमएल की बुनियादी जानकारी
- Linux के सामान्य कमांड के बारे में जानकारी
सर्वे
इस ट्यूटोरियल का इस्तेमाल कैसे किया जाएगा?
Java के साथ अपने अनुभव को आप कितनी रेटिंग देंगे?
Google Cloud की सेवाओं को इस्तेमाल करने के अपने अनुभव को क्या रेटिंग देंगे?
2. बैकग्राउंड
Spring Native प्रोजेक्ट, डेवलपर को नेटिव ऐप्लिकेशन की परफ़ॉर्मेंस देने के लिए कई टेक्नोलॉजी का इस्तेमाल करता है.
Spring Native को पूरी तरह से समझने के लिए, इनमें से कुछ कॉम्पोनेंट टेक्नोलॉजी को समझना मददगार होता है. इससे यह भी पता चलता है कि ये टेक्नोलॉजी हमारे लिए क्या काम करती हैं और यहां एक साथ कैसे काम करती हैं.
AOT कंपाइलेशन
जब डेवलपर कंपाइल करने के समय javac को सामान्य रूप से चलाते हैं, तो हमारा .java सोर्स कोड, .class फ़ाइलों में कंपाइल हो जाता है. ये फ़ाइलें, बाइटकोड में लिखी होती हैं. यह बाइटकोड सिर्फ़ Java वर्चुअल मशीन को समझना होता है. इसलिए, हमारा कोड चलाने के लिए, JVM को अन्य मशीनों पर इस कोड का विश्लेषण करना होगा.
इस प्रोसेस की मदद से, Java के सिग्नेचर को पोर्ट किया जा सकता है. इससे, "एक बार लिखकर हर जगह चलाया जा सकता है". हालांकि, नेटिव कोड को चलाने की तुलना में यह प्रोसेस ज़्यादा महंगी है.
सौभाग्य से, JVM के ज़्यादातर लागू होने के लिए, इस इंटरप्रिटेशन लागत को कम करने के लिए, जस्ट-इन-टाइम कंपाइलेशन का इस्तेमाल किया जाता है. ऐसा करने के लिए, किसी फ़ंक्शन के इस्तेमाल की गिनती की जाती है. अगर किसी फ़ंक्शन को थ्रेशोल्ड ( डिफ़ॉल्ट रूप से 10,000) तक इस्तेमाल किया जाता है, तो उसे रन टाइम पर नेटिव कोड में कंपाइल किया जाता है, ताकि बार-बार अनुवाद करने की ज़रूरत न पड़े.
पहले से कंपाइल करने की सुविधा, इसके उलट काम करती है. यह कंपाइलेशन के समय, सभी ऐक्सेस किए जा सकने वाले कोड को नेटिव एक्सीक्यूटेबल में कंपाइल करती है. इससे, रन टाइम के दौरान मेमोरी की खपत कम करने और परफ़ॉर्मेंस को बेहतर बनाने के लिए, पोर्टेबिलिटी का त्याग किया जाता है.
हालांकि, यह एक तरह का समझौता है और हमेशा इसे स्वीकार करना सही नहीं होता. हालांकि, AOT कंपाइलेशन का इस्तेमाल कुछ खास मामलों में किया जा सकता है, जैसे:
- ऐसे ऐप्लिकेशन जिनका इस्तेमाल कम समय के लिए किया जाता है और जिनके लिए स्टार्टअप समय अहम होता है
- ऐसे एनवायरमेंट जहां मेमोरी की कमी हो और JIT का इस्तेमाल करना बहुत महंगा हो
दिलचस्प बात यह है कि AOT कंपाइलेशन को JDK 9 में प्रयोग के तौर पर उपलब्ध सुविधा के तौर पर पेश किया गया था. हालांकि, इसे लागू करने और बनाए रखने में ज़्यादा खर्च आता था और यह कभी भी लोकप्रिय नहीं हुआ. इसलिए, Java 17 में इसे चुपचाप हटा दिया गया, ताकि सिर्फ़ GraalVM का इस्तेमाल करने वाले डेवलपर इसका फ़ायदा ले सकें.
GraalVM
GraalVM, ज़्यादा ऑप्टिमाइज़ किया गया ओपन सोर्स JDK डिस्ट्रिब्यूशन है. यह बहुत तेज़ी से स्टार्टअप होता है. इसमें AOT नेटिव इमेज कंपाइलेशन और पॉलीग्लॉट की सुविधाएं होती हैं. इनकी मदद से, डेवलपर एक ही ऐप्लिकेशन में कई भाषाओं का इस्तेमाल कर सकते हैं.
GraalVM पर लगातार काम किया जा रहा है. इसमें नई सुविधाएं जोड़ी जा रही हैं और मौजूदा सुविधाओं को बेहतर बनाया जा रहा है. इसलिए, हमारा सुझाव है कि डेवलपर इस पर नज़र बनाए रखें.
हाल ही में हासिल की गई कुछ उपलब्धियां:
- उपयोगकर्ता के हिसाब से बनाया गया नया नेटिव इमेज बिल्ड आउटपुट ( 2021-01-18)
- Java 17 के लिए सहायता ( 2022-01-18)
- कई भाषाओं में कॉन्टेंट को कंपाइल करने में लगने वाले समय को कम करने के लिए, डिफ़ॉल्ट रूप से मल्टी-टीयर कंपाइलेशन की सुविधा चालू करना ( 2021-04-20)
Spring Native
आसान शब्दों में कहें, तो Spring Native, GraalVM के नेटिव-इमेज कंपाइलर का इस्तेमाल करके, Spring ऐप्लिकेशन को नेटिव एक्सीक्यूटेबल में बदलता है.
इस प्रोसेस में, कंपाइल के समय आपके ऐप्लिकेशन का स्टैटिक विश्लेषण किया जाता है. इससे, आपके ऐप्लिकेशन में मौजूद वे सभी तरीके ढूंढे जा सकते हैं जो एंट्री पॉइंट से ऐक्सेस किए जा सकते हैं.
इससे आपके ऐप्लिकेशन के लिए "क्लोज़्ड-वर्ल्ड" कॉन्सेप्ट बनता है. इसमें, कंपाइल करने के समय सभी कोड के बारे में जानकारी होती है. साथ ही, रनटाइम के दौरान कोई नया कोड लोड नहीं किया जा सकता.
ध्यान रखें कि नेटिव इमेज जनरेट करने की प्रोसेस में ज़्यादा मेमोरी का इस्तेमाल होता है. यह प्रोसेस, किसी सामान्य ऐप्लिकेशन को कंपाइल करने से ज़्यादा समय लेती है. साथ ही, यह प्रोसेस Java के कुछ पहलुओं पर सीमाएं लगाती है.
कुछ मामलों में, Spring Native के साथ काम करने के लिए, ऐप्लिकेशन के कोड में कोई बदलाव करने की ज़रूरत नहीं होती. हालांकि, कुछ स्थितियों में सही तरीके से काम करने के लिए, खास नेटिव कॉन्फ़िगरेशन की ज़रूरत होती है. ऐसे मामलों में, Spring Native अक्सर इस प्रोसेस को आसान बनाने के लिए नेटिव हिंट उपलब्ध कराता है.
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 के रिलीज़ होने के बाद, इस सैंपल के लिए Java 17 का इस्तेमाल भी किया जा सकता है. हम इस ट्यूटोरियल के लिए अब भी Java 11 का इस्तेमाल करेंगे, ताकि कॉन्फ़िगरेशन को कम किया जा सके.
कमांड-लाइन पर ज़िप फ़ाइल होने के बाद, हम अपने प्रोजेक्ट के लिए सब-डायरेक्ट्री बना सकते हैं और उसमें फ़ोल्डर को अनज़िप कर सकते हैं:
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 की हमारी रिपॉज़िटरी कॉन्फ़िगर करना
यह प्रोजेक्ट अभी एक्सपेरिमेंट के तौर पर चल रहा है. इसलिए, हमें अपने ऐप्लिकेशन को कॉन्फ़िगर करना होगा, ताकि हम एक्सपेरिमेंट के तौर पर उपलब्ध आर्टफ़ैक्ट ढूंढ सकें. ये आर्टफ़ैक्ट, मेवन के सेंट्रल रिपॉज़िटरी में उपलब्ध नहीं हैं.
इसके लिए, आपको हमारे pom.xml में ये एलिमेंट जोड़ने होंगे. इन्हें अपनी पसंद के एडिटर में जोड़ा जा सकता है.
हमारे pom में ये रिपॉज़िटरी और pluginRepositories सेक्शन जोड़ें:
<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-plugin को अपडेट करेंगे. साथ ही, अपनी नेटिव इमेज बनाने के लिए, 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. Docker रिपॉज़िटरी तैयार करना
हम डेटा स्टोर करने की जगह बनाकर, इस प्रोसेस को शुरू कर सकते हैं:
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. Artifact Registry में अपनी इमेज को पुश करना
इसके बाद, हम अपनी इमेज को टैग करेंगे:
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 पर डिप्लॉय करना
अब हम Artifact Registry में सेव की गई इमेज को Cloud Run में डिप्लॉय करने के लिए तैयार हैं:
gcloud run deploy --image us-central1-docker.pkg.dev/$PROJECT_ID/native-image-docker-repo/quickstart-image:tag2
हमने अपने ऐप्लिकेशन को नेटिव इमेज के तौर पर बनाया और डिप्लॉय किया है. इसलिए, हमें भरोसा है कि हमारा ऐप्लिकेशन, इंफ़्रास्ट्रक्चर की लागत का बेहतर तरीके से इस्तेमाल कर रहा है.
बेसलाइन ऐप्लिकेशन और इस नए नेटिव ऐप्लिकेशन के खुलने में लगने वाले समय की तुलना करें!
7. खास जानकारी/साफ़-सफ़ाई
Google Cloud पर Spring नेटिव ऐप्लिकेशन बनाने और उसे डिप्लॉय करने के लिए बधाई!
हमें उम्मीद है कि इस ट्यूटोरियल से आपको Spring Native प्रोजेक्ट के बारे में ज़्यादा जानकारी मिली होगी. साथ ही, आने वाले समय में अगर आपको इसकी ज़रूरत पड़ती है, तो इसे ध्यान में रखें.
ज़रूरी नहीं: डेटा मिटाना और/या सेवा बंद करना
इस कोडलैब के लिए, आपने चाहे नया Google Cloud प्रोजेक्ट बनाया हो या किसी मौजूदा प्रोजेक्ट का फिर से इस्तेमाल किया हो, ध्यान रखें कि हमने जिन संसाधनों का इस्तेमाल किया है उनसे आपको कोई अतिरिक्त शुल्क न देना पड़े.
आपके पास, हमारी बनाई गई Cloud Run सेवाओं को मिटाने या बंद करने, होस्ट की गई इमेज को मिटाने या पूरे प्रोजेक्ट को बंद करने का विकल्प है.
8. अन्य संसाधन
फ़िलहाल, Spring Native प्रोजेक्ट एक नया और प्रयोग के तौर पर शुरू किया गया प्रोजेक्ट है. हालांकि, इसमें पहले से ही कई अच्छे संसाधन मौजूद हैं. इनकी मदद से, शुरुआती उपयोगकर्ता समस्याओं को हल कर सकते हैं और इसमें शामिल हो सकते हैं:
अन्य संसाधन
यहां कुछ ऑनलाइन संसाधन दिए गए हैं, जो इस ट्यूटोरियल के लिए काम के हो सकते हैं:
- नेटिव हिंट के बारे में ज़्यादा जानें
- GraalVM के बारे में ज़्यादा जानें
- इसमें शामिल होने का तरीका
- नेटिव इमेज बनाते समय, 'मेमोरी खत्म हो गई' गड़बड़ी का दिखना
- ऐप्लिकेशन शुरू नहीं हो सका
लाइसेंस
इस काम के लिए, Creative Commons Attribution 2.0 जनरल लाइसेंस के तहत लाइसेंस मिला है.