Welcome back, dear readers, to the world of "Wtf is gRPC?" – where we turn tech into a comedy show! 🚀
In Part 3, we're diving into real-time chat with the grace of caffeine-fueled squirrels and the humor of rubber chickens at a stand-up gig!
Get ready to leave slow chats in the dust and make your app as snappy as a squirrel on roller skates. Grab your code editor, sense of humor, and let's chat it up in style. Part 3 is gonna be chat-tastic! 🍰💬😄
Table of Contents: Part 3 - Real-Time Chat Extravaganza 🎉
- Introduction to gRPC Bi-directional: The Magic of Two-Way Chats
- Setup Your Server: Where the Real-Time Chat Spells Begin
- Start with Flutter Application: Embark on Your Chatting Adventure
- Outputs: The Hilarious Messages of Success
Introduction to gRPC Bi-directional: The Magic of Two-Way Chats
As we've journeyed through gRPC, we first delved into unary communication, which is essentially one-way communication from the client to the server. Then, we ventured into the world of server-side streaming.
Now, let's dive into gRPC Bi-directional communication. This is where things get really interesting! In Bi-directional streaming RPC, both the client and server engage in a lively conversation by sending a continuous stream of messages back and forth.
Here's how it works:
- The client initiates the action by setting up an HTTP stream with some initial header frames.
- Once this connection is established, both the client and the server can send messages simultaneously, without waiting for the other party to finish. It's like a dynamic chat where no one has to take turns.
Think of gRPC as the superhero chat line. It's like Tony's suit and Bruce's lab assistant, J.A.R.V.I.S., having a direct line to each other.
Socket vs. gRPC Bi-directional: A Cinematic Showdown" 🍿🎥
Aspect | Socket | gRPC Bi-directional | Top-Rated Movie Comparison |
---|---|---|---|
Setup Complexity | Generally low, traditional sockets require less setup | Requires setting up gRPC services and protocols, which can be more complex | Like the simplicity of "The Shawshank Redemption" |
Communication Type | Low-level and can be used for any data format | High-level, designed for structured data transmission | Similar to the plot intricacies of "The Godfather" |
Performance | Can be efficient for simple data exchange | Optimized for high-performance, especially in microservices | Like the action-packed "Mad Max: Fury Road" |
Bi-directional | Possible with added complexity and custom handling | Inherently supports efficient bi-directional communication | Much like "Pulp Fiction's" non-linear storytelling |
Error Handling | Requires custom error handling | Provides built-in status codes and error handling | Comparable to the suspense in "The Silence of the Lambs" |
Cross-Language | Supports multiple programming languages | Multi-language support with protocol buffers | Like the multilingual charm of "Inglourious Basterds" |
Integration Ease | Requires more manual integration | Easier integration into gRPC-compatible systems | Much like the seamless blend in "The Dark Knight" |
Setup Your Server: Where the Real-Time Chat Spells Begin
- Let's continue code from part 2,where we left.
git clone git@github.com:Djsmk123/Wtf-is-grpc.git
- Switch to part-2 branch(for starter code) ```
git switch part2
- As we are going to create Chat one to one service for that we required to get all the users and then communicate.
- Create profobuf for getting users(`rpc_users.proto`)
- Add
GetUsers
rpc service in GRPCServerService
// add this line to import
import "rpc_users.proto";
service GrpcServerService {
// add this line too
rpc GetUsers(UsersListRequest) returns (ListUserMessage) {};
}
- Generate equivalent code for server(`Golang`)
protoc --proto_path=proto --go_out=pb --go_opt=paths=source_relative \
--go-grpc_out=pb --go-grpc_opt=paths=source_relative \
proto/*.proto
- Add function in `auth` package `get_users.go`
- Let's call this function in grpc-server interface(package
gapi
) and add this function in auth.go
.
- We are done with this. But we required users so that we can send message so don't, I am providing list of users (around 35), just load them into
mongodb
.
> Note: Password for each user is 12345678
.
Setting Up Your Chat Service:
Establishing a chat service is a straightforward procedure.
-
RPC Service Creation : Creating an RPC (Remote Procedure Call) service is at the core of this setup.
-
Bidirectional Message Stream: Implement a bidirectional stream of messages.Messages are initiated by the client and sent to the server.
-
Bidirectional Response Stream: Create a bidirectional stream for responses.Responses are generated by the server and sent back to the client.
-
Acknowledgment Mechanism: Before actual communication begins, an acknowledgment mechanism is necessary to connect both the client and the server.This mechanism helps ensure a successful connection.
-
Database Handling: Notably, the acknowledgment message is designed to be transient and is not stored in the database.
Confirmation of Connection: The acknowledgment message serves as a confirmation of a successful connection.
Setting up protos
: Setup proto for Sending message and get messages
- Add these
rpc
function into service
service GrpcServerService {
//add these lines
rpc SendMessage(stream SendMessageRequest) returns (stream Message){};
rpc GetAllMessage(GetAllMessagesRequest) returns (GetAllMessagesResponse){};
}
- Generate equivalent code
protoc --proto_path=proto --go_out=pb --go_opt=paths=source_relative \
--go-grpc_out=pb --go-grpc_opt=paths=source_relative \
proto/*.proto
- We need JWT-based authentication for both receiving and sending messages. While there are two types of interceptors available, namely `unary interceptors` and `stream interceptors`, we currently utilize the `unary interceptor` as our middleware and lets add middleware for stream service too.
![user-auth](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uze08vugtzoi7h5mkqw2.jpg)
- update `middleware.go` in `gapi` package.
- Register middleware in
main.go
//update this line
grpcServer := grpc.NewServer(
grpc.UnaryInterceptor(server.UnaryAuthInterceptor),
grpc.StreamInterceptor(server.StreamAuthInterceptor),
)
- Now create `Message` Object for `mongodb` collection
- Update MongoCollection
type MongoCollections struct {
Users *mongo.Collection
Chats *mongo.Collection
}
- Before going to into complex part to listen message and store them into db, lets deal with simple rpc function `GetAllMessage` from db and `SendMessage` to store the message.
- Now we are going to use go-routine to listen and send message to the client
> We are using goroutines
and channels to listen for changes in the database. If you want to learn more about goroutines
, you can read this blog
- Let's Define
SendMessage
and GetAllMesasge
for grpc server interface.
> Note: To make connection b/w two users, its required to send first message Join_room
so that other user can get acknowledgment.
Testing gRPC in CLI
Start with Flutter Application: Embark on Your Chatting Adventure
- Add following dependency into your project:
flutter pub add intl
- Generate equivalent code for dart from `protos`.
protoc --proto_path=proto --dart_out=grpc:lib/pb proto/*.proto
- As it is known issue dart does not generate code `google/protobuf/timestamp.pb.dart`,[read here](https://github.com/google/protobuf.dart/issues/483). So download this file [google_timestamp.pb.dart](https://gist.github.com/Djsmk123/83025e60cbf2c5a931ffefdf2b3a5b7e) and update the generated code as required(just change import path)
- Before starting chat service we need to display all the users into home screen so that we can send message to other users
> PS: I am not going to talk about basic UI building for showing the list of users.
- Add function to get all users in `AuthService` class
class AuthServices{
//keep same, add this function
static Future> getUsers(
{int pageNumber = 1, String? search}) async {
final res = await GrpcService.client.getUsers(
UsersListRequest(pageSize: 10, pageNumber: pageNumber, name: search),
options: CallOptions(metadata: {'authorization': 'bearer $authToken'}));
return res.users.map((e) => UserModel(e.id, e.username, e.name)).toList();
}
}
- Call this function in `HomeScreen()`,for reference use [this ui](https://gist.github.com/Djsmk123/3c39818c3a00c0b01ad8eadb36486102) or create your own.
- Let's Create chat service to get messages(History)
class ChatService {
static Future> getMessages(String username) async {
final res = await GrpcService.client.getAllMessage(
GetAllMessagesRequest(
reciever: username,
),
options: CallOptions(
metadata: {'authorization': 'bearer ${AuthService.authToken}'}));
return res.messages;
}
}
- Let's listen for messages,send message and fetch history.
> Note: UI component maybe missing or you create your own. for reference check source code
- Let's talk about what we did in above class.
- In
initState()
we are starting to listen message by sending first message Join_room
to make connection and another function fetchChatsHistory()
as name suggested, getting earlier messages by calling grpc
unary service.
- on sending message we are adding message data to stream-controller to send stream of message to the server.
Outputs: The Hilarious Messages of Success
End of the series
Thank you to everyone who has supported me throughout this series. I promise to be more consistent next time. The blog series titled 'Wtf is gRPC?' comes to an end with this post. In Part 1, we delved into authentication and authorization using unary gRPC communication. In Part 2, we had some fun creating a custom notification service in Flutter, along with server-side streaming gRPC. And now, in the grand finale, Part 3, we explored bi-directional gRPC to create a simple real-time chat application. Thanks again, and remember, gRPC isn't as mysterious as it sounds – it's just a protocol! Stay tuned for more tech adventures!
Thank for 2k+ Followers on dev.to
Source code :
Follow me on
Top comments (0)