gRPC是google开源提供的一个RPC软件框架,它的特点是极大简化了传统RPC的开发流程和代码量,使用户可以免除许多陷阱并聚焦于实际应用逻辑中。作为一种google的最新RPC解决方案,gRPC具备了以下这些强项:

1、gRPC在HTTP/2协议上用protobuf取代了json实现了最佳效率

2、用IDL(Interface Definition Language),一种简单的描述语言来自动产生RPC的api源代码

3、支持blocking/non-blocking双向数据流交互,适合程序的流程控制

gRPC的使用非常简单,具体流程如下:

1、在一个.proto字符类文件中用IDL来描述用户自定义的数据类型和服务

2、用protoc编译器编译文件并产生自定义数据类型和服务的api源代码

3、在server端实现.proto中定义的服务函数

4、在client端通过自动产生的stub来调用服务

下面我们就来示范gRPC的编程流程。gRPC支持下面这几种服务类型:

1、Unary:独立的一对client-request/server-response,是我们常用的http交互模式

2、Server-Streaming:client发出一个request后从server端接收一串多个response

3、Client-Streaming:client向server发送一串多个request后从server接收一个response

4、Bidirectional-Streaming:由client首先发送request启动连接,然后在这个连接上两端可以不断交互信息。

在本篇讨论中我们先示范Unary-service的编程流程,下面是.proto文件内容:

syntax = "proto3";

import "google/protobuf/wrappers.proto";
import "scalapb/scalapb.proto"; package learn.grpc.services; /*
* Returns a greeting for the given person optionally including a custom message.
*/
service HelloWorld {
rpc SayHello(ToBeGreeted) returns (Greeting) {}
} message Person {
string name = ;
} message ToBeGreeted {
Person person = ;
google.protobuf.StringValue msg = ;
} message Greeting {
string message = ;
}

这段IDL描述了一个HelloWorld服务,包括了一个服务函数SayHello。三种数据类型:Person,ToBeGreeted,Greeting。通过对.proto文件进行编译后产生文件中包括一个HelloWorldGrpc.scala文件,里面提供了一些重要的api:

trait HelloWorld -> 用于实现HelloWorld服务的trait
trait HelloWorldBlockingClient -> 用于实现客户端stub
class HelloWorldBlockingStub -> blocking客户端stub
class HelloWorldStub -> non-blocking客户端stub
def bindService -> 服务类型绑带方法

我们先实现HelloWorld服务:

 class HelloService extends HelloWorldGrpc.HelloWorld {
override def sayHello(request: ToBeGreeted): Future[Greeting] = {
val greeter = request.person match {
case Some(p) => p.name
case None => "friendo"
}
Future.successful(Greeting(message = s"Hello $greeter, ${request.msg}"))
}
}

可以看到我们直接使用了IDL描述的自定义数据类型如:ToBeGreeted,Greeting。在客户端调用服务并输出返回结果response:

    //build connection channel
val channel = io.grpc.ManagedChannelBuilder
.forAddress("LocalHost",)
.usePlaintext(true)
.build() //construct requestHelloService
val greeter = ToBeGreeted()
.withMsg("remote greetings!")
.withPerson(ToBeGreeted.Person("mickey")) //async call
val asyncStub: HelloWorldGrpc.HelloWorldStub = HelloWorldGrpc.stub(channel)
val futResponse: Future[Greeting] = asyncStub.sayHello(greeter) import scala.concurrent.ExecutionContext.Implicits.global
futResponse.foreach(greeting => println(greeting.message)) val greeter2 = ToBeGreeted(person = Some(Person("jacky")),msg = Some("how are you?"))
//sync call
val syncStub: HelloWorldGrpc.HelloWorldBlockingClient = HelloWorldGrpc.blockingStub(channel)
val response: Greeting = syncStub.sayHello(greeter2) println(s"${response.message}")

下面是bindService方法的使用示范:

  def main(args: Array[String]): Unit = {
val service = HelloWorldGrpc.bindService(new HelloService,ExecutionContext.global)
runServer(service)
}

runServer函数定义如下:

package learn.grpc.server
import io.grpc.{ServerBuilder,ServerServiceDefinition} trait gRPCServer {
def runServer(service: ServerServiceDefinition): Unit = {
val server = ServerBuilder
.forPort()
.addService(service)
.build
.start // make sure our server is stopped when jvm is shut down
Runtime.getRuntime.addShutdownHook(new Thread() {
override def run(): Unit = server.shutdown()
}) server.awaitTermination()
} }

注意我们还使用了io.grpc库里的类型和方法,这是protobuf项目提供的一个标准库。在客户端也需要使用它来构建通道:

   //build connection channel
val channel = io.grpc.ManagedChannelBuilder
.forAddress("LocalHost",)
.usePlaintext(true)
.build()

我们将在后面的讨论里介绍gRPC的streaming编程方法。下面是本次示范的源代码:

project/scalapb.sbt

addSbtPlugin("com.thesamet" % "sbt-protoc" % "0.99.18")
libraryDependencies += "com.thesamet.scalapb" %% "compilerplugin" % "0.7.1"

build.sbt

import scalapb.compiler.Version.scalapbVersion
import scalapb.compiler.Version.grpcJavaVersion
name := "learn-gRPC"
version := "0.1"
scalaVersion := "2.12.6"
libraryDependencies ++= Seq(
"com.thesamet.scalapb" %% "scalapb-runtime" % scalapbVersion % "protobuf",
"io.grpc" % "grpc-netty" % grpcJavaVersion,
"com.thesamet.scalapb" %% "scalapb-runtime-grpc" % scalapbVersion
)
PB.targets in Compile := Seq(
scalapb.gen() -> (sourceManaged in Compile).value
)

src/main/protobuf/hello.proto

syntax = "proto3";

import "google/protobuf/wrappers.proto";
import "scalapb/scalapb.proto"; package learn.grpc.services; /*
* Returns a greeting for the given person optionally including a custom message.
*/
service HelloWorld {
rpc SayHello(ToBeGreeted) returns (Greeting) {}
} message Person {
string name = ;
} message ToBeGreeted {
Person person = ;
google.protobuf.StringValue msg = ;
} message Greeting {
string message = ;
}

src/main/scala/gRPCServer.scala

package learn.grpc.server
import io.grpc.{ServerBuilder,ServerServiceDefinition} trait gRPCServer {
def runServer(service: ServerServiceDefinition): Unit = {
val server = ServerBuilder
.forPort()
.addService(service)
.build
.start // make sure our server is stopped when jvm is shut down
Runtime.getRuntime.addShutdownHook(new Thread() {
override def run(): Unit = server.shutdown()
}) server.awaitTermination()
} }

src/main/scala/HelloServer.scala

package learn.grpc.hello.server
import learn.grpc.services.hello._
import learn.grpc.server.gRPCServer
import scala.concurrent._ object HelloServer extends gRPCServer { class HelloService extends HelloWorldGrpc.HelloWorld {
override def sayHello(request: ToBeGreeted): Future[Greeting] = {
val greeter = request.person match {
case Some(p) => p.name
case None => "friendo"
}
Future.successful(Greeting(message = s"Hello $greeter, ${request.msg}"))
}
} def main(args: Array[String]): Unit = {
val service = HelloWorldGrpc.bindService(new HelloService,ExecutionContext.global)
runServer(service)
}
}

src/main/scala/HelloClient.scala

package learn.grpc.hello.client
import learn.grpc.services.hello.ToBeGreeted.Person
import learn.grpc.services.hello._ import scala.concurrent.Future
object HelloClient {
def main(args: Array[String]): Unit = { //build connection channel
val channel = io.grpc.ManagedChannelBuilder
.forAddress("LocalHost",)
.usePlaintext(true)
.build() //construct requestHelloService
val greeter = ToBeGreeted()
.withMsg("remote greetings!")
.withPerson(ToBeGreeted.Person("mickey")) //async call
val asyncStub: HelloWorldGrpc.HelloWorldStub = HelloWorldGrpc.stub(channel)
val futResponse: Future[Greeting] = asyncStub.sayHello(greeter) import scala.concurrent.ExecutionContext.Implicits.global
futResponse.foreach(greeting => println(greeting.message)) val greeter2 = ToBeGreeted(person = Some(Person("jacky")),msg = Some("how are you?"))
//sync call
val syncStub: HelloWorldGrpc.HelloWorldBlockingClient = HelloWorldGrpc.blockingStub(channel)
val response: Greeting = syncStub.sayHello(greeter2) println(s"${response.message}") } }

ScalaPB(2): 在scala中用gRPC实现微服务的更多相关文章

  1. 微服务优化之使用gRPC做微服务的内部通信

    使用gRPC做微服务的内部通信 gRPC是一个由Google开源的远程服务调用框架,具有多路复用和双向流式通信的特性. 大家好,在本文中将为大家介绍为什么我们应该使用gRPC代替RESTful或JSO ...

  2. 如何基于gRPC沟通微服务框架

    本文我们来讲解一下如何使用 gRPC构建微服务,gRPC是一个开源框架,可用于构建可扩展且高性能的微服务并创建服务之间的通信. 背景 随着企业越来越多地转向微服务,对构建这些微服务的低延迟和可扩展框架 ...

  3. python3和grpc的微服务探索实践

    对于微服务的实践,一般都是基于Java和Golang的,博主最近研究了下基于Python的微服务实践,现在通过一个简单的服务来分析Python技术栈的微服务实践 技术栈:Python3 + grpc ...

  4. grpc 实现微服务生态笔记

    微服务的发展可谓是一波三折,一代一代经历和N多技术成果,grpc只是其中一个,因为其东家是google,明显比较稳定.加上其强大的文档和技术支持和跨平台的支持,在企业级应用上有很大的可信任感,所以也有 ...

  5. 微服务架构攀登之路(四)之使用gRPC构建微服务

    做一个处理用户信息的微服务 客户端通过用户名,可以从服务端查询用户的基本信息 gRPC proto user.proto 定义客户端请求.服务端响应的数据格式 user.pb.go 自动生成的,为数据 ...

  6. .net core 用grpc实现微服务

    GRPC 是Google发布的一个开源.高性能.通用RPC(Remote Procedure Call)框架.提供跨语言.跨平台支持.以下以.NET Core 使用控制台.docker中演示如何使用G ...

  7. ScalaPB(0): 找寻合适的内部系统微服务集成工具

    前一段时间我们探讨了SDP的一个基于集群的综合数据平台解决方案,由多种数据库组成,包括:JDBC, Cassandra 及MongoDB.其中Cassandra和MongoDB属于分布式数据库,可以在 ...

  8. 我所理解的SOA和微服务

    本文原创,原文地址为:http://www.cnblogs.com/fengzheng/p/5847441.html SOA和微服务到底是什么关系? 说实话,我确实不明白SOA和微服务到底有什么本质上 ...

  9. SOA和微服务

    SOA和微服务 SOA和微服务到底是什么关系? 说实话,我确实不明白SOA和微服务到底有什么本质上的区别,两者说到底都是对外提供接口的一种架构设计方式.我倒觉得微服务其实就是随着互联网的发展,复杂的平 ...

随机推荐

  1. UNIX环境高级编程——时间和日期

    由UNIX内核提供的基本时间服务是计算自国际标准时间公元1970年1月1日00:00:00以来经过的秒数.这种秒数是以数据类型time_t表示. 1.     time函数返回当前时间和日期: tim ...

  2. pig的cogroup详解

    从实例出发 %default file test.txt A = load '$file' as (date, web, name, food); B = load '$file' as (date, ...

  3. leetcode 204题求素数个数

        Description: Count the number of prime numbers less than a non-negative number, n 提示晒数法: http:// ...

  4. H5学习之旅-H5的格式化(4)

    H5的格式设置: b代表是粗体 i斜体 big 字体变大 small变小 em强调 strong 加强和变粗差不多 sub 定义下标字 sup 定义上标字 ins 插入字 del 删除字 代码实例 & ...

  5. 关于MySQL主从复制中UUID的警告信息

    日期: 2014年5月23日 博客: 铁锚 最近在查看MariaDB主从复制服务器 Master 的错误日志时看到很多条警告信息,都是提示 UUID()函数不安全,可能 Slave 产生的值和 Mas ...

  6. LIRe 源代码分析 7:算法类[以颜色布局为例]

    ===================================================== LIRe源代码分析系列文章列表: LIRe 源代码分析 1:整体结构 LIRe 源代码分析 ...

  7. Linux下C语言的调试 - gdb

    调试是每个程序员都会面临的问题. 如何提高程序员的调试效率, 更好更快地定位程序中的问题从而加快程序开发的进度, 是大家共同面对的问题. 可能Windows用户顺口就会说出:用VC呗 :-) , 它提 ...

  8. Linux下的 .o、.a、.so文件

    http://blog.sina.com.cn/s/blog_656681710100qzmy.html 工程里很多函数只是有声明,找不到实现的代码.因为那些实现代码已经编译成库所以看不见,我所看见的 ...

  9. S3c2440A WINCE平台HIVE注册表+binfs的实现

    今天最大的收获莫过于把binfs和hive注册表同时在三星的平台上实现了,这可是前无古人啊(只是看到好多哥们说找不到三星的HIVE资料),哈哈哈.怕今天的成果日后成炮灰,还是写下来比较好,要养成这样的 ...

  10. Android特效专辑(七)——飞机升空特效,一键清理缓存,灵活运用动画会有不一样的感受

    Android特效专辑(七)--飞机升空特效,一键清理缓存,灵活运用属性动画 最近的几篇博文反响还不错,也会继续的写下去的,关于这些特效的专辑,大多数也是借鉴大神的,最近由于工作的关系,会深入的了解一 ...