Reactive Web - Servlet & Async, Non-blocking I/O

140

Transcript of Reactive Web - Servlet & Async, Non-blocking I/O

Page 1: Reactive Web - Servlet & Async, Non-blocking I/O
Page 2: Reactive Web - Servlet & Async, Non-blocking I/O
Page 3: Reactive Web - Servlet & Async, Non-blocking I/O

!"

#$

%

'

Page 4: Reactive Web - Servlet & Async, Non-blocking I/O

'

Servlet 2.2

JSP 1.1

Servlet 2.3

JSP 1.2 JSTL 1.0

Servlet 2.4

JSP 2.0 JSTL 1.1

Servlet 2.5

JSP 2.1 EL 2.1 JSTL 1.2

Servlet 3.0

JSP 2.2 EL 2.2 JSTL 1.2

Servlet 3.1

JSP 2.3 EL 3.0 JSTL 1.2

Page 5: Reactive Web - Servlet & Async, Non-blocking I/O

'

!

#%

!

%$

(

%

%

(

"

)

Page 6: Reactive Web - Servlet & Async, Non-blocking I/O

'

"

#$

%

!*

*

GET /hello?name=arwan HTTP/1.1

<html> … </html>

TomcatUndertowJetty

HttpServletRequest HttpServletResponse

@WebServlet(url

Patterns = {"/he

llo"})

class HelloServ

let extends Http

Servlet { … }

Page 7: Reactive Web - Servlet & Async, Non-blocking I/O
Page 8: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 9: Reactive Web - Servlet & Async, Non-blocking I/O

!"

#$

'

#

#

#

Page 10: Reactive Web - Servlet & Async, Non-blocking I/O

!"

#$

'

#

#

#

$

$

$

{ “content”: “…”}

Page 11: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 12: Reactive Web - Servlet & Async, Non-blocking I/O

'

! #

Page 13: Reactive Web - Servlet & Async, Non-blocking I/O

'

var es = new EventSource(‘/notification/stream');

es.onmessage = function (event) { // };

es.addEventListener(‘feed-notify', function(event) { // ‘feed-notify' console.log(event.data); }, false);

es.onerror = function (event) { // };

Page 14: Reactive Web - Servlet & Async, Non-blocking I/O

'

@WebServlet("/notification/stream") public class NotificationStreamServlet extends HttpServlet {

final static Log log = LogFactory.getLog(NotificationStreamServlet.class); @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception { log.info("in notification/stream"); response.setCharacterEncoding("utf-8"); response.setContentType("text/event-stream"); try { NotificationStream notificationStream = new NotificationStream(obtainUsername(request)); Iterator<Notification> notifies = notificationStream.feedNotifies(); while (notifies.hasNext()) { Notification notification = notifies.next(); ServletOutputStream outputStream = response.getOutputStream(); outputStream.write(("event: " + notification.getEvent() + "\n").getBytes()); outputStream.write(("data: " + notification.getData() + "\n\n").getBytes()); outputStream.flush(); } } catch (Exception error) { log.error("error notification/stream : " + error.getMessage()); } log.info("out notification/stream”); } String obtainUsername(HttpServletRequest request) { return request.getParameter("username"); }}

Page 15: Reactive Web - Servlet & Async, Non-blocking I/O

'

class FeedService { final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed"; final ObjectMapper MAPPER = new ObjectMapper(); FeedNotify getFeedNotify(String username) { try { URL url = new URL(String.format(FEED_NOTIFY_URL, username)); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setRequestMethod("GET"); urlConnection.setRequestProperty("Accept", "application/json"); urlConnection.setConnectTimeout(3000); urlConnection.setReadTimeout(3000); return MAPPER.readValue(urlConnection.getInputStream(), FeedNotify.class); } catch (IOException error) { throw new ServiceOperationException(error); } }}

// FeedService , FeedService feedService = new FeedService();String username = "guest"; Iterator<Notification> feedNotifies = Stream.generate(() -> feedService.getFeedNotify(username)) .map(Notification::of) .iterator();while (feedNotifies.hasNext()) { Notification next = feedNotifies.next(); System.out.println(next);}

Page 16: Reactive Web - Servlet & Async, Non-blocking I/O
Page 17: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 18: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 19: Reactive Web - Servlet & Async, Non-blocking I/O

'

!

#!

)

%

"$

)

"$

Page 20: Reactive Web - Servlet & Async, Non-blocking I/O

'

!

!

!

%%$

(

Page 21: Reactive Web - Servlet & Async, Non-blocking I/O
Page 22: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 23: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 24: Reactive Web - Servlet & Async, Non-blocking I/O

'

!

!

!

%%$

(!

Page 25: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 26: Reactive Web - Servlet & Async, Non-blocking I/O

'

@WebServlet(urlPatterns = "/async", asyncSupported = true) public class SimpleAsyncServlet extends HttpServlet { protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception { // AsyncContext asyncContext = request.startAsync(request, response); // asyncContext.setTimeout(60 * 1000); // asyncContext.addListener(new AsyncListener() { // public void onComplete(AsyncEvent event) throws IOException { … } // AsyncContext.setTimeout() public void onTimeout(AsyncEvent event) throws IOException { … } // public void onError(AsyncEvent event) throws IOException { … } // public void onStartAsync(AsyncEvent event) throws IOException { … } }); // asyncContext.start(new Runnable() { public void run() { // asyncContext.complete(); // } }); }}

Page 27: Reactive Web - Servlet & Async, Non-blocking I/O

'

@WebServlet(urlPatterns = "/notification/stream/async", asyncSupported = true) public class AsyncNotificationStreamServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception { log.info("in notification/stream/async"); response.setCharacterEncoding("utf-8"); response.setContentType("text/event-stream"); AsyncContext asyncContext = request.startAsync(); asyncContext.setTimeout(30000); asyncContext.addListener(new AsyncListener() { public void onComplete(AsyncEvent event) throws IOException { … } public void onTimeout(AsyncEvent event) throws IOException { … } public void onError(AsyncEvent event) throws IOException { … } public void onStartAsync(AsyncEvent event) throws IOException { … } }); asyncContext.start(() -> { try { NotificationStream notificationStream = new NotificationStream(obtainUsername(request)); Iterator<Notification> notifies = notificationStream.feedNotifies(); while (notifies.hasNext() && !asyncDone.get()) { Notification notification = notifies.next(); ServletOutputStream outputStream = asyncContext.getResponse().getOutputStream(); outputStream.write(("event: " + notification.getEvent() + "\n").getBytes()); outputStream.write(("data: " + notification.getData() + "\n\n").getBytes()); outputStream.flush(); } } catch (Exception error) { log.error("error notification/stream/async - " + error.getMessage()); } finally { asyncContext.complete(); } }); log.info("out notification/stream/async"); } }

Page 28: Reactive Web - Servlet & Async, Non-blocking I/O
Page 29: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 30: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 31: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 32: Reactive Web - Servlet & Async, Non-blocking I/O

!"

#$

'

#

#

#

!!!!!!

Page 33: Reactive Web - Servlet & Async, Non-blocking I/O
Page 34: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 35: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NotifyApi { static Log LOG = LogFactory.getLog(NotifyApi.class); final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed"; final String FRIEND_NOTIFY_URL = "http://localhost:9000/user/%s/friend-recommendation"; final ObjectMapper MAPPER = new ObjectMapper(); FeedNotify getFeedNotify(String username) { LOG.info("FeedNotify ."); try { URL url = new URL(String.format(FEED_NOTIFY_URL, username)); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); FeedNotify feedNotify = MAPPER.readValue(urlConnection.getInputStream(), FeedNotify.class); LOG.info("FeedNotify ."); return feedNotify; } catch (IOException error) { throw new ServiceOperationException(error); } } FriendRecommendationNotify getFriendRecommendationNotify(String username) { LOG.info("FriendRecommendationNotify ."); try { URL url = new URL(String.format(FRIEND_NOTIFY_URL, username));

// LOG.info("FriendRecommendationNotify ."); return notify; } catch (IOException error) { throw new ServiceOperationException(error); } }}

Page 36: Reactive Web - Servlet & Async, Non-blocking I/O

'

public class SynchronousCallExample { public static void main(String[] args) { NotifyApi notifyApi = new NotifyApi(); String username = "guest"; FeedNotify feedNotify = notifyApi.getFeedNotify(username); // feedNotify FriendRecommendationNotify friendNotify = notifyApi.getFriendRecommendationNotify(username); // friendNotify

} }

Page 37: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 38: Reactive Web - Servlet & Async, Non-blocking I/O

'

FeedNotify

getFeedNotify(username)

HTTP /

FriendRecommendationNotify

getFriendRecommendationNotify(username)

HTTP /

Page 39: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 40: Reactive Web - Servlet & Async, Non-blocking I/O

'

public class AsynchronousCallByThreadExample { static Log LOG = LogFactory.getLog(AsynchronousCallByThreadExample.class); public static void main(String[] args) throws Exception { NotifyApi notifyApi = new NotifyApi(); String username = "guest";

Thread feedThread = new Thread(new Runnable() { @Override public void run() { FeedNotify feedNotify = notifyApi.getFeedNotify(username); LOG.info(feedNotify); } }); feedThread.start();

Thread friendThread = new Thread(() -> { FriendRecommendationNotify friendNotify = notifyApi.getFriendRecommendationNotify(username); LOG.info(friendNotify); }); friendThread.start(); }}

Page 41: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 42: Reactive Web - Servlet & Async, Non-blocking I/O

'

)

Page 43: Reactive Web - Servlet & Async, Non-blocking I/O

'

<<Interface>>

Executor

<<Interface>>

ExecutorService

<<Interface>>

ScheduledExecutorService

ForkJoinPool

AbstractExecutorService

ThreadPoolExecutor

ScheduledThreadPoolExecutor

ExecutorService executorService = Executors.newFixedThreadPool(4);

Page 44: Reactive Web - Servlet & Async, Non-blocking I/O

'

public class AsynchronousCallByExecutorExample { static Log LOG = LogFactory.getLog(AsynchronousCallByExecutorExample.class); public static void main(String[] args) throws Exception { NotifyApi notifyApi = new NotifyApi(); String username = "guest"; Executor executor = Executors.newFixedThreadPool(4); executor.execute(new Runnable() { @Override public void run() { FeedNotify feedNotify = notifyApi.getFeedNotify(username); LOG.info(feedNotify); } });

executor.execute(() -> { FriendRecommendationNotify friendNotify = notifyApi.getFriendRecommendationNotify(username); LOG.info(friendNotify); }); } }

Page 45: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 46: Reactive Web - Servlet & Async, Non-blocking I/O

'

ExecutorService executorService = Executors.newCachedThreadPool();

Future<String> future = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { // return "asynchronous call"; } });

String result = future.get(1, TimeUnit.SECONDS);

Page 47: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NotifyApi { static Log LOG = LogFactory.getLog(NotifyApi.class); final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed"; final String FRIEND_NOTIFY_URL = "http://localhost:9000/user/%s/friend-recommendation"; final ExecutorService executorService = Executors.newCachedThreadPool(); final ObjectMapper objectMapper = new ObjectMapper();

FeedNotify getFeedNotify(String username) { … } Future<FeedNotify> getFeedNotifyForFuture(String username) { return executorService.submit(new Callable<FeedNotify>() { @Override public FeedNotify call() throws Exception { return getFeedNotify(username); } }); }

FriendRecommendationNotify getFriendRecommendationNotify(String username) { … } Future<FriendRecommendationNotify> getFriendRecommendationNotifyForFuture(String username) { return executorService.submit(() -> getFriendRecommendationNotify(username)); }}

Page 48: Reactive Web - Servlet & Async, Non-blocking I/O

'

NotifyApi notifyApi = new NotifyApi();String username = "guest"; Future<FeedNotify> feedFuture = notifyApi.getFeedNotifyForFuture(username);Future<FriendRecommendationNotify> friendFuture = notifyApi.getFriendRecommendationNotifyForFuture(username);

for (;;) { if (feedFuture.isDone()) { FeedNotify feedNotify = feedFuture.get(); // feedNotify break; }

// // feedFuture.cancel(true); LOG.info("FeedNotify ."); Thread.sleep(100);}

FriendRecommendationNotify friendNotify = friendFuture.get(1, TimeUnit.SECONDS); // friendNotify

Page 49: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 50: Reactive Web - Servlet & Async, Non-blocking I/O

'

getFeedNotify(username)

HTTP /

feedFuture

HTTP /

friendFuture

getFriendRecommendationNotify(username)

feedFuture.isDone() or cancel()

feedFuture.get()

friendFuture.get()

Page 51: Reactive Web - Servlet & Async, Non-blocking I/O

'

NotifyApi notifyApi = new NotifyApi();String username = "elton";

Future<User> userFuture = notifyApi.getUserForFuture(username);for (;;) { if (userFuture.isDone()) { User user = userFuture.get(); FeedNotify feedNotify = notifyApi.getFeedNotifyForFuture(user) .get(1, TimeUnit.SECONDS); // break; } LOG.info("User ."); Thread.sleep(100);}

Page 52: Reactive Web - Servlet & Async, Non-blocking I/O

'

NotifyApi notifyApi = new NotifyApi();String username = "guest";

notifyApi.getFeedNotify(username, new CompletionHandler<FeedNotify>() { @Override public void onSuccess(FeedNotify result) { // } @Override public void onFailure(Throwable ex) { // } });

Page 53: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NotifyApi {

// interface CompletionHandler<R> extends SuccessCallback<R>, FailureCallback {

}

// interface SuccessCallback<R> { void onSuccess(R result); }

// interface FailureCallback { void onFailure(Throwable ex); }

}

Page 54: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NotifyApi { static Log LOG = LogFactory.getLog(NotifyApi.class); final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed"; final String FRIEND_NOTIFY_URL = "http://localhost:9000/user/%s/friend-recommendation"; final ExecutorService executorService = Executors.newCachedThreadPool(); final ObjectMapper objectMapper = new ObjectMapper(); FeedNotify getFeedNotify(String username) { … } void getFeedNotify(String username, CompletionHandler<FeedNotify> completionHandler) { executorService.execute(new Runnable() { @Override public void run() { try { completionHandler.onSuccess(getFeedNotify(username)); } catch (Exception error) { completionHandler.onFailure(error); } } }); }

FriendRecommendationNotify getFriendRecommendationNotify(String username) { … } void getFriendRecommendationNotify(String username, CompletionHandler<FriendRecommendationNotify> handler) { … }}

Page 55: Reactive Web - Servlet & Async, Non-blocking I/O

'

NotifyApi notifyApi = new NotifyApi();String username = "guest"; notifyApi.getFeedNotify(username, new CompletionHandler<FeedNotify>() { @Override public void onSuccess(FeedNotify result) { // } @Override public void onFailure(Throwable ex) { // } });notifyApi.getFriendRecommendationNotify(username, new CompletionHandler<FriendRecommendationNotify>() { @Override public void onSuccess(FriendRecommendationNotify result) { } @Override public void onFailure(Throwable ex) { } });

Page 56: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 57: Reactive Web - Servlet & Async, Non-blocking I/O

'

execute callback

getFeedNotify(username)

HTTP /

execute callback

getFriendRecommendationNotify(username)

HTTP /

Page 58: Reactive Web - Servlet & Async, Non-blocking I/O

'

NotifyApi notifyApi = new NotifyApi();String username = "elton";

notifyApi.getUser(username, new NotifyApi.CompletionHandler<User>() { @Override public void onSuccess(User user) {

notifyApi.getFeedNotify(user, new NotifyApi.CompletionHandler<FeedNotify>() { @Override public void onSuccess(FeedNotify feedNotify) { // } @Override public void onFailure(Throwable error) { // } });

} @Override public void onFailure(Throwable error) { // } });

Page 59: Reactive Web - Servlet & Async, Non-blocking I/O

'

CompletableFuture.supplyAsync(() -> { // return "async task"; }).thenApply(result -> { // return result.length();}).exceptionally(error -> { // return Integer.MIN_VALUE; }).thenAccept(length -> { // LOG.info("Length : " + length);});

Page 60: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NotifyApi { static Log LOG = LogFactory.getLog(NotifyApi.class); final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed"; final String FRIEND_NOTIFY_URL = "http://localhost:9000/user/%s/friend-recommendation"; final ExecutorService executorService = Executors.newCachedThreadPool(); final ObjectMapper objectMapper = new ObjectMapper();

FeedNotify getFeedNotify(String username) { … } CompletableFuture<FeedNotify> getFeedNotifyForCF(String username) { return CompletableFuture.supplyAsync(new Supplier<FeedNotify>() { @Override public FeedNotify get() { return getFeedNotify(username); } }, executorService); }

FriendRecommendationNotify getFriendRecommendationNotify(String username) { … } CompletableFuture<FriendRecommendationNotify> getFriendRecommendationNotifyForCF(String username) { return CompletableFuture.supplyAsync(() -> getFriendRecommendationNotify(username), executorService); }}

Page 61: Reactive Web - Servlet & Async, Non-blocking I/O

'

NotifyApi notifyApi = new NotifyApi();String username = "guest"; notifyApi.getFeedNotifyForCF(username).thenAccept(feedNotify -> { // feedNotify });

notifyApi.getFriendRecommendationNotifyForCF(username).thenAccept(friendNotify -> { // friendNotify });// 2 notifyApi.getUserForCF(username) .thenCompose(notifyApi::getFeedNotifyForCF) .thenAccept(notifications -> { // });

Page 62: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 63: Reactive Web - Servlet & Async, Non-blocking I/O

'

)

%

%

%

"

Page 64: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NotificationStreamObservable { ExecutorService executorService = Executors.newFixedThreadPool(1); Future<?> future = new FutureTask<>(() -> {}, null); NotifyApi notifyApi = new NotifyApi(); String username; List<NotificationStreamObserver> observers = new CopyOnWriteArrayList<>(); NotificationStreamObservable(String username) { this.username = username; } void register(NotificationStreamObserver observer) { observers.add(observer); } void unregister(NotificationStreamObserver observer) { observers.remove(observer); } void subscribe() { future = executorService.submit(() -> { boolean running = true; while (running) { Notification feedNotify = Notification.of(notifyApi.getFeedNotify(username)); observers.forEach(observer -> observer.onNotification(feedNotify));

// } }); } void unsubscribe() { future.cancel(true); observers.clear(); }}

Page 65: Reactive Web - Servlet & Async, Non-blocking I/O

'

interface NotificationStreamObserver { void onNotification(Notification notification);}

String username = "guest"; NotificationStreamObservable observable = new NotificationStreamObservable(username);observable.register(notification -> { // });observable.subscribe();// , // observable.unsubscribe();

Page 66: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 67: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 68: Reactive Web - Servlet & Async, Non-blocking I/O

'

@WebServlet(urlPatterns = "/notification/stream/async", asyncSupported = true) public class AsyncNotificationStreamServlet extends HttpServlet {

@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception { log.info("in notification/stream/async"); response.setCharacterEncoding("utf-8"); response.setContentType("text/event-stream"); AtomicBoolean asyncDone = new AtomicBoolean(false); AsyncContext asyncContext = request.startAsync(); asyncContext.setTimeout((new Random().nextInt(5) + 5) * 1000); asyncContext.addListener(new AsyncListener() { @Override public void onComplete(AsyncEvent event) throws IOException { } @Override public void onTimeout(AsyncEvent event) throws IOException { } @Override public void onError(AsyncEvent event) throws IOException { } @Override public void onStartAsync(AsyncEvent event) throws IOException { } }); asyncContext.start(() -> {

// }); log.info("out notification/stream/async"); }

}

Page 69: Reactive Web - Servlet & Async, Non-blocking I/O

'

@WebServlet(urlPatterns = "/notification/stream/async", asyncSupported = true) public class AsyncNotificationStreamServlet extends HttpServlet {

@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception { log.info("in notification/stream/async"); response.setCharacterEncoding("utf-8"); response.setContentType("text/event-stream"); AtomicBoolean asyncDone = new AtomicBoolean(false); AsyncContext asyncContext = request.startAsync(); asyncContext.setTimeout((new Random().nextInt(5) + 5) * 1000); asyncContext.addListener(new AsyncListener() { @Override public void onComplete(AsyncEvent event) throws IOException { } @Override public void onTimeout(AsyncEvent event) throws IOException { } @Override public void onError(AsyncEvent event) throws IOException { } @Override public void onStartAsync(AsyncEvent event) throws IOException { } }); asyncContext.start(() -> {

// }); log.info("out notification/stream/async"); }

}

Page 70: Reactive Web - Servlet & Async, Non-blocking I/O

'

!#

$

#$

$

$

%

Page 71: Reactive Web - Servlet & Async, Non-blocking I/O

'

public class NotificationStreamObservable extends Observable { final static ExecutorService executorService = Executors.newCachedThreadPool(); Iterator<Notification> feedNotifies; Future<?> feedFuture; public NotificationStreamObservable(NotificationStream stream) { this.feedNotifies = Objects.requireNonNull(stream.feedNotifies()); } public void subscribe() { feedFuture = executorService.submit(() -> { boolean running = true; while (running) { if (countObservers() > 0) { try { Notification notification = feedNotifies.next(); setChanged(); notifyObservers(notification); } catch (Exception error) { running = false; setChanged(); notifyObservers(error); } } if (Thread.interrupted()) { running = false; } Thread.yield(); } }); } public void unsubscribe() { feedFuture.cancel(true); deleteObservers(); } }

Page 72: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NotificationStreamObserver implements Observer, AsyncListener { final AsyncContext asyncContext; public NotificationStreamObserver(AsyncContext asyncContext) { this.asyncContext = Objects.requireNonNull(asyncContext); } @Override public void update(Observable observable, Object event) { if (event instanceof Notification) { handler((Notification) event); } // if (event instanceof Throwable) { handlerError(observable, (Throwable) event); } } @Override public void onComplete(AsyncEvent event) throws IOException { log.info("complete notification/stream/async-observer"); } @Override public void onTimeout(AsyncEvent event) throws IOException { handlerError(new TimeoutException("timeout")); } @Override public void onError(AsyncEvent event) throws IOException { handlerError(event.getThrowable()); } @Override public void onStartAsync(AsyncEvent event) throws IOException { }

// handle, handleError }

Page 73: Reactive Web - Servlet & Async, Non-blocking I/O

'

@WebServlet(urlPatterns = "/notification/stream/observer", asyncSupported = true) public class ObserverNotificationStreamServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception { log.info("in notification/stream/observer"); response.setCharacterEncoding("utf-8"); response.setContentType("text/event-stream"); AsyncContext asyncContext = request.startAsync(request, response); asyncContext.setTimeout((new Random().nextInt(5) + 5) * 1000);

NotificationStream notificationStream = new NotificationStream(obtainUsername(request)); NotificationStreamObservable streamObservable = new NotificationStreamObservable(notificationStream); NotificationStreamObserver streamObserver = new NotificationStreamObserver(asyncContext); streamObservable.addObserver(streamObserver); asyncContext.addListener(streamObserver);

streamObservable.subscribe(); log.info("out notification/stream/observer"); } String obtainUsername(HttpServletRequest request) { String username = request.getParameter("username"); if (StringUtils.hasText(username)) { return username; } return "anonymous"; } }

Page 74: Reactive Web - Servlet & Async, Non-blocking I/O
Page 75: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 76: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 77: Reactive Web - Servlet & Async, Non-blocking I/O

'

!"

#$

#

#

#

$

$

$

Page 78: Reactive Web - Servlet & Async, Non-blocking I/O

'

public class NotificationStreamObservable extends Observable { final static ExecutorService executorService = Executors.newCachedThreadPool(); Iterator<Notification> feedNotifies; Future<?> feedFuture; public NotificationStreamObservable(NotificationStream stream) { this.feedNotifies = requireNonNull(stream).feedNotifies(); } public void subscribe() { feedFuture = executorService.submit(() -> { boolean running = true; while (running) { if (countObservers() > 0) { try { Notification notification = feedNotifies.next(); setChanged(); notifyObservers(notification); } catch (Exception error) { running = false; setChanged(); notifyObservers(error); } } // } }); } // }

Page 79: Reactive Web - Servlet & Async, Non-blocking I/O

'

public class NotificationStreamObservable extends Observable { final static ExecutorService executorService = Executors.newCachedThreadPool(); Iterator<Notification> feedNotifies; Future<?> feedFuture;

Iterator<Notification> friendNotifies; Future<?> friendFuture; public NotificationStreamObservable(NotificationStream stream) { this.feedNotifies = requireNonNull(stream).feedNotifies(); this.friendNotifies = requireNonNull(stream).friendRecommendationNotifies(); } public void subscribe() { feedFuture = executorService.submit(() -> { boolean running = true; while (running) { if (countObservers() > 0) { try { Notification feedNotify = feedNotifies.next(); setChanged(); notifyObservers(feedNotify);

Notification friendNotify = friendNotifies.next(); setChanged(); notifyObservers(friendNotify); } catch (Exception error) { running = false; setChanged(); notifyObservers(error); } } // } }); } // }

Page 80: Reactive Web - Servlet & Async, Non-blocking I/O

'

feedNotifies.next()

friendNotifies.next()

feedNotify

friendNotify

Page 81: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NotificationPublisher implements Runnable { final Iterator<Notification> notifies; final AtomicBoolean running = new AtomicBoolean(true); NotificationPublisher(Iterator<Notification> notifies) { this.notifies = notifies; } @Override public void run() { while (running.get()) { if (countObservers() > 0) { try { Notification notification = notifies.next(); setChanged(); notifyObservers(notification); } catch (Exception error) { cancel(); setChanged(); notifyObservers(error); } } if (Thread.interrupted()) { cancel(); } Thread.yield(); } } void cancel() { running.set(false); }}

Page 82: Reactive Web - Servlet & Async, Non-blocking I/O

'

class MultipleNotificationStreamObservable extends Observable { final static ExecutorService executor = Executors.newCachedThreadPool(); NotificationPublisher feedPublisher; NotificationPublisher friendPublisher; public MultipleNotificationStreamObservable(NotificationStream stream) { this.feedPublisher = new NotificationPublisher(requireNonNull(stream).feedNotifies()); this.friendPublisher = new NotificationPublisher(requireNonNull(stream).friendRecommendationNotifies()); } public void subscribe() { executor.execute(feedPublisher); executor.execute(friendPublisher); } public void unsubscribe() { feedPublisher.cancel(); friendPublisher.cancel(); deleteObservers(); }}

Page 83: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 84: Reactive Web - Servlet & Async, Non-blocking I/O

'

!

Page 85: Reactive Web - Servlet & Async, Non-blocking I/O

'

!

Page 86: Reactive Web - Servlet & Async, Non-blocking I/O

'

class MultipleNotificationStreamObserver implements Observer, AsyncListener { @Override public void update(Observable observable, Object event) { synchronized (this) { if (event instanceof Notification) { handler((Notification) event); } // if (event instanceof Throwable) { handlerError((Throwable) event); } } } // }

Page 87: Reactive Web - Servlet & Async, Non-blocking I/O

'

class MultipleNotificationStreamObserver implements Observer, AsyncListener { ReentrantLock reentrantLock = new ReentrantLock(); @Override public void update(Observable observable, Object event) { reentrantLock.lock(); if (event instanceof Notification) { handler((Notification) event); } // if (event instanceof Throwable) { handlerError((Throwable) event); } reentrantLock.unlock(); } // }

Page 88: Reactive Web - Servlet & Async, Non-blocking I/O

'

!

Page 89: Reactive Web - Servlet & Async, Non-blocking I/O

'

BlockingQueue<Object> notifyQueue = new LinkedBlockingQueue<>();

class SequentialNotifyObserversPublisher implements Runnable { final Iterator<Notification> notifies; final AtomicBoolean running = new AtomicBoolean(true); SequentialNotifyObserversRunnable(Iterator<Notification> notifies) { this.notifies = notifies; } @Override public void run() { while (running.get()) { if (countObservers() > 0) { try { notifyQueue.put(notifies.next()); } catch (Exception error) { cancel(); try { notifyQueue.put(error); } catch (InterruptedException ignore) { } } } if (Thread.interrupted()) { cancel(); } Thread.yield(); } } void cancel() { running.set(false); }}

Page 90: Reactive Web - Servlet & Async, Non-blocking I/O

'

class MultipleNotificationStreamObservable extends Observable { final static ExecutorService executor = Executors.newFixedThreadPool(3); BlockingQueue<Object> notifyQueue = new LinkedBlockingQueue<>(); Future<?> notifyFuture; SequentialNotifyObserversPublisher sequentialFeed; SequentialNotifyObserversPublisher sequentialFriend; public MultipleNotificationStreamObservable(NotificationStream stream) { this.sequentialFeed = new SequentialNotifyObserversRunnable(stream.feedNotifies()); this.sequentialFriend = new SequentialNotifyObserversRunnable(stream.friendRecommendationNotifies()); } public void subscribe() { executor.execute(sequentialFeed); executor.execute(sequentialFriend); notifyFuture = executor.submit(() -> { boolean running = true; while (running) { if (countObservers() > 0) { try { Object event = notifyQueue.take(); setChanged(); notifyObservers(event); } catch (InterruptedException e) { running = false; } } if (Thread.interrupted()) { running = false; } Thread.yield(); } }); } // }

Page 91: Reactive Web - Servlet & Async, Non-blocking I/O
Page 92: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 93: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 94: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 95: Reactive Web - Servlet & Async, Non-blocking I/O
Page 96: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 97: Reactive Web - Servlet & Async, Non-blocking I/O

'

void printUserInfo(String username) { NotifyApi notifyApi = new NotifyApi(); NotifyApi.User user = notifyApi.getUser(username); Long feedCount = notifyApi.getFeedCount(username); Long friendCount = notifyApi.getFriendCount(username); UserInfo userInfo = new UserInfo(user.getName(), feedCount, friendCount); System.out.println("User Info"); System.out.println("name = " + userInfo.getName()); System.out.println("feedCount = " + userInfo.getFeedCount()); System.out.println("friendCount = " + userInfo.getFriendCount());}

printUserInfo("fay"); // 1printUserInfo("murphy"); // 2printUserInfo("nichole"); // 3

Page 98: Reactive Web - Servlet & Async, Non-blocking I/O

'

void printUserInfo(String username) { … }

ExecutorService executorService = Executors.newFixedThreadPool(3); executorService.execute(() -> printUserInfo("fay")); // 1 executorService.execute(() -> printUserInfo("murphy")); // 2 executorService.execute(() -> printUserInfo("nichole")); // 3

Page 99: Reactive Web - Servlet & Async, Non-blocking I/O

'

void printUserInfo(String username) { … }

ExecutorService executorService = Executors.newFixedThreadPool(3); executorService.execute(() -> printUserInfo("fay")); // 1 executorService.execute(() -> printUserInfo("murphy")); // 2 executorService.execute(() -> printUserInfo("nichole")); // 3 executorService.execute(() -> printUserInfo("phillip")); // 4 executorService.execute(() -> printUserInfo("adrienne")); // 5 executorService.execute(() -> printUserInfo("nita")); // 6

Page 100: Reactive Web - Servlet & Async, Non-blocking I/O

'

ExecutorService executorService = Executors.newFixedThreadPool(1); void printUserInfo(String username) { CompletableFuture.supplyAsync(() -> notifyApi.getUser(username), executorService) .thenCompose(user -> { CompletableFuture<Long> feedCountCF = CompletableFuture.supplyAsync( () -> notifyApi.getFeedCount(username), executorService); CompletableFuture<Long> friendCountCF = CompletableFuture.supplyAsync( () -> notifyApi.getFriendCount(username), executorService); return feedCountCF.thenCombineAsync(friendCountCF, (feedCount, friendCount) -> { return new UserInfo(user.getName(), feedCount, friendCount); }, executorService); }) .thenAccept(userInfo -> { System.out.println("User Info"); System.out.println("name = " + userInfo.getName()); System.out.println("feedCount = " + userInfo.getFeedCount()); System.out.println("friendCount = " + userInfo.getFriendCount()); });}

printUserInfo("fay"); // 1printUserInfo("murphy"); // 2printUserInfo("nichole"); // 3

Page 101: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 102: Reactive Web - Servlet & Async, Non-blocking I/O

'

ExecutorService executorService = Executors.newFixedThreadPool(3); void printUserInfo(String username) {

// } printUserInfo("fay"); // 1printUserInfo("murphy"); // 2printUserInfo("nichole"); // 3printUserInfo("phillip"); // 4printUserInfo("adrienne"); // 5printUserInfo("nita"); // 6

Page 103: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NotificationPublisher implements Runnable { Iterator<Notification> notifies; NotifyObserversRunnable(Iterator<Notification> notifies) { this.notifies = notifies; } @Override public void run() { boolean running = true; while (running) { if (countObservers() > 0) { try { Notification notification = notifies.next(); setChanged(); notifyObservers(notification); } catch (Exception error) { running = false; setChanged(); notifyObservers(error); } } if (Thread.interrupted()) { running = false; } Thread.yield(); } }}

Page 104: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NotificationPublisher implements Runnable { final Iterator<Notification> notifies; final AtomicBoolean running = new AtomicBoolean(true); NotifyObserversRunnable(Iterator<Notification> notifies) { this.notifies = notifies; } public void run() { if (countObservers() > 0) { CompletableFuture<Object> completableFuture = new CompletableFuture<>(); completableFuture.thenAcceptAsync(event -> { if (running.get()) { setChanged(); notifyObservers(event); } schedule(20); }, notifyExecutor).exceptionally(throwable -> { cancel(); setChanged(); notifyObservers(throwable); return null; }); try { completableFuture.complete(notifies.next()); } catch (Exception error) { completableFuture.completeExceptionally(error); } } schedule(50); } void schedule(long millis) { if (running.get()) { serviceExecutor.schedule(this, millis, TimeUnit.MILLISECONDS); } } void cancel() { running.set(false); }}

Page 105: Reactive Web - Servlet & Async, Non-blocking I/O

'

class AsyncMultipleNotificationStreamObservable extends Observable { final static ScheduledExecutorService serviceExecutor = Executors.newScheduledThreadPool(2); final static ScheduledExecutorService notifyExecutor = Executors.newScheduledThreadPool(1); NotifyObserversRunnable feedPublisher; NotifyObserversRunnable friendPublisher; public AsyncMultipleNotificationStreamObservable(NotificationStream stream) { this.feedPublisher = new NotifyObserversRunnable(requireNonNull(stream).feedNotifies()); this.friendPublisher = new NotifyObserversRunnable(requireNonNull(stream).friendRecommendationNotifies()); } public void subscribe() { serviceExecutor.schedule(feedPublisher, 10, TimeUnit.MILLISECONDS); serviceExecutor.schedule(friendPublisher, 10, TimeUnit.MILLISECONDS); } public void unsubscribe() { feedPublisher.cancel(); friendPublisher.cancel(); }

}

Page 106: Reactive Web - Servlet & Async, Non-blocking I/O
Page 107: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 108: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 109: Reactive Web - Servlet & Async, Non-blocking I/O
Page 110: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 111: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 112: Reactive Web - Servlet & Async, Non-blocking I/O

'

SocketChannel channel = SocketChannel.open();channel.configureBlocking(true);

Socket socket = channel.socket();socket.connect(new InetSocketAddress("www.naver.com", 80));BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintStream output = new PrintStream(socket.getOutputStream());output.println("GET / HTTP/1.0");output.println();StringBuilder response = new StringBuilder();String line = input.readLine();while (Objects.nonNull(line)) { response.append(line).append("\n"); line = input.readLine(); // body if ("".equals(line)) { while (Objects.nonNull(line)) { line = input.readLine(); } }} input.close();output.close();

Page 113: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 114: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 115: Reactive Web - Servlet & Async, Non-blocking I/O

'

!

!

!

!

)

PollSelectorImpl

WindowsSelectorImpl

KQueueSelectorImpl

EPollSelectorImpl

Page 116: Reactive Web - Servlet & Async, Non-blocking I/O

'

Selector selector = Selector.open();SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);socketChannel.connect(new InetSocketAddress("www.naver.com", 80));socketChannel.register(selector, SelectionKey.OP_CONNECT);while (socketChannel.isOpen()) { if (selector.select() > 0) { Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey selectionKey = iterator.next(); if (selectionKey.isConnectable()) { SocketChannel channel = (SocketChannel) selectionKey.channel(); // } else if (selectionKey.isWritable()) { SocketChannel channel = (SocketChannel) selectionKey.channel(); // } else if (selectionKey.isReadable()) { SocketChannel channel = (SocketChannel) selectionKey.channel(); // }

// iterator.remove(); } }}

Page 117: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 118: Reactive Web - Servlet & Async, Non-blocking I/O

'

AsynchronousChannelGroup asynchronousChannelGroup = AsynchronousChannelGroup.withFixedThreadPool(1); AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel.open(asynchronousChannelGroup);

SocketAddress socketAddress = new InetSocketAddress(“www.naver.com”, 80); asynchronousSocketChannel.connect(socketAddress, null, new CompletionHandler<Void, Void>() { @Override public void completed(Void v, Void attachment) {

// asynchronousSocketChannel.write(writeBuffer, null, new CompletionHandler<Integer, Void>() { @Override public void completed(Integer length, Void attachment) {

// asynchronousSocketChannel.read(readBuffer, null, new CompletionHandler<Integer, Void>() { @Override public void completed(Integer length, Void attachment) { // } @Override public void failed(Throwable error, Void attachment) { // } }); } @Override public void failed(Throwable error, Void attachment) { // } }); } @Override public void failed(Throwable exc, Void attachment) { // }});

Page 119: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 120: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 121: Reactive Web - Servlet & Async, Non-blocking I/O

'

✔ �Netty(ht

tp://netty.io

)

✔ �gRPG(htt

p://www.g

rpc.io)

✔ �AsyncHtt

pClient(http

s://hc.apac

he.org/)

Page 122: Reactive Web - Servlet & Async, Non-blocking I/O

'

public FriendRecommendationNotify getRecommendationNotify(String username) throws ServiceOperationException { try { HttpGet request = new HttpGet(String.format(url, username, processing.name())); HttpResponse response = httpClient.execute(request); if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { throw new ServiceOperationException(); } return objectMapper.readValue(response.getEntity().getContent(), FriendRecommendationNotify.class); } catch (IOException error) { throw new ServiceOperationException(error); }}

Page 123: Reactive Web - Servlet & Async, Non-blocking I/O

'

public CompletableFuture<FriendRecommendationNotify> getRecommendationNotifyAsync(String username) { CompletableFuture<FriendRecommendationNotify> completableFuture = new CompletableFuture<>(); HttpGet request = new HttpGet(String.format(url, username, processing.name())); httpAsyncClient.execute(request, new FutureCallback<HttpResponse>() { @Override public void completed(HttpResponse response) { try { int statusCode = response.getStatusLine().getStatusCode(); InputStream content = response.getEntity().getContent(); if (statusCode != HttpStatus.SC_OK) { completableFuture.completeExceptionally(new ServiceOperationException()); } completableFuture.complete(objectMapper.readValue(content, FriendRecommendationNotify.class)); } catch (IOException error) { completableFuture.completeExceptionally(new ServiceOperationException(error)); } } @Override public void failed(Exception error) { completableFuture.completeExceptionally(error); } @Override public void cancelled() { completableFuture.cancel(true); } }); return completableFuture;}

Page 124: Reactive Web - Servlet & Async, Non-blocking I/O

'

@Testpublic void getRecommendationNotify() throws Exception { StopWatch stopWatch = new StopWatch("recommendationNotify"); stopWatch.start(); IntStream.rangeClosed(1, 10) .mapToObj(value -> friendService.getRecommendationNotify("user-" + value)) .forEach(System.out::println); stopWatch.stop(); System.out.println(); System.out.println(stopWatch.prettyPrint());}

@Testpublic void getRecommendationNotifyAsync() throws Exception { StopWatch stopWatch = new StopWatch("recommendationNotifyAsync"); stopWatch.start(); IntStream.rangeClosed(1, 10) .mapToObj(value -> friendService.getRecommendationNotifyAsync("user-" + value)) .collect(Collectors.toList()) .stream() .map(CompletableFuture::join) .forEach(System.out::println); stopWatch.stop(); System.out.println(); System.out.println(stopWatch.prettyPrint());}

Page 125: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NotificationPublisher implements Runnable { final Supplier<CompletableFuture<Notification>> notifies; final AtomicBoolean running = new AtomicBoolean(true); NotifyObserversRunnable(Supplier<CompletableFuture<Notification>> notifies) { this.notifies = notifies; } @Override public void run() { if (countObservers() > 0) { notifies.get().thenAcceptAsync(event -> { if (running.get()) { setChanged(); notifyObservers(event); } schedule(20); }, notifyExecutor).exceptionally(throwable -> { cancel(); setChanged(); notifyObservers(throwable); return null; }); } else { schedule(50); } } void schedule(long millis) { if (running.get()) { serviceExecutor.schedule(this, millis, TimeUnit.MILLISECONDS); } } void cancel() { running.set(false); }}

Page 126: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NonBlockingAsyncMultipleNotificationStreamObservable extends Observable { final static ScheduledExecutorService serviceExecutor = Executors.newScheduledThreadPool(1); final static ScheduledExecutorService notifyExecutor = Executors.newScheduledThreadPool(1); NotifyObserversRunnable feed; NotifyObserversRunnable friend; public NonBlockingAsyncMultipleNotificationStreamObservable(NotificationStream stream) { requireNonNull(stream); this.feed = new NotifyObserversRunnable(stream::feedNotifyAsync); this.friend = new NotifyObserversRunnable(stream::friendRecommendationNotifyAsync); } public void subscribe() { serviceExecutor.execute(feed); serviceExecutor.execute(friend); } public void unsubscribe() { if (nonNull(feed)) feed.cancel(); if (nonNull(friend)) friend.cancel(); deleteObservers(); }}

Page 127: Reactive Web - Servlet & Async, Non-blocking I/O
Page 128: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 129: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 130: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NonBlockingAsyncMultipleNotificationStreamObserver implements Observer, AsyncListener { final NonBlockingAsyncMultipleNotificationStreamObservable streamObservable; final AsyncContext asyncContext; public NonBlockingAsyncMultipleNotificationStreamObserver(N…Observable streamObservable, AsyncContext asyncContext) { this.streamObservable = Objects.requireNonNull(streamObservable); this.asyncContext = Objects.requireNonNull(asyncContext); } @Override public void update(Observable observable, Object event) { if (event instanceof Notification) { handler((Notification) event); } if (event instanceof Throwable) { handlerError((Throwable) event); } } private void handler(Notification notification) { try { log.info("revised notification/stream/non-blocking-async-concurrency : " + notification.getEvent()); ServletOutputStream outputStream = asyncContext.getResponse().getOutputStream(); outputStream.write(("event: " + notification.getEvent() + "\n").getBytes()); outputStream.write(("data: " + notification.getData() + "\n\n").getBytes()); outputStream.flush(); } catch (IOException error) { throw new DataWriteException(error); } } private void handlerError(Throwable error) { log.error("error notification/stream/non-blocking-async-concurrency : " + error.getMessage()); streamObservable.unsubscribe(); asyncContext.complete(); }}

Page 131: Reactive Web - Servlet & Async, Non-blocking I/O

'

✔�비봉쇄�입/출력을�위한�새로운�인터페이스�•ReadListener�-�사용�가능한�데이터를�읽을�수�있는�방법�

•WriteListener�-�데이터�쓰기가�가능한�때를�알�수�있는�방법�

✔�ServletInputStream�에�추가된�기능�•isFinished()�

•isReady()�

•setReadListener()�

✔�ServletOutputStream�에�추가된�기능�•isReady()�

•setWriterListener()

Page 132: Reactive Web - Servlet & Async, Non-blocking I/O

'

ServletInputStream servletInputStream = request.getInputStream();servletInputStream.setReadListener(new ReadListener() { @Override public void onDataAvailable() throws IOException { // } @Override public void onAllDataRead() throws IOException { // } @Override public void onError(Throwable throwable) { // }});

ServletOutputStream servletOutputStream = response.getOutputStream();servletOutputStream.setWriteListener(new WriteListener() { @Override public void onWritePossible() throws IOException { // } @Override public void onError(Throwable throwable) { // }});

Page 133: Reactive Web - Servlet & Async, Non-blocking I/O

'

class NonBlockingAsyncMultipleNotificationStreamObserver implements Observer, AsyncListener, WriteListener { final NonBlockingAsyncMultipleNotificationStreamObservable streamObservable; final AsyncContext asyncContext; final ServletOutputStream outputStream; final AtomicInteger counter = new AtomicInteger(1); public NonBlockingAsyncMultipleNotificationStreamObserver(N…Observable streamObservable, AsyncContext asyncContext) { this.streamObservable = Objects.requireNonNull(streamObservable); this.asyncContext = Objects.requireNonNull(asyncContext); this.outputStream = asyncContext.getResponse().getOutputStream(); }

// @Override public void onWritePossible() throws IOException { // ignore } @Override public void onError(Throwable throwable) { handlerError(throwable); } private void handler(Notification notification) { String content = notification.toContent(); ioExecutor.schedule(() -> write(content.getBytes()), 10, TimeUnit.MILLISECONDS); }

private void handlerError(Throwable error) { log.error("error notification/stream/non-blocking-async-concurrency : " + error.getMessage()); streamObservable.unsubscribe(); asyncContext.complete(); }

// …

Page 134: Reactive Web - Servlet & Async, Non-blocking I/O

'

//

final static ScheduledExecutorService ioExecutor = Executors.newScheduledThreadPool(1);

private void write(byte[] data) { if (outputStream.isReady()) { try { outputStream.write(data); ioExecutor.schedule(this::flush, 10, TimeUnit.MILLISECONDS); } catch (IOException error) { handlerError(error); } } else { ioExecutor.schedule(() -> write(data), 10, TimeUnit.MILLISECONDS); } } private void flush() { if (outputStream.isReady()) { try { outputStream.flush(); } catch (IOException error) { handlerError(error); } } else { ioExecutor.schedule(this::flush, 10, TimeUnit.MILLISECONDS); } }}

Page 135: Reactive Web - Servlet & Async, Non-blocking I/O
Page 136: Reactive Web - Servlet & Async, Non-blocking I/O

'

!"

#$

#

#

#

$

Page 137: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 138: Reactive Web - Servlet & Async, Non-blocking I/O

'

Page 139: Reactive Web - Servlet & Async, Non-blocking I/O
Page 140: Reactive Web - Servlet & Async, Non-blocking I/O