आबाद करने के लिए एक अपस्ट्रीम सेवा के लिए एक अक्का धारा पास करना


9

मुझे आउटपुटस्ट्रीम पर डेटा पुश करने के लिए एक अपस्ट्रीम सेवा (एज़्योर ब्लॉब सर्विस) को कॉल करने की आवश्यकता होती है, जो तब मुझे इधर-उधर मुड़ने और इसे क्लाइंट पर वापस धकेलने की आवश्यकता होती है, एक्का के माध्यम से। एक्का (और सिर्फ सर्वलेट कोड) के बिना, मैं सिर्फ ServletOutputStream प्राप्त करूंगा और इसे azure सेवा के तरीके से पास करूंगा।

सबसे नज़दीकी मैं ठोकर खाने की कोशिश कर सकता हूं, और स्पष्ट रूप से यह गलत है, ऐसा कुछ है

        Source<ByteString, OutputStream> source = StreamConverters.asOutputStream().mapMaterializedValue(os -> {
            blobClient.download(os);
            return os;
        });

        ResponseEntity resposeEntity = HttpEntities.create(ContentTypes.APPLICATION_OCTET_STREAM, preAuthData.getFileSize(), source);

        sender().tell(new RequestResult(resposeEntity, StatusCodes.OK), self());

विचार यह है कि मैं एक अपस्ट्रीम सेवा को कॉल कर रहा हूं ताकि ब्लॉब्स्टिएंट.डाउट (ओएस) को कॉल करके आउटपुट आउटपुट प्राप्त किया जा सके;

ऐसा लगता है कि लैम्ब्डा फ़ंक्शन को कॉल किया जाता है और वापस लौटता है, लेकिन फिर बाद में यह विफल हो जाता है, क्योंकि कोई डेटा या कुछ नहीं है। जैसे कि मुझे लगता है कि लैम्बडा फंक्शन काम करने वाला नहीं है, लेकिन शायद कुछ ऑब्जेक्ट वापस करता है जो काम करता है? निश्चित नहीं।

कोई इसे कैसे करता है?


का व्यवहार क्या है download? क्या यह डेटा को स्ट्रीम करता है osऔर डेटा लिखे जाने के बाद ही वापस लौटता है?
एलेक

जवाबों:


2

यहाँ वास्तविक मुद्दा यह है कि एज़्योर एपीआई को बैक-प्रेशरिंग के लिए डिज़ाइन नहीं किया गया है। आउटपुट स्ट्रीम के लिए एज़्योर को वापस संकेत देने का कोई तरीका नहीं है कि यह अधिक डेटा के लिए तैयार नहीं है। इसे दूसरे तरीके से रखने के लिए: यदि एज़्योर डेटा को तेज़ी से धकेलता है, तो आप इसका उपभोग करने में सक्षम हैं, तो कहीं न कहीं कुछ बदसूरत बफर अतिप्रवाह विफलता होगी।

इस तथ्य को स्वीकार करते हुए, अगली सबसे अच्छी बात हम कर सकते हैं:

  • Source.lazySourceडाउनस्ट्रीम डिमांड (उर्फ सोर्स चलाया जा रहा है और डेटा रिक्वेस्ट किया जा रहा है) होने पर केवल डेटा डाउनलोड करना शुरू करें ।
  • downloadकॉल को किसी अन्य थ्रेड में रखें ताकि स्रोत को बिना लौटाए उसे निष्पादित करना जारी रहे। एक बार ऐसा करने का तरीका एक के साथ है Future(मुझे यकीन नहीं है कि जावा सर्वोत्तम प्रथाएं क्या हैं, लेकिन दोनों तरह से ठीक काम करना चाहिए)। हालांकि यह शुरू में मायने नहीं रखेगा, आपको इसके अलावा एक निष्पादन संदर्भ चुनने की आवश्यकता हो सकती है system.dispatcher- यह सब इस बात पर निर्भर करता है कि downloadअवरुद्ध है या नहीं।

मैं अग्रिम में माफी माँगता हूँ अगर यह जावा कोड विकृत है - मैं अकाला का उपयोग स्काला के साथ करता हूं, इसलिए यह सब अक्का जावा एपीआई और जावा सिंटैक्स संदर्भ को देखने से है।

ResponseEntity responseEntity = HttpEntities.create(
  ContentTypes.APPLICATION_OCTET_STREAM,
  preAuthData.getFileSize(),

  // Wait until there is downstream demand to intialize the source...
  Source.lazySource(() -> {
    // Pre-materialize the outputstream before the source starts running
    Pair<OutputStream, Source<ByteString, NotUsed>> pair =
      StreamConverters.asOutputStream().preMaterialize(system);

    // Start writing into the download stream in a separate thread
    Futures.future(() -> { blobClient.download(pair.first()); return pair.first(); }, system.getDispatcher());

    // Return the source - it should start running since `lazySource` indicated demand
    return pair.second();
  })
);

sender().tell(new RequestResult(responseEntity, StatusCodes.OK), self());

बहुत खुबस। बहुत धन्यवाद। आपके उदाहरण के लिए एक छोटा सा संपादन है: Futures.future () -> {blobClient.download (pair.first ()); return pair.first ();}; system.getDispatcher ());
MeBigFatGuy

@MeBigFatGuy अधिकार, धन्यवाद!
एलेक

1

OutputStreamइस मामले में की "मूल्य materialized" है Sourceऔर यह केवल एक बार धारा चलाया जाता है बनाया जाएगा (या एक चल धारा में "materialized")। इसे चलाना आपके नियंत्रण से बाहर है क्योंकि आप Sourceअक्का HTTP को सौंपते हैं और जो बाद में वास्तव में आपके स्रोत को चलाएगा।

.mapMaterializedValue(matval -> ...)आमतौर पर इसका उपयोग भौतिक मूल्य को बदलने के लिए किया जाता है, लेकिन जब से इसे भौतिककरण के एक भाग के रूप में लागू किया जाता है, तब आप इसका उपयोग कर सकते हैं जैसे कि संदेश में मैटल भेजना जैसे साइड इफेक्ट्स करना, जैसे आपने पता लगाया है, जरूरी नहीं कि इसके साथ कुछ गलत हो भले ही यह फंकी लग रहा हो। यह समझना महत्वपूर्ण है कि धारा अपना भौतिककरण पूरा नहीं करेगी और तब तक चलती रहेगी जब तक कि लैम्ब्डा पूरा नहीं हो जाता। इसका मतलब यह है कि समस्याओं download()को एक अलग धागे पर कुछ काम बंद करने और तुरंत लौटने के बजाय अवरुद्ध होने पर।

हालांकि एक और उपाय है: Source.preMaterialize()यह स्रोत को उत्प्रेरित करता है और आपको एक Pairभौतिक मूल्य प्रदान करता है और एक नया Sourceजो पहले से शुरू किए गए स्रोत का उपभोग करने के लिए इस्तेमाल किया जा सकता है:

Pair<OutputStream, Source<ByteString, NotUsed>> pair = 
  StreamConverters.asOutputStream().preMaterialize(system);
OutputStream os = pair.first();
Source<ByteString, NotUsed> source = pair.second();

ध्यान दें कि आपके कोड में सोचने के लिए कुछ अतिरिक्त चीजें हैं, सबसे महत्वपूर्ण बात अगर blobClient.download(os)कॉल तब तक होती है जब तक कि यह पूरा नहीं हो जाता है और आप अभिनेता से कॉल करते हैं, उस स्थिति में आपको यह सुनिश्चित करना होगा कि आपका अभिनेता डिस्पैचर को भूखा न रखे और रोकें क्रियान्वयन से आपके आवेदन में अन्य कलाकार (देखें अक्का डॉक्स: https://doc.akka.io/docs/akka/current/typed/dispatchers.html#blocking-needs-careful-management )।


1
जवाब देने के लिए धन्यवाद। मैं नहीं देखता कि यह संभवतः कैसे काम कर सकता है? जब ब्लब क्लिएंट। डाउनलोड (ओएस) कहा जाता है, तो बाइट्स कहां जाते हैं (यदि मैं इसे स्वयं कह रहा हूं)? कल्पना करें कि डेटा का एक टेराबाइट बैठा है जिसके लिखे जाने की प्रतीक्षा है। यह मुझे लगता है कि blobClient.download कॉल को प्रेषक से भेजना होगा। कॉल कॉल करें ताकि यह मूल रूप से एक IOUtils.copy- जैसा ऑपरेशन हो .. preMaterialize का उपयोग करके मैं यह नहीं देख सकता कि ऐसा कैसे होता है?
MeBigFatGuy

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

1
लेकिन अगर मैं प्री-मेटेरियलाइज़ करता हूं, और आउटपुटस्ट्रीम को प्राप्त करता हूं, तो यह मेरा कोड है जो blobClient.download (ओएस) कर रहा है; सही बात? इसका मतलब है कि आगे बढ़ने से पहले इसे पूरा करना होगा, जो असंभव है।
MeBigFatGuy

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

इस बिंदु पर मैं इसे पूरी तरह से काम करने की कोशिश कर रहा हूं। यह 10 बाइट फ़ाइल भी संसाधित नहीं कर सकता है।
MeBigFatGuy
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.