

+

~2006
~2006




- 3.5 milhões de queries por dia

- 3.5 milhões de queries por dia (em 1999)

- 3.5 milhões de queries por dia (em 1999)
- Escalabilidade vertical era insustentável


Borg
(gerenciamento de containers)

Borg
(gerenciamento de containers)
Stubby
(comunicação)

Borg
(gerenciamento de containers)
Stubby
(comunicação)


Borg
(gerenciamento de containers)
Stubby
(comunicação)



Borg
(gerenciamento de containers)
Stubby
(comunicação)


Borg
(gerenciamento de containers)
Stubby
(comunicação)

Natan Streppel
- Software Developer @ bornlogic
- Trabalha como dev há mais de 6 anos



AGENDA
-
gRPC
-
Protobuffers
-
gRPC & Kotlin
gRPC
gRPC Remote Procedure Call
Title Text

gRPC
}
Remote Procedure Call
-
Sistema de comunicação client-server
Remote Procedure Calls
-
Sistema de comunicação client-server
-
Cliente chama função normalmente, mas ela é executada remotamente, em outra máquina
Remote Procedure Calls
-
Sistema de comunicação client-server
-
Cliente chama função normalmente, mas ela é executada remotamente, em outra máquina
-
Tem suas origens nos anos 70, mas vem tendo um forte come-back por conta do conceito de microsserviços
Remote Procedure Calls

gRPC é um framework RPC
gRPC é um framework RPC
- Open source (backed by Google)
gRPC é um framework RPC
- Open source (backed by Google)
- Utilizado por grandes players





gRPC é um framework RPC
- Open source (backed by Google)
- Utilizado por grandes players
- Com ótimas features
"out of the box"


É uma opção ao tradicional HTTP1.1 + JSON
enquanto em soluções web HTTP você tem um endpoint
GET /user/1
usando gRPC você tem uma chamada para um método
.getUser(RequestInput)
gRPC features
HTTP/2
HTTP/2
-
Comunicação em binário (ao contrário de texto, como HTTP/1.1)
HTTP/2
-
Comunicação em binário (ao contrário de texto, como HTTP/1.1)
-
Header Compressions (HPACK)

HTTP/2
-
Comunicação em binário (ao contrário de texto, como HTTP/1.1)
-
Header Compressions (HPACK)
-
Req/Resp Multiplexing

Protocol Buffers
Protocol Buffers são um mecanismo de serialização de dados
enquanto gRPC define como a mensagem será passada...
... protobuffer define a mensagem passada por essa comunicação
(pense como sendo o JSON da interação HTTP + JSON)
Protobuffers vivem dentro de arquivos .proto
message MyMessage {
string first_name = 1;
bool is_validated = 2;
}Exemplo de mensagem definida em um arquivo .proto
message MyMessage {
string first_name = 1;
bool is_validated = 2;
}Exemplo de mensagem definida em um arquivo .proto
Varints
message MyMessage {
string first_name = 1;
bool is_validated = 2;
}Exemplo de mensagem definida em um arquivo .proto
Varints
são a "key" do mapa "key-value"
{
"last_name": "dasylva"
}mensagem JSON
{
"last_name": "dasylva"
}mensagem JSON
}
}
9 bytes
+ 7 bytes
18 bytes
Protobuff
message MyMessage {
string last_name = 2;
}Protobuff
}
1 byte
message MyMessage {
string last_name = 2;
}mensagem encodada com "dasylva" vira isso:
12 07 64 61 73 79 6c 76 61
9 bytes
message MyMessage {
string last_name = 2;
}mensagem encodada com "dasylva" vira isso:
12 07 64 61 73 79 6c 76 61
}
0x12 = field 2, tipo 2 (tipo variável)
o 0x07 ao lado significa que o tamanho variável é 7
message MyMessage {
string last_name = 2;
}E o que fazemos com o arquivo .proto?

Protocol buffers are now Google's lingua franca for data – at time of writing, there are 306,747 different message types defined in the Google code tree across 348,952 .proto files. They're used both in RPC systems and for persistent storage of data in a variety of storage systems.
vamos definir nosso proto file!

meu_protinho.proto
syntax = "proto3";
package kotlinconf;
syntax = "proto3";
package kotlinconf;
message Atendee {
string first_name = 1;
string last_name = 2;
int32 meetups_atended = 3;
bool is_cool_person = 4;
}
syntax = "proto3";
package kotlinconf;
message Atendee {
string first_name = 1;
string last_name = 2;
int32 meetups_atended = 3;
bool is_cool_person = 4;
}
service KotlinConfService {
}
syntax = "proto3";
package kotlinconf;
message Atendee {
string first_name = 1;
string last_name = 2;
int32 meetups_atended = 3;
bool is_cool_person = 4;
}
service KotlinConfService {
rpc getKotlinConfAtendees() returns ();
}
syntax = "proto3";
package kotlinconf;
message Atendee {
string first_name = 1;
string last_name = 2;
int32 meetups_atended = 3;
bool is_cool_person = 4;
}
service KotlinConfService {
rpc getKotlinConfAtendees(Input) returns (Output);
}
syntax = "proto3";
package kotlinconf;
message Atendee {
string first_name = 1;
string last_name = 2;
int32 meetups_atended = 3;
bool is_cool_person = 4;
}
service KotlinConfService {
rpc getKotlinConfAtendees(Input) returns (Output);
}
message Input {
int32 year = 1;
}
syntax = "proto3";
package kotlinconf;
message Atendee {
string first_name = 1;
string last_name = 2;
int32 meetups_atended = 3;
bool is_cool_person = 4;
}
service KotlinConfService {
rpc getKotlinConfAtendees(Input) returns (Output);
}
message Input {
int32 year = 1;
}
message Output {
Atendee atendees = 1;
}
syntax = "proto3";
package kotlinconf;
message Atendee {
string first_name = 1;
string last_name = 2;
int32 meetups_atended = 3;
bool is_cool_person = 4;
}
service KotlinConfService {
rpc getKotlinConfAtendees(Input) returns (Output);
}
message Input {
int32 year = 1;
}
message Output {
repeated Atendee atendees = 1;
}
uff difícil né

Agora vamos definir o nosso server
Lembram do compilador de protos?

ele se chama protoc
ele se chama protoc
protoc \
--java_out=src/main/kotlin \
--proto_path=src/main/proto \
kotlinconf.protocomo resultado, temos stubs pré-gerados!

vamos criar uma classe implementando o serviço KotlinConfService
service KotlinConfService {
rpc getKotlinConfAtendees(Input) returns (Output);
} class KService {}
class KService:
KotlinConfServiceGrpc.KotlinConfServiceImplBase() {
}
class KService:
KotlinConfServiceGrpc.KotlinConfServiceImplBase() {
// alt enter pra dar override automático
}
class KService:
KotlinConfServiceGrpc.KotlinConfServiceImplBase() {
override fun getKotlinConfAtendees(
request: Kotlinconf.Input?,
responseObserver: StreamObserver<Kotlinconf.Output>?
) {
super.getKotlinConfAtendees(request, responseObserver)
}
}
class KService:
KotlinConfServiceGrpc.KotlinConfServiceImplBase() {
override fun getKotlinConfAtendees(
request: Kotlinconf.Input?,
responseObserver: StreamObserver<Kotlinconf.Output>?
) {
}
}
class KService:
KotlinConfServiceGrpc.KotlinConfServiceImplBase() {
override fun getKotlinConfAtendees(
request: Kotlinconf.Input?,
responseObserver: StreamObserver<Kotlinconf.Output>?
) {
val year = request?.year ?: getCurrentYear()
}
}
class KService:
KotlinConfServiceGrpc.KotlinConfServiceImplBase() {
override fun getKotlinConfAtendees(
request: Kotlinconf.Input?,
responseObserver: StreamObserver<Kotlinconf.Output>?
) {
val year = request?.year ?: getCurrentYear()
// List<AtendeeEntity>
val atendees = KConfUseCase(year)
}
}
class KService:
KotlinConfServiceGrpc.KotlinConfServiceImplBase() {
override fun getKotlinConfAtendees(
request: Kotlinconf.Input?,
responseObserver: StreamObserver<Kotlinconf.Output>?
) {
val year = request?.year ?: getCurrentYear()
// List<AtendeeEntity>
val atendees = KConfUseCase(year)
val listOfAtendees = atendees.map {
Kotlinconf.Atendee.newBuilder()
.setFirstName(it.FirstName)
.setLastName(it.LastName)
.setMeetupsAtended(it.MeetupsAttended)
.setIsCoolPerson(it.IsCool)
.build()
}.toList()
}
}
class KService:
KotlinConfServiceGrpc.KotlinConfServiceImplBase() {
override fun getKotlinConfAtendees(
request: Kotlinconf.Input?,
responseObserver: StreamObserver<Kotlinconf.Output>?
) {
val year = request?.year ?: getCurrentYear()
// List<AtendeeEntity>
val atendees = KConfUseCase(year)
val listOfAtendees = atendees.map {
//map entities...
}.toList()
}
}
class KService:
KotlinConfServiceGrpc.KotlinConfServiceImplBase() {
override fun getKotlinConfAtendees(
request: Kotlinconf.Input?,
responseObserver: StreamObserver<Kotlinconf.Output>?
) {
val year = request?.year ?: getCurrentYear()
// List<AtendeeEntity>
val atendees = KConfUseCase(year)
val listOfAtendees = atendees.map {
//map entities...
}.toList()
val response = Kotlinconf
.Output.newBuilder()
}
}
class KService:
KotlinConfServiceGrpc.KotlinConfServiceImplBase() {
override fun getKotlinConfAtendees(
request: Kotlinconf.Input?,
responseObserver: StreamObserver<Kotlinconf.Output>?
) {
val year = request?.year ?: getCurrentYear()
// List<AtendeeEntity>
val atendees = KConfUseCase(year)
val listOfAtendees = atendees.map {
//map entities...
}.toList()
val response = Kotlinconf
.Output.newBuilder()
.addAllAtendees(listOfAtendees)
}
}
class KService:
KotlinConfServiceGrpc.KotlinConfServiceImplBase() {
override fun getKotlinConfAtendees(
request: Kotlinconf.Input?,
responseObserver: StreamObserver<Kotlinconf.Output>?
) {
val year = request?.year ?: getCurrentYear()
// List<AtendeeEntity>
val atendees = KConfUseCase(year)
val listOfAtendees = atendees.map {
//map entities...
}.toList()
val response = Kotlinconf
.Output.newBuilder()
.addAllAtendees(listOfAtendees)
responseObserver?.onNext(response.build())
responseObserver?.onCompleted()
}
}
xuxexo, agora vamos para a main()
fun main() {
}
import io.grpc.ServerBuilder
fun main() {
val s = ServerBuilder
}
import io.grpc.ServerBuilder
fun main() {
val s = ServerBuilder
.forPort(8080)
}
import io.grpc.ServerBuilder
fun main() {
val kService = KService()
val s = ServerBuilder
.forPort(8080)
.addService(kService)
.build()
}
import io.grpc.ServerBuilder
fun main() {
val kService = KService()
val s = ServerBuilder
.forPort(8080)
.addService(kService)
.build()
println("starting server")
s.start().awaitTermination()
}
server done! vamos ao client
fun main() {
}
import io.grpc.ManagedChannelBuilder
fun main() {
var channel = ManagedChannelBuilder
.forAddress("localhost", 8080)
.usePlaintext()
.build()
}
import io.grpc.ManagedChannelBuilder
fun main() {
var channel = ManagedChannelBuilder
.forAddress("localhost", 8080)
.usePlaintext()
.build()
var stub = KotlinConfServiceGrpc
.newBlockingStub(channel)
}
import io.grpc.ManagedChannelBuilder
fun main() {
var channel = ManagedChannelBuilder
.forAddress("localhost", 8080)
.usePlaintext()
.build()
var stub = KotlinConfServiceGrpc
.newBlockingStub(channel)
val input = Kotlinconf
.Input.newBuilder()
.setYear(2019)
.build()
val response = stub.getKotlinConfAtendees(input)
println("got response $response")
}

Conclusão
gRPC não é a solução para todos seus problemas
mas é uma solução robusta, industry-proven e eficiente para problemas modernos
Podemos usar gRPC quando...
ambos serviços são internos e precisam se comunicar
Podemos usar gRPC quando...
nos interessamos em grande eficiência em processamento*
*no processo de serialization e deserialization
Podemos usar gRPC quando...
nos interessamos em grande eficiência em consumo de bandwidth (mobile, anyone??)
Existem diversas libs Kotlin que ajudam a deixar o código mais Kotlin-idiomatic
E é isso. Obrigado!
grpc & kotlin
By Natan Streppel
grpc & kotlin
- 116