BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Presentations Reactive Spring

Reactive Spring

Bookmarks
47:01

Summary

Josh Long discusses Spring Framework 5 and its support for reactive programming.

Bio

Josh Long is the Spring Developer Advocate at Pivotal. Josh is a Java Champion, author or co-author of 6 books (including “Reactive Spring” and O'Reilly's “Cloud Native Java: Designing Resilient Systems with Spring Boot, Spring Cloud, and Cloud Foundry”) and numerous best-selling video trainings, and an open-source contributor (Spring Boot, Spring Integration, Spring Cloud, Activiti and Vaadin).

About the conference

Software is changing the world. QCon empowers software development by facilitating the spread of knowledge and innovation in the developer community. A practitioner-driven conference, QCon is designed for technical team leads, architects, engineering directors, and project managers who influence innovation in their teams.

Transcript

Long: My name is Josh Long. I work on the Spring team as a developer advocate. If you're in China, this is a great thing to know about. Here are some videos that I do on Safari. These are on the All You Can Eat side of technical marketplace, it's a buffet for technical content. They are five, six, seven, eight hours each one. Lots of good content there.

I have a book called, "Cloud-native Java" that's all about how to build applications that survive in production using Spring Boot and Spring Cloud, and Cloud Foundry. The book is something that I'm very proud of, it's something that took a long time to get right. There's a good reason that we were so delayed. We finished the book pretty quickly, but there was a long debate, a long back and forth, a long discussion that we had with our publisher, O'Reilly, that took a little bit longer. Not much longer, we took six months to write the book, and then it took another two years. It was not a big deal. The extra two years we spent debating the animal on the cover.

Anybody who knows anything about O'Reilly books, knows that it doesn't matter what's in the book itself, obviously, nobody cares. Have you ever read these books? No. Look at all the Amazon reviews, it's just, "Great animal, great animal." "Good choice." "Great selection." We eventually settled upon a blue-eared kingfisher. It's a bird with blue ears that is only found in the Indonesian Java islands. It's native to Java and birds fly through the clouds. It's a Cloud-native Java bird. I have a podcast every Friday called "A Bootiful Podcast." I do videos every Wednesday, that's Spring Tips playlist. You find me doing half-hour, hour long introductions to all sorts of different topics: how do you Spring in Google cloud, Alibaba cloud, Microsoft Azure, how to use messaging, integration, security, everything, all sorts of topics, circuit breakers, whatever.

Reactive Programming

I have a new book called, "Reactive Spring." This book is all about how to build applications using reactive programming, and that's what we're going to talk about today, my friends. It's a very exciting thing to discuss. It's exciting, but it is not new; or really actually, reactive programming is kind of new, but the problem that it solves is very old. The problem that we're trying to solve is, how do I handle more users in my system? As I add more capacity to the system, how can I scale the number of users I can support?

If you have been following the patterns and the trends from the last 10 years, then you were already building microservices. You're doing cloud-native applications, you're doing 12-Factor App style services, stateless, small, singly focused microservices, and these services are cheap to scale out horizontally. You can just put them in a load balancer and then randomly load balance load across the different instances and you're fine. That's a very easy pattern to do. All cloud infrastructure today will do that for you. It's a built-in thing, no problem at all.

This is a useful pattern because it allows us to handle more users. The reason we do this is because we can't handle infinite numbers of users on one machine. We talk about Netflix, the cloud-native architecture from 10 years ago, right around the same time as Q-Con first came out. The first cloud-native architecture was, "Let's get stateless microservices and Cassandra." All the state is in the database and the database scales horizontally. The reason that's such a useful pattern is because you can write applications that are just HTTP APIs, and even if the HTTP API is not super-efficient, who cares? You can just add more app instances.

I work for Pivotal, and at Pivotal we sell cloud computing technology. I love that answer, that's a great answer. Every time you scale out, companies like mine make more money. It's awesome, I love that, but there's a question. Why can't we do more with the same machine? How do we handle more users with the same hardware? The question is, how can I do more with the same computer? Not, how can I go faster per transaction? The question then is, how do I handle more users in the same computer? Obviously, there are some interesting answers here. You've probably heard about the C10k problem, the 10,000 concurrent users' problem. This is a very simple problem posed 20 plus years ago. The question was, how do I handle more than 10,000 users at the same time on a single machine? The answer is, not with threads. Threads are actually a bit of a problem.

Think about how most traditionally approaches to input and output work. You have a web server and each time a user comes in, a new thread is created, and in that thread, a response is produced, and while you're producing the response, what are you doing? You're calling the database, waiting for the bytes to come back. You're calling other APIs, other gRPC, or REST, or whatever services you're calling. You're making network calls across the wire. You might be calling things in the file system, might be sending messages over to message cues. All the while, as this is happening, you're waiting and waiting, and bytes are coming back and forth. You're sitting on that thread waiting for the bytes. The problem is that, while you're doing this, you're stuck on that thread, that thread is not usable for anybody else. It's unavailable.

The question is, is that ok? Can I just scale out infinitely? Can I just add more threads forever? Of course, the answer is no. I'm sure you've all tried creating a thousand threads. That's not going to be a great result. There are a few reasons for this. First of all, threading is an illusion. It doesn't actually exist beyond the number of cores you have. Beyond the number of cores, you're just scheduling, you're doing work very quickly and moving it from one processor to another. After that, you're just going to be thrashing; there's a cost for context switching.

Now, even then, can you do the thrashing? Can you do the context switching very fast? That's actually a slow operation, to go from user space to the kernel and then back again for code like that. That's slow, so you're not going to have a lot of good results if you create a large number of threads. Also, on the JVM, each thread takes about 1 megabyte of space RAM. If you have a thousand threads, you've got a gig of extra RAM. That's before you've done anything. You haven't solved any of your problems yet, and you've already wasted a gig. This is not a good solution.

The question is, what can we do to be more efficient with the threads that we've got? We can look back at that situation I just described, where we're waiting for the bytes to come back. The reason we're waiting for the bytes is because we're using synchronous blocking input and output. The answer here is to use asynchronous I/O. Asynchronous I/O is not a new idea at all. In fact, it's been in Java since 1.4. 1.4 came out in 2002, 17 years ago. It's been around for a long time. You can use it to great effect today, it's already been there for 17 years. You know what that means? If it's in Java, every single operating system that supports Java has to already have good support for asynchronous I/O.

This means that every operating system that supports Java and has asynchronous I/O support has had it for at least another year or two more than Java. They don't just throw that into Java immediately after its added to an operating system, they let it stabilize a little bit. This means that every operating system, for at least let's say 20 years, has supported asynchronous I/O, even Windows. We have asynchronous I/O support, why don't we all use this? Are you all writing code using java.nio? Is your everyday line of business all about that? I think not. I think most of us don't write code that low level.

Most of us don't write code that low level and we don't use Java I/O either. We use higher order abstractions. We have collections and things like this. These collections, these abstractions map nicely to the underlying machine, to the mechanism that we use for input and output. Now, we need something that supports that same kind of mapping for asynchronous I/O and this is where the Reactive Stream specification comes in. The Reactive Stream specification was defined by at least four different companies, maybe a few more. You've got Pivotal, you've got Netflix, you've got the Eclipse Foundation, you've got Lightbend – they were called Typesafe at that time. All these different organizations got together, and we defined four very simple types. These are like asynchronous arrays, they're the reactive streams types. They're very useful, but they're kind of like an array, they're not high level enough. Most of us want operators so that we can deal with streams of data.

This is where we get project Reactor, and RxJava, and Akka Streams, and so on. These projects support higher order computations on streams from these reactive stream types. Is that enough? Can we go to production yet? The answer for most people is no, not really. What good are those types if they don't support working with other kinds of infrastructure? If they cannot support us in our efforts to build business software to deliver into production, what good is that?

Imagine, just hypothetical, for whatever reason, that these technologies, these tools that we are familiar with, the ones that we have used for so long, imagine that these technologies did not understand or support java.util.Collection types, like java.util.List or java.util.ArrayList. Imagine, for whatever reason, that whenever you tried to use Hibernate to map a one-to-many relationship. Imagine that it didn't just throw an exception. It really hates java.util.Collection. Obviously it does not, but imagine it did. It really hates java.util.List and set, it really hates it. It doesn't just throw an exception, it actually renders an ASCII middle finger, and then kernel panics the machine.

Would you continue to use this if you knew that you can expect this kind of abuse? Of course not. You would use whatever Hibernate told you to use so that you could get software into production. Your job is to go to production and to deliver business value. It's not to figure out how to reinvent Hibernate. You can do better - you will do better, so you'll just use the path of least resistance. The same is true here. What good are these reactive streams types if the software that we use to deliver business value don't support them? This is where, for Spring, a big day came in September of 2017 when we released Spring Framework 5, and then Spring Boot, and then Spring Data, and Spring Security, and Spring Cloud. All these releases, after that, supported Java 8 as a baseline and the reactive streams types and a reactive web runtime.

My friends, today, we're going to take a very quick journey and look at all these different components, all these different pieces, and see how they help us build better software, faster. Of course, to do that, we're going to go to my second favorite place on the internet. My first favorite place of course is production, I love production. You should love production, you should go as early and often as possible, but if you haven't been to production, you can begin your journey here at start.spring.io.

start.spring.io – live coding

We're going to build a new application called the reservation-service. We're going to use Spring Boot 2.2 snapshot. We're going to bring in RSocket, we're going to bring in Lombok and the Reactive Web. We've got all the good stuff that we need there. I'm happy with my selections, RSocket, Lombok, Reactive Web, Reactive MongoDB. I'm going to hit generate and that's going to give us a new zip file. I'm going to open up this zip file in my IDE. I'm going to go here, CD, downloads, UAO.

We have a new application, it's started up. It's a public static void main Spring Boot application. What I'm going to do is I'm going to build an application that writes data to the database and I have MongoDB running in the background on my local machine. It's going to be an object that we're going to map that's going to have two fields, ID and name. We're going to say “@ID” and “@” and then "name". It's going to be a document. In MongoDB, a document is a single row and you have a collection which is multiple rows. It's like a table. This is the essence of what I want to express, but of course, it's Java, so getters, and setters, and two string, and constructor, and equals, and hashCode, so modern, so good. No, probably not.

We get rid of that and we use Lombok. Lombok is a compile time code generator. It's an annotation processor. With that, I have my getters and setters. Now, what I want to do is to write data into the database, and I'm going to create a repository which is a pattern of course from domain-driven design. It's a thing that handles the boring lead, write, update, and delete logic. This interface, we don't have to implement it, we just define it and Spring will provide an object that supports these methods, all these methods like save, save all, find by ID, check if it exists, find all, delete, count, etc - all these things that are very obvious. The names are obvious, but the types might be a little different for you.

First of all, here's a publisher. A publisher comes from the reactive streams. It's one of those four types that I was telling you about before. The publisher is a thing that publishes, it emits, it broadcasts items of type T to a subscriber. The subscriber listens for the item, and whenever the item arrives, onNext is called. When there is an error of some sort, throwable is given in the onError method. Now, keep in mind, in Reactive programming, it's kind of functional programming, and so in this respect, errors are not special, they're not exceptional. They don't get their own control flow mechanism. You deal with them in the same way that you deal with any other kind of error. Then finally we have onComplete which gets called when they're done processing the data.

I skipped over this because this is the most important part. When the subscriber subscribes to the publisher, it is given a subscription. The subscription is used by the subscriber to ask the publisher to slow down, it's very important. When the subscriber is being overwhelmed, it can request just to five records, or 10, or a million, or whatever. It will not get more than this, this is called flow control. We ensure stability by slowing down the publisher. In flow control, in the world of reactive programming, we call flow control "back pressure." If you have seen back pressure, if you have heard that word before, now you know what it means. If you want to cancel the production of data, you just call cancel.

That's three of those four types. The fourth type is called a processor, and that's this one right here, processor. Subscriber and publisher, that's it. I forgot to do something very important on the Spring initializer. I'm going to use Java 11, that's the only sane choice in 2019. I'll go ahead and add that. Now, I've got my application, let's write some data to the database. I'm going to say "sample data initializer," and it's just going to be a Spring bean that listens for an event. ApplicationReadyEvent.class, public void go. When the application starts up, this Spring bean is going to be given a call back. I'm going to inject a reservation repository like this into the constructor. I can do that like this, or I can use Lombok and say “@RequiredArgsConstructor.”

What I want to do is, I want to write some data to the database. I'll say "flux.just" and we'll have some names here. I've got Louise, I've got Josh, I've got Anna, Spencer, Cornelia, Veronica, Madura, and Olga. Just eight different names. I've got a reactive streams publisher. Now, you haven't seen this type, Flux is from Reactor, it extends publisher. You can see it's a publisher, but Flux also supports operations - flat map, map, filter, all these kinds of things above and beyond regular publishers. It publishes 0 to X values, where X could be unlimited.

I have a publisher of names, and now I want to map each one, each name like this, new reservation, null passing and that. That gives me a publisher of reservations, res. Now, I want to map each one of those, map r and I'm going to save this to the database using the repository, so saving that in.

What does map do? Map takes the return value from the Lambda and it collects it into a container type. What does save do? Well, save returns a mono. You can see it says "mono of reservation". A mono is another publisher, it produces at most one value. I've got now a mono and I've got a publisher, this is not what I want. If you look at the results, I get publisher of publisher of reservation. I don't want that, I don't want both. Instead, I want to get just one publisher, so I say, "flat map" and I get rid of the intermediate type.

If I run this code, obviously there are some opportunities here to clean it up. I can say "saved". Now, if I run this code, what happens? The answer is nothing. Nothing is going to happen because I have to activate the stream. It's a cold stream, in order to make it hot, you say "saved.subscribe". Now, if I run this code, we will see some problems. First of all, when I run this code multiple times, I'll have eight records in the database, and then 16, and then 24, and then the same data over and over. I want to delete everything. Can I just do this? What about this? This .reservationRepository.deleteAll. Well, deleteAll returned to publisher, so I have to subscribe. The problem with this, of course, is that it's asynchronous. This could execute on a different thread. I need to force this to happen before this. I could do this.

This code makes me want to take a shower, this is not what we're supposed to do. Instead, use the operators here. I've got a publisher. What I want to do is I want to turn this code into a nice single pipeline, a single intermediate-staged pipeline with multiple pipeline stages. Here we go, now I'll say, delete everything, then write the data to the database, and then find all the data. Then finally when all the data comes back, let's log it out. I'm going to use a logger, again, from Lombok. Now, run this, let's see what happens. Is it going to work? Look at that, it worked. We're all there in the database. You can see the unique IDs, the names, everything worked. Of course it worked, it was a demo. What were you expecting?

What I wanted to talk to you about is this. This is the Spring Boot ASCII artwork. This artwork took a long time to get right, but we on the Spring team have many people who are doctors, PhDs, and they worked very hard on this artwork. It makes me very sad and it makes me want to talk about this very problematic feature in IntelliJ, something that I consider a serious flaw in the product. Do you see this checkbox? If you click this checkbox, it suppresses the output of the ASCII artwork. What the hell? Why is this there? That's a dumb feature. Nobody even asked you, IntelliJ, ok? I do what everybody would do. I went on the internet and I cried, I complained on the internet. I was sent a message of hope from my friend Yann Cebron who's a software developer by passion at Jet Brains. He sent me this message which I want to share with you here today. He says, "Don't worry, Josh. The next release will no doubt contain this feature. It will be fixed in the next release. Just relax, Josh." He tells me all the time and we have been friends for a long time. I want to believe him, but you know what? I'm starting to think maybe he's not being serious.

Anyway, we have a good ASCII artwork, we have data in the database. Now, we need to have an API. We can do this using the new functional reactive style in Spring Boot, in Spring Framework. I'm going to define a bean, like so, hit "build". I'm going to create an end point that serves all the reservation data. In order to do this, I'm going to inject my repository, so I'll say, "new HandlerFunction". I'm going to say "return a ServerResponse.body" and I can say "rr.findAll(), Reservation.class." There's my handler function and my builder. It's a nice Lambda, I can remove that. There you go, my brand-new HTTP M point. Not bad, I've got an HTTP service, not a big deal.

Moving on, I like HTTP. It's awesome obviously, but a lot of people asked me, "Well, can I use asynchronous protocols? How do I do that?" Of course, in this case, remember, findAll just returns a publisher. Even though there are only eight names, only eight records, it's still an asynchronous stream. Everything in the reactive world is a publisher, it makes life very simple. What if you want to do WebSockets? Well, you just use publishers. WebsocketConfig and you just create a bean like this, Simple Url Handler Mapping. All this is going to do is it's going to tell the framework how to expose, how to mount a particular service, so I'll say Map.of ("/ws/greeting") and I'm going to inject a WebSocket handler. The WebSocket handler will have the actual business object and we'll define that in a second.

Here's my handler, and of course I'm going to set the order as well, so 10. Then the other thing I need to create is a WebSocket handler adapter. Then the next thing I need is a WebSocket handler and this is actually our business logic. Think about what I'm going to do here. I'm going to create a thing that is asynchronous. It doesn't just return the reservation data, it's going to return a stream of values that will never stop. Here, I'm going to create a simple bean that just produces new data all the time. "Flux greet," and I'm going to create a greetings request. I haven't defined these types, so let's do that right now. Class GreetingResponse, and class GreetingRequest. I'm just defining two types and the response has a greeting in it, private String message and the request has the name of the person to be greeted, so @data, @AllArgsConstructor, @NoArgsConstructor, that's beautiful. I want to take that and paste.

Now, I've got the simple service. It's a simple thing that will produce data for me. It's a never-ending stream. In order to do that, I'm going to use a publisher. I'm going to say, Stream.generate, a new stream from a new supplier and I'm going to create a new greeting response using the request.name, "Bom dia" Request.getName @instant.now. There's my greeting. Now, the thing is, this is going to create a never-ending stream, it's a stream that will go forever. What I want to do is I want to delay it, I want to stagger the results. What I will do is I will introduce some time. I can delay it by one second there. The reason I can do this is because there is a scheduler behind the scenes that controls threads.

Down here, when I wanted to write data to the database, remember, each line is on a different thread, it could be. It doesn't have to be, but it could be, and that is controlled by the scheduler. By default, you have one thread per core. You can control that if you want, you can overwrite it by using a custom thread pool back to scheduler like this. I don't need to, you shouldn't need to, it's considered a code smell if you do that, but if you need to, it's there. Now I've got my greetings producer. Let's actually produce a response here, so producer. What I'm going to do is I'm going to say, "Ok, when the session comes in, when I am connected to the client or when the client connects to my service, I will be given a brand-new WebSocket session." I'm going to receive WebSocket messages and I'm going to map each WebSocket message into some text. I'm going to map each one of those into a greetings request, aren't I? New greetings request passing in the text, that's the name.

Now, what I want to do is I want to take each greetings request and call producer.greet like that. That's going to give me a stream of greetings responses that I want to turn into text and then send back into the WebSocket as a text message, so session.textMessage. I'm creating a reactive stream, I'm responding to requests that come in and I'm turning them into strings that get set back out like that. It's all very functional in Lambda E and all that stuff. The result of this all is a stream of WebSocket messages, a publisher of WebSocket message. I'm just going to send that session.send, like so. There's my WebSocket handler. of course, this could be a Lambda.

In order to demonstrate this, I need to have something to use it, a client. I need to do something now that I'm not proud of, something that I only do because I think we're friends. What I'm going to do is some JavaScript. File, WS HTML, body HTML, script, and I'm going to say "window.addEventListener, load, function E," and then I'm going to create a WebSocket event, a WebSocket client, WS, Local host. I called it ws greetings, 8080/ws/greetings. I'm going to say addEventListener ("open"). When the WebSocket client is opened, I'm going to initiate a request. I'll say, ws.send a name. Then I'll say what name, "var nameToGreet will be window.prompt ("who should we greet?"), I'll say nameToGreet.

In order to get the data that returns, I'll say ("message"), let's listen for the message, and log it out on the console, "new greeting" + msg.data. Local host, ws.html, 404 local host 8080. Friends, obviously, you need to tell Spring about your configuration otherwise it won't care. That endpoint wasn't registered because it didn't know to look for the beans there. Who should we greet? Bob. There you go, every second, forever. The point of that is that we're not actually blocking the thread on the server site. Each time it's going to publish an event and then the thread is reused by something else.

You understand now that you can use publishers to create very simple stuff and it's easy to do what we can already do. The real possibility comes when we take reactive programming and go further, so far we have done reactive MongoDB. We're not blocking anywhere in MongoDB. MongoDB is a good example of a database that works naturally very nicely with reactive APIs. The driver is natively reactive. It's asynchronous I/O from the bottom up. It's not just a reactive facade on top of something that is blocking.

There are some nice things there. You can do transactions, you can do tailable queries, I like MongoDB. Let's say that I want to go back and change my data layer, well, a question people ask is, can I use reactive SQL? There's not been a great option here for a long time. There's github.com, R2DBC from Pivotal, and this is I think a good choice, but keep in mind, this is not yet GA, it's a simple SPI that supports natively reactive SQL data store integrations for PostgreSQL, Microsoft SQL server, and H2, and there's even a third party one supporting MySQL. We can use this in our code, but right now, for the moment, there is no autoconfiguration of the box in Spring Boot, so we have to add it ourselves.

What I'm going to do is I'm going to is I'm going to go to my build, and add these dependencies here, supporting PostgreS, and Spring Data R2DBC and PostgreSQL. I'm going to comment out MongoDB, I don't want MongoDB in this example for the moment. Because of that, I will have to change some things. First of all, that goes away, and second of all, I no longer have a primary key of a string; instead I have a monotonically incrementing ID. In order for this to work, I also need to connect my application to the database, extends AbstractR2dbcConfiguration, and I'm going to overwrite a connection factory. I'm doing a little bit of the work that you would normally not need to do in the future. In the future, this would be done for you automatically by Spring Boot.

This is a configuration class, and it's going to have R2DBC repository support. In order for me to connect to my database, I need to tell it where to go, so I'm going to do something terrible. I'm just going to write the information here on the stage. Now, the application, psql -U orders, orders, select all from reservation. There we are, there are our names. We had it in MongoDB as well, so we can see now the IDs are primary keys, we're using primary keys to do reactive SQL. I like this, I like R2DBC, it gives us a path forward for the existing applications using SQL data stores. Not all databases are supported, but more are coming all the time. If you have one of those four databases, we'd love to see you try it and let us know what we can do to make it better.

I'm going to say something pretty controversial here, this is kind of scandalous. I think that HTTP is going to be big. I think people are going to use it. They're going to like it, HTTP has a future, I think it's a great document retrieval protocol. There is a discussion to be made that it's not a great application protocol, is that ok? Is that fair? What I mean by that is that there are certain kinds of message exchange patterns that aren't well supported by HTTP. For example, HTTP does request/response. I make a request, I get a response, that's very simple. But what about server-side push? How do I push the data down? Well, I could do Server-Sent events. I could do WebSockets, but in Server-sent events, it's all text. How do I encode data that's binary using Server-Sent events? I have to Base64 encode it, and then unencode it on the client.

What about security for both WebSockets and Server-sent events? There are no headers. There's no message frame in those messages for headers. How do I propagate a security token for these kinds of things? These are basic things that all applications need, and yet aren't addressed by these basic protocols. A lot of high scale organizations that want to do a better job that cannot afford to lose so much bandwidth and so much inefficiency on HTTP have used RPC or some sort of binary message encoding.

Google, very famously, created gRPC. Anybody here using gRPC? Some people are using it. It makes sense for certain use cases. It uses and requires Google protocol buffs, so that's ok. Salesforce is a company that is in the Bay Area where I live, and they have a terrible ugly looking tower in the middle of the city. They created a pro talk gRPC compiler that uses our project Reactor. That's ok, there's another small website where I come from in San Francisco in the Bay Area called Facebook. They are apparently a very big website now and they needed to scale, so they created a binary protocol called RSocket.

RSocket is supported in Spring Framework 5.2. What I'm going to do is I'm going to create a RSocket controller here. RSocket is binary by default, you can use whatever message encoding you want. It supports four different message exchange patterns: request/response, single value in and single value out, single value in and stream out, stream in and stream out, and stream or single value in and no response, like fire and forget. You can create RSocket services, and they also have an interesting extra thing. It's built by the team that came from Netflix that built RX Java. It's natively reactive. The protocol is built to support reactive streams of data. It's not just like RPC, it's actually asynchronous reactive RPC on the network protocol. There are C++ bindings, JavaScript bindings and Java bindings. The Java binding from Facebook uses reactor and you can imagine what kind of scale that gets to use that.

We're going to use RSocket here. It's a service that we're going to use. I'm going to create a GreetingsRsocketController. I'm going to create a publisher of greetings response, greet given a GreetingsRequest. Does this look familiar? In order to do that, I'm going to inject my GreetingsProducer and we'll say @RequiredArgs and I'm going to say MessageMapping("greeting"). This is the mapping. How does a client find this end point in the service? Normally if you use RSocket directly, you end up creating this switch statement, basically, but I'm just going to use RSocket like that. That's my service, in order for me to start it up on the server, I need to specify which server I want to run on. I'm going to say port=8000.

On the client, I want to create a client to talk to this. I'm going to go back here, and I'll just create a client, reservation client, and I'm going to bring in RSocket itself and I'll bring in the Reactive Web, and Lombok, and there we go. I'm just going to bring in the basics here. My reactive RSocket service, I'll open up the client here in my IDE. Keep in mind, RSocket also has an extra thing that's very nice. It has the ability to tell you its up time. In the protocol itself, there is information about the service's health, and you can change what shows up there, but it's a good way for clients to do very smart load balancing. Imagine you connect to the service and the server says, "I'm too overwhelmed. I have too many users. Go to somebody else, please." This is what actuator does. If you're using Spring Boot, you have the actuator, this does that for you. Now it's in the wire protocol itself.

I’m going to create an RSocket client, and in order to do this, I'm going to do something terrible, something that you should never ever do at home, I'm going to copy and paste some code. Here are my client types, and I'm going to run this application on port, let's say, 9090. I'm going to create a simple API Adapter, not a Gateway. For more on Gateways, you should see Spencer Gibb's talk on Gateways, API Gateways later today. That's going to be good. I won't focus on Gateways.

We're going to create a simple API Adapter. That API Adapter is going to be an HTTP endpoint that just streams the data across the wire using RSocket. I'm going to create another functional reactive endpoint like this, and that's going to be greetings. I'm going to stream the greetings here - actually, better yet, let's just do a Spring MVC style controller, class GreetingshttpController, GetMapping ("/greetings/{name}") flux of GreetingResponse, greet (@PathVariable String name). In order to make this network call, I'm going to use an RSocket client in order to get the data. I need to create an RSocket instance first of all, like this, return RSocketFactory, .connect(), .transport(TcpClientTransport on port 8000, so it will be .start(), .block().

Before I connect, I need to connect with a mime type. I'm going to use MimeType utils, application_json_utf8_value, and frame decoder will be this one. There's my basic RSocket reference, and I can use that to create an RSocket requester. I'll say, return RSocketRequester.create, passing in the RSocket reference. I'm going to change the mime type, I'm going to use a RSocket strategy, I'm going to inject RSocket strategy.

There's my client, and in order for this to work, I'm going to make this an endpoint that serves server-sent events, so produces = MediaType.text_event_stream_value. In order to use this, I'll use private final RSocketRequester, required args constructor, return this.requester.route, "greetings", .data(new GreetingRequest) passing in the name. Retrieve a publisher of greetings response.class. I'm creating a server-sent event endpoint on the different port. This is my client running a port 9090. We know that if we go to 9090/greetings/name, it's going to call my RSocket service on port 8000. 9090/greetings/Sao Paulo.

I hope you see the possibilities here. I'm a big fan, obviously, but you don't have to take my word for it. Like I say, Netflix, Facebook, Alibaba, eBay, lots of other companies are building on Spring Boot, and Spring Cloud, and Reactive to build the better software faster, to get to production faster, and that, at the end of the day, is all that really matters.

 

See more presentations with transcripts

Recorded at:

Aug 30, 2019

BT