सॉफ्टवेयर पाइपलाइन में साझा डेटा को एनकैप्सुलेट करने के लिए अच्छी कार्यान्वयन रणनीतियाँ


13

मैं एक मौजूदा वेब सेवा के कुछ पहलुओं को फिर से तथ्य पर काम कर रहा हूं। जिस तरह से सेवा API को कार्यान्वित किया जाता है वह एक प्रकार की "प्रसंस्करण पाइपलाइन" होने से होता है, जहाँ ऐसे कार्य होते हैं जो क्रम से किए जाते हैं। अप्रत्याशित रूप से, बाद के कार्यों को पहले के कार्यों द्वारा गणना की गई जानकारी की आवश्यकता हो सकती है, और वर्तमान में यह जिस तरह से किया जाता है वह फ़ील्ड्स को "पाइपलाइन राज्य" वर्ग में जोड़ देता है।

मैं सोच रहा था (और उम्मीद है?) कि वहाँ एक बेहतर तरीका है पाइपलाइन के बीच जानकारी साझा करने के लिए एक zillion क्षेत्रों के साथ एक डेटा ऑब्जेक्ट होने से, जिनमें से कुछ प्रसंस्करण कदम और दूसरों को नहीं समझ में आता है। इस वर्ग को थ्रेड-सेफ बनाने के लिए यह एक बड़ा दर्द होगा (मुझे नहीं पता कि यह संभव भी होगा), इसके आक्रमणकारियों के बारे में तर्क करने का कोई तरीका नहीं है (और यह संभव है कि इसका कोई मतलब नहीं है)।

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

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


जैसा कि अनुरोध किया गया है, मेरे उपयोग के मामले को स्पष्ट करने के लिए कुछ छद्मकोड:

"पाइपलाइन संदर्भ" ऑब्जेक्ट में फ़ील्ड का एक गुच्छा होता है जो विभिन्न पाइपलाइन चरणों को आबाद / पढ़ सकता है:

public class PipelineCtx {
    ... // fields
    public Foo getFoo() { return this.foo; }
    public void setFoo(Foo aFoo) { this.foo = aFoo; }
    public Bar getBar() { return this.bar; }
    public void setBar(Bar aBar) { this.bar = aBar; }
    ... // more methods
}

प्रत्येक पाइपलाइन चरण भी एक वस्तु है:

public abstract class PipelineStep {
    public abstract PipelineCtx doWork(PipelineCtx ctx);
}

public class BarStep extends PipelineStep {
    @Override
    public PipelineCtx doWork(PipelieCtx ctx) {
        // do work based on the stuff in ctx
        Bar theBar = ...; // compute it
        ctx.setBar(theBar);

        return ctx;
    }
}

इसी तरह एक काल्पनिक के लिए FooStep, जिसे बारस्टेप द्वारा बार गणना की आवश्यकता हो सकती है, अन्य डेटा के साथ। और फिर हमारे पास असली एपीआई कॉल है:

public class BlahOperation extends ProprietaryWebServiceApiBase {
    public BlahResponse handle(BlahRequest request) {
        PipelineCtx ctx = PipelineCtx.from(request);

        // some steps happen here
        // ...

        BarStep barStep = new BarStep();
        barStep.doWork(crx);

        // some more steps maybe
        // ...

        FooStep fooStep = new FooStep();
        fooStep.doWork(ctx);

        // final steps ...

        return BlahResponse.from(ctx);
    }
}

6
स्थानांतरित करने के लिए एक पद के लिए ध्वज को पार न करें
शाफ़्ट फ़्रेक

1
आगे जाऊंगा, मुझे लगता है कि मुझे खुद को नियमों के साथ परिचित करने में अधिक समय बिताना चाहिए। धन्यवाद!
रुस्लानड

1
क्या आप अपने कार्यान्वयन के लिए किसी भी लगातार डेटा संग्रहण से बच रहे हैं, या इस बिंदु पर कब्र के लिए कुछ भी नहीं है?
कोकोबो

1
हाय रुस्लान और आपका स्वागत है! यह वास्तव में स्टैक ओवरफ्लो की तुलना में प्रोग्रामर के लिए अधिक उपयुक्त है, इसलिए हमने एसओ संस्करण को हटा दिया। ध्यान रखें कि @ratchetfreak ने जो उल्लेख किया है, आप मॉडरेशन ध्यान के लिए ध्वज लगा सकते हैं और एक अधिक उपयुक्त साइट पर माइग्रेट किए जाने के लिए प्रश्न पूछ सकते हैं, पोस्ट पार करने की आवश्यकता नहीं है। दो साइटों के बीच चयन के लिए अंगूठे का नियम यह है कि प्रोग्रामर उन समस्याओं के लिए हैं जो आप सामना कर रहे हैं जब आप अपनी परियोजनाओं को डिजाइन करने वाले व्हाइटबोर्ड के सामने होते हैं, और स्टैक ओवरफ्लो अधिक तकनीकी समस्याओं (जैसे कार्यान्वयन मुद्दों) के लिए होता है। अधिक जानकारी के लिए हमारे FAQ देखें ।
यानिस २०'१२

1
यदि आप एक पाइपलाइन के बजाय आर्किटेक्चर को एक प्रसंस्करण डीएजी (निर्देशित एसाइक्लिक ग्राफ) में बदलते हैं, तो आप पहले के चरणों के परिणामों को स्पष्ट रूप से पास कर सकते हैं।
पैट्रिक

जवाबों:


4

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

या तो मामले में, पाइपलाइन में प्रत्येक चरण को वह सब कुछ दिया जाना चाहिए जो उसे अपना काम करने की आवश्यकता है। ऐसा कोई कारण नहीं है कि आप बाहरी स्टोर (उदाहरण के लिए, डेटाबेस) का उपयोग नहीं कर सकते हैं, लेकिन ज्यादातर मामलों में डेटा को एक चरण से दूसरे चरण में पारित करना बेहतर होता है।

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

फिर आपके पास बहुत सी लचीलापन है कि आप अपनी वास्तविक संदेश वस्तुओं को कैसे लागू करते हैं। एक दृष्टिकोण एक विशाल डेटा ऑब्जेक्ट का उपयोग करना है जो सभी आवश्यक इंटरफेस को लागू करता है। एक और सरल के आसपास आवरण कक्षाएं बनाने के लिए है Map। फिर भी एक डेटाबेस के चारों ओर एक आवरण वर्ग बनाना है।


1

कुछ विचार हैं जो मन में उछलते हैं, जिनमें से पहला यह है कि मेरे पास पर्याप्त जानकारी नहीं है।

  • क्या प्रत्येक चरण पाइपलाइन से परे उपयोग किए गए डेटा का उत्पादन करता है, या क्या हम केवल अंतिम चरण के परिणामों की परवाह करते हैं?
  • क्या कई बड़ी डेटा चिंताएं हैं? अर्थात। स्मृति संबंधी चिंताएँ, गति संबंधी चिंताएँ आदि

जवाब शायद मुझे डिजाइन के बारे में अधिक ध्यान से सोचने देंगे, हालांकि आपने जो कहा है उसके आधार पर 2 दृष्टिकोण हैं जो मैं शायद पहले विचार करूंगा।

प्रत्येक चरण की संरचना करें क्योंकि यह स्वयं की वस्तु है। प्रतिनिधियों की सूची के रूप में n चरण में n-1 चरणों के माध्यम से 1 होगा। प्रत्येक चरण डेटा और डेटा के प्रसंस्करण को संकुचित करता है; प्रत्येक वस्तु के भीतर समग्र जटिलता और क्षेत्रों को कम करना। आप बाद के चरणों में भी डेटा का उपयोग कर सकते हैं जैसा कि प्रतिनिधियों को ट्रैवर्स करके पहले के चरणों से आवश्यक है। आपके पास अभी भी सभी वस्तुओं में बहुत तंग युग्मन है क्योंकि यह चरणों के परिणाम है (अर्थात। सभी अटार) जो महत्वपूर्ण हैं, लेकिन यह काफी कम हो गया है और प्रत्येक चरण / वस्तु संभवतः अधिक पठनीय और समझने योग्य है। आप डेलिगेट्स की सूची को आलसी बना सकते हैं और आवश्यकतानुसार प्रत्येक ऑब्जेक्ट में डेलिगेट सूची को पॉप्युलेट करने के लिए थ्रेड सेफ कतार का उपयोग करके इसे सुरक्षित बना सकते हैं।

वैकल्पिक रूप से मैं शायद आपके काम के समान कुछ करूंगा। एक विशाल डेटा ऑब्जेक्ट जो प्रत्येक चरण का प्रतिनिधित्व करने वाले कार्यों से गुजरता है। यह अक्सर बहुत तेज़ और हल्का होता है, लेकिन अधिक जटिल और त्रुटि के कारण यह डेटा विशेषताओं का एक बड़ा ढेर है। स्पष्ट रूप से धागा-सुरक्षित नहीं।

ईमानदारी से मैंने बाद में ईटीएल और इसी तरह की कुछ अन्य समस्याओं के लिए एक बार किया। मैं स्थिरता की बजाय डेटा की मात्रा के कारण प्रदर्शन पर केंद्रित था। इसके अलावा, वे एक-बंद थे जो फिर से उपयोग नहीं किए जाएंगे।


1

यह GoF में एक चेन पैटर्न जैसा दिखता है।

एक अच्छा शुरुआती बिंदु यह देखना होगा कि कॉमन्स-चेन क्या करता है।

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

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

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

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

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


0

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

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

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


अरे, मैंने कुछ छद्मकोड के साथ अपने प्रश्न को अपडेट किया - हम वास्तव में चरणों को स्पष्ट करते हैं।
रुस्लानड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.