2014-12-30 7 views
11

हमारे पास एक डैशबोर्ड वाला एक वेब एप्लिकेशन है जो लगातार अपडेट के लिए मतदान कर रहा है। सर्वर-साइड पर, अपडेट के लिए अनुरोध अतुल्यकालिक बना दिया जाता है ताकि जब हम श्रोता/अधिसूचना प्रणाली के माध्यम से कोई अपडेट होता है तो हम जवाब दे सकते हैं।AsyncContext प्रतिक्रिया मूल आने वाले अनुरोध से मेल नहीं खाती है?

समस्या हम देख रहे हैं कि जब इन मतदान अनुरोधों में से एक के लिए प्रतिक्रिया व्यक्त की जा रही है, यह कुछ मामलों में एक यूजर-क्लिक किया लिंक के लिए अनुरोध/प्रतिक्रिया करने के लिए लिख सकते हैं।

async अद्यतन के लिए भेजे अनुरोध लगता है:

@RequestMapping("/getDashboardStatus.json") 
public void getDashboardStatus(HttpServletRequest request, ...) { 
    final AsyncContext asyncContext = request.startAsync(); 
    // 10 seconds 
    asyncContext.setTimeout(10000); 
    asyncContext.start(new Runnable() { 
     public void run() { 
      // .. (code here waits for an update to occur) .. 
      sendMostRecentDashboardJSONToResponse(asyncContext.getResponse()); 
      if (asyncContext.getRequest().isAsyncStarted()) { 
       asyncContext.complete(); 
      } 
     } 
    }); 
} 

क्या अजीब है, वहाँ इस डैशबोर्ड पर लिंक है कि अन्य पृष्ठों पर जाना हैं कि है। प्रत्येक ~ 100 क्लिक या तो, उनमें से एक चयनित पृष्ठ प्रदर्शित करने के बजाय, वास्तव में ऊपर भेजा गया JSON प्रदर्शित करेगा!

उदाहरण के लिए, हम एक अलग MVC विधि है:

@RequestMapping("/result/{resultId}") 
public ModelAndView getResult(@PathVariable String resultId) { 
    return new ModelAndView(...); 
} 

और जब डैशबोर्ड कि /result/1234, हर नीला चाँद का दौरा पर एक लिंक पर क्लिक करने, पेज एक 200 ठीक स्थिति के साथ लोड होगा, लेकिन इसके बजाय अपेक्षित एचटीएमएल युक्त, वास्तव में मतदान अनुरोध के लिए JSON शामिल है!

प्रति ग्राहक केवल एक अनुरोध है? क्या एक क्लिक किए गए लिंक द्वारा शुरू किया गया अनुरोध ओवरराइड किसी भी async अनुरोध जो पहले से ही उसी क्लाइंट से सर्वर-साइड पर बैठा है?

हम इन अनुरोधों का प्रबंधन कैसे कर सकते हैं यह सुनिश्चित करने के लिए कि एसिंक प्रतिक्रिया async अनुरोध पर जाती है?

मैंने AsyncContext ऑब्जेक्ट पर hasOriginalRequestAndResponse() विधि को देखा, लेकिन जावाडोक से समझने में कठिनाई हो रही है कि मैं यही देख रहा हूं।

अद्यतन: मैं सिर्फ इतना की तरह एक टुकड़ा कहा:

String requestURI = ((HttpServletRequest)asyncContext.getRequest()).getRequestURI()); 
System.out.println("Responding w/ Dashboard to: " + requestURI); 
sendMostRecentDashboardJSONToResponse(asyncContext.getResponse(), clientProfileKey); 

और उचित व्यवहार के दौरान इस मुद्दे को पुन: पेश करने, सक्षम था, मैं देख रहा हूँ:

Responding w/ Dashboard to: /app/getDashboardStatus.json 

लेकिन जब मैं जेएसओएन को क्लिक-शुरू किए गए अनुरोधों पर धक्का दिया गया है, मैं देखता हूं:

Responding w/ Dashboard to: null 
+0

इस प्रश्न में उपयोग किए गए टैग के बारे में एक चल रही मेटा चर्चा है: http://meta.stackoverflow.com/questions/281443/how-general-must-a-problem-be-to-warrant-use-of -ए-भाषा-पुस्तकालय-टैग –

+0

@ क्रैग ओटिस क्या आपने 'request.startAsync (अनुरोध, प्रतिक्रिया) का उपयोग करने का प्रयास किया था,' request.startAsync() 'के बजाय? – fmodos

+0

@fmodos मैंने इसके लिए दस्तावेज़ पढ़े, लेकिन वास्तव में इसका उपयोग नहीं किया - ऐसा लगता है कि इस परिदृश्य में व्यवहार बदल जाएगा? ऐसा लगता है कि उस विधि में पारित अनुरोध/प्रतिक्रिया तर्क आने वाले अनुरोध/प्रतिक्रियाओं के समान (या रैपर) होने की आवश्यकता होगी, ऐसा लगता है कि यह वही काम करेगा। –

उत्तर

3

मुझे यह पता चला है। अनुरोध/प्रतिक्रिया वास्तव में पुनर्नवीनीकरण किया जा रहा है, इसलिए AsyncContext को दी गई प्रतिक्रिया पर लटककर, मैं अलग अनुरोध से संबंधित प्रतिक्रिया के लिए लिख रहा था।

कॉलिंग startAsync() गारंटी देता है कि अनुरोध/प्रतिक्रिया ऑब्जेक्ट्स को पुनर्नवीनीकरण नहीं किया जाएगा जब तक कि एसिंक्रोनस संदर्भ पूरा नहीं हो जाता है।मेरे खोज के बावजूद कोई संदर्भ नहीं जहां संदर्भ समय-समय पर या गलती से पूरा हो जाएगा, पूरा हो रहा था:

टाइमआउट द्वारा।

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

दो समाधान थे जो मैंने पाया।

पहले संदर्भ में AsyncListener जोड़ना है, और समय-समय पर हुआ था या नहीं, इसका ट्रैक रखें। जब आपका श्रोता एक टाइमआउट का पता लगाता है, तो आप boolean फ़्लिप करते हैं, और प्रतिक्रिया में लिखने से पहले इसे जांचते हैं।

दूसरा प्रतिक्रिया पर लिखने से पहले अनुरोध पर isAsyncStarted() पर कॉल करना है। यदि संदर्भ का समय समाप्त हो गया है, तो यह विधि false वापस आ जाएगी। यदि संदर्भ अभी भी वैध/प्रतीक्षा कर रहा है, तो यह true लौटाएगा।

0

आप वसंत संदर्भ डॉक्स में Asynchronous Request Processing अध्याय पढ़ रहे हैं, तो आप देखेंगे कि आप अपने समाधान को आसान बनाने में कर सकते हैं:

@RequestMapping(value = "/getDashboardStatus.json", 
       produces = MediaType.APPLICATION_JSON_VALUE) 
public Callable<MostRecentDashboard> getDashboardStatus() { 
    return new Callable() { 
     @Override 
     public MostRecentDashboard call() { 
      // .. (code here waits for an update to occur) .. 
      return ...; 
     } 
    }); 
} 

MostRecentDashboard उदाहरण जैक्सन का उपयोग कर धारावाहिक की जाएगी (बशर्ते कि जैक्सन 2 classpath पर है)।

फिर, आपको एक TaskExecutor जरूरत है कि Callable (यह भी आमतौर पर डिफ़ॉल्ट टाइमआउट कॉन्फ़िगर करने के लिए प्रयोग किया जाता है), विकल्प के लिए Spring MVC Async Config पैरा पढ़ निष्पादित करेंगे। वैकल्पिक रूप से, यदि आप MostRecentDashboard के मान को निर्दिष्ट करते हैं, तो स्प्रिंग को उस थ्रेड को नहीं पता है, तो आप DeferredResult<MostRecentDashboard> वापस कर सकते हैं। MostRecentDashboard उदाहरण जेएमएस, रेडिस या इसी तरह से पढ़े जाते हैं। अधिक जानकारी के लिए, Introducing Servlet 3, Async Support ब्लॉग पोस्ट पढ़ें।

Spring version 4.1 के रूप में, आप एक ListenableFuture<MostRecentDashboard> सीधे अपने नियंत्रक, जो उपयोगी है जब आप MostRecentDashboard एक AsyncRestTemplate का उपयोग कर लाने से लौट सकते हैं।

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