WebFlux Flow Chart
× This post explains the flow of WebFlux request processing through detailed sequence diagrams. ...
× This post explains the flow of WebFlux request processing through detailed sequence diagrams. ...
Java NIO Purpose Netty is a library that abstracts Java NIO, so Java NIO is often discussed. This post provides a brief explanation. Features NonBlocking I/O I/O operations can be performed asynchronously without waiting Multiple connections can be implemented in a non-blocking way (Selectors) Channels Bidirectional communication Unlike InputStream and OutputStream, a single channel can perform both read and write Direct Buffer support Performance improvement by reducing buffer copy overhead through memory-mapped buffers Read/write for socket communication is only supported with native memory - stack overflow (Java Champion’s answer) Native memory is part of the JVM’s native area If you use a buffer created in JVM heap memory for socket communication, there is overhead from copying to native memory To reduce buffer copy overhead, direct buffers are introduced, which are created and written directly in native memory Due to direct buffer optimization, there is a difference in the number of direct buffers between Webflux and MVC models - https://oss.navercorp.com/nspa/nspa-server/issues/3219#issuecomment-14853076 MVC: 200~300 Webflux: 75 Main Components Selector Handles events for multiple channels in a non-blocking way ServerSocketChannel Server socket channel. Supports bidirectional communication. By implementing SelectableChannel, Selector can register to receive events for the channel. SelectionKey Token issued when registering a channel with a Selector. Represents the connection between selector and channel. ...
select vs poll vs epoll Purpose Netty uses epoll. Understanding how epoll works helps in understanding Netty. By comparing various I/O handling methods, we can better understand epoll. Select The user program registers interested events for each file descriptor in a set The user program keeps checking for events on fds in the set Handles the occurred events Time complexity O(N) [N = number of registered events] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/select.h> #include <fcntl.h> int main() { int fd1 = STDIN_FILENO; // Standard input int fd2 = open("file2.txt", O_RDONLY); // Another file int fd3 = open("file3.txt", O_RDONLY); // Another file if (fd2 == -1 || fd3 == -1) { perror("Failed to open files"); return 1; } // Initialize fd_set for select fd_set readfds; FD_ZERO(&readfds); // Initialize fd_set FD_SET(fd1, &readfds); // Add fd1 to readfds FD_SET(fd2, &readfds); // Add fd2 to readfds FD_SET(fd3, &readfds); // Add fd3 to readfds // Set timeout for select (e.g., 5 seconds) struct timeval timeout; timeout.tv_sec = 5; timeout.tv_usec = 0; // Call select. Wait for events int ret = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout); if (ret == -1) { perror("select failed"); return 1; } else if (ret == 0) { printf("Timeout occurred! No data available within 5 seconds.\n"); } else { // Check which file descriptor has a read event if (FD_ISSET(fd1, &readfds)) { printf("Data is available for reading on fd1 (STDIN)\n"); } if (FD_ISSET(fd2, &readfds)) { printf("Data is available for reading on fd2 (file2.txt)\n"); } if (FD_ISSET(fd3, &readfds)) { printf("Data is available for reading on fd3 (file3.txt)\n"); } } // Close file descriptors close(fd2); close(fd3); return 0; } Poll Compared to select, declared as a (fd, event, received) struct Lower performance than epoll, where the OS directly puts events in a queue. However, poll()’s struct is similarly used in epoll() Time complexity O(N) [N = number of registered events] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <stdio.h> #include <stdlib.h> #include <poll.h> #include <unistd.h> #include <fcntl.h> int main() { // Prepare file descriptors struct pollfd fds[2]; int timeout = 5000; // Wait 5 seconds // fd1: standard input fds[0].fd = STDIN_FILENO; fds[0].events = POLLIN; // Monitor for readable event // fd2: file (e.g., open file descriptor) int fd2 = open("example.txt", O_RDONLY); if (fd2 == -1) { perror("Failed to open file"); return 1; } fds[1].fd = fd2; fds[1].events = POLLIN; // Monitor for readable event // Call poll int ret = poll(fds, 2, timeout); if (ret == -1) { perror("poll failed"); return 1; } else if (ret == 0) { printf("Timeout! No events occurred within 5 seconds.\n"); } else { // Check events if (fds[0].revents & POLLIN) { // revents = returned events printf("Data is available on stdin.\n"); } if (fds[1].revents & POLLIN) { printf("Data is available in example.txt.\n"); } } // Close file descriptor close(fd2); return 0; } Epoll The OS directly registers events in the event queue, and the user program only monitors the event queue (blocking), maximizing efficiency Time complexity: Event queue: R/B tree. O(logn) for fd registration and deletion Event collection: O(1) or O(M) [M = number of occurred events] Note: When running Netty: Mac OS uses kqueue Linux uses epoll ...