Scala中使用SOFA jraft 实现rpc的优化 二

2023-10-27

背景

第一版实现Processable宏之后,各方面已经满足需求,也正常使用,并引入进bitlap,使得我们能以方法的形式管理所有Processor对象,而不需要创建太多的类文件。
但是细心的人会发现,为了实现这个小小的功能,我们在scala-macro-tools中引入了重量级包"com.alipay.sofa" % "jraft-core" % "1.3.9"。同时为了测试,我们还引入了protobuf-java

特别是sofa这个包,场景有限,当我们不需要使用Processable宏,却被迫引入了这么大的jar,着实是划不来的。所以接下来优化目的是减小包的依赖。

之所以我们需要依赖jraft,是因为我们期望通过泛型参数限定符([_ <: Rpcxxx])来自动使用编译期的类型检测,现在我们可以换个思路,能不能既保留类型检查又不需要引入包?如果我们在宏的实现中自己检查类型呢?

答案当然有,那就是类似gradle的API依赖,将依赖的导入转交给使用者决定。在宏中这种方式更为彻底,甚至都不需要引入包,只需在代码中直接用全类名即可,因为宏没有调用前不会类型检查。

使用该宏的人不引入jraft则会编译不了

重新设计Processable

为此需要新增泛型参数,同时在宏里面处理类型检查。该实现和上篇文章一样,有三种不同定义,下面先给出最简洁的一种定义:

一 简单粗暴

只有泛型和2个函数参数

宏定义如下:

  def apply[Service, RRC, RRP[_ <: Req], RC, Req, Resp]
    (
    processRequest:   (Service, RRC, Req) ⇒ Resp,
    processException: (Service, RC, Exception) ⇒ Resp
  ): RRP[Req] = macro ProcessorCreatorMacro.OnlyWithFunctionalParameters[Service, RRC, RRP[_ <: Req], RC, Req, Resp]
}

宏实现如下:

  def OnlyWithFunctionalParameters[Service: c.WeakTypeTag, RRC: c.WeakTypeTag, RRP: c.WeakTypeTag, RC: c.WeakTypeTag, Req: c.WeakTypeTag, Resp: c.WeakTypeTag]
    (c: blackbox.Context)(
    processRequest:   c.Expr[(Service, RRC, Req) ⇒ Req],
    processException: c.Expr[(Service, RC, Exception) ⇒ Req]
  ): c.Expr[RRP] = {
    import c.universe._
    // 类型检查,这里是编译期间的检查,如果宏不被调用,则不会执行,这很重要,这样我们才能实现下面的代码,这是前提!!!
    checkTree[RRC, RRP, RC, Service](c)
    // 使用弱类型标记得到类型即可,类型检查会弱点,因为弱标记,serviceType中的属性有的是空的,但是在这里我们不需要那么多信息,有类型就足够。
    val serviceType = weakTypeOf[Service]
    // 类型使用ID自增计数拼接一个前缀
    val className = TypeName(classNamePrefix + MacroCache.getIdentityId)
    val reqProtoType = weakTypeOf[Req]
    val rpcRequestClosureType = weakTypeOf[RRC]
    val rpcContextType = weakTypeOf[RC]
    val respProtoType = weakTypeOf[Resp]
    val respProtoCompanionType = weakTypeOf[Resp].companion //getDefaultInstance is static method, it's in companion
    val processor =
      q"""
       // 前面使用了通用的父类对RpcRequestProcessor做了一层简单包装,这里已经不需要了。
       class $className(private val service: $serviceType, executor: java.util.concurrent.Executor = null)
         // getDefaultInstance是Resp的静态方法,所以使用respProtoCompanionType,这个是Resp的伴生对象
         extends com.alipay.sofa.jraft.rpc.RpcRequestProcessor[$reqProtoType](executor, $respProtoCompanionType.getDefaultInstance)
         with com.typesafe.scalalogging.LazyLogging {
            override def handleRequest(rpcCtx: com.alipay.sofa.jraft.rpc.RpcContext, request: $reqProtoType) {
              try {
                val msg = processRequest(request, new com.alipay.sofa.jraft.rpc.RpcRequestClosure(rpcCtx, this.defaultResp))
                if (msg != null) {
                  rpcCtx.sendResponse(msg)
                }
              } catch {
                case e: Exception =>
                  logger.error("handleRequest" + request + "failed", e)
                  rpcCtx.sendResponse(processError(rpcCtx, e))
              }
            }
           override def interest(): String = classOf[$reqProtoType].getName

           def processRequest(request: $reqProtoType, done: $rpcRequestClosureType): $respProtoType = {
              $processRequest(service, done, request)
           }

           def processError(rpcCtx: $rpcContextType, exception: Exception): $respProtoType = {
              $processException(service, rpcCtx, exception)
           }
       }
       //运行时反射service对象,使用无参构造函数
       val service = new io.github.dreamylost.macros.Creator[$serviceType].createInstance(null)(0)
       new $className(service)
     """
    printTree[RRP](c)(processor)
  }

这样就完成了新的宏设计,来看怎么使用:

    // 参数有点多?其实对于ProcessorCreator来说,四个参数对一个服务而言基本是相同的,NetService, RpcRequestClosure, RpcRequestProcessor, RpcContext
    // 可以再用个工具类包层即可,但是考虑到依赖,应该由使用者自己实现,这很简单。
    val openSession = ProcessorCreator[NetService, RpcRequestClosure, RpcRequestProcessor, RpcContext, BOpenSessionReq, BOpenSessionResp](
      (service, rpc, req) => {
        import scala.jdk.CollectionConverters.MapHasAsScala
        val username = req.getUsername
        val password = req.getPassword
        val configurationMap = req.getConfigurationMap
        val ret = service.openSession(username, password, configurationMap.asScala.toMap)
        BOpenSessionResp.newBuilder().setSessionHandle(ret).build()
      },
      (service, rpc, exception) => {
        BOpenSessionResp.newBuilder().setStatus(exception.getLocalizedMessage).build()
      }
    )

二 灵活拓展

灵活又不失优雅

宏定义如下:

  def apply[RRC, RRP[_ <: Req], RC, Req, Resp, Service, E <: Executor]
    (
    defaultResp:      Resp,
    processRequest:   (Service, RRC, Req) ⇒ Resp,
    processException: (Service, RC, Exception) ⇒ Resp
  )(implicit service: Service, executor: E): RRP[Req] = macro ProcessorCreatorMacro.SimpleImpl[RRC, RRP[_ <: Req], RC, Req, Resp, Service, E]

由于serviceexecutor通常是固定的,所以我们使用implicit传入,既支持了自定义参数,又能方便的被别的Processor复用。

宏的使用如下:

    // 注意:这样所以同作用域的Processor对象将使用一个该类型的对象,这样很方便,不用每次都传进去
    implicit val service = new NetService
    implicit val executor: Executor = new Executor {
      override def execute(command: Runnable): Unit = ()
    }

    val openSession = ProcessorCreator[RpcRequestClosure, RpcRequestProcessor, RpcContext, BOpenSessionReq, BOpenSessionResp, NetService, Executor](
      BOpenSessionResp.getDefaultInstance,
      (service, rpcRequestClosure, req) => {
        import scala.jdk.CollectionConverters.MapHasAsScala
        val username = req.getUsername
        val password = req.getPassword
        val configurationMap = req.getConfigurationMap
        val ret = service.openSession(username, password, configurationMap.asScala.toMap)
        BOpenSessionResp.newBuilder().setSessionHandle(ret).build()
      },
      (service, rpcContext, exception) => {
        BOpenSessionResp.newBuilder().setStatus(exception.getLocalizedMessage).build()
      }
    )

宏定义就不贴了,感兴趣可以看源码

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Scala中使用SOFA jraft 实现rpc的优化 二 的相关文章

随机推荐

  • java基础之集合

    集合 基础里的重头戏来喽 一 集合概述 集合和数组的区别 概述 集合是java中用来存放多个 引用数据类型 数据的容器 它是解决了数组的一些弊端的一个多数据容器 他有 的操作集合的方法 比如增加和删除方法 java中集合的体系使用接口和类进
  • RAC重建OCR/Voting disk遇到的一些故障

    author skate time 2010 05 09 我的测试环境 母系统 win2003虚拟软件 vmware3 2 1guest系统 centos4 7oracle db oracle10 2 1 以下是我在重建rac的ocr vo
  • 全球分布式云大会:AntDB超融合流式实时数仓,打造分布式数据库新纪元

    日前 全球分布式云大会北京站在北京金茂万丽酒店举办 亚信科技AntDB数据库受邀参会 会上技术负责人北陌发表以 AntDB超融合流式实时数仓 打造分布式数据库新纪元 为主题的演讲 通过分享AntDB在数据库前沿技术的研发实践 与参会嘉宾一起
  • springboot的安全性

    如何实现 Spring Boot 应用程序的安全性 为了实现 Spring Boot 的安全性 我们使用 spring boot starter security 依赖项 并且必须添加安全配置 它只需要很少的代码 配置类将必须扩展WebSe
  • BM8 链表中倒数最后k个结点

    struct ListNode int val struct ListNode next ListNode int x val x next nullptr class Solution public 代码中的类名 方法名 参数名已经指定
  • 代码块11

    import numpy as np rot matrix 0 5 np asarray 1 1 1 1 1 1 1 1 np sqrt 2 np sqrt 2 0 0 0 0 np sqrt 2 np sqrt 2 判断矩阵是否正交 pr
  • flash开发ipa踩的坑

    1 xcode中找到libclang rt ios a添加到AIRSDK lib aot lib中 2 ios 打包ane 要添加 如果是 tbd则当dylib一样处理 去AIRSDK lib aot stub里面看有没有 dylib需要在
  • C++类和动态内存分配

    1 动态内存和类 静态数据成员在类声明中声明 在包含类方法的文件中初始化 初始化时使用作用域运算符来指出静态成员所属类 如果静态成员是整形或枚举型const 则可以在类声明中初始化 在构造函数中使用new来分配内存时 必须在相应的析构函数中
  • 高阶源码分析:ConcurrentHashMap

    高阶源码分析 ConcurrentHashMap 一 文章导读 这部分内容让大家读懂ConcurrentHashMap源码的底层实现从而在工作中合理去使用他并且在面试中能做到游刃有余 主要内容如下 核心构造方法 核心成员变量 put方法过程
  • html中图片左右切换,超简单的图片左右切换滑动

    网上看过很多图片左右切换滑动的效果 不过大都是使用插件实现 插件虽方便 但是对于新手的学习并不是最好的 本文使用jquery这个由原生的JavaScript封装的库 用最简短的代码实现此功能 效果预览如下图 代码部分 直接复制代码便可使用
  • c++数组长度函数length_数据结构-数组

    数组 在数组末尾插入元素 push let numbers 3 4 7 0 1 6 numbers push 11 14 返回数组长度 在数组开头插入元素 unshift numbers unshift 11 14 返回数组长度 手写一个
  • 带有Spring Boot和Spring Cloud的Java微服务

    朋友不允许朋友写用户身份验证 厌倦了管理自己的用户 立即尝试Okta的API和Java SDK 在几分钟之内即可对任何应用程序中的用户进行身份验证 管理和保护 Java是开发微服务架构时使用的一种很棒的语言 实际上 我们行业中的一些知名人士
  • 解决pip的ImportError: cannot import name ‘PackageFinder‘ from ‘pip._internal.index‘ (xxxx)

    问题描述 使用pip时报错 ImportError cannot import name PackageFinder from pip internal index xxxx 问题解决 直接更新 curl https bootstrap p
  • Kotlin 中初始化块、初始化的顺序、lateinit延迟初始化详解

    前些天发现了一个蛮有意思的人工智能学习网站 8个字形容一下 通俗易懂 风趣幽默 感觉非常有意思 忍不住分享一下给大家 点击跳转教程 1 初始化块 初始化块可以设置变量或值 以及执行有效性检查 如检查传给某构造函数的值是否有效 初始化块代码会
  • linux 回收站

    参考 28条消息 Linux中为其配置 回收站 刘瑜澄的博客 CSDN博客 rm命令将文件移至回收站 可跟多个参数 多个文件 文件夹 被移入回收站 设置了移入回收站不覆盖同名文件 如同名文件a txt按照123的顺序移入回收站 回收站中的文
  • React路由

    路由组件 Switch
  • 激活函数之logistic sigmoid函数介绍及C++实现

    logistic sigmoid函数 logistic sigmoid函数通常用来产生Bernoulli分布中的参数 因为它的范围是 0 1 处在 的有效取值范围内 logisitic sigmoid函数在变量取绝对值非常大的正值或负值时会
  • csharp:Learn how to post JSON string to generic Handler using jQuery in ASP.Net

  • C++:指针:void*指针(跳跃力未定的指针)

    先分享一段代码 觉得很有意思 include
  • Scala中使用SOFA jraft 实现rpc的优化 二

    背景 继第一版实现Processable宏之后 各方面已经满足需求 也正常使用 并引入进bitlap 使得我们能以方法的形式管理所有Processor对象 而不需要创建太多的类文件 但是细心的人会发现 为了实现这个小小的功能 我们在scal