 Hello, and welcome again to another OpenShift Commons briefing. This time I've invited Ray Sang from Google to reprise a talk that he gave in the developer room on microservices and containers that caused him earlier this year on GRPC and microservices. He used a lot of great demos and things. I think it will be really interesting to all the Java developers and folks using IntelliJ and other things. So I'm hoping we can have a good conversation afterwards and do some Q&A to pick Ray's brain about this stuff. So without too much further ado, I'm going to let you all ask questions in the chat, and we'll try and answer them, and we're going to let Ray take it away. All right. Before having me, we're happy to be able to present on this OpenShift Commons briefing, and let me get started. My name is Ray. I am a developer advocate for the Google Cloud Platform, and what that means is I love to bring Google's greatest and latest technology to developers all over the world, but I also like to get your thoughts, your feedback, and how you're using our technology today, whether it's the cloud or whether it's the open source projects. So if you have any thoughts, feedback, questions, or just wanted to chat or need any help, just ping me on Twitter at Saddenism, OK? So aside from developing, I love to travel. I love to travel all over the world, and I love to take photos, and this particular photo is special to me because I was backpacking in Asia, in northern China, and being a cheap backpacker, I wanted to find some of the most cost-effective place to stay, and there was this one city where they told me I could stay for free if I go to a desert and hike into the desert for about four hours, and I would actually find an oasis with water and I can stay there overnight for free, and I don't know if anyone on this call actually hiked in the desert before, but it's not very easy, right? Every step you take, you kind of sink into the sand a little bit and you kind of pull yourself out, and you got to go through the sand dunes, you got to go up and down rather than going around it because, you know, when somebody tells you to go into the desert for four hours, you just go in that one direction, otherwise you could veer off. Anyways, it is a very tough process for me, and well, it kind of reminds me of writing a J2ED application back in the day, but the end was beautiful. Once I got through to the oasis, which I'm happy I found, I was alive, and the end is beautiful. Beautiful sunset, and I was very, very happy I made the journey, but this is how I look at my job, right? I'd like to take all of you, I want to take you onto a helicopter and airdrop you into the oasis rather than having to go through all the troubles, so let's get started. We have talked about microservices and microservices architecture for the past year or two years or so, very heavily, and we are seeing a lot of adoptions in this architecture today as well. Whether you are migrating to microservices or whether you are creating a new microservices architecture project, there are some things that you have to really think about. Well first of all, if you're breaking things down or if you're just building microservices, you will have more and more components you have to manage independently, right? And very importantly, it's you need the resources or the team, the small teams to create and maintain these microservices and operating it, but you also need the technology to be able to keep all of them up and running and actually know what's running in your environment. And with this type of architecture, you will be adding some overhead, and luckily, we have tools that we can use to orchestrate and manage all of the microservices instances that you may actually spin up. So for example, rather than having just two instances of something, you may end up with 10, 20, 50, 100 instances of different services, right? So for that, you can use, you know, container technology and container orchestration technology like Kubernetes to help you organize and manage and deploy, you know, as many instances as you want, literally. However, one thing that we don't really think about is how do these microservices communicate with each other? I see it in two types, right? We have services that you need to consume from the browser, right? And for that, the browser can only consume REST, though that is the default protocol that we use. However, in between services, we also defaulted to using REST or JSON over HTTP to have these inter-micro services communication, but is that really the best way to do it, right? And we really need to think about how and why are we choosing and picking the technology and also what type of technologies are available to us. And this is where GRPC come in, and you might be thinking, RPC, well, that sounds a little familiar. I think we have tried RPC before, and I thought nobody liked it, right? But RPC has been around for a long time in distributed systems from the past, and I don't know if anyone on the code remembers Corba. Corba is, of course, one of the prominent RPC technology where you can define an IDL, so you can define an interface definition language where you can, you know, say what it is that you are trying to build, what does the message payload look like, and it's going to generate a stop for you so you can implement it, just the operations you want to implement. But then I remember that if you want to connect to the server from the client, it's like 100 lines of code or something like that, and it gets a little tricky when you look at RPC and having to go through all the troubles to implement it, similar things with Decom, but Java, Java, we traditionally had a really good RPC support with RMI, and it's really, really easy to implement an RMI server, an RMI client, however, it is not really interoperable. Now, what happened then next is that we introduced SOAP, right? There was SOAP in the industry, and it is interoperable because we use the most interoperable format, which is literally text and XML, for that matter, and a lot of people think that, well, in hindsight, SOAP was slow, SOAP was slow because of all the XML processing we have to put in place, so you couldn't really live up to the problems that they are trying to solve, right? However, just like what we have done before, which we defaulted our services to SOAP protocol, today we're defaulting literally to REST, which is JSON over HTTP, but if you think SOAP is slow, REST is actually not much better, I mean, it's definitely better than SOAP, but it is still text-based, and you still have to go through the parsing, you still have to go through a lot of marshaling and marshaling, and you still need some kind of definition language if you need to be able to generate the client, so you don't need to write your own client error again, and so rather than using with SOAP, we needed to use Swagger, for example, to document and define what does that REST endpoint look like? But why RPC? Well, in addition to performance, RPC is going to be more efficient, because most of these RPC frameworks are going to be transmitting over a binary protocol, and most importantly it's going to be strongly typed, because you have to define your interface definition language upfront, you have to define your message payloads upfront, and then the generated stops for you will have strongly typed semantics in there. However, more importantly, when we default something to REST, we are locked in by the REST semantics, which are basically based on HTTP, which are generally CRUD operations, like the put, the post, the patches, the delete, but there's just some operations you cannot simply express in CRUD manner. For example, if you need to deduct a balance from an account and transfer it to another account, how would you do that in REST? Potentially, you have to modify two resources, and what you end up happening is we will potentially end up with some kind of JSON RPC, so we're just going back to RPC in one way, one for more another, where we're just using a text protocol to do that, but I think RPC can be really great if it is simple to use, just like RMI, but also interoperable, just like REST or SOAP, and that's what we actually have developed. At Google, we use an internal technology called Stubby. Stubby is what we use for most of our services, for service communication. In fact, we transmit 10 to the 10 RPC calls per second via Stubby, and that is an incredible amount of service calls per second at Google. Stubby is a binary protocol, and it is made to be very efficient in the usage of the bandwidth and all that, but just imagine if we use something else that's text-based, the 10 to the 10 RPC per second, we will be using a lot more bytes to transmit that. Even for Stubby, if we just take one more byte than necessary, that would be 10 to the 10 more bytes we have to transmit per second, and that is a mind-boggling amount. So what happened next is that Google wanted to open source Stubby, and I heard this company, Square, wanted to also develop the next generation of their internal RPC framework as well. So the two joined forces and decided to open source this GRPC together. So GRPC is really the next generation of Stubby. It's based on the experiences and the code that we have internally developed, and we open source it, and we call it GRPC. The G in GRPC stands not for Google. The G doesn't stand for Google. It's actually a recursive acronym. GRPC stands for GRPC Remote Procedural Code Framework, and it is simple to use, it's performant, and it is also made to be interoperable. And we are able to achieve the performance interoperability by basing it on standard technology and newer technology. So the transport layer of GRPC is not just a straight up TCP socket, right? We actually decided to use HTTP2 on top of TCP to provide this transportation layer. And why HTTP2? And what is it in HTTP2 that makes it compelling? Well, first of all, HTTP2 is binary. Rather than having to transmit the headers or the methods, whether it's get, put, or post or whatnot, rather than transmitting all of those things in plain text, with HTTP2, they can be encoded in binary. So the get that puts the deletes, they're just going to be encoded into a binary format rather than the entire string. And the headers are going to be compressed as well. If you ever look at the payload of an HTTP request, what you will find is that sometimes the payload is very small and the header is actually taking more bytes than the payload itself. And in HTTP2, we can compress it with HPEC. Now, more importantly, in HTTP1, we have the issues of keeping the connections alive. You can't really do streaming. And if you need to make multiple calls, you've got to do pipelining and all that. In HTTP2, we solve the problem by just you are able to have a single connection and we can multiplex multiple strings through the same connection. So rather than having to open multiple connections, you just open one. And we can also do streaming. We can do streaming from the client to the server and the server to the client and also both ways at the same time that you can multiplex it. And for the payload, we're using PortableBuffer3. And that is a way for you to basically marshal data into this portable format. It's binary encoded as well. And we can then transmit it to multiple different languages and every one of them will be able to parse it. And there's a quick comparison between the two protocols. There's binary, which is GRPC. You can see the throughput is, of course, faster than JSON over HTTP. But of course, it is faster. Anything that's binary that doesn't require a lot of parsing and marshaling is, of course, going to be faster than something that requires it. So that is not so interesting to see. I would be really concerned if the graph is the other way around. But what's most interesting to me, though, is that if you look at the per-CPU throughput, GRPC really all shines the regular text-based protocols by multiple folds. And this is important, especially if you're moving into a cloud-native mode, where you want to run your applications in a scalable fashion. And as a cloud provider myself, I rather than telling you to use as much as you want, I always argue that you want to use your CPUs as efficient as possible. And to achieve that, you have to pick efficient technologies. And in this case, GRPC will be really great if you run it on a server side. But not only that, we can also run GRPC clients on the mobile devices. So what that means is rather than costing the mobile device a lot of CPU cycles to perform the parsing and transmitting textual data over HTTP, you can actually use GRPC and transmitting binary data over HTTP2 from mobile devices. And what that translates to is actually more efficient usages of the mobile device CPU and power, for that matter. Here's a quick overview of the languages that we support. And I want you to focus on three, which is Objective-C, C-Sharp, and Java. And these three languages are the primary languages that you would otherwise use for mobile development. And GRPC was really made with mobile first in mind. So enough of the overview and the slides, I'm just going to go into the code and show you what it feels like to write something in GRPC. So here I have a bootstrapped project. I have a few demos I can show. The first one I want to touch on is actually a simple unary request and response. What that means is all I want to do is to send in one request. I want to say hello back. And we'll see how this works with GRPC. And you can also compare this with other frameworks, whether it's RMI or Corba, if you like, if you still remember how to do that. So with GRPC, the first thing that we needed to do is to define the IDL. We need to use the IDL to define what it is that we can actually transmit, what kind of service are we going to create. And the IDL is going to be created with the Kotl3 language. And so to do that, let me into my microphone down a little bit. So to do that, first of all, you've got to create a Kotl file. And in this Kotl file, we need to say syntax is equal to Kotl3. That is because we needed to make sure we're using the Kotl3 syntax. Otherwise, it could be using an older version of the syntax. And then, just like Java, we can put classes into specific packages. So can we in GRPC? So we can say something.com.example.grpc. And what that means is everything that we define in this IDL, what we generate is going to be generated into the corresponding Java packages. Now, with GRPC, this file, this Kotl file, will have to be compiled, basically, or translated into actual code for the target language. And there is what we call a KotlBuffer generator compiler, or Kotl compiler, a protogen compiler. They have many names for it. But through this compiler, you can give it additional options. And so you can specify different knobs where you generate into different languages. So for example, for Java, by default, it's going to generate everything into a single large Java file. But written in half doing that, what I really want to say, Java multiple files is equal to true. And what I would do is that when the compiler compiles this into Java code, it's going to generate individual Java files for individual classes. So that's just bootstrapping the IDL. The next thing that we needed to do is to define the request and the response. So to define the request and response, what we needed to do is to define the message payload. So we can define the message that says that's called hello request, for example. And what that's going to do is to generate the current spamming pojo code hello request in Java, in which you can get access to all the properties or attributes that you define in this message payload. And the way you define the message payload is simply define the type that you want to use, because everything is strongly typed. This is great. And you can say the first name. So I'm defining an attribute of first name or a property equal first name that is of a string type. And very importantly, you have to give it a tag. A tag is the number that uniquely represents this field in this particular message. And this tag, this number, is actually what's used to be transmitted in the binary encoding of this message. So rather than sending the whole text called first name, like people do with JSON, where it takes a lot of bytes to transmit over the wire, we just send over the number in binary format. So that would be just one byte to transmit this one number. And so we can define another one. Let's say last name equal to 2. I can also use integer 64 for the age equal to 3. For example, I don't think anyone would live that long. But I'm just going to use integer for now. And we can also define enumerations. In Java, we have enums, where you can define a set of possible choices for your value. And in Java, you see I can do a very similar thing. So I can define an enum called sentiment. And this is how we feel right now. I feel pretty happy. So I'm going to do happy is equal to 0. Hopefully, you are not sleepy, but you could be, because it is 9 AM in the morning. Not to say sleepy is equal to 1. By the end of this talk, you could be extremely angry at me for having spent an hour here. Then we're going to put angry equal to 2. Well, hopefully, that's not the case. But just in case, just in case, hopefully, you would choose happy in the end. So once you define the enumeration, I can go ahead and say sentiment is equal to 4. Done. I can also use other types. So if I have other messages that I defined, you can think of them as other structs or other classes. I can just use those messages nested in here as well. So for example, I can say if I define another message called language, I can just use language type here instead. But I don't have that right now. In addition to some of these basic types, we can also do a list. So if you have a list of something you want to return, you can say repeated, for example, hobbies is equal to 5. So you repeated the string. You need to strongly type this. So repeated string, hobbies is equal to 5. And we can also do create strongly typed maps. And this is quite nice. So if you have a bag or a map or a hash table of something you want to transmit, you can say map, the key type, and the value type. And this is very similar to Java, by the way. So this should be no problem to just picking this up. Now say back of tricks, OK? Like coding is definitely not in my back of tricks in this case. So once you have defined the request, all you can do is to define the response. So I'm going to say hello response. And I can, again, define the property. And in this message, I can define the tag. And of course, I can use tag number 1 in this message because they just need to be unique to the message. Once you have defined the payloads, the request and the response, then what you can do is to define the service. So I'm going to code this greeting service. And I can define the operation I want on the service. So the operation is going to be greet in this case. And it's going to take in a hello response. And it's going to return a hello, oh, sorry. I'm going to take in a hello request. As you can see, this is really early for me. And I'm going to take a return a hello response. There we go. OK. Now, so this is what we call a unary request. What that means is there's a single input and a single output. You give the server one request, and the server is going to give you one response. If you want to return multiple responses, you can return that in a string. And so what that means is the server can stream the data to the client. So the server can stream the data to the client at will. And to do that, all you need to do is to add a stream keyword. If you're building an IoT device needed to stream data, like metrics, to the server, then you add a string to the request parameters. So in this case, the client will be able to establish the connection and then stream multiple requests to the server. And in the end, the server will give you one response. Or you can return no response if you don't want to do that. OK, so that's great. That's the IDO. And the next thing we need to do is to compile this. Now, usually what you need to do in the past is you've got to figure out, OK, am I running on Mac? Am I running on Windows or Linux? And then let me go and download the compiler, the Protogen compiler, and then you have to pass in these low and complicated communes. We really streamlined this whole experience really, really well. So now all you need to do is go to GitHub, for example. And let's find the gRPC. So here's the gRPC GitHub repository. If you just scroll down, it actually gives you all the things you need to get started. So because I'm using Maven, I'm going to go ahead and, first of all, add my dependencies. So I'm going to go to my Maven palm. And here we go. I'm going to add the dependencies, dependencies. And there you go. But these are just dependencies for the code to consume, because you need the gRPC API. However, in order to compile this portal file and translating that into the Java code, what we need to do is by using the plugins. And we have plugins for Maven. We have a plugin for Gradle as well. I don't know what the breakdown here, but usually I see 50-50. If you're using Gradle, here's the code that you can use. And if you're using Maven, here's the code that we can use. So because I'm using Maven, this is what I'm going to use. And I'm going to go ahead and paste it in my palm.xml. This little snippet of code does a lot. Well, first of all, it determines which operating system you're running on and it's going to put it into a variable. And then it's going to be able to download the right binary based on the operating system that you're running. And then it's going to execute this binary and by binding into the different Maven build phases. So in this case, I'm saying whenever I'm compiling this Java code, I want to go ahead and make sure my portal file is also compiled and so my classes can reference and use it. So let me try it out. I'm going to go to my here and go to my terminal. You want to say Maven, clean and package. And maybe I made a mistake in my portal file. Maybe not. It is early in the morning, but we will find out. Ah, everything worked. Well, that is great. And if I see target and if I go to generate sources, there's the portal buff directory. And here I can go into Java, GRPC, and you can see all of the messages and also the services that I defined. So it generated all the stops and all the approaches for me just from a simple IDL. That's good. So let's go ahead and implement the stop. So what I'm going to do is to create a new Java class, I'm going to call this greeting service input, for example. No, let's not add this to it. And so for this stop, just like many of the other RPC frameworks, when you implement this stop, you need to extend or implement an interface. You either extend the base class or you implement an interface. In this case, we're going to extend a base class. That was automatically generated for me, which is greeting service input base. This base class actually implemented the greeting method by default. And the default implementation is just going to store an arrow that says there's no operation. It hasn't been implemented yet. So for you to make sure that this works for the client side, you have to implement. I'm going to say greet. And here's where things get a little tricky. And this is why I'm here, because I want to make sure you have the best experience when you're using this. So if you look at our initial definition, it is a request and a response. Now, if you look at the generated stop, it looks slightly different from what I would otherwise expect. Because usually when you have a request and response, you will say hello response as return value. You would say greet. And then you will have the hello request in the parameter. But if you look at this implementation carefully, I'm getting the request in the parameter. And I need to respond by using a response observable callback. I need to use a stream observer callback. And the reason that we're doing this is because we want to make sure the server side is as efficient as possible. And that's why in the server side implementation, everything needed to be implemented and synchronously. And because it is an async implementation, so we cannot just do the return in return value because that could potentially be blocking. So rather than doing that, we ask everyone to just use the stream observer to pass the data back to the client. So this is one of the most important concepts here is by using the stream observer. So I can say the name, for example, is equal to let me say is the get first name plus let's just say get last name. I can say the greeting message is equal to let me say hello last name. So you can see the request object has been generated for you that has all the setters and getters and the executor actually. And I can just simply use it. In fact, I can do something like this. I can say system.out.cleanline the request because we actually implement and generate the two string as well. So you can debug this very, very easily. You can log your request if you want to. And then what we can do is to create a response. So I can say hello response is equal to now the thing in GRPC is that everything uses a builder. Everything is almost immutable. And for you to create and construct a new instance of something, you have to use the builder pattern. So to create a new response, I need to create a new builder. So we can say a new builder. In the end, we say build that's going to construct me the actual instance Apollo response. But in the builder, we have all of the same fields that we can actually set. So here I can just say greeting is greeting. So I'm just setting the response back. And to give it back to the client rather than the return value, we have to use the callback. And this is interesting now because if you see the callback, we have three. There's onNext, which is to give a valid response back to the client. We have onArrow, which is if your service ever caught the exception, you can use onArrow to give it back to the client. And you can also use uncompleted, which will terminate the string. And if you look closely on these three methods, this actually closely resembles a reactive paradigm. This is pretty similar to RxJava and many of the reactive callbacks that you have seen in the past as well. That is because when you string something here and also asynchronously, this literally is implementing a reactive service. So I can say onNext and the response. The response, there you go. And that will send the response back to the client. However, this is not done yet because what you also need to do is to terminate this request session or string. So you have to call uncompleted. If you don't call uncompleted, even though the client is only expecting one response, the string will be held open until you call uncompleted. So you must do this no matter whether you're returning a string of responses or if you're returning just one. Not only that, if you do string observer call onNext, you can actually call it twice. But that is invalid in this context because you are supposed to only be returning one response. And in this case, we will actually give you a runtime exception. So just be very careful when you work with the API, where you could potentially make a little error like that. Just watch out for it. Now we have the servers implemented. All we need to do is to go back and implement a little server runtime to start and listen on the port to respond to the requests. Now because GRPC uses HTTP2, and HTTP2 support doesn't come into Java until Java 9. And also, GRPC comes with a server. So what that means is you don't really deploy this service into a container, like a web container. So you don't really run a GRPC server in Tomcat or Jetty, for that matter. What you do is to just run the Java Java files directly. So you need to implement potentially your own main methods, and then you need to start the server yourself. And so to start the server, first of all, you need to create a new server. And to do that, you can use, guess what, a buter. Everything in GRPC uses a buter pattern. So I can say, I want a new server that lives in some port 8080, and go ahead and build it. That's really it. And I can potentially assign this to a variable. So bear with me with my recently messed up key binding. So I'm going to click on the UI instead. I'm going to introduce a variable code server. And I can add the server's implementation I just created. So I can say, new greeting service input, and that's it. So this is interesting too, because you just need to give it one implementation instance. And you can somehow create this instance with dependency injection or passing the necessary constructor parameters. You can construct this service elsewhere, and then pass it in and register it. If you have multiple services, you can just register multiple ones. Once you have the server's registers and the server built, you have to start it. And what this is going to do is to start in the background threads. And because it is in the background, if I don't do anything else, this main process, which is exit, and all the background threads, which is die all together, so we're not really running the server anymore. So what we need to do is to await termination of the server. So I'm going to block these main methods. And that should be it. Now I really took the long route to get here, because I want to show all the details of implementing a service. But once you get started, just remember, first thing you do is defining the IDL. Second thing is make sure you have the palm where you can just generate the stops. Then you implement it, which is really straightforward. And you run the server, and that takes literally a couple lines of code. Once you have this, then we can go ahead and try to do this to run it. So I'm going to run a command line queue up here. I'm going to do maybe install, execute, and in the main class, I just create it. So let's see if this works in the first try. So compiling, that is good. Executing, and this is actually working. This is actually just listening on port. I'm not clicking anything. That's why you don't see any logs. But this is just listening and waiting for connections. Great. Now the next thing we can do is to implement the client. And the client is really, really straightforward to implement. This is nothing scary. In fact, I will argue this is probably more straightforward and definitely more straightforward than Corba, potentially even as straightforward as RMI. This is really easy to implement. So let me show you how this is done. Well, first of all, we need to be able to specify a connection. We need to connect to somewhere. We need to connect to the server. To do that, we need to specify the IP and the port. And rather than having you to deal with the underlying TCP and HTTP to connections, what we wanted to do is to hide all of these complexity and give you a higher level abstraction code, a channel. So I can create a channel via, of course, a builder. I can say I need this channel to connect to localhost on port 8080, in this case. And because I am running this in development, I don't want to deal with SSL, because that could take a whole other hour or of top time to talk about using SSL in Java. I'm not going to do that, but I'm going to say context is equal to true and I'm going to build. Okay. Now looking over here, in this many channels, it can also handle load balancing for you. So if you ever needed to load balance on the client side, you can specify a load balancer factory on the client side where we can actually perform run-robin load balancing for you. But if you want to load balance your request, what you also need is you needed to know a logical name and how it maps to the actual server addresses. Because for a single service logical name, you may have two, 10, 20, 100 different IP addresses you can possibly hit. So to do that, you can specify a implementation of a name resolver. And what the name resolver needs to do is to turn that logical name into a list of IP. And I have an additional code on this. You can look it up in my GitHub where I actually will type a name resolver into say Eureka for name resolution. Okay. And you can implement your own. Once I have this channel, let me assign this to a variable. Hope channel. Once you have the channel, then you can start to consume it and you can consume from a pre-generally stop. And the stop is called greeting GRPC. And I can create a new stop that we're taking a channel. Now, again, look carefully here. This is cool. On the server side, we implement everything as sequentially, that is for performance. But it is up to the client to decide whether the client wants to block the operation or not. So the client has a choice in terms of the stop that they create. So you can create a fully asynchronous stop by calling new stop. You can create a stop that returns the future by calling the new feature stop. You can also create a blocking stop instead. So let's use the blocking stop for this example. So I'm going to create the stop. Oh, and assign this to a variable somehow by clicking on that. I'm going to call this stop. And because this is a greeting stop, what I can do is I can give it a request just like how I envisioned the original stop would look like. And I'm going to get back the response. Like, oh, there we go. There we go. So I can get back the response. And this operation will then be blocking. And I can go ahead and do a system out of the clean line and I can print out the response, okay? Now, in the request, what I can do then is to construct something just with the builder. So for example, I can set in the first name. I'm going to say I'm Ray. Set the last name, same. I can also set the age, it's a low value, but I'm just going to say I'm 18 for now. And remember all the repeated fields, we added like an array of data or a map. Well, all of those things has convenient methods you can call. So you can say add hobbies. I can say photography, for example. And I can put something into the map, put back off the trick. Live coding, definitely not good. All right, so that should be it. And this is the moment where I find out whether I can live code or not because I'm going to try to run this and we'll find out. So I have the server running here. Here I'm just going to say, maybe I'm clean. So I'm going to clean everything just to make sure I'm not cheating. Package it up. I'm going to execute the client-side code and let's see what we get back. So here we go. Ah, there we go. It actually worked the first time, which is not bad. So it connected to the server, it sent in the request vrgrpc over hdb2 binary encoded. And here's the server side. And like I said, we implemented the two streams. So you can see all the details here as well. Pretty nice, right? Now that is actually too easy because it's just request and response, right? But I really wanted to show you what that feels like. But the real power here is you can implement bi-directional streaming services, streaming services just as easily with exactly the same paradigm, okay? So to demonstrate, I'm going to do this fairly quickly in the next 10 minutes or so. I'm going to implement a chat server. In a chat room, a chat server, right? We can really leverage bi-directional streaming because the chat messages are coming in continuously. And whenever a new chat message appears, we need to broadcast it and send it out to all of the connected clients in a streaming fashion as well. So here is the video that I created to demonstrate this. So we have the payload chat message, which has the who sent it and what message is being sent. I created another payload just to clarify that this response is actually coming from the server. Okay, so we have two types. If you look at the definition of the service, I just add a stream to both ends and now we have a bi-directional streaming service, okay? And if you look closely on the top, you can also import the types or messages from another proto file. You can import another IDL. And this is almost like importing a package in Java, right? You can import a package or a class from another package. And once you import it, you can actually refer and nest those types as if it's your own and you can use these types to specify typesave attributes as well, okay? And this is going to generate a stuff for me that I can implement, okay? So this is the server type stop. And if you look at this stop carefully, this is all generated for me, right? I added a few code here, but here's the trick. Because it is streaming both ways, what that means is I have to deal with stream observers both ways. And we have two observers here. That means there are two streams going on, okay? And there's where you really have to pay attention, okay? The request that's coming in to the server is going to be listened on by a streamer server. And that streamer server is what the server returned because the server returns a code back implementation where it's going to be listening to request, okay? So rather than having the request in the request parameter and the response in response parameter, this is actually reversed. So the chat request parameter is actually an observer that you need to use to send data to the client, okay? So for me to implement this, I need to first of all return a new stream observer, okay? And this is what the server will be using to listen to the data coming from the client, okay? So as the new message come from the client, it's going to trigger this on next, okay? And then this is receiving data from the client. When I receive the data from the client, well, what do I need to do? I need to broadcast the message to every connected client here. And every connected client will be an instance of the response observer that they pass in from the request right here. So what I can do then is say, I can create a new chat message from the server, gotta use the builder, right? And I can set the message to the chat message that just came in. And I can assign this to a variable, so the from server message from the server. And then I can either way through all the observers that I have. So I can use a stream, I can say for each of the stream with observer, I'm gonna say oh, on next, and the message from the server and I will then send everything to the client, okay? And then you need to handle errors if the client ever giving an error, it's going to trigger the unerror callback. And I'm going to do what all the good Java developers do. When you catch an error, we do nothing, all right? Not just kidding. In this case, we can do something else. We can disconnect the client so I can remove my current connection from the list of connections from the server. And we can do exactly the same thing when the connection has been completed. And that should be it. That's all I need to do to implement the server side. So let me go ahead and run this. You wanna run this server, do a clean install, and there we go. My server should be on print running. Now, in case you're wondering what a second, he didn't implement the main class. Well, I actually implemented already and because it takes so few lines of code, it's exactly what we have done before, okay? So that's all you need to do to register service and make this service bi-directional. Okay, now the last thing I need to do is to show you this works. If I don't show you this works, this could just be an empty while loop main class, right? But I wanna show you this works. So to do that, I'm going to do a main and clean and on the client, I made this Java FX client GFX wrong. So this client does nothing right now, but it is a Java FX application, there it is. I can say Ray and hello. If I click on send, it should actually send a message to the server, to the chat room, and everybody else connected should be getting the message as well. Of course, this doesn't work right now because I haven't implemented it, okay? Whoa, so let me go back to the client and this should be fairly easy. So let's see. So in this code, I have a lot of bootstrapping codes just for Java FX, but that's okay. And then I have the channel, which I'm going to establish to connect to the server. I can do that. I'm going to create a synchronous stop in this case. So I'm gonna call new stop. And this is a fully asynchronous stop, fully reactive. So what we can say is, well, let me create code to chat service and code to chat operation. Now here is where things really need to connect because I need to pass in a stream observer here as well. And remember what this observer actually do. What is this stream? This stream is a stream of chat messages that the server sends to me, okay? So whenever a server send me a message, it's going to trigger this on next. And when I receive this message, what I want to do is to add it to the list of messages. Yeah. Wait, your screen is not being shared right now. Oh, it is not. Oh, let me try this again. Yeah, cut out just a minute ago. Yeah, I thought the Java FX thing really turned that thing off. There we go. Are we sharing? Yep, you're good. Okay, fantastic. Thank you so much. Okay, so now we're actually implementing the on next to, I want to display the message on the Java FX client. So to do that, I need to do platform.run later because I cannot touch the background thread and UI threads at the same time. So I need to do this later. But what I'm going to do is I'm going to add messages. I'm going to add a new message and the message will be something like let's say string.format will be the front and the message itself, right? And what does that look like? Well, I have the chat message from the server. So it gets the message and get the front. And also I'm going to get the message payload as well. So that should just whenever a new chat message arrive it's going to trigger on next it's going to add the message to the list and that will then render in Java FX in the client side. Okay, now here if I catch an arrow I'm really gonna do nothing at this moment for now. And when the server terminates my connection I'm gonna do nothing as well. That's it. Yay. That's receiving the stream. Now I need to actually send the data in a stream as well. So what I can do is I'm going to assign this to yet another observer, okay? But this observer is really listening to by the server. So I'm gonna say this is a to server, okay? So whenever I need to send something to the server I gotta use this code back on next and I can construct the chat message like that. I can use the builder, I can build in this message. I need to set the front from the name field. Okay, I need to set message from the message field. And whenever I trigger this code back it's going to send this new chat message to the server. And I only wanna do this if when my send button is click on. So I'm gonna say if the button is click on set on action, I get an event, okay? And whenever somebody click on the button I'm going to send the data to the server just like that. And I think that should be it. This is how easily I can implement a bi-directional service and also implement a real client consuming it. So let's see if that works. So I'm gonna run Java FX again. I'm not sure this is going to crash the sharing. So let's try it. Oh, there we go. I think sharing just died. So the share died? No, still sharing, good. So there we go, there we go, you're good. Okay, good. So I should be able to say Ray and say hello and click on send and there you go. You can see that the message was returned to me, right? I can say, that worked. There you go. Okay, but that's only just one client. What I wanna do is to make sure this works with multiple clients. So I'm gonna run another GFX client here. Let's see. Okay, so hopefully I'm still sharing. Okay, you should be able to see this. I'm gonna just be, I'm gonna be my manager. His name is Greg, you gotta say, great job, Ray. All right, and there you go. And now I have bi-directional streaming with multiple clients. Yeah, why? Thank you, Greg. Yeah, there you go. And I can chat with myself as long as I want, but I'm not gonna do that, okay? So hopefully this kinda shows you how we can create the service very easily in GRPC and you can stream data now, which is not something you can easily do with the RESTful services. Now let me go back to this slide. Just one more thing I wanna talk about. There you go, streaming, yay. So we talk about that, but there's something even better. GRPC was really designed from ground up to reflect many of the use cases that Google internally have faced. And one of the biggest challenges that we had was to make sure that we respond to the client within certain deadline constraints, right? So you can actually specify a deadline. What that means is if you have like a search service of some sort, you just type in the query, you say search, well, maybe we want to restrict that to just one second. I want everything to be responded within a second, right? So I need to set a deadline. I can say the search service can respond only within a second, but the search service is going to call multiple other services in parallel, right? And some of these other children services that we call could take longer than one second. And then those services will call yet another service. So you have nested services. So in GRPC, when you set the deadline, the deadline is actually propagated through all of the service co-layers downstream. So if the search service, the top service can only take a second, and the second service took half a second, and the third one took say another half a second, then your time is almost up. And if the downstream took more than a second, this entire call will be canceled and you can listen to these cancellation events as well, right? So if you're doing parallel processing, if one of the service doesn't respond to you in time, if that's okay, then you can just respond whatever you got already to the client. You can also implement interceptors. These are like filters in the serverlet. And you can also propagate data, like a tokens or authentication tokens from one service to another via the headers, okay? So there's a lot more to this. And we can address a lot of the microservices concerns. If you're interested, definitely, definitely let me know. One of the most frequently asked question is, can you do server-side log on and seeing the answers? Yes, there are some of the things you can use to do that. And the reason that I love doing this is that I really want to see you trying out GRPC. So if you like to do that, go ahead and go to grpc.io. And you can check out my demos and code on GitHub. And most importantly, if you are able to contribute, if you have feedback, please check out GRPC.io Contribute. We love to hear your experiences and feedback and seeing your contributions as well. So thank you for your time and thanks for having me here, Diane. Cool. And that's all I have. I'm gonna stop the sharing for now and I see many chat messages. There are a couple of questions here and I'm definitely gonna watch this again on a slower speed so that I can follow some of the typing that you packed in a lot in an hour. There was an early question around what Peter asked was, so is there a un-martial effect and given each language package's data differently? And... In terms of the un-martial effect, I'm not quite clear on the nature of the question though, but the binary format is interoperable between multiple languages. And when we marshal and marshal into those languages, we make sure that it is the same in that language, right? But that means you're doing a decoding between them, just like you do from text. It's just a different type of decoding. Oh, gotcha. Yeah, so the decoding is gonna be based on the binary format, but in Polar Buffer 3, this is something that's really, really cool that they're doing, which is zero copy. So when you're decoding something, if you know the structure upfront, because we do, because we have this predefined, then potentially you can have just zero copy decoding. What that means is rather than mapping everything into a new pojo and just copy, re-copy the strings over and over again, you can just point to that byte first and there you go. But different languages will potentially behave a little bit differently. Now that being said, there's a public dashboard in terms of it actually shows you the GRPC performances across multiple languages. And that is a really nice dashboard. You can find it on grpc.il. You can see the performance differences between different languages in that case, because yes, there may be some over-heading different languages as well. Okay. So there was one other one a little bit further down. Jed was asking, are there concurrency antics to manage transaction type situations, async and stink? Transactions, I see. Yes, so you gotta be careful with concurrency. There's no doubt about it. And you handle it just like everything else. And you could probably assume that once you're in that code back, once your thing got code, it has its own thread local, its own thread. So what you wanna do is to, like if you have anything that you want to pass between services within, say the same transaction or the same code within the same server boundary, you have to use a context propagation in that case. But essentially what that hides behind the scenes is the thread locals that they store behind the scenes. Let's see. That might be all of the questions. Dun, dun, dun, dun, dun, dun, dun, dun, dun, dun, that I'm seeing. Ray, could you throw back up on the scope? There's one tag, follow up from Judd. So the tagging between GRBC and language concurrency is left to the developer. Just confirming that. The tagging is definitely left to the developer. Absolutely, yeah, definitely. In concurrency, I mean, we will, the GRPC server will manage multiple threads behind the scenes. I mean, as all developers, everyone needs to be aware of concurrency regardless, right? That makes sense. So if you could put your last screen with your how to connect with you details back up, share that. I managed to do this whole thing tethered to my iPhone without losing power and power has not come back into my house yet. So I still have 16% left on my laptop, so nothing has died. So that's a really good sign. That's really lovely for me, yeah. And I did put up, and I will, when I blog with this, the link to download the IntelliJ plugin for OpenShift and Kubernetes. So for folks who are looking to do all this and deploy directly to OpenShift and Kubernetes, there's a great way to do this. I gotta say, I love the name resolving in IntelliJ and everything there too, so great demo of their product. And might almost might make me wanna program in Java again, but doubtful as a Python program. I can appreciate it, but so thanks so much, Ray. We definitely will have you back as the doing when the next round of things and features come out in GRPC. And look forward to running into you or maybe crossing tracks in Germany as you're going over there and we're going over there for two different events, but if you can make it to the OpenShift Commons Day next Tuesday in Berlin, we'd love to have you there as well. If you can sneak out of, where's your conference, which conference are you going to? Java land, and that's closer to Frankfurt, yeah. Yeah, everything's just a train ride in Europe. Yeah, that's true. Cool. So thanks again, it was a great demo and a good way to get an overview on GRPC. So we really appreciate it and look forward to hearing more from you. Thank you. All right.