2013-02-13 10 views
9

Chciałbym wielowątkowe serwlety GAE tak, aby ten sam serwlet na tym samym wystąpieniu mógł obsłużyć do 10 (na instancji frontend I wierzę max # wątków jest 10) współbieżnych żądań od różnych użytkowników w tym samym czasie, mnożąc czas między nimi.Serwlety GAE wielowątkowe do obsługi równoczesnych użytkowników

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); 
    } 
} 

Więc zasadniczo kiedy GAE jest uruchamiany po raz pierwszy robi się żądanie do tego apletu, Executor jest tworzony i następnie zapisany. Następnie każde nowe żądanie serwletu używa tego executora do odrodzenia nowego wątku. Oczywiście wszystko wewnątrz MyTask musi być wątkowo bezpieczne.

Martwi mnie to, czy to naprawdę robi to, co mam nadzieję, że robi. To znaczy, , czy ten kod tworzy nieblokujący serwlet, który może obsługiwać wiele żądań od wielu użytkowników w tym samym czasie? Jeśli nie, dlaczego i co muszę zrobić, aby to naprawić? A ogólnie rzecz biorąc, czy jest coś jeszcze, co maestro GAE może zauważyć, że jest martwe? Z góry dziękuję.

+0

+1 - Bardzo podoba mi się sposób, w jaki myślisz o problemie i sposobie, w jaki próbujesz podejść do rozwiązania. Ale (nie-) na szczęście GAE nie działa w ten sposób, a metoda 'currentRequestThreadFactory()' nie całkiem robi to, czego można się spodziewać na podstawie tego, jak można odczytać jej nazwę. Poniżej zamieściłem odpowiedź, która, mam nadzieję, rozwiąże niejednoznaczność nazwy metody. (It's currentRequest-Thread-Factory, a nie current-RequestThread-Factory);) –

+0

Po prostu z ciekawości: Czy jest coś konkretnego, co próbujesz osiągnąć? Pytam, ponieważ chwilę temu szukałem dokładnie tego samego pytania. Miałem nadzieję, że zaimplementuję coś w rodzaju długich powiadomień push przy użyciu GAE, ale okazało się, że istnieje wiele innych dodatkowych problemów, które staną ci na drodze z czymś takim. GAE jest naprawdę dość ograniczony pod względem możliwości dostosowania sposobu, w jaki planuje i obsługuje zgłoszenia. Niektóre z tych ograniczeń są nieco niejasne ... Ale, oczywiście, to pozwala na to, że jest tak szybki i skalowalny ... –

Odpowiedz

6

Nie sądzę, aby Twój kod działał.

Metoda doGet działa w wątkach zarządzanych przez kontener serwletu. Gdy przychodzi żądanie, wątek serwletu jest zajęty i nie zostanie zwolniony, dopóki nie zwróci się metoda doGet. W twoim kodzie executor.submit zwróci obiekt Future. Aby uzyskać rzeczywisty wynik, należy wywołać metodę get na obiekcie Future, która będzie blokować, dopóki nie zakończy się zadanie MyTask. Dopiero po tym, metoda doGet powroty i nowe wnioski można kopać.

nie jestem zaznajomiony z GAE, ale według their docs można zadeklarować swój serwlet jako bezpieczny wątku, a następnie pojemnik wyśle ​​wiele żądań do każdej sieci serwer równolegle:

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

niejawnie dwa pytania, więc pozwól mi odpowiedzieć zarówno:

1. Jak mogę dostać mój AppEngine instancji do obsługi wielu jednoczesnych żądań?

Naprawdę tylko trzeba zrobić dwie rzeczy:

  1. Dodaj oświadczenie <threadsafe>true</threadsafe> do pliku appengine-web.xml, który można znaleźć w folderze war\WEB-INF.
  2. Upewnij się, że kod wewnątrz wszystkie Twoje zapytanie koparki jest rzeczywiście bezpieczny wątku, to znaczy tylko używać zmiennych lokalnych w swoim doGet(...), doPost(...) itp metod lub upewnij się zsynchronizować wszystkie dostępu do klasy lub zmiennych globalnych.

to powie usĹ,udze App Engine ram serwera instancja, że ​​kod jest bezpieczny wątku i że jesteś pozwalając mu zadzwonić wszystko swojego zgłoszenia teleskopowe wiele razy w różnych wątkach obsłużyć kilka żądań jednocześnie. Uwaga: AFAIK, Nie jest możliwe ustawienie tego na podstawie serwletu. Więc, twoje serwlety muszą być bezpieczne dla wątków!

W związku z tym opublikowany kod wykonawczy jest już zawarty w kodzie serwera każdej instancji AppEngine i faktycznie wywołuje metodę doGet(...) z metody uruchamiania oddzielnego wątku tworzonego (lub ponownego użycia) przez AppEngine dla każde żądanie. Zasadniczo doGet() już jest swój MyTask().

Odpowiednia część Dokumentów jest tutaj (choć tak naprawdę nie wiele powiedzieć): https://developers.google.com/appengine/docs/java/config/appconfig#Using_Concurrent_Requests

2. Czy pisał kod przydatna dla (lub jakiegokolwiek innego) cel?

AppEngine w swojej obecnej formie nie pozwala tworzyć własnych wątków i używać ich do akceptowania żądań. To tylko pozwala tworzyć wątki wewnątrz programu obsługi doGet(...) metodą currentRequestThreadFactory() wzmiance, ale tylko zrobić przetwarzania równoległego dla tego jednego wniosku i nie do zaakceptowania drugi równolegle (to się dzieje pozadoGet()).

Nazwa currentRequestThreadFactory() może być tu trochę myląca. Nie oznacza to, że zwróci onaz RequestThreads, tj. Wątki, które obsługują żądania. Oznacza to, że zwraca on kod Factory, który może utworzyć Threads wewnątrz currentRequest. Tak więc, niestety, nie można nawet używać zwróconego ThreadFactory poza zakres obecnego wykonania doGet(), tak jak sugerujesz, tworząc Executor na jego podstawie i utrzymując go w zmiennej klasy.

W przypadku instancji frontend wszelkie wątki tworzone wewnątrz połączenia doGet() zostaną zakończone natychmiast po zwróceniu metody doGet(). W przypadku instancji backendowych możesz tworzyć wątki, które nadal działają, ale ponieważ nie masz uprawnień do otwierania gniazd serwera w celu przyjmowania żądań w tych wątkach, nie będą one w stanie samodzielnie zarządzać obsługą żądań.

można znaleźć więcej szczegółów na temat tego, co może i nie można zrobić wewnątrz serwletu interfejsu App Engine tutaj:

The Java Servlet Environment - The Sandbox (konkretnie wątki fragment)

Dla kompletności, zobaczmy w jaki sposób Twój kod może być "legalny":

The Następujące powinny działać, ale nie będzie to miało wpływu na to, że twój kod będzie w stanie obsłużyć wiele żądań równolegle. Będzie to określone wyłącznie przez ustawienie <threadsafe>true</threadsafe> w twoim appengine-web.xml. Tak więc, technicznie, ten kod jest po prostu bardzo nieefektywny i dzieli zasadniczo liniowy przebieg programu na dwa wątki.Ale tutaj jest tak czy inaczej:

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 
    } 
} 

Ponieważ jesteś już wewnątrz osobnym wątku, który jest specyficzny dla żądanie aktualnie obsługi, powinno się oszczędzić sobie „gwint wewnątrz wątku” i po prostu to zrobić w zamian:

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 
    } 
} 

Albo, jeszcze lepiej, po prostu przenieś kod z MyTask.call() do metody doGet(). ;)

bok - Jeśli chodzi o limit 10 jednoczesnych wątków serwletów Państwo wymienić:

Jest to (tymczasowe) projekt-decyzja, która pozwala Google kontrolować obciążenie serwerów łatwiej (konkretnie pamięć użycie serwletów).

Można znaleźć więcej dyskusji na temat tych kwestii tutaj:

Ten wątek został ubzdurałeś do cholery z mnie też, bo jestem zwolennikiem ultra-chudego kodu serwletu, więc moje zwykłe serwlety mogłyby z łatwością obsłużyć setki, jeśli nie tysiące, równoczesnych żądań. Konieczność płacenia za więcej instancji z powodu tego arbitralnego limitu 10 wątków na instancję jest dla mnie trochę irytująca. Ale czytając powyższe linki, wydaje mi się, że są tego świadomi i pracują nad lepszym rozwiązaniem. Więc zobaczmy co Komunikaty Google I/O 2013 przyniesie w maju ... :)

2

I drugi z oceny Ericson i Markus A.

Jeśli jednak z jakiegoś powodu (lub z jakiegoś innego scenariusza) chcesz podążać ścieżką, która wykorzystuje swój fragment kodu jako punkt wyjścia, chciałbym zasugerować, że po zmianie definicji Executora do:

private static Executor executor; 

tak, że staje się statyczne całej instancji.

Powiązane problemy