我正面臨着一個奇怪的問題與氣氛,並不能如何解決它。 我試圖實現服務器推送通知。 Notofications應廣播給所有連接的客戶端(最多10個,它是Intranet Web應用程序),屬於ANDROID 4.2瀏覽器。Spring + Atmosphere + Tomcat線程泄漏
通知工作正常,所有的東西都被推送,但氣氛創建了大量的tomcat線程,最終在約3-4k頁面請求後發生線程泄漏。 Tomcat的7.0.40如果構造成與連接器NIO,用150個最大線程數和超時的60000
web.xml中:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.XmlWebApplicationContext</param-value>
</context-param>
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<!-- Hibernate -->
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- SECURITY -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- char encoding -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Declare a DispatcherServlet as usual -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.atmosphere.cpr.MeteorServlet</servlet-class>
<init-param>
<param-name>org.atmosphere.servlet</param-name>
<param-value>org.springframework.web.servlet.DispatcherServlet</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.cpr.asyncSupport</param-name>
<param-value>org.atmosphere.container.Tomcat7BIOSupportWithWebSocket</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.cpr.broadcasterClass</param-name>
<param-value>org.atmosphere.cpr.DefaultBroadcaster</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.cpr.broadcasterCacheClass</param-name>
<param-value>org.atmosphere.cache.UUIDBroadcasterCache</param-value>
</init-param>
<!-- <init-param> -->
<!-- <param-name>org.atmosphere.cpr.sessionSupport</param-name> -->
<!-- <param-value>true</param-value> -->
<!-- </init-param> -->
<init-param>
<param-name>org.atmosphere.resumeOnBroadcast</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.cpr.broadcaster.shareableThreadPool</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.cpr.broadcaster.maxProcessingThreads</param-name>
<param-value>20</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.cpr.broadcaster.maxAsyncWriteThreads</param-name>
<param-value>20</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.useNative</param-name>
<param-value>true</param-value>
</init-param>
<!-- <init-param> -->
<!-- <param-name>org.atmosphere.useBlocking</param-name> -->
<!-- <param-value>false</param-value> -->
<!-- </init-param> -->
<init-param>
<param-name>org.atmosphere.useStream</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.cpr.AtmosphereInterceptor</param-name>
<param-value>org.atmosphere.interceptor.HeartbeatInterceptor</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.interceptor.HeartbeatInterceptor.heartbeatFrequencyInSeconds</param-name>
<param-value>60</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.cpr.broadcasterLifeCyclePolicy</param-name>
<param-value>EMPTY_DESTROY</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Default page to serve -->
<display-name>cielo-cp</display-name>
<welcome-file-list>
<welcome-file>/</welcome-file>
</welcome-file-list>
<session-config>
<session-timeout>1440</session-timeout>
</session-config>
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/accessDenied</location>
</error-page>
</web-app>
Spring MVC的控制器:
@Controller
public class PushController extends AbstractController{
@ResponseBody
@RequestMapping(value="/push")
public void pushAsync(AtmosphereResource atmosphereResource){
AtmosphereUtils.suspend(atmosphereResource);
}
}
BroadcasterService:
@Service
public class BroadcasterService {
@Autowired
private PushNotificationService service;
private ObjectMapper mapper = new ObjectMapper();
public void receiveMessage(@Observes PushEvent e) {
List<PushableMessage> messages = service.pollMessages(e.getType());
try {
Broadcaster b = AtmosphereUtils.lookupBroadcaster(false);
try {
b.broadcast(mapper.writeValueAsString(messages));
} catch(Exception ex){
logger.error(ex.getMessage(), ex);
}
} catch (Throwable t){
logger.debug(t.getMessage(), t);
}
}
}
and
@Service
public class PushNotificationService {
private static Logger logger = Logger.getLogger(PushNotificationService.class);
@Autowired
private Event<PushEvent> event;
public List<PushableMessage> pollMessages(TipologiaPush key) {
....
return result;
}
public void pushMessage(PushableMessage message){
...
queue.put(message);
event.fire(message.getEvent());
}
}
和utils的:
public final class AtmosphereUtils {
public static AtmosphereResource getAtmosphereResource(HttpServletRequest request) {
return getMeteor(request).getAtmosphereResource();
}
public static Meteor getMeteor(HttpServletRequest request) {
return Meteor.build(request);
}
public static void suspend(final AtmosphereResource resource) {
final CountDownLatch countDownLatch = new CountDownLatch(1);
resource.addEventListener(new AtmosphereResourceEventListenerAdapter() {
@Override
public void onSuspend(AtmosphereResourceEvent event) {
countDownLatch.countDown();
logger.debug("Suspending Client..." + resource.uuid() + " with transport " + resource.transport());
resource.removeEventListener(this);
}
@Override
public void onDisconnect(AtmosphereResourceEvent event) {
logger.debug("Disconnecting Client..." + resource.uuid());
super.onDisconnect(event);
}
});
if (AtmosphereResource.TRANSPORT.LONG_POLLING.equals(resource.transport())) {
resource.resumeOnBroadcast(true).suspend();
} else {
resource.suspend();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
logger.error("Interrupted while trying to suspend resource {}", resource);
}
AtmosphereUtils.lookupBroadcaster(true).addAtmosphereResource(resource);
}
public static Broadcaster lookupBroadcaster(boolean create) {
Broadcaster b = BroadcasterFactory.getDefault().lookup("/*", create);
return b;
}
}
其他服務呼叫:notificationService.pushMessage(....); 的JavaScript部分是:
var socket = $.atmosphere;
function handleAtmosphere(url, handleResult) {
var request = new $.atmosphere.AtmosphereRequest();
request.transport = "websocket"; // "streaming is even worse";
request.url = url;
request.contentType = "application/json";
request.fallbackTransport = "long-polling"; //for android 4.2, default browser don't support websocket
request.onMessage = function(response){
....
};
var subSocket = socket.subscribe(request);
}
他這樣說,我已經嘗試了很多配置,但還是有些線程保持活躍。 我使用Spring 3.2.5,SpringSec 3.2 RC2和大氣2.0.4
請張貼片段,而不是所有的腳本,如果你這樣做,你的問題得到解決的機會將增加。 –
我沒有經驗的氣氛。也就是說,大量線程的原因之一可能是線程沒有被釋放回來。如果有人在提供請求後仍然拒絕,那麼就是我要檢查的代碼/配置。我無法檢查你提供的所有代碼,但是我會檢查你是否處理/管理任何線程或任何氣氛線程,請先檢查它們。 – prabugp
好的,我刪除了不相關的代碼。 –