2013-02-13 2 views
9

같은 인스턴스의 동일한 서블릿이 최대 10 개를 처리 할 수 ​​있도록 GAE 서블릿을 다중 쓰레드로 만들고 싶습니다. (프론트 엔드 인스턴스에서 은 최대 # 개의 스레드가 10 개임) 동시 요청 동시에 다른 사용자로부터 각자의 타임 슬레이브로 전송됩니다.동시 사용자를 처리하기위한 다중 쓰레드 GAE 서블릿

public class MyServlet implements HttpServlet { 
    private Executor executor; 

    @Override 
    public void doGet(HttpServletRequest request, HttpServletResponse response) { 
     if(executor == null) { 
      ThreadFactory threadFactory = ThreadManager.currentRequestFactory(); 
      executor = Executors.newCachedThreadPoolthreadFactory); 
     } 

     MyResult result = executor.submit(new MyTask(request)); 

     writeResponseAndReturn(response, result); 
    } 
} 

그러니까 기본적으로 GAE는, 그것은이 서블릿에 대한 요청을 받으면 처음을 시작하는 Executor이 생성 한 후 저장할 때. 그런 다음 각각의 새 서블릿 요청은 해당 executor를 사용하여 새 스레드를 생성합니다. 분명히 MyTask 안에있는 모든 것은 쓰레드에 안전해야합니다.

나는 이것이 내가 바라는 바를 실제로하는지 아닌지에 대해 우려하고 있습니다. 즉, 이 코드는 동시에 여러 사용자의 여러 요청을 처리 할 수있는 비 차단 서블릿을 만듭니다.? 그렇지 않다면 왜 고쳐야합니까? 그리고 일반적으로, GAE의 마에스트로가 잘못 발견 한 다른 것이 있습니까? 미리 감사드립니다.

+0

+1 - 저는 여러분이 문제에 대해 생각하고있는 방식과 해결책에 접근하려는 방식을 정말 좋아합니다. 그러나 (다행히도) GAE는 이런 식으로 작동하지 않으며'currentRequestThreadFactory()'메소드는 이름을 읽는 방법에 따라 기대하는 바를하지 않습니다. 아래의 대답을 게시하면 메서드 이름의 모호성을 해결할 수 있습니다. (현재 RequestThread-Factory가 아닌 currentRequest-Thread-Factory입니다) –

+0

호기심에서 벗어나십시오 : 달성하고자하는 것이 있습니까? 나는 잠시 뒤로, 나는 똑같은 질문을 들여다 보았 기 때문에 묻고있다. GAE를 사용하여 일종의 긴 폴링 푸시 알림을 구현하기를 희망했지만 그와 같은 방식으로 진행되는 많은 추가 문제가 있음이 밝혀졌습니다. GAE는 요청을 예약하고 처리하는 방식을 조정할 수있는 측면에서 매우 제한적입니다. 그리고 이러한 제한 사항 중 일부는 다소 불분명합니다 ... 물론, 그렇게 빨리 확장 할 수 있습니다 ... –

답변

6

코드가 작동하지 않을 것이라고 생각합니다.

doGet 메서드는 서블릿 컨테이너가 관리하는 스레드에서 실행 중입니다. 요청이 들어 오면 서블릿 스레드가 점유되고 doGet 메서드가 반환 될 때까지 해제되지 않습니다. 코드에서 executor.submitFuture 개체를 반환합니다. 실제 결과를 얻으려면, Future 개체에서 get 메서드를 호출해야하며 MyTask 작업이 완료 될 때까지 차단됩니다. 그 후에 만 ​​doGet 메서드가 반환되고 새 요청이 시작될 수 있습니다.

저는 GAE에 익숙하지 않지만 their docs에 따르면 서블릿을 스레드로부터 안전하다고 선언하면 컨테이너가 각 웹에 여러 요청을 전달합니다 병렬 서버 :

<!-- in appengine-web.xml --> 
<threadsafe>true</threadsafe> 
5

당신은 암시 적으로 두 가지 질문, 그래서 나를 모두 대답하자

1. 어떻게하는 App Engine 인스턴스가 여러 개의 동시 요청을 처리 할 수 ​​있습니까?

당신은 정말 두 가지 작업을 수행해야합니다

  1. 당신이 war\WEB-INF 폴더에서 찾을 수 있습니다 appengine-web.xml 파일로 문 <threadsafe>true</threadsafe>를 추가합니다.
  2. 모든 요청 처리기가 실제로 스레드 안전 내부의 코드, 즉 당신의 doGet(...), doPost(...) 등의 방법으로 로컬 변수를 사용하거나 클래스 또는 전역 변수에 대한 모든 액세스를 동기화해야합니다 있는지 확인합니다.

이는 코드가 스레드 안전입니다 AppEngine에 인스턴스 서버 프레임 워크 말할 것입니다 당신은 동시에 여러 개의 요청을 처리하기 위해 다른 스레드에서 요청 처리기의 모든를 여러 번 호출 할 수있게됩니다. 참고 : AFAIK, 서블릿 단위로 설정할 수 없습니다. 따라서 ALL 서블릿은 스레드로부터 안전해야합니다!

게시자가 게시 한 실행 코드는 이미 각 AppEngine 인스턴스의 서버 코드에 포함되어 있으며 실제로 AppEngine이 만들거나 재사용하는 별도의 스레드 실행 메서드 내부에서 doGet(...) 메서드를 호출합니다. 각 요청. 기본적으로 doGet()은 이미 이며, MyTask()입니다. https://developers.google.com/appengine/docs/java/config/appconfig#Using_Concurrent_Requests

2.이 유용 게시 된 코드 (또는 다른) 목적이다 : (정말 많은 말을하지 않지만)

의 Docs의 관련 부분이 여기에있다?

AppEngine의 현재 양식을 사용하면 요청을 수락하기 위해 자신의 스레드를 만들고 사용할 수 없습니다. 그것은 단지 (이 외부doGet()을 발생하는) 당신은 당신이 언급 한 currentRequestThreadFactory() 방법을 사용하여, 당신의 doGet(...) 핸들러 내부 스레드 을 만들 수 있지만 병렬로 두 번째를 허용하도록이 하나 개의 요청에 대한 병렬 처리를 수행하지 할 수 있습니다.

이름이 currentRequestThreadFactory() 인 경우 약간의 혼란을 줄 수 있습니다. 그렇다고해서 currentFactoryRequestThreads, 즉 요청을 처리하는 스레드를 반환한다는 의미는 아닙니다. 이는 안에 Threads을 생성 할 수있는 Factory을 반환한다는 것을 의미합니다. 불행하게도 실제로는 doGet() 실행 범위를 넘어 반환 된 ThreadFactory를 사용하는 것은 허용되지 않습니다. 예를 들어, Executor를 기반으로 Executor를 작성하고 클래스 변수에 유지하는 것이 좋습니다.

프론트 엔드 인스턴스의 경우 doGet() 메서드가 반환 될 때 doGet() 호출 내에서 만든 모든 스레드가 즉시 종료됩니다. 백엔드 인스턴스의 경우 계속 실행중인 스레드를 작성할 수 있지만 이러한 스레드 내에서 요청을 수락하기 위해 서버 소켓을 열 수 없기 때문에 여전히 요청 처리를 관리 할 수 ​​없습니다. 합니다 (스레드 섹션 구체적으로) 완전성에 대해

은, 어디 보자

The Java Servlet Environment - The Sandbox :

당신은 당신이 는 여기에서 appengine 서블릿 내부하지 할 수 있는지에 대한 자세한 정보를 찾을 수 있습니다 귀하의 코드가 "합법적"인 방법 :

다음은 작동해야하지만 코드가 여러 요청을 동시에 처리 할 수 ​​있다는 점에서 차이를 만들지는 않습니다. 이는 appengine-web.xml의 <threadsafe>true</threadsafe> 설정에 의해서만 결정됩니다. 따라서 기술적으로이 코드는 실제로 비효율적이며 본질적으로 선형 프로그램 흐름을 두 스레드로 분할합니다.당신은 당신이 현재 처리중인 요청에 특정한 별도의 스레드 내부에 이미 있기 때문에

public class MyServlet implements HttpServlet { 

    @Override 
    public void doGet(HttpServletRequest request, HttpServletResponse response) { 
     ThreadFactory threadFactory = ThreadManager.currentRequestThreadFactory(); 
     Executor executor = Executors.newCachedThreadPool(threadFactory); 

     Future<MyResult> result = executor.submit(new MyTask(request)); // Fires off request handling in a separate thread 

     writeResponse(response, result.get()); // Waits for thread to complete and builds response. After that, doGet() returns 
    } 
} 

, 당신은 확실히 "스레드 내부 스레드"너 자신을 저장해야합니다 단순히이 대신 수행하지만 여기 어쨌든입니다 :

public class MyServlet implements HttpServlet { 

    @Override 
    public void doGet(HttpServletRequest request, HttpServletResponse response) { 
     writeResponse(response, new MyTask(request).call()); // Delegate request handling to MyTask object in current thread and write out returned response 
    } 
} 

또는 더 나은 방법은 MyTask.call()에서 doGet() 메소드로 코드를 이동하는 것입니다. 이보다 쉽게 ​​구글이 자신의 서버에 부하를 제어 할 수있는 (일시적으로) 디자인 결정 (특히 메모리입니다

: -)

을 제외하고

당신이 언급 한 10 개 개의 동시 서블릿 스레드의 한계에 대해서 서블릿 사용).

현재 이러한 문제에 대한 토론을 찾을 수

:

이 항목은 내가 때문에, 너무 나를 밖으로 지옥 도청되었습니다 울트라 린 서블릿 코드를 강력하게 믿는 사람이기 때문에 평소의 서블릿은 수천 가지가 아니라도 수백 개의 동시 요청을 쉽게 처리 할 수 ​​있습니다. 인스턴스 당 10 개의 스레드를 임의로 제한하여 더 많은 인스턴스를 지불해야하는 것은 최소한 저에게 거의 성가신 일이 아닙니다. 위에 게시 한 링크를 통해 읽는다면, 그들은이 사실을 알고 있고 더 나은 해결책을 찾고있는 것 같습니다. 그래서, ...의 구글 I/O 2013 월에 무슨 일이 일어날 공고 참조 :

2

하자 내가 두 번째 에릭슨마르쿠스 A의 평가를 어떤 이유로

그러나 만약, (또는 당신이 출발점으로 당신의 코드를 사용하는 경로를 따라 할) 다른 시나리오, 나는 당신이 당신의 집행자 정의를 변경하는 것이 좋을 것 :이 인스턴스에서 정적된다

private static Executor executor; 

있도록.