2015-11-14 26 views
8

के साथ डेटाबेस मतदान एक विरासत प्रणाली की स्थिति जानने के लिए मुझे समय-समय पर डेटाबेस से पूछताछ करना है। मैंने Observable के आसपास क्वेरी को लपेटने का विचार किया है, लेकिन मुझे ऐसा करने का सही तरीका नहीं पता है।प्रतिक्रियाशील एक्सटेंशन

असल में, यह हर 5 सेकंड में एक ही प्रश्न होगा। लेकिन मुझे डर है कि मुझे इन समस्याओं का सामना करना पड़ेगा:

  • क्या होगा यदि क्वेरी के निष्पादन में 10 सेकंड लगते हैं? यदि 0 पिछला अभी भी संसाधित हो रहा है, तो मैं किसी भी नई क्वेरी को निष्पादित नहीं करना चाहता हूं।
  • इसके अलावा, एक टाइमआउट होना चाहिए। यदि वर्तमान क्वेरी निष्पादित नहीं करती है, उदाहरण के लिए, 20 सेकंड, एक सूचनात्मक संदेश लॉग होना चाहिए और एक नया प्रयास (एक ही क्वेरी) भेजा जाना चाहिए।

अतिरिक्त विवरण:

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

मुझे लगभग यकीन है कि क्वेरी को किसी अन्य धागे में निष्पादित किया जाना चाहिए, लेकिन मुझे नहीं पता कि अवलोकन योग्य कैसा दिखना चाहिए, कभी भी Introduction to Rx by Lee Campbell पढ़ना चाहिए।

+0

क्या आप अधिक जानकारी जोड़ सकते हैं? क्वेरी वापस क्या डेटा प्राप्त करती है? क्या क्वेरी एक ऑब्जेक्ट लौटाती है? टाइमआउट के मामले में आप कहते हैं कि आप एक नई क्वेरी लॉन्च करना चाहते हैं, वह क्वेरी क्या है? –

उत्तर

14

यह किसी अन्य प्रणाली को मतदान करने के लिए आरएक्स का उपयोग करने का एक काफी क्लासिक मामला है। अधिकांश लोग Observable.Interval का उपयोग अपने जाने-माने ऑपरेटर के रूप में करेंगे, और अधिकांश के लिए यह ठीक होगा।

हालांकि आपके पास टाइमआउट पर पुनः आवश्यकताएं हैं और पुनः प्रयास करें। इस मामले मुझे लगता है कि आप ऑपरेटरों के संयोजन का उपयोग बेहतर कर रहे हैं:

  • Observable.Timer आप एक निर्दिष्ट समय
  • Timeout की पहचान करने और करने के लिए आपकी क्वेरी को निष्पादित करने की अनुमति के लिए डेटाबेस क्वेरी, जो करने के लिए
  • ToObservable() लंघन है अपने Task परिणामों को एक अवलोकन अनुक्रम में मैप करें।
  • Retry आपको टाइमआउट
  • Repeat के बाद पुनर्प्राप्त करने के लिए आपको सफल डेटाबेस प्रश्नों के बाद जारी रखने की अनुमति देने के लिए अनुमति देता है। यह पिछले डेटाबेस क्वेरी के पूरा होने और अगले के प्रारंभ के बीच प्रारंभिक अवधि/अंतर भी रखेगा।

यह काम कर LINQPad टुकड़ा आप दिखाना चाहिए क्वेरी ठीक से काम करता है:

void Main() 
{ 
    var pollingPeriod = TimeSpan.FromSeconds(5); 
    var dbQueryTimeout = TimeSpan.FromSeconds(10); 

    //You will want to have your Rx query timeout after the expected silence of the timer, and then further maximum silence. 
    var rxQueryTimeOut = pollingPeriod + dbQueryTimeout; 

    var scheduler = new EventLoopScheduler(ts => new Thread(ts) { Name = "DatabasePoller" }); 

    var query = Observable.Timer(pollingPeriod, scheduler) 
        .SelectMany(_ => DatabaseQuery().ToObservable()) 
        .Timeout(rxQueryTimeOut, Observable.Return("Timeout"), scheduler) 
        .Retry() //Loop on errors 
        .Repeat(); //Loop on success 

    query.StartWith("Seed") 
     .TimeInterval(scheduler) //Just to debug, print the timing gaps. 
     .Dump(); 
} 

// Define other methods and classes here 
private static int delay = 9; 
private static int delayModifier = 1; 
public async Task<string> DatabaseQuery() 
{ 
    //Oscillate the delay between 3 and 12 seconds 
    delay += delayModifier; 
    var timespan = TimeSpan.FromSeconds(delay); 
    if (delay < 4 || delay > 11) 
     delayModifier *= -1; 
    timespan.Dump("delay"); 
    await Task.Delay(timespan); 
    return "Value"; 
} 

परिणाम की तरह लग रहे:

Seed 00:00:00.0125407 
Timeout 00:00:15.0166379 
Timeout 00:00:15.0124480 
Timeout 00:00:15.0004520 
Timeout 00:00:15.0013296 
Timeout 00:00:15.0140864 
Value 00:00:14.0251731 
Value 00:00:13.0231958 
Value 00:00:12.0162236 
Value 00:00:11.0138606 

नमूने के महत्वपूर्ण हिस्सा है ....

var query = Observable.Timer(TimeSpan.FromSeconds(5), scheduler) 
       .SelectMany(_ => DatabaseQuery().ToObservable()) 
       .Timeout(rxQueryTimeOut, Observable.Return("Timeout"), scheduler) 
       .Retry() //Loop on errors 
       .Repeat(); //Loop on success 

संपादित करें: यहां इस समाधान पर पहुंचने का एक और विवरण दिया गया है। https://github.com/LeeCampbell/RxCookbook/blob/master/Repository/Polling.md

+0

यह सुनिश्चित करने के लिए कि क्या क्वेरी समान थ्रेड पर चलती है, EventLoopScheduler का उपयोग करने का मुख्य उद्देश्य है? क्या यह आरएक्स का उपयोग कर किसी अन्य सिस्टम को मतदान करते समय जाने का सबसे अच्छा तरीका है? – jumpercake

+0

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

+0

कार्य के साथ नमूना कोड के लिंक को शामिल करने के लिए अपडेट किया गया –

1

मुझे लगता है कि यह आपको क्या करना चाहिए है:

var query = 
    from n in Observable.Interval(TimeSpan.FromSeconds(5.0)) 
    from ds in Observable.Amb(
     Observable.Start(() => /* Your DataSet query */), 
     Observable 
      .Timer(TimeSpan.FromSeconds(10.0)) 
      .Select(_ => new DataSet("TimeOut"))) 
    select ds; 

यह एक अंतराल के साथ एक नई क्वेरी चलाता है के बीच 5 सेकंड के लिए सजा। पिछले एक के शुरू होने के बाद से यह 5 सेकंड नहीं है, आखिरी एक समाप्त होने के बाद से यह 5 सेकंड है।

फिर आप अपनी क्वेरी का प्रयास करें, लेकिन आप .Amb यह एक टाइमर के साथ जो 10 सेकंड के बाद विशेष DataSet देता है। यदि आपकी क्वेरी 10 सेकंड से पहले खत्म हो जाती है तो यह जीत जाती है, लेकिन अन्यथा विशेष DataSet वापस कर दिया जाता है। .Amb ऑपरेटर मूल रूप से एक "रेस" ऑपरेटर है - मूल्य जीतने के लिए पहली बार देखा जा सकता है।

+0

वाह, अवलोकन के संयोजन काफी प्रभावशाली है! आपका मतलब है कि दोनों अवलोकन योग्य दौड़ेंगे और अंब आने वाले पहले व्यक्ति होंगे। मुझे क्या खो देता है 2 क्लोज़ से घिरा हुआ है। क्या वह हिस्सा वह हिस्सा है जो उस भाग को अनुमति देता है जिसमें आप कहते हैं "यह 5 सेकंड के निष्पादन के बीच एक अंतराल के साथ एक नई क्वेरी ट्रिगर करता है। आखिरी एक के बाद से यह 5 सेकंड नहीं है, आखिरी एक समाप्त होने के बाद से 5 सेकंड है।" – SuperJMN

+1

@SuperJMN - धन्यवाद। 'अवलोकन योग्य। अंतराल (टाइमस्पेन। फ्रॉमसेकंड्स (5.0)) सभी ग्राहकों ने अपना काम पूरा करने के बाद केवल 5 सेकंड के बाद आग लगा दी। तो अगर क्वेरी का दूसरा भाग अंतराल पर काम कर रहा है तो इसे पूरा होने के 5 सेकंड तक आग नहीं लगेगी। – Enigmativity

+0

मैं _think_ के रूप में आप 'selectMany' का उपयोग कर अपने' पर्यवेक्षित। अंतराल 'का उपभोग करने के लिए उपयोग कर रहा हूं, जो कि खपत _blocking_ कुछ भी नहीं है, इसलिए यह हर 5 सेकंड में टिकेगा, यानी डीबी क्वेरी समाप्त होने के बाद नहीं। –

संबंधित मुद्दे