Đã xảy ra sự cố gần đây trong đó tất cả 200 luồng chứa web bị treo, nghĩa là không có chủ đề nào sẵn có để phục vụ các yêu cầu đến và do đó ứng dụng bị đóng băng.Triển khai thực hiện WebSphere 7 HTTPSession có trái với thông số J2EE không?
Đây là một ứng dụng web đơn giản và kiểm tra JMeter mà tôi nghĩ rằng chứng minh nguyên nhân của vấn đề này. Các ứng dụng web bao gồm hai lớp, các servlet sau:
public class SessionTestServlet extends HttpServlet {
protected static final String SESSION_KEY = "session_key";
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// set data on session so the listener is invoked
String sessionData = new String("Session data");
request.getSession().setAttribute(SESSION_KEY, sessionData);
PrintWriter writer = response.getWriter();
writer.println("<html><body>OK</body></html>");
writer.flush();
writer.close();
}
}
và việc thực hiện sau đây của HttpSessionListener và HTTPSessionAttributeListener:
public class SessionTestListener implements
HttpSessionListener, HttpSessionAttributeListener {
private static final ConcurrentMap<String, HttpSession> allSessions
= new ConcurrentHashMap<String, HttpSession>();
public void attributeRemoved(HttpSessionBindingEvent hsbe) {}
public void attributeAdded(HttpSessionBindingEvent hsbe) {
System.out.println("Attribute added, " + hsbe.getName()
+ "=" + hsbe.getValue());
int count = 0;
for (HttpSession session : allSessions.values()) {
if (session.getAttribute(SessionTestServlet.SESSION_KEY) != null) {
count++;
}
}
System.out.println(count + " of " + allSessions.size()
+ " sessions have attribute set.");
}
public void attributeReplaced(HttpSessionBindingEvent hsbe) {}
public void sessionCreated(HttpSessionEvent hse) {
allSessions.put(hse.getSession().getId(), session);
}
public void sessionDestroyed(HttpSessionEvent hse) {
allSessions.remove(hse.getSession().getId());
}
}
Các thử nghiệm JMeter đã 100 yêu cầu nhấn servlet mỗi giây:
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.1">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">100</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<longProp name="ThreadGroup.start_time">1327193422000</longProp>
<longProp name="ThreadGroup.end_time">1327193422000</longProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">9080</stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/SESSION_TESTWeb/SessionTestServlet</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
</HTTPSamplerProxy>
<hashTree>
<ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="Constant Timer" enabled="true">
<stringProp name="ConstantTimer.delay">1000</stringProp>
</ConstantTimer>
<hashTree/>
</hashTree>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>true</xml>
<fieldNames>false</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>true</xml>
<fieldNames>false</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
</hashTree>
</jmeterTestPlan>
Khi thử nghiệm này được chạy với ứng dụng web thử nghiệm được triển khai trên WebSphere 7, ứng dụng sẽ nhanh chóng dừng đáp ứng và kết xuất lõi hiển thị điều này:
1LKDEADLOCK Deadlock detected !!!
NULL ---------------------
NULL
2LKDEADLOCKTHR Thread "WebContainer : 2" (0x000000000225C600)
3LKDEADLOCKWTR is waiting for:
4LKDEADLOCKMON sys_mon_t:0x00000000151938C0 infl_mon_t: 0x0000000015193930:
4LKDEADLOCKOBJ com/ibm/ws/session/store/memory/[email protected]/00000000A38EA0D4:
3LKDEADLOCKOWN which is owned by:
2LKDEADLOCKTHR Thread "WebContainer : 1" (0x00000000021FB500)
3LKDEADLOCKWTR which is waiting for:
4LKDEADLOCKMON sys_mon_t:0x0000000015193820 infl_mon_t: 0x0000000015193890:
4LKDEADLOCKOBJ com/ibm/ws/session/store/memory/[email protected]/00000000A14E22CC:
3LKDEADLOCKOWN which is owned by:
2LKDEADLOCKTHR Thread "WebContainer : 2" (0x000000000225C600)
NULL
Dường như khi một sợi (T1) thực hiện phương pháp của servlet doGet() gọi setAttribute() trên các trường hợp thực hiện HttpSession (S1), nó khóa trên màn hình của S1. Trong khi giữ khóa đó, nó đi vào sự lặp lại của allSessions bên trong phương thức attributeAdded() của người nghe và gọi getAttribute(). Nó trông giống như bên trong getAttribute(), WebSphere khóa trên màn hình của cá thể đó (có thể vì nó đang thiết lập một trường lastUpdateTime?). Vì vậy, T1 sẽ lần lượt khóa màn hình của S1, S2, S3, S4, S5 ... tất cả trong khi giữ khóa trên S1 từ cuộc gọi setAttribute() trong servlet. Vì vậy, nếu cùng một lúc một luồng (T2) khác đang khóa trên màn hình phiên khác (S2) trong servlet và sau đó đi vào vòng lặp trong addAttribute(), khóa bế tắc trên màn hình S1 và S2.
tôi đã không thể tìm thấy bất cứ điều gì rõ ràng trong thông số kỹ thuật J2EE về vấn đề này nhưng phần này của Servlet 2.4 đặc tả ngụ ý rằng các thùng chứa không nên đồng bộ hóa trên các trường hợp triển khai HttpSession:
SRV.7.7 .1 Các vấn đề về luồng
Nhiều chuỗi yêu cầu thực thi servlet có thể có quyền truy cập hoạt động một đối tượng phiên duy nhất cùng một lúc. Nhà phát triển có trách nhiệm để đồng bộ hóa quyền truy cập vào tài nguyên phiên như thích hợp.
JBoss không hiển thị bất kỳ deadlocks nào khi chúng tôi chạy thử nghiệm chống lại nó. Vì vậy, câu hỏi của tôi là:
- Sự hiểu biết của tôi có đúng không?
- Nếu có, đây có phải là lỗi hoặc trái với thông số J2EE trong WebSphere không?
- Nếu không, và đó là hành vi hợp lệ mà nhà phát triển nên biết và mã hóa xung quanh, hành vi này có được ghi lại ở bất kỳ đâu không?
Cảm ơn
WS giữ tôi tuyệt vời. Đẹp testcase. Tôi không thể trả lời từ kinh nghiệm hoặc tài nguyên có thẩm quyền, nhưng tôi sẽ không ngạc nhiên khi điều này thực sự là một quirk khác. – BalusC