├── .gitignore ├── README.md ├── The exploration of HttpHandler.md └── demo-reactor ├── .gitignore ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── dockerx │ │ └── demoreactor │ │ ├── DemoRecatorApplication.java │ │ ├── Unsafe_test.java │ │ └── edu │ │ ├── Books.java │ │ ├── Grade.java │ │ ├── Mark.java │ │ ├── Student.java │ │ ├── StudentPro.java │ │ └── Teacher.java └── resources │ └── application.properties └── test └── java └── com └── dockerx └── demoreactor ├── DemoContext.java ├── DemoHooksTrace.java ├── DemoOperator.java ├── DemoProcessor.java ├── DemoReactorApplicationTests.java ├── DemoReactorFluxTest.java ├── DemoReactorTest.java ├── DemoSubscriber.java ├── Outter.java ├── WorkQueueProcessorTest.java └── edu ├── StudentProTest.java └── StudentTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | 27 | # OS generated files # 28 | ###################### 29 | .DS_Store* 30 | ehthumbs.db 31 | Icon? 32 | Thumbs.db 33 | 34 | # Editor Files # 35 | ################ 36 | *~ 37 | *.swp 38 | 39 | # Gradle Files # 40 | ################ 41 | .gradle 42 | .ivy2 43 | .ivy2.cache 44 | .m2 45 | 46 | # Build output directies 47 | /target 48 | */target 49 | /build 50 | */build 51 | 52 | # IntelliJ specific files/directories 53 | out 54 | .idea 55 | *.ipr 56 | *.iws 57 | *.iml 58 | atlassian-ide-plugin.xml 59 | 60 | # Eclipse specific files/directories 61 | .classpath 62 | .project 63 | .settings 64 | .metadata 65 | 66 | # NetBeans specific files/directories 67 | .nbattrs 68 | /bin 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | #### Java编程方法论-响应式篇-Reactor 分享视频 已完结 3 | 4 | B站: 5 | 6 | 油管:https://www.youtube.com/playlist?list=PL95Ey4rht7980EH8yr7SLBvj9XSE1ggdy 7 | 8 | 9 | 10 | #### Java编程方法论-响应式篇-Reactor-Netty 分享视频 在分享 11 | 12 | 相关博文:https://juejin.im/user/59c7640851882578e00ddf90/posts 13 | 14 | 视频分享: 15 | 16 | B站:https://www.bilibili.com/video/av45556406/ 17 | 18 | 油管:https://www.youtube.com/watch?v=6qLh2L75KdM&list=PL95Ey4rht79-ISlb_Yr9ToaEI0K8ARmH6 19 | 20 | 21 | 22 | #### Java编程方法论-JDK 篇 之 NIO 分享视频 在分享 23 | 24 | 相关博文:https://juejin.im/user/59c7640851882578e00ddf90/posts 25 | 26 | B站:https://www.bilibili.com/video/av43230997 27 | 28 | 油管:https://www.youtube.com/watch?v=ZZnCI8xaTRo&list=PL95Ey4rht799NVLgQiSV9skTqY6VuspIk 29 | 30 | 31 | #### Java编程方法论-Netty篇 在分享 32 | 33 | B站:https://www.bilibili.com/video/av50169264 34 | 35 | 油管:https://www.youtube.com/watch?v=AHNW9YCF9aI&list=PL95Ey4rht798WiqkvGYChWdUtHie0j-IU 36 | 37 | [点此查看具体目录](./Java编程方法论-Netty篇.md) 38 | 39 | 40 | -------------------------------------------------------------------------------- /The exploration of HttpHandler.md: -------------------------------------------------------------------------------- 1 | ## The exploration of HttpHandler 2 | In the previous chapters, we have known about the details of the design and implementation of Reactor-Netty. This involves `reactor.netty.http.server.HttpServer#handle`. And exactly say it is a `SPI(Service Provider Interface) `. It provides `BiFunction> handler`. So that, for different situations, we can implement different handlers. Spring WebFlux and Reactor-Netty both have their own implementations. or Spring WebFlux, it needs to adapt with Spring Web, it made a lot of adaption designs. And this process is much complicated. 3 | But for Reactor-Netty, it provides simple and flexible implementations for us. So in this section, we will start with implementations in Reactor-Netty. Then we can move to Spring WebFlux. 4 | 5 | ## The role of HttpServerRoutes 6 | When we send `HTTP` requests to the backend Server side, it'll involve several types of requests such as `get`,`put`,`post` and `head`. It will also includes the request URL. Based on the type of requests and the URL, Server side will provide the specified service, then it will handle with your request. So, can we extract the process that find the service from this to become a Service Router? 7 | 8 | So in `Reactor-Netty`, the author designed a `HttpServerRoutes` interface. This interface inherits from `BiFunction>`. And it's used for handling route requests. When requests come in, then we do a lookup in our collection to find a suitable rule which match the type of this request. Once it matches, then we will invoke the `handler` which is responsible to deal with it. In `HttpServerRoutes` interface, it designed specified router rules for different types of requests such as `GET`, `POST`, `PUT`, `HEAD`, `DELETE` and so on. (For details, please check the following source code) 9 | 10 | When we use this, at first we'll invoke `HttpServerRoutes#newRoutes` to get an instance of `DefaultHttpServerRoutes`. Then we put our router rules into this. For the design of router rules, basically we just use a collection to manage these rules into a collection. When requests come in, then we do a lookup in this collection to find the matched rule. Thus, the core method of this is `reactor.netty.http.server.HttpServerRoutes#route`. After we designed the router rule, then we can design the functional implementations for each rule by using `BiFunction>`. Finally, when the request matches the rule, then we can invoke our implementation of `BiFunction` to handle the request. 11 | 12 | ```java 13 | //reactor.netty.http.server.HttpServerRoutes 14 | public interface HttpServerRoutes extends 15 | BiFunction> { 16 | 17 | static HttpServerRoutes newRoutes() { 18 | return new DefaultHttpServerRoutes(); 19 | } 20 | 21 | 22 | default HttpServerRoutes delete(String path, 23 | BiFunction> handler) { 24 | return route(HttpPredicate.delete(path), handler); 25 | } 26 | 27 | ... 28 | 29 | default HttpServerRoutes get(String path, 30 | BiFunction> handler) { 31 | return route(HttpPredicate.get(path), handler); 32 | } 33 | 34 | default HttpServerRoutes head(String path, 35 | BiFunction> handler) { 36 | return route(HttpPredicate.head(path), handler); 37 | } 38 | 39 | default HttpServerRoutes index(final BiFunction> handler) { 40 | return route(INDEX_PREDICATE, handler); 41 | } 42 | 43 | default HttpServerRoutes options(String path, 44 | BiFunction> handler) { 45 | return route(HttpPredicate.options(path), handler); 46 | } 47 | 48 | default HttpServerRoutes post(String path, 49 | BiFunction> handler) { 50 | return route(HttpPredicate.post(path), handler); 51 | } 52 | 53 | default HttpServerRoutes put(String path, 54 | BiFunction> handler) { 55 | return route(HttpPredicate.put(path), handler); 56 | } 57 | 58 | HttpServerRoutes route(Predicate condition, 59 | BiFunction> handler); 60 | 61 | ... 62 | } 63 | ``` 64 | For the design of router rules,as I mentioned before, we can use a List to store these rules in our implementation class of `HttpServerRoutes` which is `DefaultHttpServerRoutes`. The next thing we need to do is to put every rule into this list. Since what we only do is just adding elements into this list, so we don't need to return anything. So, we can use `Consumer` to deal with this process. For the rule matching, it's basically predication for conditions. So, we can use `Predicate` to do this,Since we have designed different `BiFunction>` for different rules,So we can combine them together. Thus, in Reactor-Netty, we'll have `reactor.netty.http.server.DefaultHttpServerRoutes.HttpRouteHandler`. 65 | 66 | ```java 67 | //reactor.netty.http.server.DefaultHttpServerRoutes.HttpRouteHandler 68 | static final class HttpRouteHandler 69 | implements BiFunction>, 70 | Predicate { 71 | 72 | final Predicate condition; 73 | final BiFunction> 74 | handler; 75 | final Function> resolver; 76 | 77 | HttpRouteHandler(Predicate condition, 78 | BiFunction> handler, 79 | @Nullable Function> resolver) { 80 | this.condition = Objects.requireNonNull(condition, "condition"); 81 | this.handler = Objects.requireNonNull(handler, "handler"); 82 | this.resolver = resolver; 83 | } 84 | 85 | @Override 86 | public Publisher apply(HttpServerRequest request, 87 | HttpServerResponse response) { 88 | return handler.apply(request.paramsResolver(resolver), response); 89 | } 90 | 91 | @Override 92 | public boolean test(HttpServerRequest o) { 93 | return condition.test(o); 94 | } 95 | } 96 | ``` 97 | Here, we may need to resolve parameters in requests. So, this class provides us a interface which is `Function>`. For `condition` and `resolver`, you can follow what we said in the above sections. 98 | At this point, `HttpRouteHandler` will play the role of the request validator and the request handler. At this point, we'll combine them together by a series of logics to become a whole process. Thus, we can use Proxy Pattern to achieve this here. 99 | 100 | In` DefaultHttpServerRoutes`, we use a list to manage a bunch of `HttpRouteHandler` instances. When we use `reactor.netty.http.server.HttpServer#handle`, we'll only see the implementation of `BiFunction>`. Thus, all of logic handling should be integrated into the apply() method. Thus, we'll get the following `reactor.netty.http.server.DefaultHttpServerRoutes`: 101 | 102 | ```java 103 | //reactor.netty.http.server.DefaultHttpServerRoutes 104 | final class DefaultHttpServerRoutes implements HttpServerRoutes { 105 | 106 | 107 | private final CopyOnWriteArrayList handlers = 108 | new CopyOnWriteArrayList<>(); 109 | ... 110 | @Override 111 | public HttpServerRoutes route(Predicate condition, 112 | BiFunction> handler) { 113 | Objects.requireNonNull(condition, "condition"); 114 | Objects.requireNonNull(handler, "handler"); 115 | 116 | if (condition instanceof HttpPredicate) { 117 | handlers.add(new HttpRouteHandler(condition, 118 | handler, 119 | (HttpPredicate) condition)); 120 | } 121 | else { 122 | handlers.add(new HttpRouteHandler(condition, handler, null)); 123 | } 124 | return this; 125 | } 126 | 127 | @Override 128 | public Publisher apply(HttpServerRequest request, HttpServerResponse response) { 129 | final Iterator iterator = handlers.iterator(); 130 | HttpRouteHandler cursor; 131 | 132 | try { 133 | while (iterator.hasNext()) { 134 | cursor = iterator.next(); 135 | if (cursor.test(request)) { 136 | return cursor.apply(request, response); 137 | } 138 | } 139 | } 140 | catch (Throwable t) { 141 | Exceptions.throwIfJvmFatal(t); 142 | return Mono.error(t); //500 143 | } 144 | 145 | return response.sendNotFound(); 146 | } 147 | ... 148 | } 149 | ``` 150 | In the `route()` method, it just creates a new instance of `HttpRouteHandler` and put this handler into list. Then in the implementation of `apply()`, we can combine all logic into this method. So that, we can design a route method in `reactor.netty.http.server.HttpServer` to provide a `SPI` . Then we should put the whole handling process into this method. It means we need to get an instance of `HttpServerRoutes`, then invoke its `route()` method to build the matching rules. For how to build the matching rules, please check the above sections. Finally, we'll get `HttpServerRoutes`. It plays the role of `BiFunction>`. Then we can use this as the argument of `HttpServer#handle`. 151 | 152 | Furthermore,we need to be mindful here. For the `apply()` method implemented in `DefaultHttpServerRoutes`, we can see that once the request match the rule, we will start to handle the request and return the result. We don't need to do a matchup any more. That means when we receive a new request, we will only invoke the first matched handler. 153 | 154 | 155 | ```java 156 | //reactor.netty.http.server.HttpServer#route 157 | public final HttpServer route(Consumer routesBuilder) { 158 | Objects.requireNonNull(routesBuilder, "routeBuilder"); 159 | HttpServerRoutes routes = HttpServerRoutes.newRoutes(); 160 | routesBuilder.accept(routes); 161 | return handle(routes); 162 | } 163 | ``` 164 | 165 | 166 | So that, we can apply the above design to the following `Demo`. 167 | 168 | ```java 169 | import reactor.core.publisher.Mono; 170 | import reactor.netty.DisposableServer; 171 | import reactor.netty.http.server.HttpServer; 172 | 173 | public class Application { 174 | 175 | public static void main(String[] args) { 176 | DisposableServer server = 177 | HttpServer.create() 178 | .route(routes -> 179 | routes.get("/hello", <1> 180 | (request, response) -> response.sendString(Mono.just("Hello World!"))) 181 | .post("/echo", <2> 182 | (request, response) -> response.send(request.receive().retain())) 183 | .get("/path/{param}", <3> 184 | (request, response) -> response.sendString(Mono.just(request.param("param"))))) 185 | .bindNow(); 186 | 187 | server.onDispose() 188 | .block(); 189 | } 190 | } 191 | ``` 192 | 193 | At` <1>`, when we send out a `GET` request to `access /hello`, then we'll get a string which is `Hello World!`. 194 | 195 | At `<2>`,when we send out a `POST` request to `access /echo`, then Server will use the request body as responding content to return. 196 | 197 | At `<3>`,when we send out a `GET` request to `access /path/{param}`, then we'll get the value of `param` in requesting URL. 198 | 199 | For the use of `SSE`, we can see the following demo. The details of codes will not be explained in detail, please check comments. 200 | 201 | 202 | ```java 203 | import com.fasterxml.jackson.databind.ObjectMapper; 204 | import io.netty.buffer.ByteBuf; 205 | import io.netty.buffer.ByteBufAllocator; 206 | import org.reactivestreams.Publisher; 207 | import reactor.core.publisher.Flux; 208 | import reactor.netty.DisposableServer; 209 | import reactor.netty.http.server.HttpServer; 210 | import reactor.netty.http.server.HttpServerRequest; 211 | import reactor.netty.http.server.HttpServerResponse; 212 | 213 | import java.io.ByteArrayOutputStream; 214 | import java.nio.charset.Charset; 215 | import java.time.Duration; 216 | import java.util.function.BiFunction; 217 | 218 | public class Application { 219 | 220 | public static void main(String[] args) { 221 | DisposableServer server = 222 | HttpServer.create() 223 | .route(routes -> routes.get("/sse", serveSse())) 224 | .bindNow(); 225 | 226 | server.onDispose() 227 | .block(); 228 | } 229 | 230 | /** 231 | * Prepare SSE response 232 | * Please check reactor.netty.http.server.HttpServerResponse#sse, then you can know * its "Content-Type" is "text/event-stream". 233 | * If Publisher emits the element, then we'll flush it. 234 | */ 235 | private static BiFunction> serveSse() { 236 | Flux flux = Flux.interval(Duration.ofSeconds(10)); 237 | return (request, response) -> 238 | response.sse() 239 | .send(flux.map(Application::toByteBuf), b -> true); 240 | } 241 | 242 | /** 243 | * Transformming elements that will be sent out from Object to ByteBuf 244 | */ 245 | private static ByteBuf toByteBuf(Object any) { 246 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 247 | try { 248 | out.write("data: ".getBytes(Charset.defaultCharset())); 249 | MAPPER.writeValue(out, any); 250 | out.write("\n\n".getBytes(Charset.defaultCharset())); 251 | } 252 | catch (Exception e) { 253 | throw new RuntimeException(e); 254 | } 255 | return ByteBufAllocator.DEFAULT 256 | .buffer() 257 | .writeBytes(out.toByteArray()); 258 | } 259 | 260 | private static final ObjectMapper MAPPER = new ObjectMapper(); 261 | } 262 | ``` -------------------------------------------------------------------------------- /demo-reactor/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /demo-reactor/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.dockerx 7 | demo-reactor 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | demo-reactor 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.1.0.RELEASE 18 | 19 | 20 | 21 | 22 | 23 | UTF-8 24 | UTF-8 25 | 1.9 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-test 37 | test 38 | 39 | 40 | io.projectreactor 41 | reactor-core 42 | 43 | 44 | io.projectreactor 45 | reactor-test 46 | test 47 | 48 | 49 | 50 | junit 51 | junit 52 | test 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-maven-plugin 61 | 62 | 63 | 64 | 65 | 95 | 96 | 97 | 98 | 99 | io.projectreactor 100 | reactor-bom 101 | LATEST 102 | pom 103 | import 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /demo-reactor/src/main/java/com/dockerx/demoreactor/DemoRecatorApplication.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoRecatorApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoRecatorApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demo-reactor/src/main/java/com/dockerx/demoreactor/Unsafe_test.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor; 2 | 3 | /** 4 | * @author Author 知秋 5 | * @email fei6751803@163.com 6 | * @time Created by Auser on 2018/6/3 13:19. 7 | */ 8 | public class Unsafe_test { 9 | public static void main(String[] args) { 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demo-reactor/src/main/java/com/dockerx/demoreactor/edu/Books.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor.edu; 2 | 3 | 4 | import java.util.function.Function; 5 | 6 | /** 7 | * @author: ZhiQiu 8 | * @email: fei6751803@163.com 9 | * @date: 2019/3/26 22:54. 10 | * 此处的Function属于外包策略, 11 | * 专门针对Books的一种逻辑,假如教师有特殊实现也可以设定一个 12 | */ 13 | public class Books implements Function { 14 | 15 | private String name; 16 | 17 | public Books() { 18 | } 19 | 20 | public Books(String name) { 21 | this.name = name; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | 29 | @Override 30 | public Student apply(Student student) { 31 | if (student.getGrade()==Grade.ONE){ 32 | return student; 33 | } 34 | return new Student(Grade.UNASSIGNED,new Mark(0),new Books("不及格大学士")); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /demo-reactor/src/main/java/com/dockerx/demoreactor/edu/Grade.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor.edu; 2 | 3 | /** 4 | * @author: ZhiQiu 5 | * @email: fei6751803@163.com 6 | * @date: 2019/3/26 23:02. 7 | */ 8 | public enum Grade { 9 | ONE,TWO,THREE,UNASSIGNED; 10 | } 11 | -------------------------------------------------------------------------------- /demo-reactor/src/main/java/com/dockerx/demoreactor/edu/Mark.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor.edu; 2 | 3 | /** 4 | * @author: ZhiQiu 5 | * @email: fei6751803@163.com 6 | * @date: 2019/3/26 23:24. 7 | */ 8 | public class Mark { 9 | private int markponit; 10 | 11 | public Mark(int markponit) { 12 | this.markponit = markponit; 13 | } 14 | 15 | public int getMarkponit() { 16 | return markponit; 17 | } 18 | 19 | public Mark setMarkponit(int markponit) { 20 | this.markponit = markponit; 21 | return this; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /demo-reactor/src/main/java/com/dockerx/demoreactor/edu/Student.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor.edu; 2 | 3 | import java.util.function.BiFunction; 4 | import java.util.function.Function; 5 | import java.util.function.Predicate; 6 | 7 | /** 8 | * @author: ZhiQiu 9 | * @email: fei6751803@163.com 10 | * @date: 2019/3/26 22:53. 11 | */ 12 | public class Student implements BiFunction,Teacher> { 13 | 14 | private Function teacherBiFunction; 15 | private Predicate predicate; 16 | 17 | private Grade grade; 18 | 19 | private Mark mark; 20 | /** 21 | * Books有对应的Function实现, 22 | * 这样,只要有这个字段也就同时又了这个Function逻辑, 23 | * 属于一箭双雕的玩法,同样,此类中如果有教师这个字段, 24 | * 也可以给教师设定一个Function实现, 25 | * 这样就可以做到灵活应用,所以在只需要books对应的 26 | * Function逻辑的时候,无须在构造器处专门传入此Function实现, 27 | * 这也是这种设计的核心,将特殊实现外包即可,即books与teacher都可能有各自的Function实现 28 | * 此为球员自带体系设计模式 29 | */ 30 | private Books books; 31 | 32 | public Student(Function teacherBiFunction, Predicate predicate) { 33 | this.teacherBiFunction = teacherBiFunction; 34 | this.predicate = predicate; 35 | } 36 | 37 | public Student(Grade grade, Mark mark,Books books) { 38 | this.grade = grade; 39 | this.mark = mark; 40 | this.books =books; 41 | } 42 | 43 | public Books getBooks() { 44 | return books; 45 | } 46 | 47 | public Student setBooks(Books books) { 48 | this.books = books; 49 | return this; 50 | } 51 | 52 | public Grade getGrade() { 53 | return grade; 54 | } 55 | 56 | public Mark getMark() { 57 | return mark; 58 | } 59 | 60 | public Student setMark(Mark mark) { 61 | this.mark = mark; 62 | return this; 63 | } 64 | 65 | public Student setGrade(Grade grade) { 66 | this.grade = grade; 67 | return this; 68 | } 69 | 70 | 71 | @Override 72 | public Teacher apply(Student student, Function studentStudentFunction) { 73 | if (this.predicate!=null&&this.predicate.test(student)) { 74 | if (teacherBiFunction != null){ 75 | Student student1 = studentStudentFunction.apply(student); 76 | return teacherBiFunction.apply(student1); 77 | } 78 | } 79 | return new Teacher(Grade.UNASSIGNED,books); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /demo-reactor/src/main/java/com/dockerx/demoreactor/edu/StudentPro.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor.edu; 2 | 3 | import java.util.function.BiFunction; 4 | import java.util.function.Function; 5 | import java.util.function.Predicate; 6 | 7 | /** 8 | * @author: ZhiQiu 9 | * @email: fei6751803@163.com 10 | * @date: 2019/3/27 0:44. 11 | * 属于球队集中式设计 12 | */ 13 | public class StudentPro implements Function { 14 | private BiFunction, StudentPro, Teacher> teacherBiFunction; 15 | private Predicate predicate; 16 | 17 | private Grade grade; 18 | 19 | private Mark mark; 20 | private Books books; 21 | 22 | public StudentPro(BiFunction, StudentPro, Teacher> teacherBiFunction, 23 | Predicate predicate) { 24 | this.teacherBiFunction = teacherBiFunction; 25 | this.predicate = predicate; 26 | } 27 | 28 | 29 | public StudentPro(Grade grade, Mark mark, Books books) { 30 | this.grade = grade; 31 | this.mark = mark; 32 | this.books = books; 33 | } 34 | 35 | public Books getBooks() { 36 | return books; 37 | } 38 | 39 | public StudentPro setBooks(Books books) { 40 | this.books = books; 41 | return this; 42 | } 43 | 44 | public Grade getGrade() { 45 | return grade; 46 | } 47 | 48 | public Mark getMark() { 49 | return mark; 50 | } 51 | 52 | public StudentPro setMark(Mark mark) { 53 | this.mark = mark; 54 | return this; 55 | } 56 | 57 | public StudentPro setGrade(Grade grade) { 58 | this.grade = grade; 59 | return this; 60 | } 61 | 62 | public Teacher getTeacher(StudentPro student) { 63 | if (this.predicate != null && this.predicate.test(student)) { 64 | if (teacherBiFunction != null) { 65 | 66 | return teacherBiFunction.apply(this, student); 67 | } 68 | } 69 | return new Teacher(Grade.UNASSIGNED, new Books("不及格小教师")); 70 | } 71 | 72 | @Override 73 | public StudentPro apply(StudentPro student) { 74 | if (student.getGrade() == Grade.THREE) { 75 | return student; 76 | } 77 | return new StudentPro(Grade.UNASSIGNED, new Mark(0), new Books("不及格大学士")); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /demo-reactor/src/main/java/com/dockerx/demoreactor/edu/Teacher.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor.edu; 2 | 3 | /** 4 | * @author: ZhiQiu 5 | * @email: fei6751803@163.com 6 | * @date: 2019/3/26 22:54. 7 | */ 8 | public class Teacher { 9 | private Grade grade; 10 | private Books books; 11 | 12 | public Teacher(Grade grade,Books books) { 13 | this.grade = grade; 14 | this.books =books; 15 | } 16 | 17 | public Grade getGrade() { 18 | return grade; 19 | } 20 | 21 | public Teacher setGrade(Grade grade) { 22 | this.grade = grade; 23 | return this; 24 | } 25 | 26 | public Books getBooks() { 27 | return books; 28 | } 29 | 30 | public Teacher setBooks(Books books) { 31 | this.books = books; 32 | return this; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /demo-reactor/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyinchen/Java-programming-methodology-Reactor-articles/e7423d3c0411c98968795fccdc444027dcb95683/demo-reactor/src/main/resources/application.properties -------------------------------------------------------------------------------- /demo-reactor/src/test/java/com/dockerx/demoreactor/DemoContext.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor; 2 | 3 | import org.junit.Test; 4 | import reactor.core.publisher.Mono; 5 | import reactor.test.StepVerifier; 6 | import reactor.util.context.Context; 7 | import reactor.util.function.Tuple2; 8 | import reactor.util.function.Tuples; 9 | 10 | import java.util.Optional; 11 | 12 | /** 13 | * @author Author 知秋 14 | * @email fei6751803@163.com 15 | * @time Created by Auser on 2018/6/14 0:51. 16 | */ 17 | public class DemoContext { 18 | @Test 19 | public void contextSimple1() { 20 | String key = "message"; 21 | Mono r = Mono.just("Hello") 22 | .flatMap( s -> Mono.subscriberContext() //<2> 23 | .map( ctx -> s + " " + ctx.get(key))) //<3> 24 | .subscriberContext(ctx -> ctx.put(key, "World")); //<1> 25 | r.subscribe(System.out::println); 26 | StepVerifier.create(r) 27 | .expectNext("Hello World") //<4> 28 | .verifyComplete(); 29 | } 30 | 31 | @Test 32 | public void contextSimple2() { 33 | String key = "message"; 34 | Mono r = Mono.subscriberContext() // <1> 35 | .map( ctx -> ctx.put(key, "Hello")) // <2> 36 | .doOnNext(ctx -> { 37 | System.out.println((String) ctx.get(key));}) 38 | .flatMap( ctx -> Mono.subscriberContext()) // <3> 39 | 40 | .map( ctx -> ctx.getOrDefault(key,"Default")); // <4> 41 | r.subscribe(System.out::println); 42 | 43 | // StepVerifier.create(r) 44 | // .expectNext("Default") // <5> 45 | // .verifyComplete(); 46 | } 47 | 48 | 49 | private static final String HTTP_CORRELATION_ID = "reactive.http.library.correlationId"; 50 | Mono> doPut(String url, Mono data) { 51 | Mono>> dataAndContext = 52 | data.zipWith(Mono.subscriberContext() 53 | .map(c -> c.getOrEmpty(HTTP_CORRELATION_ID))); 54 | return dataAndContext 55 | .handle((dac, sink) -> { 56 | if (dac.getT2().isPresent()) { 57 | sink.next("PUT <" + dac.getT1() + "> sent to " + url + " with header X-Correlation-ID = " + dac.getT2().get()); 58 | } 59 | else { 60 | sink.next("PUT <" + dac.getT1() + "> sent to " + url); 61 | } 62 | sink.complete(); 63 | }) 64 | .map(msg -> Tuples.of(200, msg)); 65 | } 66 | Mono> doPut0(String url, Mono data) { 67 | Mono>> dataAndContext = 68 | data.zipWith(Mono.subscriberContext() 69 | .map(c -> c.getOrEmpty(HTTP_CORRELATION_ID))); 70 | return dataAndContext 71 | .map(dac -> { 72 | if (dac.getT2().isPresent()) { 73 | return "PUT <" + dac.getT1() + "> sent to " + url + " with header X-Correlation-ID = " + dac.getT2().get(); 74 | } 75 | else { 76 | return "PUT <" + dac.getT1() + "> sent to " + url; 77 | } 78 | }) 79 | .map(msg -> Tuples.of(200, msg)); 80 | } 81 | 82 | @Test 83 | public void contextForLibraryReactivePut() { 84 | Mono put = doPut("www.example.com", Mono.just("Walter")) 85 | .subscriberContext(Context.of(HTTP_CORRELATION_ID, "2-j3r9afaf92j-afkaf")) 86 | .filter(t -> t.getT1() < 300) 87 | .map(Tuple2::getT2); 88 | StepVerifier.create(put) 89 | .expectNext("PUT sent to www.example.com with header X-Correlation-ID = 2-j3r9afaf92j-afkaf") 90 | .verifyComplete(); 91 | } 92 | @Test 93 | public void contextForLibraryReactivePutNoContext() { 94 | Mono put = doPut("www.example.com", Mono.just("Walter")) 95 | .filter(t -> t.getT1() < 300) 96 | .map(Tuple2::getT2); 97 | StepVerifier.create(put) 98 | .expectNext("PUT sent to www.example.com") 99 | .verifyComplete(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /demo-reactor/src/test/java/com/dockerx/demoreactor/DemoHooksTrace.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import org.reactivestreams.Subscription; 6 | import reactor.core.CoreSubscriber; 7 | import reactor.core.publisher.*; 8 | import reactor.test.StepVerifier; 9 | 10 | /** 11 | * @author Author 知秋 12 | * @email fei6751803@163.com 13 | * @time Created by Auser on 2018/6/26 1:23. 14 | */ 15 | public class DemoHooksTrace { 16 | @Test 17 | public void testTrace() { 18 | Hooks.onOperatorDebug(); 19 | try { 20 | Mono.fromCallable(() -> { 21 | throw new RuntimeException(); 22 | }) 23 | .map(d -> d) 24 | .block(); 25 | } 26 | catch(Exception e){ 27 | e.printStackTrace(); 28 | Assert.assertTrue(e.getSuppressed()[0].getMessage().contains("MonoCallable")); 29 | return; 30 | } 31 | finally { 32 | Hooks.resetOnOperatorDebug(); 33 | } 34 | // throw new IllegalStateException(); 35 | } 36 | @Test 37 | public void testTrace1() { 38 | Hooks.onOperatorDebug(); 39 | try { 40 | Mono.fromCallable(() -> { 41 | System.out.println("1"); 42 | return 1; 43 | }) 44 | .map(d -> d) 45 | .subscribe(e->{throw new RuntimeException();}); 46 | } 47 | catch(Exception e){ 48 | e.printStackTrace(); 49 | Assert.assertTrue(e.getSuppressed()[0].getMessage().contains("MonoCallable")); 50 | return; 51 | } 52 | finally { 53 | Hooks.resetOnOperatorDebug(); 54 | } 55 | // throw new IllegalStateException(); 56 | } 57 | 58 | 59 | /* @Test 60 | public void lastOperatorTest() { 61 | Hooks.onLastOperator(Operators.lift((sc, sub) -> 62 | new CoreSubscriber<>() { 63 | @Override 64 | public void onSubscribe(Subscription s) { 65 | sub.onSubscribe(s); 66 | } 67 | 68 | @Override 69 | public void onNext(Object o) { 70 | sub.onNext(((Integer) o) + 1); 71 | } 72 | 73 | @Override 74 | public void onError(Throwable t) { 75 | sub.onError(t); 76 | } 77 | 78 | @Override 79 | public void onComplete() { 80 | sub.onComplete(); 81 | } 82 | })); 83 | 84 | StepVerifier.create(Flux.just(1, 2, 3) 85 | .log() 86 | .log()) 87 | .expectNext(2, 3, 4) 88 | .verifyComplete(); 89 | 90 | StepVerifier.create(Mono.just(1) 91 | .log() 92 | .log()) 93 | .expectNext(2) 94 | .verifyComplete(); 95 | 96 | StepVerifier.create(ParallelFlux.from(Mono.just(1), Mono.just(1)) 97 | .log() 98 | .log()) 99 | .expectNext(2, 2) 100 | .verifyComplete(); 101 | 102 | Hooks.resetOnLastOperator(); 103 | } 104 | 105 | @Test 106 | public void lastOperatorFilterTest() { 107 | Hooks.onLastOperator(Operators.lift(sc -> sc.tags() 108 | .anyMatch(t -> t.getT1() 109 | .contains("metric")), 110 | (sc, sub) -> new CoreSubscriber<>() { 111 | @Override 112 | public void onSubscribe(Subscription s) { 113 | sub.onSubscribe(s); 114 | } 115 | 116 | @Override 117 | public void onNext(Object o) { 118 | sub.onNext(((Integer) o) + 1); 119 | } 120 | 121 | @Override 122 | public void onError(Throwable t) { 123 | sub.onError(t); 124 | } 125 | 126 | @Override 127 | public void onComplete() { 128 | sub.onComplete(); 129 | } 130 | })); 131 | 132 | StepVerifier.create(Flux.just(1, 2, 3) 133 | .tag("metric", "test") 134 | .log() 135 | .log()) 136 | .expectNext(2, 3, 4) 137 | .verifyComplete(); 138 | 139 | StepVerifier.create(Mono.just(1) 140 | .tag("metric", "test") 141 | .log() 142 | .log()) 143 | .expectNext(2) 144 | .verifyComplete(); 145 | 146 | StepVerifier.create(ParallelFlux.from(Mono.just(1), Mono.just(1)) 147 | .tag("metric", "test") 148 | .log() 149 | .log()) 150 | .expectNext(2, 2) 151 | .verifyComplete(); 152 | 153 | StepVerifier.create(Flux.just(1, 2, 3) 154 | .log() 155 | .log()) 156 | .expectNext(1, 2, 3) 157 | .verifyComplete(); 158 | 159 | StepVerifier.create(Mono.just(1) 160 | .log() 161 | .log()) 162 | .expectNext(1) 163 | .verifyComplete(); 164 | 165 | StepVerifier.create(ParallelFlux.from(Mono.just(1), Mono.just(1)) 166 | .log() 167 | .log()) 168 | .expectNext(1, 1) 169 | .verifyComplete(); 170 | 171 | Hooks.resetOnLastOperator(); 172 | }*/ 173 | 174 | /*@Test 175 | public void eachOperatorTest() { 176 | Hooks.onEachOperator(Operators.lift((sc, sub) -> 177 | new CoreSubscriber<>() { 178 | @Override 179 | public void onSubscribe(Subscription s) { 180 | sub.onSubscribe(s); 181 | } 182 | 183 | @Override 184 | public void onNext(Object o) { 185 | sub.onNext(((Integer) o) + 1); 186 | } 187 | 188 | @Override 189 | public void onError(Throwable t) { 190 | sub.onError(t); 191 | } 192 | 193 | @Override 194 | public void onComplete() { 195 | sub.onComplete(); 196 | } 197 | })); 198 | 199 | StepVerifier.create(Flux.just(1, 2, 3) 200 | .log() 201 | .log()) 202 | .expectNext(4, 5, 6) 203 | .verifyComplete(); 204 | 205 | StepVerifier.create(Mono.just(1) 206 | .log() 207 | .log()) 208 | .expectNext(4) 209 | .verifyComplete(); 210 | 211 | StepVerifier.create(ParallelFlux.from(Mono.just(1), Mono.just(1)) 212 | .log() 213 | .log()) 214 | .expectNext(6, 6) 215 | .verifyComplete(); 216 | 217 | Hooks.resetOnEachOperator(); 218 | }*/ 219 | 220 | 221 | @Test 222 | public void checkpointTest() { 223 | Flux.just(1, 0) 224 | .map(x -> 1 / x) 225 | .checkpoint("test") 226 | .subscribe(System.out::println); 227 | } 228 | 229 | @Test 230 | public void logTest() { 231 | Flux.range(1, 2) 232 | .log("Test") 233 | .subscribe(System.out::println); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /demo-reactor/src/test/java/com/dockerx/demoreactor/DemoOperator.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor; 2 | 3 | import org.junit.Test; 4 | import reactor.core.Disposable; 5 | import reactor.core.publisher.ConnectableFlux; 6 | import reactor.core.publisher.Flux; 7 | import reactor.core.scheduler.Schedulers; 8 | 9 | import java.time.Duration; 10 | import java.util.Arrays; 11 | import java.util.Random; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | import java.util.function.Function; 14 | import java.util.stream.Stream; 15 | 16 | /** 17 | * @author Author 知秋 18 | * @email fei6751803@163.com 19 | * @time Created by Auser on 2018/5/19 23:07. 20 | */ 21 | public class DemoOperator { 22 | @Test 23 | public void filter_test() { 24 | Flux.range(1, 10) 25 | .filter(i -> i % 2 == 0) 26 | .subscribe(System.out::println); 27 | } 28 | 29 | @Test 30 | public void advancedCompose() { 31 | Function, Flux> filterAndMap = 32 | f -> f.filter(color -> !color.equals("orange")) 33 | .map(String::toUpperCase); 34 | 35 | Flux.fromIterable(Arrays.asList("blue", "green", "orange", "purple")) 36 | .doOnNext(System.out::println) 37 | .transform(filterAndMap) 38 | .subscribe(d -> System.out.println("Subscriber to Transformed MapAndFilter: " + d)); 39 | } 40 | 41 | @Test 42 | public void advancedTransform() { 43 | AtomicInteger ai = new AtomicInteger(); 44 | Function, Flux> filterAndMap = f -> { 45 | if (ai.incrementAndGet() == 1) { 46 | return f.filter(color -> !color.equals("orange")) 47 | .map(String::toUpperCase); 48 | } 49 | return f.filter(color -> !color.equals("purple")) 50 | .map(String::toUpperCase); 51 | }; 52 | 53 | Flux composedFlux = 54 | Flux.fromIterable(Arrays.asList("blue", "green", "orange", "purple")) 55 | .doOnNext(System.out::println) 56 | .compose(filterAndMap); 57 | 58 | composedFlux.subscribe(d -> System.out.println("Subscriber 1 to Composed MapAndFilter :" + d)); 59 | composedFlux.subscribe(d -> System.out.println("Subscriber 2 to Composed MapAndFilter: " + d)); 60 | } 61 | 62 | @Test 63 | public void buffer_test() { 64 | Flux.range(1, 40).buffer(20).subscribe(System.out::println); 65 | System.out.println("+++++++++++++++++++++"); 66 | Flux.range(1, 10).bufferUntil(i -> i % 2 == 0).subscribe(System.out::println); 67 | Flux.range(1, 10).bufferWhile(i -> i % 2 == 0).subscribe(System.out::println); 68 | Flux.interval(Duration.ofSeconds(1)) 69 | .buffer(Duration.ofSeconds(10)) 70 | .take(2) 71 | .toStream() 72 | .forEach(System.out::println); 73 | } 74 | 75 | @Test 76 | public void window_test() { 77 | Flux.range(1, 40).window(20).subscribe(System.out::println); 78 | System.out.println("############"); 79 | Flux.range(1, 4).windowUntil(i -> i % 2 == 0).subscribe(System.out::println); 80 | System.out.println("############"); 81 | Flux.range(1, 4).windowWhile(i -> i % 2 == 0).subscribe(System.out::println); 82 | System.out.println("############"); 83 | 84 | Stream> fluxStream = Flux.interval(Duration.ofSeconds(1)) 85 | .window(Duration.ofSeconds(4)) 86 | .take(2) 87 | .toStream(); 88 | fluxStream.forEach(x -> x.toStream().forEach(System.out::print)); 89 | 90 | } 91 | 92 | @Test 93 | public void window_test2() { 94 | Flux.range(1, 40).window(20). 95 | subscribe(x -> 96 | x.subscribe(System.out::print, 97 | e -> System.out.println(e.getMessage()), 98 | () -> System.out.println("DONE!"))); 99 | System.out.println("############"); 100 | Flux.range(1, 4).windowUntil(i -> i % 2 == 0).subscribe(x -> 101 | x.subscribe(System.out::print, 102 | e -> System.out.println(e.getMessage()), 103 | () -> System.out.println("DONE!"))); 104 | System.out.println("############"); 105 | Flux.range(1, 4).windowWhile(i -> i % 2 == 0).subscribe(x -> 106 | x.subscribe(System.out::print, 107 | e -> System.out.println(e.getMessage()), 108 | () -> System.out.println("DONE!"))); 109 | System.out.println("############"); 110 | 111 | Stream> fluxStream = Flux.interval(Duration.ofSeconds(1)) 112 | .window(Duration.ofSeconds(4)) 113 | .take(2) 114 | .toStream(); 115 | fluxStream.forEach(x -> x.toStream().forEach(System.out::print)); 116 | 117 | } 118 | 119 | @Test 120 | public void buffer_window_test() { 121 | Flux.range(1, 30) 122 | .buffer(10, 5) 123 | .subscribe(System.out::println); 124 | System.out.println("+++++++++++++++++++++"); 125 | Flux.range(1, 10) 126 | .window(5, 3) 127 | .toStream() 128 | .forEach(x -> x.subscribe(t -> System.out.print(t + " "), 129 | System.out::println, 130 | () -> System.out.println("end"))); 131 | } 132 | 133 | @Test 134 | public void group_test() { 135 | Flux.just(1, 3, 5, 2, 4, 6, 11, 12, 13) 136 | .groupBy(i -> i % 2 == 0 ? "even" : "odd") 137 | .concatMap(g -> g.defaultIfEmpty(-1) //if empty groups, show them 138 | .map(String::valueOf) //map to string 139 | .startWith(g.key())) //start with the group's key 140 | .subscribe(t -> System.out.print(t + " ")); 141 | } 142 | 143 | @Test 144 | public void group_test1() { 145 | Flux.just(1, 3, 5, 2, 4, 6, 11, 12, 13) 146 | .groupBy(i -> i % 2 == 0 ? "even" : "odd") 147 | .subscribe(t -> System.out.print(t + " ")); 148 | } 149 | @Test 150 | public void group_test2() { 151 | Flux.just(1, 3, 5, 2, 4, 6, 11, 12, 13) 152 | .groupBy(i -> i % 2 == 0 ? "even" : "odd") 153 | .publishOn(Schedulers.parallel()) 154 | .subscribe(t ->{ 155 | System.out.println("Key: "+ t.key()); 156 | t.subscribe(System.out::print,e-> System.out.println(e.getMessage()),() -> System.out.println("Done!")); 157 | }); 158 | try { 159 | Thread.sleep(2000); 160 | } catch (InterruptedException e) { 161 | e.printStackTrace(); 162 | } 163 | } 164 | @Test 165 | public void merge_test() { 166 | Flux.merge(Flux.interval(Duration.ZERO, Duration.ofMillis(100)).take(5), 167 | Flux.interval(Duration.ofMillis(50), Duration.ofMillis(100)).take(5)) 168 | .toStream() 169 | .forEach(t -> System.out.print(t + " ")); 170 | System.out.println(); 171 | Flux.mergeSequential(Flux.interval(Duration.ZERO, Duration.ofMillis(100)).take(5), 172 | Flux.interval(Duration.ofMillis(50), Duration.ofMillis(100)).take(5)) 173 | .toStream() 174 | .forEach(t -> System.out.print(t + " ")); 175 | } 176 | 177 | @Test 178 | public void flatMap_test() { 179 | Flux.just(5, 10) 180 | .flatMap(x -> 181 | Flux.interval(Duration.ofMillis(x * 10), Duration.ofMillis(100)) 182 | .map(i -> x + ": " + i).take(x - 3)) 183 | .toStream() 184 | .forEach(t -> System.out.print(t + " ")); 185 | System.out.println(); 186 | Flux.just(5, 10) 187 | .flatMapSequential(x -> 188 | Flux.interval(Duration.ofMillis(x * 10), Duration.ofMillis(100)) 189 | .map(i -> x + ": " + i).take(x - 3)) 190 | .toStream() 191 | .forEach(t -> System.out.print(t + " ")); 192 | } 193 | 194 | @Test 195 | public void concatMap_test() { 196 | Flux.just(5, 10) 197 | .concatMap(x -> 198 | Flux.interval(Duration.ofMillis(x * 10), Duration.ofMillis(100)) 199 | .map(i -> x + ": " + i).take(x - 3)) 200 | .toStream() 201 | .forEach(t -> System.out.print(t + " ")); 202 | } 203 | 204 | @Test 205 | public void concatMap_test_multi() { 206 | Random random = new Random(); 207 | Flux.just(5, 10,11,8) 208 | .concatMap(x -> 209 | Flux.interval(Duration.ofMillis(random.nextInt(500))) 210 | .map(i -> x + ": " + i).take(x - 3).doOnNext(d -> System.out.println(Thread.currentThread().getName() + " OnNext: " + d))) 211 | // .doOnNext(x -> System.out.println(Thread.currentThread().getName() + " OnNext: " + x)) 212 | .subscribe(t -> { 213 | 214 | try { 215 | Thread.sleep(random.nextInt(100)); 216 | } catch (InterruptedException e) { 217 | e.printStackTrace(); 218 | } 219 | System.out.println(t + " "); 220 | }); 221 | try { 222 | Thread.sleep(15000); 223 | } catch (InterruptedException e) { 224 | e.printStackTrace(); 225 | } 226 | } 227 | 228 | @Test 229 | public void combineLatest_test() { 230 | Flux.combineLatest(Flux.interval(Duration.ofSeconds(2)) 231 | .map(x -> "Java" + x).take(5), 232 | Flux.interval(Duration.ofSeconds(1)) 233 | .map(x -> "Spring" + x).take(5), 234 | (s, f) -> f + ":" + s) 235 | .toStream() 236 | .forEach(System.out::println); 237 | } 238 | 239 | @Test 240 | public void advancedConnectable() throws InterruptedException { 241 | Flux source = Flux.interval(Duration.ofSeconds(2)) 242 | .doOnSubscribe(s -> System.out.println("subscribed to source")); 243 | 244 | //ConnectableFlux co = source.publish(); 245 | ConnectableFlux co = source.replay(2); 246 | 247 | co.subscribe(t -> System.out.println(t + " One"), e -> { 248 | }, () -> { 249 | }); 250 | co.subscribe(t -> System.out.println(t + " Two"), e -> { 251 | }, () -> { 252 | }); 253 | 254 | System.out.println("done subscribing"); 255 | Thread.sleep(500); 256 | System.out.println("will now connect"); 257 | 258 | co.connect(); 259 | //Thread.sleep(5000); 260 | Thread.sleep(10000); 261 | co.subscribe(t -> System.out.println(t + " Three"), e -> { 262 | }, () -> { 263 | }); 264 | Thread.sleep(5000); 265 | } 266 | 267 | @Test 268 | public void advancedConnectablepro() throws InterruptedException { 269 | Flux source = Flux.interval(Duration.ofSeconds(1)) 270 | .doOnSubscribe(s -> System.out.println("subscribed to source")); 271 | 272 | //Flux co = source.publish().refCount(3); 273 | Flux co = source.publish().refCount(3, Duration.ofSeconds(3)); 274 | 275 | Disposable subscribe = 276 | co.subscribe(t -> 277 | System.out.println(t + " One"), System.out::println, () -> { 278 | }); 279 | 280 | co.subscribe(t -> System.out.println(t + " Two"), System.out::println, () -> { 281 | }); 282 | Thread.sleep(3000); 283 | co.subscribe(t -> System.out.println(t + " Three"), System.out::println, () -> { 284 | }); 285 | Thread.sleep(3000); 286 | subscribe.dispose(); 287 | 288 | Thread.sleep(10000); 289 | co.subscribe(t -> System.out.println(t + " Four"), System.out::println, () -> { 290 | }); 291 | 292 | Thread.sleep(100000); 293 | } 294 | 295 | @Test 296 | public void advancedConnectablepro2() throws InterruptedException { 297 | Flux source = Flux.interval(Duration.ofSeconds(1)) 298 | .doOnSubscribe(s -> System.out.println("subscribed to source")); 299 | 300 | Flux co = source.publish().refCount(3, Duration.ofSeconds(3)); 301 | 302 | Disposable one = 303 | co.subscribe(t -> 304 | System.out.println(t + " One"), System.out::println, () -> { 305 | }); 306 | 307 | final Disposable two = co.subscribe(t -> System.out.println(t + " Two"), System.out::println, () -> { 308 | }); 309 | Thread.sleep(3000); 310 | Disposable three = co.subscribe(t -> System.out.println(t + " Three"), System.out::println, () -> { 311 | }); 312 | 313 | Thread.sleep(3000); 314 | one.dispose(); 315 | 316 | Thread.sleep(3000); 317 | two.dispose(); 318 | three.dispose(); 319 | //Thread.sleep(3000); // <1> 320 | Thread.sleep(2000); 321 | co.subscribe(t -> System.out.println(t + " Four"), System.out::println, () -> { 322 | }); 323 | 324 | Thread.sleep(5000); 325 | } 326 | 327 | @Test 328 | public void advancedConnectablepro3() throws InterruptedException { 329 | Flux source = Flux.interval(Duration.ofSeconds(1)) 330 | .doOnSubscribe(s -> System.out.println("subscribed to source")); 331 | 332 | Flux co = source.publish().refCount(3); 333 | 334 | Disposable one = 335 | co.subscribe(t -> 336 | System.out.println(t + " One"), System.out::println, () -> { 337 | }); 338 | 339 | final Disposable two = co.subscribe(t -> System.out.println(t + " Two"), System.out::println, () -> { 340 | }); 341 | Thread.sleep(3000); 342 | Disposable three = co.subscribe(t -> System.out.println(t + " Three"), System.out::println, () -> { 343 | }); 344 | 345 | Thread.sleep(3000); 346 | one.dispose(); 347 | 348 | Thread.sleep(3000); 349 | two.dispose(); 350 | three.dispose(); 351 | //Thread.sleep(3000); // <1> 352 | Thread.sleep(2000); 353 | co.subscribe(t -> System.out.println(t + " Four"), System.out::println, () -> { 354 | }); 355 | 356 | Thread.sleep(5000); 357 | } 358 | 359 | 360 | @Test 361 | public void advancedConnectablepro_publish() throws InterruptedException { 362 | Flux source = Flux.interval(Duration.ofSeconds(1)) 363 | .doOnSubscribe(s -> System.out.println("subscribed to source")); 364 | 365 | // Flux co = source.doOnNext(t-> System.out.println("The item is :"+t)).publish(3).autoConnect(); 366 | // ConnectableFlux co = source.doOnNext(t-> System.out.println("The item is :"+t)).publish(3); 367 | // co.connect(); 368 | Flux co = source.doOnNext(t-> System.out.println("The item is :"+t)) 369 | .publish(2) 370 | .refCount(); 371 | 372 | Disposable subscribe = 373 | co.subscribe(t -> 374 | System.out.println(t + " One"), System.out::println, () -> { 375 | },s ->s.request(5)); 376 | 377 | 378 | co.subscribe(t -> System.out.println(t + " Two"), System.out::println, () -> { 379 | },s ->s.request(6)); 380 | 381 | co.subscribe(t -> System.out.println(t + " Three"), System.out::println, () -> { 382 | },s ->s.request(7)); 383 | 384 | co.subscribe(t -> System.out.println(t + " Four"), System.out::println, () -> { 385 | },s ->s.request(8)); 386 | System.out.println("main thread wait for a moment"); 387 | 388 | Thread.sleep(6000); 389 | System.out.println("sleep is done"); 390 | subscribe.dispose(); 391 | Thread.sleep(100000); 392 | } 393 | 394 | @Test 395 | public void advancedConnectablepro_publish2() throws InterruptedException { 396 | Flux source = Flux.interval(Duration.ofSeconds(1)) 397 | .doOnSubscribe(s -> System.out.println("subscribed to source")); 398 | 399 | Flux co = source.doOnNext(t-> System.out.println("The item is :"+t)).publish(3).autoConnect(4); 400 | // ConnectableFlux co = source.doOnNext(t-> System.out.println("The item is :"+t)).publish(3); 401 | // co.connect(); 402 | //Flux co = source.doOnNext(t-> System.out.println("The item is :"+t)) 403 | // .publish(2) 404 | // .refCount(); 405 | 406 | Disposable subscribe = 407 | co.subscribe(t -> 408 | System.out.println(t + " One"), System.out::println, () -> { 409 | },s ->s.request(50)); 410 | 411 | 412 | co.subscribe(t -> System.out.println(t + " Two"), System.out::println, () -> { 413 | },s ->s.request(60)); 414 | 415 | co.subscribe(t -> System.out.println(t + " Three"), System.out::println, () -> { 416 | },s ->s.request(70)); 417 | 418 | co.subscribe(t -> System.out.println(t + " Four"), System.out::println, () -> { 419 | },s ->s.request(80)); 420 | System.out.println("main thread wait for a moment"); 421 | 422 | Thread.sleep(6000); 423 | System.out.println("sleep is done"); 424 | subscribe.dispose(); 425 | Thread.sleep(100000); 426 | } 427 | 428 | } 429 | -------------------------------------------------------------------------------- /demo-reactor/src/test/java/com/dockerx/demoreactor/DemoProcessor.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor; 2 | 3 | import org.junit.Test; 4 | import reactor.core.Disposable; 5 | import reactor.core.publisher.*; 6 | import reactor.util.concurrent.Queues; 7 | import sun.misc.Unsafe; 8 | 9 | import java.lang.reflect.Field; 10 | import java.util.Arrays; 11 | import java.util.Queue; 12 | import java.util.concurrent.ConcurrentLinkedQueue; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | import java.util.concurrent.atomic.AtomicInteger; 16 | import java.util.stream.IntStream; 17 | 18 | //import static org.junit.Assert.assertEquals; 19 | 20 | 21 | /** 22 | * @author Author 知秋 23 | * @email fei6751803@163.com 24 | * @time Created by Auser on 2018/5/23 23:33. 25 | */ 26 | public class DemoProcessor { 27 | @Test 28 | public void unicastProcessor_test() { 29 | UnicastProcessor unicastProcessor = UnicastProcessor.create(); 30 | FluxSink sink = unicastProcessor.sink(); 31 | sink.next("aaa"); 32 | sink.next("aaa"); 33 | sink.next("aaa"); 34 | sink.next("aaa"); 35 | Flux.just("Hello", "DockerX").subscribe(unicastProcessor); 36 | unicastProcessor.subscribe(System.out::println); 37 | // unicastProcessor.subscribe(System.out::println); /*只允许一个订阅者,打开注释执行会报错*/ 38 | sink.next("aaa"); 39 | } 40 | @Test 41 | public void directProcessor_test() { 42 | DirectProcessor directProcessor = DirectProcessor.create(); 43 | FluxSink sink = directProcessor.sink(); 44 | sink.next("000"); 45 | directProcessor.subscribe(System.out::println); 46 | sink.next("xxx"); 47 | directProcessor.subscribe(System.out::println); 48 | sink.next("aaa"); 49 | Flux.just("Hello", "DockerX").subscribe(directProcessor); 50 | sink.next("bbb"); 51 | sink.next("bbb"); 52 | directProcessor.subscribe(System.out::println); 53 | sink.next("ccc"); 54 | } 55 | @Test 56 | public void emitterProcessor_test() { 57 | EmitterProcessor emitterProcessor = EmitterProcessor.create(); 58 | FluxSink sink = emitterProcessor.sink(); 59 | sink.next("000"); 60 | Disposable subscribe = emitterProcessor.subscribe(System.out::println); 61 | sink.next("xxx"); 62 | Disposable subscribe1 = emitterProcessor.subscribe(System.out::println); 63 | sink.next("aaa"); 64 | Flux.just("Hello", "DockerX").subscribe(emitterProcessor); 65 | sink.next("bbb"); 66 | Disposable subscribe2 = emitterProcessor.subscribe(System.out::println); 67 | sink.next("ccc"); 68 | subscribe.dispose(); 69 | subscribe1.dispose(); 70 | subscribe2.dispose(); 71 | sink.next("ddd"); 72 | } 73 | @Test 74 | public void replayProcessor_test() { 75 | ReplayProcessor replayProcessor = ReplayProcessor.create(); 76 | // Flux.just("Hello", "DockerX").subscribe(replayProcessor); 77 | FluxSink sink = replayProcessor.sink(); 78 | sink.next("000"); 79 | Disposable subscribe = replayProcessor.subscribe(System.out::println); 80 | sink.next("xxx"); 81 | Disposable subscribe1 = replayProcessor.subscribe(System.out::println); 82 | sink.next("aaa"); 83 | Flux.just("Hello", "DockerX").subscribe(replayProcessor); 84 | sink.next("bbb"); 85 | Disposable subscribe2 = replayProcessor.subscribe(System.out::println); 86 | sink.next("ccc"); 87 | subscribe.dispose(); 88 | subscribe1.dispose(); 89 | subscribe2.dispose(); 90 | sink.next("ddd"); 91 | System.out.println("###################"); 92 | Disposable subscribe3 = replayProcessor.subscribe(System.out::println); 93 | } 94 | 95 | 96 | @Test 97 | public void topicProcessor_test() throws InterruptedException { 98 | //TopicProcessor topicProcessor = TopicProcessor.create(); 99 | TopicProcessor topicProcessor = TopicProcessor.share("share",Queues.SMALL_BUFFER_SIZE); 100 | //TopicProcessor topicProcessor = TopicProcessor.share("share",2); 101 | //Flux.interval(Duration.ofSeconds(1)).subscribe(topicProcessor); 102 | //Flux.just("Hello", "DockerX").subscribe(topicProcessor); 103 | //Disposable subscribe0 = topicProcessor.subscribe(System.out::println); 104 | //FluxSink sink = topicProcessor.sink(); 105 | Flux.just("Hello", "DockerX").subscribe(topicProcessor); 106 | AtomicInteger a = new AtomicInteger(); 107 | AtomicInteger b = new AtomicInteger(); 108 | AtomicInteger c = new AtomicInteger(); 109 | 110 | topicProcessor.onNext("000"); 111 | Disposable subscribe = topicProcessor.subscribe(t -> System.out.println(t + " ZERO " + a.incrementAndGet())); 112 | topicProcessor.onNext("xxx"); 113 | Disposable subscribe1 = topicProcessor.subscribe(t -> System.out.println(t + " One " + b.incrementAndGet())); 114 | topicProcessor.onNext("aaa"); 115 | // Flux.just("Hello", "DockerX").subscribe(topicProcessor); 116 | topicProcessor.onNext("bbb"); 117 | Disposable subscribe2 = topicProcessor.subscribe(t -> System.out.println(t + " Two " + c.incrementAndGet())); 118 | topicProcessor.onNext("ccc"); 119 | //subscribe.dispose(); 120 | //subscribe1.dispose(); 121 | //subscribe2.dispose(); 122 | topicProcessor.onNext("ddd"); 123 | topicProcessor.onNext("eee"); 124 | topicProcessor.onNext("fff"); 125 | topicProcessor.onNext("fff"); 126 | // topicProcessor.dispose(); 127 | Flux.just("Hello", "DockerX").doOnNext(System.out::println).subscribe(topicProcessor); 128 | topicProcessor.subscribe(t -> System.out.println(t + " Four ")); 129 | Flux.just("Hello1", "DockerX1").subscribe(topicProcessor); 130 | Thread.sleep(10000); 131 | } 132 | @Test 133 | public void topicProcessor_coldSource_test() throws InterruptedException { 134 | 135 | TopicProcessor topicProcessor = TopicProcessor.share("share",Queues.SMALL_BUFFER_SIZE); 136 | Disposable subscribe0 = topicProcessor.subscribe(t -> System.out.println(t + " XXX " )); 137 | Flux.just("Hello", "DockerX").subscribe(topicProcessor); 138 | AtomicInteger a = new AtomicInteger(); 139 | AtomicInteger b = new AtomicInteger(); 140 | AtomicInteger c = new AtomicInteger(); 141 | 142 | // Thread.sleep(1000); 143 | topicProcessor.onNext("000"); 144 | Disposable subscribe = topicProcessor.subscribe(t -> System.out.println(t + " ZERO " + a.incrementAndGet())); 145 | topicProcessor.onNext("xxx"); 146 | Disposable subscribe1 = topicProcessor.subscribe(t -> System.out.println(t + " One " + b.incrementAndGet())); 147 | topicProcessor.onNext("aaa"); 148 | 149 | topicProcessor.onNext("bbb"); 150 | Disposable subscribe2 = topicProcessor.subscribe(t -> System.out.println(t + " Two " + c.incrementAndGet())); 151 | topicProcessor.onNext("ccc"); 152 | 153 | topicProcessor.onNext("ddd"); 154 | topicProcessor.onNext("eee"); 155 | topicProcessor.onNext("fff"); 156 | topicProcessor.onNext("fff"); 157 | 158 | Flux.just("Hello", "DockerX").doOnNext(System.out::println).subscribe(topicProcessor); 159 | topicProcessor.subscribe(t -> System.out.println(t + " Four ")); 160 | Thread.sleep(10000); 161 | } 162 | @Test 163 | public void testProcessingMessages() throws Exception { 164 | int numberOfMessages = 1000; 165 | 166 | TopicProcessor processor = TopicProcessor.share("share",Queues.SMALL_BUFFER_SIZE); 167 | FluxSink sink = processor.sink(); 168 | 169 | ExecutorService executorService = Executors.newFixedThreadPool(4); 170 | 171 | Queue messages = new ConcurrentLinkedQueue<>(); 172 | 173 | processor.subscribe(messages::add); 174 | 175 | AtomicInteger counter = new AtomicInteger(0); 176 | for (int i = 0; i < numberOfMessages; i++) { 177 | executorService.submit(() -> sink.next(counter.incrementAndGet()) 178 | ); 179 | } 180 | 181 | Thread.sleep(10000); 182 | System.out.println(messages.size()); 183 | //assertEquals(numberOfMessages, messages.size()); 184 | } 185 | 186 | @Test 187 | public void testProcessingMessages2() throws Exception { 188 | int numberOfMessages = 1000; 189 | 190 | TopicProcessor processor = TopicProcessor.share("share",Queues.SMALL_BUFFER_SIZE); 191 | 192 | ExecutorService executorService = Executors.newFixedThreadPool(4); 193 | 194 | Queue messages = new ConcurrentLinkedQueue<>(); 195 | 196 | processor.subscribe(messages::add); 197 | 198 | AtomicInteger counter = new AtomicInteger(0); 199 | for (int i = 0; i < numberOfMessages; i++) { 200 | executorService.submit(() -> processor.onNext(counter.incrementAndGet()) 201 | ); 202 | } 203 | 204 | Thread.sleep(10000); 205 | System.out.println(messages.size()); 206 | //assertEquals(numberOfMessages, messages.size()); 207 | } 208 | 209 | 210 | @Test 211 | public void testWorkQueueProcessor() throws InterruptedException { 212 | WorkQueueProcessor workQueueProcessor = WorkQueueProcessor.create(); 213 | 214 | workQueueProcessor.subscribe(e -> System.out.println("One subscriber: "+e)); 215 | workQueueProcessor.subscribe(e -> System.out.println("Two subscriber: "+e)); 216 | workQueueProcessor.subscribe(e -> System.out.println("Three subscriber: "+e)); 217 | 218 | IntStream.range(1,20) 219 | .forEach(workQueueProcessor::onNext); 220 | Thread.sleep(10000); 221 | 222 | } 223 | 224 | @Test 225 | public void advancedCold() { 226 | Flux source = Flux.fromIterable(Arrays.asList("blue", "green", "orange", "purple")) 227 | .doOnNext(System.out::println) 228 | .filter(s -> s.startsWith("o")) 229 | .map(String::toUpperCase); 230 | source.subscribe(d -> System.out.println("Subscriber 1: "+d)); 231 | source.subscribe(d -> System.out.println("Subscriber 2: "+d)); 232 | } 233 | 234 | @Test 235 | public void hot() { 236 | EmitterProcessor hotSource = EmitterProcessor.create(); 237 | 238 | hotSource.subscribe(d -> System.out.println("Subscriber 1 to Hot Source: "+d)); 239 | hotSource.onNext("blue"); 240 | hotSource.onNext("green"); 241 | hotSource.subscribe(d -> System.out.println("Subscriber 2 to Hot Source: "+d)); 242 | hotSource.onNext("orange"); 243 | hotSource.onNext("purple"); 244 | hotSource.onComplete(); 245 | } 246 | @Test 247 | public void advancedHot() { 248 | UnicastProcessor hotSource = UnicastProcessor.create(); 249 | Flux hotFlux = hotSource.publish() 250 | .autoConnect() 251 | .map(String::toUpperCase); 252 | hotFlux.subscribe(d -> System.out.println("Subscriber 1 to Hot Source: "+d)); 253 | hotSource.onNext("blue"); 254 | hotSource.onNext("green"); 255 | hotFlux.subscribe(d -> System.out.println("Subscriber 2 to Hot Source: "+d)); 256 | hotSource.onNext("orange"); 257 | hotSource.onNext("purple"); 258 | hotSource.onComplete(); 259 | } 260 | 261 | public static Unsafe getUnsafeInstance() throws Exception{ 262 | Field unsafeStaticField = 263 | Unsafe.class.getDeclaredField("theUnsafe"); 264 | unsafeStaticField.setAccessible(true); 265 | return (Unsafe) unsafeStaticField.get(null); 266 | } 267 | 268 | 269 | @Test 270 | public void unsafeInstance_test() throws Exception { 271 | Unsafe u = getUnsafeInstance(); 272 | //Unsafe u = Unsafe.getUnsafe(); 273 | int[] arr = {1,2,3,4,5,6,7,8,9,10}; 274 | 275 | int b = u.arrayBaseOffset(int[].class); 276 | 277 | int s = u.arrayIndexScale(int[].class); 278 | 279 | u.putInt(arr, (long)b+s*9, 1); 280 | 281 | for(int i=0;i<10;i++){ 282 | 283 | int v = u.getInt(arr, (long)b+s*i); 284 | 285 | System.out.print(v+" "); 286 | 287 | } 288 | } 289 | static DemoProcessor de(){ 290 | return new DemoProcessor(); 291 | } 292 | @Test 293 | public void unsafeInstance_test2() { 294 | 295 | int[] arr = {1,2,3,4,5,6,7,8,9,10}; 296 | 297 | int[] b = arr; 298 | arr=null; 299 | System.out.println(Arrays.toString(b)); 300 | System.out.println(de()); 301 | System.out.println(de()); 302 | 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /demo-reactor/src/test/java/com/dockerx/demoreactor/DemoReactorApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class DemoReactorApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /demo-reactor/src/test/java/com/dockerx/demoreactor/DemoReactorFluxTest.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor; 2 | 3 | import org.junit.Test; 4 | import org.reactivestreams.Subscription; 5 | import org.springframework.scheduling.concurrent.CustomizableThreadFactory; 6 | import reactor.core.publisher.BaseSubscriber; 7 | import reactor.core.publisher.Flux; 8 | import reactor.core.publisher.Mono; 9 | import reactor.core.scheduler.Scheduler; 10 | import reactor.core.scheduler.Schedulers; 11 | import reactor.test.StepVerifier; 12 | 13 | import java.time.Duration; 14 | import java.util.*; 15 | import java.util.concurrent.*; 16 | import java.util.stream.Stream; 17 | 18 | /** 19 | * @author Author 知秋 20 | * @email fei6751803@163.com 21 | * @time Created by Auser on 2018/5/3 21:39. 22 | */ 23 | public class DemoReactorFluxTest { 24 | 25 | private static void sleep(long millis) { 26 | try { 27 | Thread.sleep(millis); 28 | } catch (InterruptedException e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | 33 | private static void log(Object msg) { 34 | sleep(20); 35 | System.out.println( 36 | Thread.currentThread().getName() + 37 | ": " + msg); 38 | } 39 | 40 | @Test 41 | public void flux_create() { 42 | Flux.create(emitter -> { 43 | 44 | for (int i = 0; i <= 1000; i++) { 45 | if (emitter.isCancelled()) { 46 | return; 47 | } 48 | System.out.println("Source created " + i); 49 | int finalI = i; 50 | new Thread(() -> { 51 | emitter.next(finalI +100); 52 | sleep(100); 53 | }).start(); 54 | emitter.next(i); 55 | } 56 | }).doOnNext(s -> System.out.println("Source pushed " + s)) 57 | .subscribeOn(Schedulers.single()) 58 | .subscribe(customer -> { 59 | sleep(10); 60 | System.out.println("所获取到的Customer的Id为: " + customer); 61 | }); 62 | sleep(10000); 63 | } 64 | 65 | @Test 66 | public void flux_createXXX() { 67 | Flux.create(emitter -> { 68 | 69 | for (int i = 0; i <= 1000; i++) { 70 | if (emitter.isCancelled()) { 71 | return; 72 | } 73 | System.out.println("Source created " + i); 74 | int finalI = i; 75 | new Thread(() -> { 76 | sleep(100); 77 | emitter.next(finalI +100); 78 | }).start(); 79 | emitter.next(i); 80 | } 81 | }).doOnNext(s -> System.out.println("Source pushed " + s)) 82 | .subscribeOn(Schedulers.parallel()) 83 | .subscribe(customer -> { 84 | sleep(10); 85 | System.out.println("所获取到的Customer的Id为: " + customer); 86 | }); 87 | sleep(10000); 88 | } 89 | 90 | @Test 91 | public void flux_create2() { 92 | Flux.create(emitter -> { 93 | 94 | for (int i = 0; i <= 1000; i++) { 95 | if (emitter.isCancelled()) { 96 | return; 97 | } 98 | System.out.println("Source created " + i); 99 | emitter.next(i); 100 | } 101 | }).doOnNext(s -> System.out.println("Source pushed " + s)) 102 | .subscribeOn(Schedulers.newElastic("aaa")) 103 | .subscribe(customer -> { 104 | sleep(1000); 105 | System.out.println("所获取到的Customer的Id为: " + customer); 106 | }); 107 | sleep(10000); 108 | } 109 | 110 | 111 | public static Scheduler custom_Scheduler() { 112 | ThreadFactory threadFactory = new CustomizableThreadFactory("自定义线程池--"); 113 | 114 | Executor executor = new ThreadPoolExecutor( 115 | 10, //corePoolSize 116 | 10, //maximumPoolSize 117 | 0L, TimeUnit.MILLISECONDS, //keepAliveTime, unit 118 | new LinkedBlockingQueue<>(1000), //workQueue 119 | threadFactory 120 | ); 121 | return Schedulers.fromExecutor(executor); 122 | } 123 | 124 | @Test 125 | public void flux_generate() { 126 | final Random random = new Random(); 127 | Flux.generate(ArrayList::new, (list, sink) -> { 128 | int value = random.nextInt(100); 129 | list.add(value); 130 | sink.next(value); 131 | if (list.size() == 5) { 132 | sink.complete(); 133 | } 134 | return list; 135 | }).doOnNext(item -> System.out.println("emitted on thread " 136 | + Thread.currentThread().getName() + " " + item)) 137 | .publishOn(Schedulers.parallel()) 138 | 139 | .map(x -> String.format("[%s] %s", Thread.currentThread().getName(), x)) 140 | .publishOn(Schedulers.elastic()) 141 | .map(x -> String.format("[%s] %s", Thread.currentThread().getName(), x)) 142 | .subscribeOn(Schedulers.parallel()) 143 | .subscribeOn(custom_Scheduler()).subscribe(DemoReactorFluxTest::log); 144 | sleep(10000); 145 | } 146 | 147 | @Test 148 | public void flux_generate2() { 149 | final Random random = new Random(); 150 | Flux.generate(ArrayList::new, (list, sink) -> { 151 | int value = random.nextInt(100); 152 | list.add(value); 153 | // change the state value in the sink.next 154 | new Thread(() -> sink.next(value)).start(); 155 | new Thread(() -> sink.next(value)).start(); 156 | 157 | //sink.next(value); 158 | if (list.size() == 5) { 159 | sink.complete(); 160 | } 161 | return list; 162 | }).doOnNext(item -> System.out.println("emitted on thread " 163 | + Thread.currentThread().getName() + " " + item)) 164 | .subscribeOn(Schedulers.parallel()) 165 | .parallel().runOn(Schedulers.parallel()) 166 | .map(x -> String.format("[%s] %s", Thread.currentThread().getName(), x)) 167 | .subscribe(DemoReactorFluxTest::log); 168 | sleep(10000); 169 | } 170 | 171 | @Test 172 | public void flux_generate3() { 173 | final Random random = new Random(); 174 | Flux.generate(ArrayList::new, (list, sink) -> { 175 | int value = random.nextInt(100); 176 | list.add(value); 177 | System.out.println("所发射元素产生的线程: "+Thread.currentThread().getName()); 178 | sink.next(value); 179 | // sink.next(2); 180 | sleep(1000); 181 | if (list.size() == 20) { 182 | sink.complete(); 183 | } 184 | return list; 185 | }).publishOn(custom_Scheduler(),1) 186 | .map(x -> String.format("[%s] %s", Thread.currentThread().getName(), x)) 187 | .subscribe(DemoReactorFluxTest::log); 188 | sleep(20000); 189 | } 190 | 191 | @Test 192 | public void flux_generate4() { 193 | final Random random = new Random(); 194 | Flux.generate(ArrayList::new, (list, sink) -> { 195 | int value = random.nextInt(100); 196 | list.add(value); 197 | System.out.println("所发射元素产生的线程: "+Thread.currentThread().getName()); 198 | sink.next(value); 199 | sleep(1000); 200 | if (list.size() == 20) { 201 | sink.complete(); 202 | } 203 | return list; 204 | }).publishOn(custom_Scheduler(),1) 205 | .map(x -> String.format("[%s] %s", Thread.currentThread().getName(), x)) 206 | .subscribe(System.out::println); 207 | sleep(20000); 208 | } 209 | 210 | 211 | public static T delayCalculation0(T value) { 212 | // sleep(ThreadLocalRandom.current().nextInt(1000)); 213 | return value; 214 | } 215 | 216 | public static T delayCalculation(T value) { 217 | sleep(ThreadLocalRandom.current().nextInt(1000)); 218 | System.out.println( 219 | Thread.currentThread().getName() + 220 | ": " + value); 221 | return value; 222 | } 223 | 224 | @Test 225 | public void custom_Scheduler_test1() { 226 | Flux.create(sink -> { 227 | for (int i = 0; i < 10; i++) { 228 | sink.next("1 " + Thread.currentThread().getName() + " 2"); 229 | } 230 | sink.complete(); 231 | }) 232 | .publishOn(custom_Scheduler()) 233 | .publishOn(Schedulers.parallel()) 234 | .subscribe(DemoReactorFluxTest::log); 235 | sleep(10000); 236 | } 237 | 238 | @Test 239 | public void custom_Scheduler_test2() { 240 | Flux.range(1, 10) 241 | .parallel().runOn(Schedulers.parallel()) 242 | .map(x -> String.format("[%s] %s", Thread.currentThread().getName(), x)) 243 | .subscribe(System.out::println); 244 | sleep(10000); 245 | } 246 | 247 | @Test 248 | public void flux_publishOn_subscribeOn() { 249 | Scheduler scheduler = Schedulers.fromExecutor(Executors.newFixedThreadPool(10)); 250 | Flux.create(sink -> { 251 | for (int i = 0; i < 10; i++) { 252 | sink.next(i+" $"); 253 | System.out.println( 254 | Thread.currentThread().getName()); 255 | // sleep(1000); 256 | } 257 | sink.complete(); 258 | }) 259 | // .doOnNext(x -> System.out.println(Thread.currentThread().getName() + "111111")) 260 | .publishOn(Schedulers.parallel()) 261 | .map(DemoReactorFluxTest::delayCalculation0) 262 | .doOnNext(x -> System.out.println(Thread.currentThread().getName() + "111111")) 263 | .map(x -> String.format("[%s] %s", Thread.currentThread().getName()+" first", x)) 264 | .publishOn(Schedulers.parallel()) 265 | .map(x -> String.format("[%s] %s", Thread.currentThread().getName()+" Ss", x)) 266 | .subscribe(msg->System.out.println( 267 | Thread.currentThread().getName() + 268 | ": " + msg)); 269 | sleep(1000); 270 | } 271 | @Test 272 | public void flux_publishOn_subscribeOn1() { 273 | Scheduler scheduler = Schedulers.fromExecutor(Executors.newFixedThreadPool(10)); 274 | Flux.create(sink -> { 275 | for (int i = 0; i < 10; i++) { 276 | sink.next(i+" $"); 277 | System.out.println( 278 | Thread.currentThread().getName()); 279 | // sleep(1000); 280 | } 281 | sink.complete(); 282 | }) 283 | // .doOnNext(x -> System.out.println(Thread.currentThread().getName() + "111111")) 284 | //.publishOn(Schedulers.parallel()) 285 | .map(DemoReactorFluxTest::delayCalculation0) 286 | .doOnNext(x -> System.out.println(Thread.currentThread().getName() + "111111")) 287 | .map(x -> String.format("[%s] %s", Thread.currentThread().getName()+" first", x)) 288 | .publishOn(Schedulers.elastic()) 289 | .map(x -> String.format("[%s] %s", Thread.currentThread().getName()+" Ss", x)) 290 | .subscribe(DemoReactorFluxTest::delayCalculation); 291 | sleep(1000); 292 | } 293 | 294 | @Test 295 | public void DemoSubscriber_test() { 296 | Flux.just("Hello", "DockerX").subscribe(new DemoSubscriber<>()); 297 | } 298 | 299 | @Test 300 | public void DemoSubscriber_test2() { 301 | Flux.just("Hello", "DockerX").subscribe(new BaseSubscriber<>() { 302 | @Override 303 | protected void hookOnNext(String value) { 304 | System.out.println(value); 305 | } 306 | 307 | @Override 308 | protected void hookOnSubscribe(Subscription subscription) { 309 | System.out.println("Subscribed"); 310 | requestUnbounded(); 311 | } 312 | }); 313 | } 314 | @Test 315 | public void DemoSubscriber_test_flux_generate3() { 316 | final Random random = new Random(); 317 | Flux.generate(ArrayList::new, (list, sink) -> { 318 | int value = random.nextInt(100); 319 | list.add(value); 320 | System.out.println("所发射元素产生的线程: "+Thread.currentThread().getName()); 321 | sink.next(value); 322 | sleep(2); 323 | if (list.size() == 6) { 324 | sink.complete(); 325 | } 326 | return list; 327 | }) 328 | .publishOn(Schedulers.elastic(),2) 329 | // .publishOn(custom_Scheduler(),2) 330 | .map(x -> String.format("[%s] %s", Thread.currentThread().getName(), x)) 331 | .subscribe(System.out::println); 332 | sleep(20000); 333 | } 334 | @Test 335 | public void flux_subscribeOn() { 336 | final Random random = new Random(); 337 | Flux.create(sink -> { 338 | List list=new ArrayList<>(); 339 | Integer i=0; 340 | while (list.size() != 100) { 341 | int value = random.nextInt(100); 342 | list.add(value); 343 | i=i+1; 344 | System.out.println("所发射元素产生的线程: "+Thread.currentThread().getName()+" num: "+i); 345 | sink.next(value); 346 | sleep(10); 347 | } 348 | sink.complete(); 349 | 350 | }).doOnRequest(x-> System.out.println("...1"+Thread.currentThread().getName())) 351 | // .subscribeOn(Schedulers.elastic()) 352 | .doOnRequest(x-> System.out.println("...2"+Thread.currentThread().getName())) 353 | .subscribeOn(Schedulers.elastic(),false) 354 | .publishOn(custom_Scheduler(),2) 355 | .map(x -> String.format("[%s] %s", Thread.currentThread().getName(), x)) 356 | .subscribe(System.out::println); 357 | sleep(10000); 358 | } 359 | @Test 360 | public void flux_creates() { 361 | Flux.just("Hello", "DockerX").subscribe(System.out::println); 362 | Flux.fromArray(new Integer[]{1, 2, 3, 4}).subscribe(System.out::println); 363 | Flux.empty().subscribe(System.out::println); 364 | Flux.range(1, 10).subscribe(System.out::println); 365 | Flux.interval(Duration.ofMillis(1000)).subscribe(System.out::println); 366 | sleep(10000); 367 | } 368 | 369 | @Test 370 | public void stream_test() { 371 | 372 | Stream.of("1", "2", "3").parallel().map(s -> { 373 | System.out.println(Thread.currentThread().getName()); 374 | return s.toUpperCase(); 375 | }).forEach(System.out::println); 376 | 377 | } 378 | 379 | @Test 380 | public void mono_creates() { 381 | Mono.fromCallable(() -> "Hello DockerX").subscribe(System.out::println); 382 | Mono.fromSupplier(() -> "Hello DockerX").subscribe(System.out::println); 383 | Mono.justOrEmpty(Optional.of("Hello DockerX")).subscribe(System.out::println); 384 | Mono.create(sink -> sink.success("Hello DockerX")).subscribe(System.out::println); 385 | Mono.ignoreElements(Mono.fromCallable(() -> "Hello DockerX")). 386 | subscribe(System.out::println,System.out::println,()-> System.out.println("done")); 387 | } 388 | 389 | @Test 390 | public void cast() { 391 | Father sample = new Son(); 392 | System.out.println("调用成员: " + sample.name); 393 | System.out.print("调用方法: "); 394 | sample.method(); 395 | 396 | } 397 | 398 | 399 | interface MyEventListener { 400 | 401 | void onDataChunk(List chunk); 402 | void processComplete(); 403 | } 404 | 405 | interface MyEventProcessor { 406 | void register(MyEventListener eventListener); 407 | void dataChunk(String... values); 408 | void processComplete(); 409 | } 410 | 411 | private MyEventProcessor myEventProcessor = new MyEventProcessor() { 412 | 413 | private MyEventListener eventListener; 414 | private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 415 | 416 | @Override 417 | public void register(MyEventListener eventListener) { 418 | this.eventListener = eventListener; 419 | } 420 | 421 | @Override 422 | public void dataChunk(String... values) { 423 | executor.schedule(() -> eventListener.onDataChunk(Arrays.asList(values)), 424 | 500, TimeUnit.MILLISECONDS); 425 | } 426 | 427 | @Override 428 | public void processComplete() { 429 | executor.schedule(() -> eventListener.processComplete(), 430 | 500, TimeUnit.MILLISECONDS); 431 | } 432 | }; 433 | 434 | @Test 435 | public void producingCreate() { 436 | Flux bridge = Flux.create(sink -> { 437 | myEventProcessor.register( // <4> 438 | new MyEventListener() { // <1> 439 | 440 | public void onDataChunk(List chunk) { 441 | for(String s : chunk) { 442 | sink.next(s); // <2> 443 | } 444 | } 445 | 446 | public void processComplete() { 447 | sink.complete(); // <3> 448 | } 449 | }); 450 | }); 451 | bridge.publishOn(Schedulers.elastic()).subscribe(System.out::println,System.out::println,()-> System.out.println("done!")); 452 | myEventProcessor.dataChunk("foo", "bar", "baz"); 453 | myEventProcessor.processComplete(); 454 | sleep(1000); 455 | StepVerifier.withVirtualTime(() -> bridge) 456 | // .expectNoEvent(Duration.ofSeconds(10)) 457 | .consumeSubscriptionWith(s-> System.out.println("xxx "+s.toString())) 458 | // .expectSubscription() 459 | //.expectNoEvent(Duration.ofSeconds(10)) 460 | .then(() -> myEventProcessor.dataChunk("foo", "bar", "baz")) 461 | .expectNext("foo", "bar", "baz") 462 | .expectNoEvent(Duration.ofSeconds(10)) 463 | .then(() -> myEventProcessor.processComplete()) 464 | .verifyComplete(); 465 | } 466 | 467 | 468 | 469 | public String alphabet(int letterNumber) { 470 | if (letterNumber < 1 || letterNumber > 26) { 471 | return null; 472 | } 473 | int letterIndexAscii = 'A' + letterNumber - 1; 474 | return "" + (char) letterIndexAscii; 475 | } 476 | @Test 477 | public void producingHandle() { 478 | Flux alphabet = Flux.just(-1, 30, 13, 9, 20) 479 | .handle((i, sink) -> { 480 | String letter = alphabet(i); // <1> 481 | if (letter != null) // <2> 482 | sink.next(letter); // <3> 483 | }); 484 | 485 | alphabet.subscribe(System.out::println); 486 | 487 | StepVerifier.create(alphabet) 488 | .expectNext("M", "I", "T") 489 | .verifyComplete(); 490 | } 491 | 492 | 493 | //代码: 494 | //父类 495 | public class Father { 496 | protected String name = "父亲属性"; 497 | 498 | public void method() { 499 | System.out.println("父类方法,对象类型:" + this.getClass()); 500 | } 501 | 502 | } 503 | 504 | 505 | //子类 506 | public class Son extends Father { 507 | protected String name = "儿子属性"; 508 | 509 | public void method() { 510 | System.out.println("子类方法,对象类型:" + this.getClass()); 511 | } 512 | 513 | } 514 | 515 | @Test 516 | public void Outter_inner() { 517 | System.out.println(new Outter.Inner().abc); 518 | System.out.println(new Outter.Inner().bbb); 519 | System.out.println(new Outter().a); 520 | } 521 | } -------------------------------------------------------------------------------- /demo-reactor/src/test/java/com/dockerx/demoreactor/DemoReactorTest.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor; 2 | 3 | import org.assertj.core.api.Assertions; 4 | import org.junit.Test; 5 | import org.reactivestreams.Publisher; 6 | import reactor.core.publisher.Flux; 7 | import reactor.core.publisher.Mono; 8 | import reactor.core.publisher.Operators; 9 | import reactor.test.StepVerifier; 10 | import reactor.test.StepVerifierOptions; 11 | import reactor.test.publisher.PublisherProbe; 12 | import reactor.test.publisher.TestPublisher; 13 | import reactor.test.scheduler.VirtualTimeScheduler; 14 | import reactor.util.context.Context; 15 | 16 | import java.time.Duration; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | import java.util.concurrent.Executors; 20 | import java.util.concurrent.ScheduledExecutorService; 21 | import java.util.concurrent.TimeUnit; 22 | import java.util.concurrent.atomic.AtomicBoolean; 23 | 24 | import static org.assertj.core.api.Assertions.*; 25 | 26 | 27 | /** 28 | * @author Author 知秋 29 | * @email fei6751803@163.com 30 | * @time Created by Auser on 2018/6/19 2:32. 31 | */ 32 | public class DemoReactorTest { 33 | public Flux appendCustomError(Flux source) { 34 | return source.concatWith(Mono.error(new IllegalArgumentException("custom"))); 35 | } 36 | @Test 37 | public void testAppendBoomError() { 38 | Flux source = Flux.just("foo", "bar"); 39 | 40 | StepVerifier.create( 41 | appendCustomError(source)) 42 | .expectNext("foo") 43 | .expectNext("bar") 44 | .expectErrorMessage("custom"); 45 | // .verify(); 46 | } 47 | @Test 48 | public void errorHandlingIntervalMillisNotContinued() throws InterruptedException { 49 | VirtualTimeScheduler virtualTimeScheduler = VirtualTimeScheduler.create(); 50 | VirtualTimeScheduler.set(virtualTimeScheduler); 51 | Flux flux = 52 | Flux.interval(Duration.ofMillis(250)) 53 | .map(input -> { 54 | if (input < 3) return "tick " + input; 55 | throw new RuntimeException("boom"); 56 | }) 57 | .onErrorReturn("Uh oh"); 58 | flux.subscribe(System.out::println); 59 | //Thread.sleep(2100); // <1> 60 | virtualTimeScheduler.advanceTimeBy(Duration.ofHours(1)); 61 | StepVerifier.withVirtualTime(() -> flux, () -> virtualTimeScheduler, Long.MAX_VALUE) 62 | .thenAwait(Duration.ofSeconds(3)) 63 | .expectNext("tick 0") 64 | .expectNext("tick 1") 65 | .expectNext("tick 2") 66 | .expectNext("Uh oh") 67 | .verifyComplete(); 68 | } 69 | 70 | 71 | //interface MyEventListener { 72 | // void onDataChunk(List chunk); 73 | // void processComplete(); 74 | //} 75 | // 76 | //interface MyEventProcessor { 77 | // void register(DemoRecatorFluxTest.MyEventListener eventListener); 78 | // void dataChunk(String... values); 79 | // void processComplete(); 80 | //} 81 | 82 | private DemoReactorFluxTest.MyEventProcessor myEventProcessor = new DemoReactorFluxTest.MyEventProcessor() { 83 | 84 | private DemoReactorFluxTest.MyEventListener eventListener; 85 | private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 86 | 87 | @Override 88 | public void register(DemoReactorFluxTest.MyEventListener eventListener) { 89 | this.eventListener = eventListener; 90 | } 91 | 92 | @Override 93 | public void dataChunk(String... values) { 94 | executor.schedule(() -> eventListener.onDataChunk(Arrays.asList(values)), 95 | 500, TimeUnit.MILLISECONDS); 96 | } 97 | 98 | @Override 99 | public void processComplete() { 100 | executor.schedule(() -> eventListener.processComplete(), 101 | 500, TimeUnit.MILLISECONDS); 102 | } 103 | }; 104 | @Test 105 | public void producingCreate() { 106 | Flux bridge = Flux.create(sink -> { 107 | myEventProcessor.register( // <4> 108 | new DemoReactorFluxTest.MyEventListener() { // <1> 109 | public void onDataChunk(List chunk) { 110 | for(String s : chunk) { 111 | sink.next(s); // <2> 112 | } 113 | } 114 | public void processComplete() { 115 | sink.complete(); // <3> 116 | } 117 | }); 118 | }); 119 | StepVerifier.withVirtualTime(() -> bridge) 120 | .expectNoEvent(Duration.ofSeconds(10)) 121 | .expectSubscription() 122 | .expectNoEvent(Duration.ofSeconds(10)) 123 | .then(() -> myEventProcessor.dataChunk("foo", "bar", "baz")) 124 | .expectNext("foo", "bar", "baz") 125 | .expectNoEvent(Duration.ofSeconds(10)) 126 | .then(() -> myEventProcessor.processComplete()) 127 | .verifyComplete(); 128 | } 129 | 130 | @Test 131 | public void expectNoEventTest() { 132 | StepVerifier.withVirtualTime(() -> Flux.interval(Duration.ofHours(4), Duration.ofDays(1)).take(2)) 133 | .expectSubscription() 134 | .expectNoEvent(Duration.ofHours(4)) 135 | .expectNext(0L) 136 | .thenAwait(Duration.ofDays(1)) 137 | .expectNext(1L) 138 | .expectNoEvent(Duration.ofHours(8)) 139 | .verifyComplete(); 140 | } 141 | 142 | @Test 143 | public void assertDroppedElementsAllPass() { 144 | StepVerifier.create(Flux.from(s -> { 145 | s.onSubscribe(Operators.emptySubscription()); 146 | s.onNext("foo"); 147 | s.onComplete(); 148 | s.onNext("bar"); 149 | s.onNext("baz"); 150 | }).take(3)) 151 | .expectNext("foo") 152 | .expectComplete() 153 | .verifyThenAssertThat() 154 | .hasDroppedElements() 155 | .hasDropped("baz") 156 | .hasDroppedExactly("baz", "bar"); 157 | } 158 | @Test 159 | public void assertNotDroppedElementsFailureOneDrop() { 160 | try { 161 | StepVerifier.create(Flux.from(s -> { 162 | s.onSubscribe(Operators.emptySubscription()); 163 | s.onNext("foo"); 164 | s.onComplete(); 165 | s.onNext("bar"); 166 | }).take(2)) 167 | .expectNext("foo") 168 | .expectComplete() 169 | .verifyThenAssertThat() 170 | .hasNotDroppedElements(); 171 | fail("expected an AssertionError"); 172 | } 173 | catch (AssertionError ae) { 174 | assertThat(ae).hasMessage("Expected no dropped elements, found <[bar]>."); 175 | } 176 | } 177 | 178 | @Test 179 | public void assertDroppedElementsFailureOneExtra() { 180 | try { 181 | StepVerifier.create(Flux.from(s -> { 182 | s.onSubscribe(Operators.emptySubscription()); 183 | s.onNext("foo"); 184 | s.onComplete(); 185 | s.onNext("bar"); 186 | s.onNext("baz"); 187 | }).take(3)) 188 | .expectNext("foo") 189 | .expectComplete() 190 | .verifyThenAssertThat() 191 | .hasDropped("foo"); 192 | fail("expected an AssertionError"); 193 | } 194 | catch (AssertionError ae) { 195 | assertThat(ae).hasMessage("Expected dropped elements to contain <[foo]>, was <[bar, baz]>."); 196 | } 197 | } 198 | 199 | @Test 200 | public void assertDroppedElementsFailureOneMissing() { 201 | try { 202 | StepVerifier.create(Flux.from(s -> { 203 | s.onSubscribe(Operators.emptySubscription()); 204 | s.onNext("foo"); 205 | s.onComplete(); 206 | s.onNext("bar"); 207 | s.onNext("baz"); 208 | }).take(3)) 209 | .expectNext("foo") 210 | .expectComplete() 211 | .verifyThenAssertThat() 212 | .hasDroppedExactly("baz"); 213 | fail("expected an AssertionError"); 214 | } 215 | catch (AssertionError ae) { 216 | assertThat(ae).hasMessage("Expected dropped elements to contain exactly <[baz]>, was <[bar, baz]>."); 217 | } 218 | } 219 | 220 | @Test 221 | public void assertDroppedErrorAllPass() { 222 | Throwable err1 = new IllegalStateException("boom1"); 223 | Throwable err2 = new IllegalStateException("boom2"); 224 | StepVerifier.create(Flux.from(s -> { 225 | s.onSubscribe(Operators.emptySubscription()); 226 | s.onError(err1); 227 | s.onError(err2); 228 | }).buffer(1)) 229 | .expectError() 230 | .verifyThenAssertThat() 231 | .hasDroppedErrors() 232 | .hasDroppedErrors(1) 233 | .hasDroppedErrorOfType(IllegalStateException.class) 234 | .hasDroppedErrorWithMessageContaining("boom") 235 | .hasDroppedErrorWithMessage("boom2") 236 | .hasDroppedErrorMatching(t -> t instanceof IllegalStateException && "boom2".equals(t.getMessage())); 237 | } 238 | 239 | @Test 240 | public void assertDroppedErrorFailureWrongType() { 241 | try { 242 | Throwable err1 = new IllegalStateException("boom1"); 243 | Throwable err2 = new IllegalStateException("boom2"); 244 | StepVerifier.create(Flux.from(s -> { 245 | s.onSubscribe(Operators.emptySubscription()); 246 | s.onError(err1); 247 | s.onError(err2); 248 | }).buffer(1)) 249 | .expectError() 250 | .verifyThenAssertThat() 251 | .hasDroppedErrorOfType(IllegalArgumentException.class); 252 | fail("expected an AssertionError"); 253 | } 254 | catch (AssertionError ae) { 255 | assertThat(ae).hasMessage("Expected dropped error to be of type java.lang.IllegalArgumentException, was java.lang.IllegalStateException."); 256 | } 257 | } 258 | 259 | @Test 260 | public void withInitialContext() { 261 | StepVerifier.create(Mono.subscriberContext(), 262 | StepVerifierOptions.create().withInitialContext(Context.of("foo", "bar"))) 263 | .assertNext(c -> assertThat(c.getOrDefault("foo", "baz")) 264 | .isEqualTo("bar")) 265 | .verifyComplete(); 266 | } 267 | 268 | @Test 269 | public void withInitialContextButNoPropagation() { 270 | StepVerifier.create(Mono.just(1) 271 | // .map(i->i) 272 | , 273 | StepVerifierOptions.create().withInitialContext(Context.of("foo", "bar"))) 274 | .expectNoAccessibleContext() 275 | .expectNext(1) 276 | .verifyComplete(); 277 | } 278 | 279 | @Test 280 | public void withInitialContextAndContextAssertionsParents() { 281 | StepVerifier.create(Mono.just(1).map(i -> i + 10), 282 | StepVerifierOptions.create().withInitialContext(Context.of("foo", "bar"))) 283 | .expectAccessibleContext() 284 | .contains("foo", "bar") 285 | .then() 286 | .expectNext(11) 287 | .verifyComplete(); 288 | } 289 | 290 | @Test 291 | public void normalDisallowsNull() { 292 | TestPublisher publisher = TestPublisher.create(); 293 | 294 | assertThatExceptionOfType(NullPointerException.class) 295 | .isThrownBy(() -> publisher.next(null)) 296 | .withMessage("emitted values must be non-null"); 297 | } 298 | 299 | @Test 300 | public void misbehavingAllowsNull() { 301 | TestPublisher publisher = TestPublisher.createNoncompliant(TestPublisher.Violation.ALLOW_NULL); 302 | 303 | StepVerifier.create(publisher) 304 | .then(() -> publisher.emit("foo", null)) 305 | .expectNext("foo", null) 306 | .expectComplete() 307 | .verify(); 308 | } 309 | 310 | @Test 311 | public void normalDisallowsOverflow() { 312 | TestPublisher publisher = TestPublisher.create(); 313 | 314 | StepVerifier.create(publisher, 1) 315 | .then(() -> publisher.next("foo")).as("should pass") 316 | .then(() -> publisher.emit("bar")).as("should fail") 317 | .expectNext("foo") 318 | .expectErrorMatches(e -> e instanceof IllegalStateException && 319 | "Can't deliver value due to lack of requests".equals(e.getMessage())) 320 | .verify(); 321 | 322 | publisher.assertNoRequestOverflow(); 323 | } 324 | 325 | @Test 326 | public void misbehavingAllowsOverflow() { 327 | TestPublisher publisher = TestPublisher.createNoncompliant(TestPublisher.Violation.REQUEST_OVERFLOW); 328 | 329 | 330 | assertThatExceptionOfType(AssertionError.class) 331 | .isThrownBy(() -> StepVerifier.create(publisher, 1) 332 | .then(() -> publisher.emit("foo", "bar")) 333 | .expectNext("foo") 334 | .expectNext("bar") 335 | .expectComplete() //n/a 336 | // .expectError() 337 | .verify()) 338 | .withMessageContaining("expected production of at most 1;"); 339 | 340 | publisher.assertRequestOverflow(); 341 | } 342 | 343 | /*@Test 344 | public void gh1236_test() { 345 | TestPublisher result = TestPublisher.createCold(); 346 | 347 | assertThat(result.emit("value").mono().block()).isEqualTo("value"); 348 | }*/ 349 | 350 | public Flux processOrFallback(Mono source, Publisher fallback) { 351 | return source 352 | .flatMapMany(phrase -> Flux.fromArray(phrase.split("\\s+"))) 353 | .switchIfEmpty(fallback); 354 | } 355 | 356 | @Test 357 | public void testSplitPathIsUsed() { 358 | StepVerifier.create(processOrFallback(Mono.just("just a phrase with tabs!"), 359 | Mono.just("EMPTY_PHRASE"))) 360 | .expectNext("just", "a", "phrase", "with", "tabs!") 361 | .verifyComplete(); 362 | } 363 | 364 | @Test 365 | public void testEmptyPathIsUsed() { 366 | StepVerifier.create(processOrFallback(Mono.empty(), Mono.just("EMPTY_PHRASE"))) 367 | .expectNext("EMPTY_PHRASE") 368 | .verifyComplete(); 369 | } 370 | 371 | 372 | 373 | private Mono executeCommand(String command) { 374 | return Mono.just(command + " DONE"); 375 | } 376 | public Mono processOrFallback(Mono commandSource, Mono doWhenEmpty) { 377 | return commandSource 378 | .flatMap(command -> executeCommand(command).then()) // <1> 379 | .switchIfEmpty(doWhenEmpty); // <2> 380 | } 381 | @Test 382 | public void testCommandEmptyPathIsUsedBoilerplate() { 383 | AtomicBoolean wasInvoked = new AtomicBoolean(); 384 | AtomicBoolean wasRequested = new AtomicBoolean(); 385 | Mono testFallback = Mono.empty() 386 | .doOnSubscribe(s -> wasInvoked.set(true)) 387 | .doOnRequest(l -> wasRequested.set(true)); 388 | processOrFallback(Mono.empty(), testFallback).subscribe(); 389 | assertThat(wasInvoked.get()).isTrue(); 390 | assertThat(wasRequested.get()).isTrue(); 391 | } 392 | @Test 393 | public void testCommandEmptyPathIsUsed() { 394 | PublisherProbe probe = PublisherProbe.empty(); // <1> 395 | StepVerifier.create(processOrFallback(Mono.empty(), probe.mono())) // <2> 396 | .verifyComplete(); 397 | probe.assertWasSubscribed(); //<3> 398 | probe.assertWasRequested(); //<4> 399 | probe.assertWasNotCancelled(); //<5> 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /demo-reactor/src/test/java/com/dockerx/demoreactor/DemoSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor; 2 | 3 | import org.reactivestreams.Subscription; 4 | import reactor.core.publisher.BaseSubscriber; 5 | 6 | /** 7 | * @author Author 知秋 8 | * @email fei6751803@163.com 9 | * @time Created by Auser on 2018/5/9 1:22. 10 | */ 11 | public class DemoSubscriber extends BaseSubscriber { 12 | public void hookOnSubscribe(Subscription subscription) { 13 | System.out.println("Subscribed"); 14 | request(1); 15 | } 16 | 17 | public void hookOnNext(T value) { 18 | System.out.println(value); 19 | request(1); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demo-reactor/src/test/java/com/dockerx/demoreactor/Outter.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor; 2 | 3 | import org.junit.Test; 4 | 5 | /** 6 | * @author Author 知秋 7 | * @email fei6751803@163.com 8 | * @time Created by Auser on 2018/5/13 22:12. 9 | */ 10 | public class Outter { 11 | int a=10; 12 | static int b =5; 13 | public Outter() { 14 | 15 | } 16 | 17 | static class Inner { 18 | 19 | String bbb= "vvvvvv"; 20 | String abc; 21 | Inner() { 22 | this.abc="aaaa"; 23 | } 24 | } 25 | 26 | public String getA() { 27 | Inner inner = new Inner(); 28 | return inner.abc; 29 | } 30 | 31 | @Test 32 | public void dummy() { 33 | System.out.println(new Outter().getA()); 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /demo-reactor/src/test/java/com/dockerx/demoreactor/WorkQueueProcessorTest.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import reactor.core.publisher.Flux; 6 | import reactor.core.publisher.FluxSink; 7 | import reactor.core.publisher.WorkQueueProcessor; 8 | 9 | import java.util.concurrent.ExecutorService; 10 | import java.util.concurrent.Executors; 11 | import java.util.concurrent.TimeUnit; 12 | import java.util.concurrent.atomic.AtomicLong; 13 | import java.util.concurrent.atomic.LongAccumulator; 14 | 15 | import static org.junit.Assert.assertEquals; 16 | 17 | 18 | /** 19 | * @author Author 知秋 20 | * @email fei6751803@163.com 21 | * @time Created by Auser on 2018/6/9 2:22. 22 | */ 23 | public class WorkQueueProcessorTest { 24 | private static final int PRODUCER_LATENCY = 5; 25 | private static final int CONSUMER_LATENCY = 4; 26 | 27 | private static final int RINGBUFFER_SIZE = 64; 28 | 29 | private static final int INITAL_MESSAGES_COUNT = 10; 30 | private static final int PRODUCED_MESSAGES_COUNT = 1024; 31 | private static final int BURST_SIZE = 5; 32 | 33 | private LongAccumulator maxRingBufferPending; 34 | private WorkQueueProcessor processor; 35 | private ExecutorService producerExecutor; 36 | private AtomicLong droppedCount; 37 | 38 | @Before 39 | public void setup() { 40 | maxRingBufferPending = new LongAccumulator(Long::max, Long.MIN_VALUE); 41 | droppedCount = new AtomicLong(0); 42 | producerExecutor = Executors.newSingleThreadExecutor(); 43 | } 44 | 45 | @Test 46 | public void test() throws Exception { 47 | processor = WorkQueueProcessor.create("test-processor", RINGBUFFER_SIZE); 48 | 49 | Flux.create(this::burstyProducer) 50 | .onBackpressureDrop(this::incrementDroppedMessagesCounter) 51 | .subscribe(processor); 52 | 53 | processor.doOnNext(x-> System.out.println(x+" One")) 54 | .map(this::complicatedCalculation) 55 | .subscribe(this::logConsumedValue); 56 | processor.doOnNext(x-> System.out.println(x+" Two")) 57 | .map(this::complicatedCalculation) 58 | .subscribe(this::logConsumedValue); 59 | processor.doOnNext(x-> System.out.println(x+" Three")) 60 | .map(this::complicatedCalculation) 61 | .subscribe(this::logConsumedValue); 62 | processor.doOnNext(x-> System.out.println(x+" Four")) 63 | .map(this::complicatedCalculation) 64 | .subscribe(this::logConsumedValue); 65 | 66 | waitForProducerFinish(); 67 | 68 | System.out.println("\n\nMax ringbuffer pending: " + maxRingBufferPending.get()); 69 | 70 | assertEquals(0, getDroppedMessagesCount()); 71 | } 72 | 73 | 74 | private void waitForProducerFinish() throws InterruptedException { 75 | producerExecutor.shutdown(); 76 | producerExecutor.awaitTermination(20, TimeUnit.SECONDS); 77 | } 78 | 79 | private void logConsumedValue(Object value) { 80 | //System.out.print(value + ","); 81 | } 82 | 83 | private long getDroppedMessagesCount() { 84 | return droppedCount.get(); 85 | } 86 | 87 | private void incrementDroppedMessagesCounter(Object dropped) { 88 | System.out.println("\nDropped: " + dropped); 89 | droppedCount.incrementAndGet(); 90 | } 91 | 92 | private Object complicatedCalculation(Object value) { 93 | maxRingBufferPending.accumulate(processor.getPending()); 94 | sleep(CONSUMER_LATENCY); 95 | return value; 96 | } 97 | 98 | private void burstyProducer(FluxSink emitter) { 99 | producerExecutor.execute(burstyProducerRunnable(emitter 100 | )); 101 | } 102 | 103 | private Runnable burstyProducerRunnable(final FluxSink emitter) { 104 | return () -> { 105 | 106 | // Let's start with some messages to keep the ringbuffer from going total empty 107 | for (int i = 0; i < INITAL_MESSAGES_COUNT; ++i) { 108 | emitter.next("initial" + i); 109 | } 110 | 111 | for (int outer = 0; outer < WorkQueueProcessorTest.PRODUCED_MESSAGES_COUNT / WorkQueueProcessorTest.BURST_SIZE; ++outer) { 112 | for (int inner = 0; inner < WorkQueueProcessorTest.BURST_SIZE; ++inner) { 113 | emitter.next(outer * WorkQueueProcessorTest.BURST_SIZE + inner); 114 | } 115 | sleep(PRODUCER_LATENCY * WorkQueueProcessorTest.BURST_SIZE); 116 | } 117 | }; 118 | } 119 | 120 | private static void sleep(int i) { 121 | try { 122 | Thread.sleep(i); 123 | } catch (InterruptedException e) { 124 | e.printStackTrace(); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /demo-reactor/src/test/java/com/dockerx/demoreactor/edu/StudentProTest.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor.edu; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.function.BiFunction; 6 | import java.util.function.Function; 7 | import java.util.function.Predicate; 8 | 9 | 10 | /** 11 | * @author: ZhiQiu 12 | * @email: fei6751803@163.com 13 | * @date: 2019/3/27 0:56. 14 | */ 15 | public class StudentProTest { 16 | 17 | @Test 18 | public void getTeacher() { 19 | BiFunction, StudentPro, Teacher> biFunction = (func, stu) -> { 20 | StudentPro student = func.apply(stu); 21 | System.out.println(student.getGrade()); 22 | if (student.getMark().getMarkponit() > 80) { 23 | return new Teacher(student.getGrade(), student.getBooks()); 24 | } 25 | return new Teacher(student.getGrade(), new Books("刚及格小教授")); 26 | }; 27 | 28 | 29 | Predicate predicate = student -> student.getGrade() != Grade.ONE; 30 | 31 | StudentPro student = new StudentPro(biFunction, predicate); 32 | student.setGrade(Grade.THREE).setMark(new Mark(90)).setBooks(new Books("蛤蟆功")); 33 | Teacher apply = student.getTeacher(student); 34 | System.out.println(apply.getBooks().getName()); 35 | 36 | } 37 | } -------------------------------------------------------------------------------- /demo-reactor/src/test/java/com/dockerx/demoreactor/edu/StudentTest.java: -------------------------------------------------------------------------------- 1 | package com.dockerx.demoreactor.edu; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.util.function.BiFunction; 7 | import java.util.function.Function; 8 | import java.util.function.Predicate; 9 | 10 | 11 | /** 12 | * @author: ZhiQiu 13 | * @email: fei6751803@163.com 14 | * @date: 2019/3/26 23:35. 15 | */ 16 | public class StudentTest { 17 | 18 | @Test 19 | public void apply() { 20 | 21 | Function biFunction = student -> { 22 | //System.out.println(student.getGrade()); 23 | if (student.getMark().getMarkponit() > 80) { 24 | return new Teacher(student.getGrade(), student.getBooks()); 25 | } 26 | return new Teacher(student.getGrade(), new Books("刚及格大学士")); 27 | }; 28 | 29 | Predicate predicate = student -> student.getGrade() != Grade.ONE; 30 | 31 | Student student = new Student(biFunction, predicate); 32 | student.setGrade(Grade.THREE) 33 | .setMark(new Mark(90)) 34 | .setBooks(new Books("蛤蟆功")); 35 | //Teacher apply = student.apply(student, new Books()); 36 | //System.out.println(apply.getBooks().getName()); 37 | 38 | Assert.assertTrue("刚及格大学士".equals(student.apply(student, student.getBooks()) 39 | .getBooks() 40 | .getName())); 41 | } 42 | } --------------------------------------------------------------------------------