Scala के साथ तेज और मजबूत REST API का निर्माण कैसे करें

"एक बिल्ली की त्वचा के लिए एक से अधिक तरीके हैं।"

यह एक लोकप्रिय कहावत है और यद्यपि मानसिक चित्र परेशान हो सकता है, यह एक सार्वभौमिक सत्य है, विशेष रूप से कंप्यूटर विज्ञान के लिए।

इस प्रकार Scala में REST API का निर्माण करने का तरीका अनुसरण करता है न कि उन्हें बनाने का तरीका।

सभी व्यावहारिक उद्देश्यों के लिए, आइए हम एक Reddit जैसे एप्लिकेशन के लिए कुछ APIs का निर्माण कर रहे हैं जहां उपयोगकर्ता अपनी प्रोफ़ाइल तक पहुंच सकते हैं और अपडेट सबमिट कर सकते हैं। Reddit रूपक पर निर्माण करने के लिए, हम कल्पना कर रहे हैं कि हम (पुनः) api / v1 / me और api / submit को लागू करें

कुछ जमीनी काम

संक्षेप में:

  1. स्काला लैम्बडा कैलकुलस पर आधारित एक ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग लैंग्वेज है जो जावा वर्चुअल मशीन पर चलती है और जावा के साथ मूल रूप से एकीकृत होती है।
  2. AKKA स्काला के ऊपर निर्मित एक पुस्तकालय है जो अभिनेताओं (बहु-थ्रेडेड सुरक्षित ऑब्जेक्ट) और अधिक प्रदान करता है।
  3. Spray.io एक HTTP लाइब्रेरी है जो एकेके के ऊपर बनाई गई है जो एक सरल, लचीला HTTP प्रोटोकॉल कार्यान्वयन प्रदान करती है ताकि आप अपनी क्लाउड सेवा को रोल कर सकें।

चुनौती

REST API प्रदान करने की अपेक्षा की जाती है:

  1. तेज, सुरक्षित कॉल स्तर प्रमाणीकरण और अनुमति नियंत्रण;
  2. तेज व्यापार तर्क संगणना और I / O;
  3. उच्च संगति के तहत उपरोक्त सभी;
  4. क्या मैंने उपवास का उल्लेख किया है?

चरण 1, प्रमाणीकरण और अनुमति

प्रमाणीकरण OAUTH या OAUTH 2 या निजी / सार्वजनिक कुंजी प्रमाणीकरण के कुछ स्वाद में लागू किया जाना चाहिए।

OAUTH2 दृष्टिकोण का लाभ यह है कि आपको एक सत्र टोकन मिलता है (जिसका उपयोग आप संबंधित उपयोगकर्ता खाते और सत्र को देखने के लिए कर सकते हैं) और एक हस्ताक्षर टोकन, उस पर एक पल में अधिक।

हम इस धारणा के तहत यहां जारी रहेंगे कि यह वही है जो हम उपयोग करते हैं।

हस्ताक्षर टोकन सामान्य रूप से SHA1 का उपयोग करके एक साझा गुप्त कुंजी के साथ अनुरोध के पूरे पेलोड पर हस्ताक्षर करके प्राप्त एक एन्क्रिप्टेड टोकन है। हस्ताक्षर टोकन इस प्रकार एक पत्थर से दो पक्षियों को मारता है:

  1. यह आपको बताता है कि क्या कॉलर सही साझा रहस्य जानता है;
  2. यह बीच में हमलों में डेटा इंजेक्शन और आदमी को रोकता है;

उपरोक्त के लिए भुगतान करने के लिए कुछ मूल्य हैं: पहला आपको अपने I / O लेयर से डेटा खींचना होगा और दूसरा आपको कॉलर से हस्ताक्षर टोकन की तुलना करने से पहले अपेक्षाकृत महंगे एन्क्रिप्शन (यानी SHA1) की गणना करनी होगी। और एक सर्वर बनाता है, जिसे सही माना जाता है क्योंकि बैक एंड को सभी जानते हैं (लगभग)।

I / O के साथ मदद करने के लिए, कोई कैश (मेमेचे; रेडिस?) जोड़ सकता है और लगातार स्टैक के लिए एक महंगी यात्रा की आवश्यकता को दूर कर सकता है (मोंगो? पोस्टग्रेज?)।

उपरोक्त संबोधित करने के लिए AKKA और Spray.io बहुत प्रभावी हैं। Spray.io HTTP हेडर सूचना और पेलोड को निकालने के लिए आवश्यक चरणों को इनकैप्सुलेट करता है। AKKA अभिनेताओं को अतुल्यकालिक कार्यों को एपीआई पार्सिंग से स्वतंत्र रूप से निष्पादित करने में सक्षम बनाता है। यह संयोजन अनुरोध हैंडलर पर लोड को कम करता है और इसे बेंच-मार्क किया जा सकता है ताकि अधिकांश एपीआई का प्रसंस्करण समय 100ms से कम हो। नोट: मैंने कहा कि प्रसंस्करण समय प्रतिक्रिया समय नहीं है, मैं नेटवर्क विलंबता शामिल नहीं हूं।

नोट: AKKA के अभिनेताओं का उपयोग करते हुए, दो समवर्ती प्रक्रियाओं में से एक, अनुमति / प्रमाणीकरण के लिए और एक व्यावसायिक तर्क के लिए संभव है। एक तो अपने कॉल बैक के लिए पंजीकरण करेगा और परिणामों को मर्ज करेगा। यह आशावादी दृष्टिकोण लेने वाले कॉल स्तर पर एपीआई कार्यान्वयन को समानांतर करता है जो प्रमाणीकरण सफल होगा। इस दृष्टिकोण के लिए न्यूनतम डेटा पुनरावृत्ति की आवश्यकता होती है जिसमें ग्राहक को उपयोगकर्ता आईडी जैसी कुछ भी व्यावसायिक तर्क की जरूरत है और जो कुछ भी आप सामान्य रूप से सत्र से निकालना चाहते हैं, भेजना होगा। मेरे अनुभव में, इस दृष्टिकोण का लाभ निष्पादन समय में लगभग 10% की कमी है और डिजाइन समय और रन समय दोनों में महंगा है क्योंकि यह अधिक सीपीयू और अधिक मेमोरी का उपयोग करता है। हालाँकि, ऐसे परिदृश्य हो सकते हैं, जहाँ अपेक्षाकृत कम लाभ प्रति मिनट लाखों कॉलों के प्रसंस्करण की सीमा तक पहुँच जाता है, जिससे बचत / लाभ बढ़ जाता है। हालांकि ज्यादातर मामलों में मैं इसकी सिफारिश नहीं करूंगा।

एक बार जब सत्र टोकन उपयोगकर्ता के लिए हल हो जाता है, तो उपयोगकर्ता प्रोफ़ाइल को कैश कर सकता है जिसमें अनुमति स्तर शामिल होते हैं और बस एपीआई कॉल करने के लिए आवश्यक अनुमति स्तर के खिलाफ इनकी तुलना करते हैं।

एक एपीआई के अनुमति स्तर को प्राप्त करने के लिए, एक URI को पार्स करता है और REST संसाधन और पहचानकर्ता (यदि लागू हो) को निकालता है और एक प्रकार को निकालने के लिए HTTP हेडर का उपयोग करता है।

उदाहरण के लिए कहें कि आप पंजीकृत उपयोगकर्ताओं को HTTP GET के माध्यम से अपनी प्रोफ़ाइल प्राप्त करने की अनुमति देना चाहते हैं

/ API / v1 / मुझे

तो यह वह है जो एक अनुमति विन्यास दस्तावेज इस तरह की प्रणाली में दिखेगा:

{
 "V1 / me": [{
 "व्यवस्थापक": ["मिल", "डाल", "पोस्ट", "हटाएं"]
 } {
 "पंजीकृत": ["मिलता है", "डाल", "पोस्ट", "हटाएं"]
 } {
 "Read_only": ["जाओ"]
 } {
 "अवरुद्ध": []
 }],
 "जमा करें": [{
 "व्यवस्थापक": ["डाल", "पोस्ट", "हटाएं"]
 } {
 "पंजीकृत": ["पोस्ट", "हटाएं"]
 } {
 "सिफ़ पढ़िये": []
 } {
 "अवरुद्ध": []
 }]
}

पाठक को ध्यान देना चाहिए कि यह एक आवश्यक है लेकिन डेटा एक्सेस की अनुमति के लिए पर्याप्त शर्त नहीं है। अब तक हमने स्थापित किया है कि कॉल करने के लिए कॉलिंग क्लाइंट अधिकृत है और उपयोगकर्ता को एपीआई तक पहुंचने की अनुमति है। हालाँकि कई मामलों में हमें यह सुनिश्चित करने की भी ज़रूरत है कि उपयोगकर्ता A का B डेटा नहीं देख (या संपादित) कर सकता है। इसलिए "get_owner" अर्थ के साथ इस संकेतन का विस्तार करें कि प्रमाणीकृत उपयोगकर्ताओं को केवल GET निष्पादित करने की अनुमति है, यदि यह संसाधन का स्वामी है। आइए देखें कि तब विन्यास कैसा दिखेगा:

{
 "V1 / me": [{
 "व्यवस्थापक": ["मिल", "डाल", "पोस्ट", "हटाएं"]
 } {
 "पंजीकृत": ["get_owner", "डाल", "पोस्ट", "हटाएं"]
 } {
 "Read_only": ["get_owner"]
 } {
 "अवरुद्ध": []
 }],
 "जमा करें": [{
 "व्यवस्थापक": ["डाल", "पोस्ट", "हटाएं"]
 } {
 "पंजीकृत": ["put_owner", "पोस्ट", "हटाएं"]
 } {
 "सिफ़ पढ़िये": []
 } {
 "अवरुद्ध": []
 }]
}

अब एक पंजीकृत उपयोगकर्ता अपने स्वयं के प्रोफाइल तक पहुंच सकता है, इसे पढ़ सकता है, इसे संशोधित कर सकता है लेकिन कोई भी (किसी व्यवस्थापक के अलावा) नहीं कर सकता। इसी प्रकार केवल स्वामी ही सबमिशन को अपडेट कर सकता है:

/ API / प्रस्तुत / <पहचानकर्ता>

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

प्रवर्तन को कुछ ऐसे कार्यों में संलग्न किया जा सकता है जो एपीआई के व्यावसायिक तर्क के अज्ञेय हो सकते हैं और प्रमाणीकरण और अनुमति को लागू और लागू कर सकते हैं:

def validateSessionToken (sessionToken: स्ट्रिंग) UserProfile = {
...
}
चेक चेक करें
  विधि: स्ट्रिंग,
  संसाधन: स्ट्रिंग,
  उपयोगकर्ता: UserProfile
) {
...
// विफलता पर एक अपवाद फेंकता है
}

इन्हें API कॉल की Spray.io हैंडलिंग की शुरुआत में बुलाया जाएगा:

// नोट: प्रोफाइलराइडर और सुंब्रिशनविटर को यहां छोड़ दिया गया है, मान लें कि वे एक AKKA अभिनेता का विस्तार कर रहे हैं।
def मार्ग =
{
pathPrefix ( "API") {
  // हेडर और HTTP जानकारी निकालें
  ...
  var उपयोगकर्ता: UserProfile = null
  प्रयत्न {
    validatedSessionToken (sessionToken)
  } पकड़ (ई: अपवाद) {
    पूरा (completeWithError (e.getMessage))
  }
  प्रयत्न {
    checkPermission (विधि, संसाधन, उपयोगकर्ता)
  } पकड़ (ई: अपवाद) {
    पूरा (completeWithError (e.getMessage))
  }
  pathPrefix ( "वी 1") {
    पथ ( "मुझे") {
      प्राप्त {
        पूरा (प्रोफाइल-राइडर? getUserProfile (user.id))
      }
    }
  } ~
  पथ ( "सबमिट करें") {
    पद {
      संस्था (जैसा [स्ट्रिंग]) {=> jsonstr
        वैल पेलोड = पढ़ें [SubmitPayload] (jsonstr)
        पूर्ण (सबमिशन। sumbit (पेलोड))
      }
    }
  }
  ...
}

जैसा कि हम देख सकते हैं, यह दृष्टिकोण Spray.io हैंडलर को पठनीय और आसान बनाए रखता है क्योंकि यह प्रत्येक एपीआई के व्यक्तिगत व्यापार तर्क से प्रमाणीकरण / अनुमति को अलग करता है। डेटा स्वामित्व प्रवर्तन, यहां नहीं दिखाया गया है, I / O परत को एक बूलियन पास करने के लिए प्राप्त किया जा सकता है जो तब दृढ़ता के स्तर पर उपयोगकर्ता डेटा स्वामित्व को लागू करेगा।

चरण 2, व्यापार तर्क

व्यवसाय तर्क को I / O अभिनेताओं में प्रस्तुत किया जा सकता है, जैसे कि सबमिशन उपरोक्त कोड के स्निपेट में उल्लिखित है। यह अभिनेता एक अतुल्यकालिक I / O ऑपरेशन को लागू करेगा जो पहले कैश परत पर लिखता है, उदाहरण के लिए एलिटोसर्च, और पसंद के डीबी के लिए दूसरा। डीबी लिखते हैं कि आग में और खराब हो सकता है और तर्क को भूल सकता है जो लॉग आधारित रिकवरी का उपयोग करेगा ताकि ग्राहक को इन महंगे ऑपरेशनों को पूरा करने के लिए इंतजार न करना पड़े।

ध्यान दें कि यह एक आशावादी गैर-लॉकिंग दृष्टिकोण है और ग्राहक के लिए यह सुनिश्चित करने का एकमात्र तरीका है कि डेटा लिखा गया था, पढ़ने के साथ पालन करना होगा। ऐसे समय तक, एक मोबाइल क्लाइंट को इस धारणा के तहत काम करना चाहिए कि संबंधित कैश डेटा गंदा है।

यह एक बहुत ही शक्तिशाली डिजाइन प्रतिमान है, हालांकि पाठक को यह बताना चाहिए कि AKKA + Spary.io के साथ आप अभिनेता कॉल स्टैक में तीन से अधिक स्तर तक नहीं जा सकते। उदाहरण के लिए यदि ये सिस्टम में अभिनेता हैं:

  1. स्प्रे राउटर के लिए एस।
  2. एपीआई हैंडलर के लिए ए।
  3. I / O हैंडलर के लिए B।

x? y संकेतन का उपयोग करने का मतलब है कि x कॉल y और कॉलबैक का अनुरोध कर रहा है और x! y का अर्थ है कि x फायर और y को भूल जाता है, निम्न कार्य करता है:

एस? ए ! बी

हालांकि ये नहीं हैं:

एस! ए ! बी

एस? ए ! बी! बी

इन दोनों मामलों में A के पूर्ण होते ही B के सभी उदाहरण नष्ट हो जाते हैं और आपके पास केवल एक बार आपके सभी भरी हुई संगणना को आग में पैक करने और अभिनेता को भूलने का मौका होता है। मेरा मानना ​​है कि यह स्प्रे की सीमा है न कि एकेके की और इसे इस पोस्ट के प्रकाशित होने के समय तक संबोधित किया जा सकता है।

अंत में, I / O और दृढ़ता

जैसा कि ऊपर दिखाया गया है, हम स्वीकार्य निष्पादन समय के भीतर API POST / PUT प्रदर्शन को बनाए रखने के लिए एसिंक्रोनस थ्रेड्स में धीमी गति से लिखने के संचालन को धक्का दे सकते हैं। ये आम तौर पर दसियों सेकंड या कम एक सौ मिलीसेकंड के बीच सर्वर प्रोफाइल पर निर्भर करते हैं और आग और भूल दृष्टिकोण का उपयोग करके कितना तर्क दिया जा सकता है।

हालाँकि अक्सर ऐसा होता है कि पढ़ी जाने वाली संख्या एक या अधिक परिमाण के क्रम से लिखती है। इस प्रकार एक अच्छा कैशिंग दृष्टिकोण उच्चतर थ्रूपुट वितरित करने के लिए महत्वपूर्ण है।

नोट: विपरीत IOT परिदृश्यों के लिए सच है, जहां संवेदी डेटा नोड्स से आ रहा है, परिमाण के कई क्रमों से पढ़ता है। इस स्थिति में लैंडस्केप सर्वर को केवल आईओटी उपकरणों से लिखने के लिए कॉन्फ़िगर किए गए सर्वरों के एक समूह के लिए कॉन्फ़िगर किया जा सकता है, सर्वरों के अलग-अलग चश्मे के साथ सर्वरों के दूसरे समूह को समर्पित करते हैं (फ्रंट एंड)। अधिकांश अगर सभी कोड-बेस को सर्वर और इन दो वर्गों के बीच साझा नहीं किया जा सकता है, तो सुरक्षा कमजोरियों को रोकने के लिए कॉन्फ़िगरेशन के माध्यम से बस बंद किया जा सकता है।

एक लोकप्रिय दृष्टिकोण रेडिस जैसे मेमोरी कैश का उपयोग करना है। जब प्रमाणीकरण के लिए उपयोगकर्ता की अनुमति संग्रहीत करने के लिए उपयोग किया जाता है, तो Redis अच्छा प्रदर्शन करता है, अर्थात्, डेटा जो अक्सर बदलता नहीं है। एक एकल Redis नोड 250 मील की दूरी तक जोड़े रख सकता है।

पढ़ता है कि कैश को क्वेरी करने के लिए हमें एक अलग समाधान की आवश्यकता है। मेमोरी इंडेक्स में एलिटिक्स खोज, भौगोलिक डेटा या डेटा के लिए असाधारण रूप से अच्छी तरह से काम करती है जिसे प्रकारों में विभाजित किया जा सकता है। उदाहरण के लिए, कुछ विषयों के लिए नवीनतम सबमिशन (सबरडिट्स) प्राप्त करने के लिए प्रकार कुत्तों और मोटरसाइकिलों के साथ सबमिशन नामक एक सूचकांक को आसानी से समझा जा सकता है।

उदाहरण के लिए, एलियस्टिक्स की http एपीआई संकेतन का उपयोग करना:

कर्ल -XPOST 'लोकलहोस्ट: 9200 / सबमिशन / डॉग्स / _सर्च? सुंदर' -d '
{
  "क्वेरी": {
    "छाना हुआ": {
      "क्वेरी": {"match_all": {}},
      "फ़िल्टर": {
        "श्रेणी": {
          "बनाया था": {
            "जीटीई": 1464913588000
          }
        }
      }
    }
  }
} '

/ डॉग्स में निर्दिष्ट तिथि के बाद सभी दस्तावेज लौटाएंगे। इसी तरह हम उन सभी पोस्ट / सबमिशन / मोटरसाइकिलों की तलाश कर सकते हैं जिनके दस्तावेजों में "डुकाटी" का काम हो।

कर्ल -XPOST 'लोकलहोस्ट: 9200 / सबमिशन / मोटरसाइकिल / _search? बहुत' -d '
{
  "क्वेरी": {"मैच": {"टेक्स्ट": "डुकाटी"}}
} '
जब डेटा को दर्ज किया जा रहा है, तो सूचकांक को सावधानीपूर्वक डिज़ाइन किया गया और बनाया जाने से पहले एलीस्टेकर्च पढ़ता है। यह कुछ को हतोत्साहित कर सकता है क्योंकि एलिस्टिक्स खोज के लाभों में से एक है, बस एक डाक्यूमेंट को पोस्ट करके एक इंडेक्स बनाने की क्षमता और इंजन के प्रकार और डेटा संरचनाओं का पता लगाना। हालांकि संरचना को परिभाषित करने के लाभों की लागत बढ़ जाती है और यह ध्यान दिया जाना चाहिए कि उपनामों का उपयोग करते समय उत्पादन वातावरण में भी एक नए सूचकांक में पलायन सीधा है।

नोट: एलीटेसर्च इंडेक्स को संतुलित पेड़ों के रूप में कार्यान्वित किया जाता है, इस प्रकार पेड़ के बड़े होने पर ऑपरेशन और विलोपन महंगा हो सकता है। लाखों दस्तावेज़ों के साथ एक इंडेक्स में सम्मिलित करना सर्वर युक्ति के आधार पर दसियों सेकंड तक हो सकता है। यह आपकी Elasticsearch आपके क्लाउड में सबसे धीमी गति से चलने वाली प्रक्रियाओं में से एक को लिखता है (अलग डीबी लिखते हैं, निश्चित रूप से)। हालाँकि, लेखन को आग में धकेलना और समस्या को हल न करने पर AKKA अभिनेता को भूल जाना चाहिए।

निष्कर्ष

Scala + AKKA + Spray.io मेमोरी कैशिंग और / या मेमोरी इंडेक्सिंग में विवाहित होने पर उच्च प्रदर्शन रेस्ट एपीआई के निर्माण के लिए एक बहुत प्रभावी तकनीक स्टैक है।

मैंने यहां बताई गई अवधारणाओं से बहुत दूर नहीं, जहां प्रति मिनट 2000 हिट प्रति नोड के हिसाब से सीपीयू लोड को 1% से ऊपर ले जाया, वहां से बहुत दूर तक कार्यान्वयन पर काम किया।

बोनस दौर: मशीन सीखने और अधिक

स्टैक के लिए Elasticsearch को जोड़ने से लाइन और ऑफ लाइन मशीन लर्निंग दोनों के लिए दरवाजा खुल जाता है क्योंकि Elasticsearch अपाचे स्पार्क के साथ एकीकृत होता है। एपीआई की सेवा के लिए उपयोग की जाने वाली समान दृढ़ता की परत को मशीन लर्निंग मॉड्यूल द्वारा कोडिंग, रखरखाव लागत और स्टैक जटिलता को फिर से उपयोग किया जा सकता है। अंत में, स्काला हमें स्टैनफोर्ड के कोर एनएलपी, ओपनसीवी, स्पार्क मालिब और अधिक जैसी चीज का लाभ उठाने के लिए किसी भी स्काला या जावा पुस्तकालय का उपयोग करके अधिक परिष्कृत डेटा प्रोसेसिंग के लिए दरवाजा खोलने की अनुमति देता है।

इस पोस्ट में उल्लिखित प्रौद्योगिकियों के लिंक

  1. http://www.scala-lang.org
  2. http://spray.io
  3. और (2) समझदारी के लिए, http://akka.io पर एक नज़र डालें