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 ...
Reactor Table of Contents Purpose WebFlux provides developers with the Reactor interface to use Netty, so it is important to understand related concepts. What is Reactive Stream? A standardized interface for processing asynchronous streams using non-blocking back pressure. Reactor implements the reactive stream specification. https://www.reactive-streams.org/ Structure Traditional Observer Pattern Publisher Registers subscribers Delivers data to subscribers Subscriber Provides an update() callback function to execute logic, which is called by the publisher Similarity to Reactive Stream Interface ...
Spring AOP Problem In Spring, self-invocation refers to a method within a class calling another method of the same class. This can be problematic when using AOP (Aspect-Oriented Programming) annotations like @Transactional. Spring AOP is proxy-based. So if you call another method directly within the same class, it bypasses the proxy, and AOP annotations won’t be applied. For example, suppose you have method A annotated with @Transactional, and it’s called from method B in the same class. If method B calls A directly, Spring won’t manage the transaction properly because the call doesn’t go through the proxy. ...
Example Code https://github.com/jurogrammer/dtrans Situation When you want to separate transactions for different operations, but creating a new service class for this feels cumbersome. Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Service @RequiredArgsConstructor public class SaveServiceVer1 { private final UserRepository userRepository; private final OrderRepository orderRepository; @Transactional public void save() { userRepository.save(new User("홍길동")); // If an error occurs while saving the order, you still want the user to be saved as is. saveOrder(); } public void saveOrder() { orderRepository.save(new Order()); throw new RuntimeException("Error occurred while saving order"); } } Idea Even if an error occurs in saveOrder and the order is rolled back, the User save should still be performed. ...
References edwith DobiSocks reviewer https://www.mimul.com/blog/di-constructor-injection/ (Why DI is needed) https://madplay.github.io/post/why-constructor-injection-is-better-than-field-injection (Why constructor injection is recommended over field injection) https://d2.naver.com/helloworld/1230 (JVM Internal) Overview In an edwith course, I submitted a project using field injection for DI. However, the reviewer recommended constructor injection and kindly provided related materials. Although I understood most of the points, I didn’t quite get how constructor injection prevents circular reference errors, so I decided to summarize it here. ...