热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaRPC框架过滤器机制原理解析

这篇文章主要介绍了JavaRPC框架过滤器机制原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

过滤器

字面义上理解的过滤器类似下图,从一堆物品中筛选出符合条件的留下,不符合的丢弃。

GOF 职责链

GOF中有一种设计模式叫职责链,或者叫责任链,常规的UML图如下:

正统的职责链是将一个请求发给第一个接收者,接收者判断是否属于自己能处理的,如果能处理则执行操作并中止请求下发,流程到此为止。如果不能处理则将请求下发给下一个接收者一直到最后一个接收者。

变体职责链

上面提到正统的职责链有一个特点:当找到符合条件的执行者后流程中止并不会将请求继续下发给后续的执行者。这类场景比较适合比如审核操作,一个报销单由主管,经理,CTO一级一级的审批不能越权。

解耦合/细分

在实际项目中,往往会遇到非常复杂的业务场景,有可能是需要执行的方法特别多,也有可能是因为需要执行的方法有可能事先不知道,需要在运行时才能判断。如果将这些逻辑全部写在一个类或者一个方法中就会出现这样的问题:

耦合度高,一个方法或者一个类需要关联所有业务方法以及相关类不易扩展,需要执行的业务经常发生变化,如果每次变化都去修改统一的方法或者类,不符合开闭原则,维护成本非常高

所以就有了下图,一堆需要执行的方法发给第一个接收者,接收者判断哪些方法是自己可以执行的,有执行的就执行,然后无论是否有可执行的方法在处理完成后都将请求继续下发给后面的接收者。每个接收者完成自己负责的内容,多个接收者完成了复杂任务的分解。

RPC 过滤器

RPC过滤器与Spring MVC中的Filter作用基本相同,其中很大一个作用就是动态的给客户端或者是服务端增加切面功能,比如:

权限控制加密解密访问日志限流控制并发控制......

下图是我在RPC项目中实现过滤器机制的UML示意图

RpcFilter

定义一个过滤器接口,只包含一个invoke方法。

public interface RpcFilter {
   T invoke(RpcInvoker invoker, RpcInvocation invocation);
}

RpcInvoker

这是RPC客户端以及服务端执行服务端方法或者是将客户端请求发送给服务端时需要调用的方法接口。

这个角色在Netty中也可以叫Handle,这个接口与上面的RpcFilter有点类似,只是在RPC框架中体现的角色不同而已,具体看UML图可知道两者关系。

public interface RpcInvoker {
  Object invoke(RpcInvocation invocation);
}

RpcServerInvoker

服务端的一个执行者实现,包含两个核心功能:

构建职责链this.filterMap,是通过注解获取到的一组过滤器,此处不详细讲因为与本文关系不大RpcInvoker的invoker方法实际调用的是RpcFilter的方法,并将自身实例传递给RpcFilter,目的是构建职责链的关联关系

public RpcInvoker buildInvokerChain(final RpcInvoker invoker) {
  RpcInvoker last = invoker;
  List filters = Lists.newArrayList(this.filterMap.values());

  if (filters.size() > 0) {
    for (int i = filters.size() - 1; i >= 0; i --) {
      final RpcFilter filter = filters.get(i);
      final RpcInvoker next = last;
      last = new RpcInvoker() {
        @Override
        public Object invoke(RpcInvocation invocation) {
          return filter.invoke(next, invocation);
        }
      };
    }
  }
  return last;
}

此处并没有考虑职责链的排序,可以通过过滤器的注解上增加排序数字来解决。目前我写的过滤器注解中并没有实现排序功能,可以增加一个order的属性,然后在需要指定顺序的过滤器上增加对应属性值来支持。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ActiveFilter {
  String[] group() default {};
  String[] value() default {};
}

发请求给职责链

服务端在读取到客户端的请求后,首先通过构建职责链得到RpcInvoker,然后调用RpcInvoker的invoke方法将请求下发。

@Override
  protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcMessage message) {

    this.executor.execute(new Runnable() {
      @Override
      public void run() {
        RpcInvoker rpcInvoker=....
        RpcResponse respOnse=(RpcResponse) rpcInvoker.invoke......
  }

过滤器案例

一般HTTP请求在不同网络角色中处理请求的能力会呈现为一个漏斗型,越上层职责越轻,越往下层职责越重,所对应的就是越上层处理请求量越大,越下层处理请求量越小。比如负载均衡器只负责请求转发而不负责具体的任务执行,而后端的Service服务器会执行大量的IO操作或者是消耗cpu的计算任务等,所以这两者在处理请求的量上往往是数量级的。

当出现大量请求时,为了有效的保护后端服务的稳定性(尽量不出现宕机),除了横向扩展服务器外还可以通过一些软件手段缓解后端服务的压力,这就是通常说的限流,本文因为需要简单实现一个限制的过滤器,所以直接引用现成的限流算法:令牌桶。

下面是客户端请求限流的一个简单实现,客户端在给服务端发起请求之前需要获取令牌,如果获取到则发送请求,如果获取不到一直等待。当然为了防止死锁,可以调用带超时时间的获取令牌方法。

@ActiveFilter(group = {ConstantConfig.CONSUMER})
public class AccessLimitFilter implements RpcFilter {

  private final static Logger logger = LoggerFactory.getLogger(AccessLimitFilter.class);

  @Override
  public Object invoke(RpcInvoker invoker, RpcInvocation invocation) {
    logger.info("before acquire");
    AccessLimitManager.acquire();

    Object rpcRespOnse=invoker.invoke(invocation);
    logger.info("after acquire");
    return rpcResponse;
  }

  static class AccessLimitManager{
    private final static RateLimiter rateLimiter=RateLimiter.create(2);

    public static void acquire(){
      rateLimiter.acquire();
    }
  }
}

实现的比较粗糙,桶的大小是写死的,应该实现为可配置型,后续抽空完善下。

本文源码

https://github.com/jiangmin168168/jim-framework

文中代码是依赖上述项目的,如果有不明白的可下载源码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • Nginx Buffer 机制引发的下载故障
    Nginx ... [详细]
  • windows平台使用NSP拦截具体进程的域名解析过程(xFsRedir的代理功能之域名代理)
    byfanxiushu2022-10-17转载或引用请注明原始作者。xFsRedir软件其中之一的功能就是实现了全方位的网络代理,从主机代理,到本地代理 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 域名解析系统DNS
    文章目录前言一、域名系统概述二、因特网的域名结构三、域名服务器1.根域名服务器2.顶级域名服务器(TLD,top-leveldomain)3.权威(Authoritative)域名 ... [详细]
  • 目录Atlas介绍Atlas部署Atlas基本管理Atlas结合MHA故障恢复读写分离建议Atlas介绍Atlas是由Qihoo360Web平台部基础架构团队开发维护的一个基于My ... [详细]
  • php网站设计实验报告,php网站开发实训报告
    本文目录一览:1、php动态网站设计的关键技术有哪些软件,及搭建步骤需要哪些页面,分别完成 ... [详细]
  • Kubernetes(k8s)基础简介
    Kubernetes(k8s)基础简介目录一、Kubernetes概述(一)、Kubernetes是什么(二& ... [详细]
  • 自己手动写一个RPC框架
    一,简单一点的过程解说图(不太清晰,凑合看吧)Gitee仓库源码:https:gitee.comfanjiangfengwrite-rpc-framworkcommon模块创建商品 ... [详细]
author-avatar
手机用户2502927277
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有