SCALABLE-IO-IN-JAVA
Intro(Scalable IO in Java) #
- Scalable network services
- Event-driven processing
- Reactor pattern
- Basic version
- Multithreaded versions
- Other variants
- Walkthrough of java.nio nonblocking IO APIs
Network Services #
Web services, Distributed Objects, etc. Most have same basic structure:
Read request
➠Decode request
➠Process service
➠Encode reply
➠Send reply
" But differ in nature and cost of each step XML parsing, File transfer, Web page generation, computational services, …Classic Service Designs #
Scalability Goals #
- Graceful degradation under increasing load(more clients)
- Continuous improvement with increasing resources (CPU, memory, disk, bandwidth)
- Also meet availability and performance goals
- Short latencies
- Meeting peak demand
- Tunable quality of service
- Divide-and-conquer is usually the best approach for achieving any scalability goal
Divide and Conquer #
Divide processing into small tasks, Each task performs an action without blocking
Execute each task when it is enabled
Here, an IO event usually serves as trigger
Basic mechanisms supported in java.nio
- Non-blocking reads and writes
- Dispatch tasks associated with sensed IO events
Endless variation possible, A family of event-driven designs
Event-driven Designs #
Usually more efficient than alternatives
Fewer resources
(Don’t usually need a thread per client)Less overhead
(Less context switching, often less locking)But dispatching can be slower
(Must manually bind actions to events)Usually harder to program
Must break up into simple non-blocking actions
- Similar to GUI event-driven actions
- Cannot eliminate all blocking: GC, page faults, etc
Must keep track of logical state of service
Background: Events in AWT #
Reactor Pattern #
1. Reactor responds to IO events by dispatching the appropriate handler(Similar to AWT thread)
2. Handlers perform non-blocking actions(Similar to AWT ActionListeners)
3. Manage by binding handlers to events(Similar to AWT addActionListener)
See Schmidt et al, Pattern-Oriented Software Architecture, Volume 2 (POSA2). Also Richard Stevens’s networking books, Matt Welsh’s SEDA framework, etc.Basic Reactor Design #
Reactor
andAcceptor
Normal
Handler
and GoFHandler
为什么需要调用wakeup()方法?
ref
当你改变了一个通道的interestOps
后,这个改变不会立即触发select()方法返回。这是因为select()方法是在等待至少有一个已注册的通道的状态变为就绪(即满足之前设置的 interestOps)时才会返回。
然而,如果你已经改变了某个通道的interestOps
,并且你希望这个改变能够立即影响到select()方法的执行(例如:你希望 select()方法立即返回以检查新的 interestOps),那么你就需要调用wakeup()
方法。
因此,改变 interestOps 后需要调用 wakeup() 的主要原因是,你希望这个改变能够立即影响到 select() 方法的执行,以便能够及时响应新的感兴趣的操作。如果不调用 wakeup(),那么 select() 方法可能会继续阻塞,直到某个已注册的通道的状态变为就绪,而这可能并不符合你的期望。Multithreaded Designs #
Strategically add threads for scalability (Mainly applicable to multiprocessors)
Worker Threads
- Reactors should quickly trigger handlers. (Handler processing slows down Reactor)
- Offload non-IO processing to other threads.
Offload non-IO processing to speed up Reactor thread
(Similar to POSA2 Proactor designs)Simpler than reworking compute-bound processing into event-driven form
(Should still be pure nonblocking computation, Enough processing to outweigh overhead)But harder to overlap processing with IO
(Best when can first read all input into a buffer)Use thread pool so can tune and control
(Normally need many fewer threads than clients)Coordinating Tasks
- Handoffs (Each task enables, triggers, or calls next one Usually fastest but can be brittle)
- Callbacks to per-handler dispatcher
- Sets state, attachment, etc
- A variant of GoF Mediator pattern
- Queues (For example, passing buffers across stages)
- Futures
- When each task produces a result
- Coordination layered on top of join or wait/notify
Using PooledExecutor
- A tunable worker thread pool
- Main method execute(Runnable r)
- Controls for:
- The kind of task queue (any Channel)
- Maximum number of threads
- Minimum number of threads
- “Warm” versus on-demand threads
- Keep-alive interval until idle threads die (to be later replaced by new ones if necessary)
- Saturation policy (block, drop, producer-runs, etc)
Multiple Reactor Threads (Reactor threads can saturate doing IO Distribute load to other reactors, Load-balance to match CPU and IO rates)
Using other java.nio features
- Multiple Selectors per Reactor (To bind different handlers to different IO events, May need careful synchronization to coordinate)
- File transfer (Automated file-to-net or net-to-file copying)
- Memory-mapped files (Access files via buffers)
- Direct buffers
- Can sometimes achieve zero-copy transfer
- But have setup and finalization overhead
- Best for applications with long-lived connections
Connection-Based Extensions
- Instead of a single service request
- Client connects
- Client sends a series of messages/requests
- Client disconnects
- Examples
- Databases and Transaction monitors
- Multi-participant games, chat, etc
- Can extend basic network service patterns
- Handle many relatively long-lived clients
- Track client and session state (including drops)
- Distribute services across multiple hosts