关于grpc:从零开始学gRPC一

前言

gRPC作为以后最热门的RPC框架之一,以其独特的跨语言、跨平台个性,博得许多公司的青眼。
诚实说,之前我只是一人传虚;万人传实并没有认真去钻研,明天我会依据官网的demo开展介绍整个gRPC的性能,
前面一篇会介绍gRPC如何整合到SpringCloud。

我这里只提供了搭建demo工程的材料,倡议本人入手来操作。没有截图我的项目也是因为官网的材料相当齐全,没必要反复造轮子。

gRPC总览

在间接应用gRPC之前,咱们先理解下它的所有个性。官网形容 我就不开展讲了,gRPC有以下几点次要性能:

  1. 应用Protocol Buffer 定义服务。
  2. 语言和平台的中立性
  3. 双向流式通信
  4. 基于HTTP2.0身份认证、SLB、tracing、health check
  5. 组件可扩大。

疾速搭建

环境筹备

  • 操作系统:Windowns
  • JDK版本:1.8
  • 编辑工具:IntelliJ IDEA Community Edition 2021.1.2 x64

    创立我的项目

    本人创立一个maven我的项目就好,没有别的要求(All in one,临时不须要分模块)。想要偷懒的同学也能够借鉴我的我的项目,这里曾经给你筹备好了。然而我倡议本人入手尝试搭建比拟好,不然印象不太粗浅,等于没学(有个词儿叫口头废人,尽管不好听但目标是心愿能让你入手实际)。

阐明: 这里之所以没用官网的demo是因为我本地切实编译不进去,而且官网给的我的项目太大,对于学习来说没必要全副下载。(我只想要examples模块,然而必须强制全副下载,就很烦)
当然,要下载的敌人看这里 。
官网的中文文档地址是这个 ,能够依照官网提供的文档做。但如果只是玩具,那我这种All on one的形式我感觉更简略。

Helloworld

咱们间接拿官网的例子来解说,官网代码地址 。外面有很多例子,我这里只讲局部。

proto文件

咱们到官网地址 抄作业时要留神,整个目录的构造。
proto文件只能放在模块的src/main上面,留神地位还有名字是否弄错,不然生成不了代码。

代码

服务端:代码地址

客户端:代码地址

个性解说

咱们晓得gRPC不仅仅是一个helloworld就能形容分明的,我上面将官网的代码例子做个分类,顺便总结下。

Stream

例子

  • proto: 代码地址
  • 代码目录: 代码地址

总结

上述代码想表白的意思是,服务端在收到全副的客户端数据之后再响应回客户端处理结果。理论状况能够是服务端先解决一部分,而后返回局部。
也能够是任意程序, 重要的是理解如何应用Stream的相干API来做交互。

TLS

例子

  • proto: 代码地址
  • 代码目录: 代码地址

(还是helloworld的例子)

总结

这个就没啥好说的,用了Http2.0的TLS个性,默认就是开启的。如果客户端要传明文则必须在channel中配置,如下所示。

SLB 和 Health Check

例子

  • proto: 代码地址
  • 代码目录: 代码地址

官网并没提供,上面都是本人的尝试

  1. Client端在创立ManagedChannel时指定SLB策略。默认实现只有 pick_firstround_robin,这里以round_robin为例子解说。
  2. target字符串做调整

  3. 新增两个类,代码如下。

    public class MyNameResolverProvider extends NameResolverProvider {
    
     /**
      * The constant containing the scheme that will be used by this factory.
      */
     public static final String STATIC_SCHEME = "static";
    
     private static final Pattern PATTERN_COMMA = Pattern.compile(",");
    
     @Nullable
     @Override
     public NameResolver newNameResolver(final URI targetUri, final NameResolver.Args args) {
         if (STATIC_SCHEME.equals(targetUri.getScheme())) {
             return of(targetUri.getAuthority(), args.getDefaultPort());
         }
         return null;
     }
    
     /**
      * Creates a new {@link NameResolver} for the given authority and attributes.
      *
      * @param targetAuthority The authority to connect to.
      * @param defaultPort The default port to use, if none is specified.
      * @return The newly created name resolver for the given target.
      */
     private NameResolver of(final String targetAuthority, int defaultPort) {
         requireNonNull(targetAuthority, "targetAuthority");
         // Determine target ips
         final String[] hosts = PATTERN_COMMA.split(targetAuthority);
         final List<EquivalentAddressGroup> targets = new ArrayList<>(hosts.length);
         for (final String host : hosts) {
             final URI uri = URI.create("//" + host);
             int port = uri.getPort();
             if (port == -1) {
                 port = defaultPort;
             }
             targets.add(new EquivalentAddressGroup(new InetSocketAddress(uri.getHost(), port)));
         }
         if (targets.isEmpty()) {
             throw new IllegalArgumentException("Must have at least one target, but was: " + targetAuthority);
         }
         return new MyStaticNameResolver(targetAuthority, targets);
     }
    
     @Override
     public String getDefaultScheme() {
         return STATIC_SCHEME;
     }
    
     @Override
     protected boolean isAvailable() {
         return true;
     }
    
     @Override
     protected int priority() {
         return 4; // Less important than DNS
     }
    
     @Override
     public String toString() {
         return "StaticNameResolverProvider [scheme=" + getDefaultScheme() + "]";
     }
    }
public class MyStaticNameResolver extends NameResolver {

  private final String authority;
  private final ResolutionResult result;

  /**
   * Creates a static name resolver with only a single target server.
   *
   * @param authority The authority this name resolver was created for.
   * @param target The target address of the server to use.
   */
  public MyStaticNameResolver(final String authority, final EquivalentAddressGroup target) {
    this(authority, ImmutableList.of(requireNonNull(target, "target")));
  }

  /**
   * Creates a static name resolver with multiple target servers.
   *
   * @param authority The authority this name resolver was created for.
   * @param targets The target addresses of the servers to use.
   */
  public MyStaticNameResolver(final String authority, final Collection<EquivalentAddressGroup> targets) {
    this.authority = requireNonNull(authority, "authority");
    if (requireNonNull(targets, "targets").isEmpty()) {
      throw new IllegalArgumentException("Must have at least one target");
    }
    this.result = ResolutionResult.newBuilder()
      .setAddresses(ImmutableList.copyOf(targets))
      .build();
  }

  /**
   * Creates a static name resolver with multiple target servers.
   *
   * @param authority The authority this name resolver was created for.
   * @param result The resolution result to use..
   */
  public MyStaticNameResolver(final String authority, final ResolutionResult result) {
    this.authority = requireNonNull(authority, "authority");
    this.result = requireNonNull(result, "result");
  }

  @Override
  public String getServiceAuthority() {
    return this.authority;
  }

  @Override
  public void start(final Listener2 listener) {
    listener.onResult(this.result);
  }

  @Override
  public void refresh() {
    // Does nothing
  }

  @Override
  public void shutdown() {
    // Does nothing
  }

  @Override
  public String toString() {
    return "StaticNameResolver [authority=" + this.authority + ", result=" + this.result + "]";
  }
}
  1. 新增文件io.grpc.NameResolverProvider

  2. Server端启动多个实现,记得要改端口(能够在server端加些日志来验证)

阐明:下面的代码是从grpc-spring-boot-start中抄过来的,前面会细讲。原生的grpc只能用dns,本地没法做,所以采纳这种形式验证SLB。
其实在这个过程中咱们也能看到,如果要基于grpc性能组件做扩大也是极不便的。

总结

负载平衡的实现是基于动态地址的,也就是说没有动静的服务发现。当然也能够本人扩大,这个会在前面集成grpc-spring-boot-start时会讲。
扩大时只须要实现io.grpc.LoadBalancerProvider而后再配置注册文件就好

Hedging

例子

  • proto: 代码地址
  • 代码目录: 代码地址
  • 配置文件: 代码地址

总结

这里次要是一些重试(容错)策略的配置,例子中通过人为的注入提早来测试。理解下参数含意就好

写在最初

其实咱们会发现,原生的grpc整个治理能力偏弱。熔断、降级等性能根本没有,也无奈做动静的服务发现,只反对dns来做负载平衡(别看文档里写的有zookeeper协定,但理论不反对)。流量的调度能力也比拟弱,并没有相似分组、版本的概念,无奈做到灰度公布。
有很多人在grpc的根底上曾经增加了服务治理的解决方案,在前面的grpc-spring-boot-start会讲到,大家不必放心。

为什么市面上其余的RPC框架性能都比这齐全,然而越来越多的公司开始转向gRPC了呢?

我集体认为次要是gRPC的传输协定以及服务定义形式都是人造的反对跨语言的,而服务端编程语言越来越多样化,因而越来越多的公司转向gRPC,即便他们要为此写很多的gRPC插件。
不过工作量并没有想想的那么多,比方Dubbo3.0目前就将他的传输层应用gRPC,而服务治理能力则是应用它本身的,只须要将服务治理能力适配到gRPC插件中就好。

踩坑笔记

  • Idea抽风,扫描不到生成的文件。解决办法: 删除idea缓存,而后重启idea。
  • proto文件生成的代码不全。解决办法: protobuf插件除了运行comple,还要运行custom

参考文献

grpc官网文档

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理