Java RPC框架过滤器机制原理解析

过滤器

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

GOF 职责链

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

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

变体职责链

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

解耦合/细分

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

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

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

RPC 过滤器

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

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

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

RpcFilter

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

public interface RpcFilter<T> {
  <T> 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<RpcFilter> 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

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

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

(0)

相关推荐

  • PHP和JAVA的XML-RPC中文问题解决办法

    问题描述:      在使用PHP和JAVA操作XML-RPC的时候,如果request中包含中文字符,会被自动编码成如下样式: 欢欢 . 环境:PHP内置XML-RPC的API,Apache的XML-RPC的JAVA API PHP下的解决方法:      起初以为是中文字符的编码问题,所以我就尝试用各种编码方式来编码中文字符,然后交给string xmlrpc_encode_request ( string method, mixed params)函数来生成XML格式的请求,可是依然如故.

  • JAVA实现较完善的布隆过滤器的示例代码

    布隆过滤器是可以用于判断一个元素是不是在一个集合里,并且相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势.布隆过滤器存储空间和插入/查询时间都是常数.但是它也是拥有一定的缺点:布隆过滤器是有一定的误识别率以及删除困难的.本文中给出的布隆过滤器的实现,基本满足了日常使用所需要的功能. 0 0 0 0 0 0 0 0 0 0 先简单来说一下布隆过滤器.其实现方法就是:利用内存中一个长度为M的位数组B并初始化里面的所有位都为0,如下面的表格所示: 然后我们根据H个不同的散列函数,对传进来

  • Java实现简单的RPC框架的示例代码

    一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用).Hessian.Http invoker等.另外,RPC是与语言无关的. rpc框架做的最重要的一件事情就是封装,调用者和被调用者的通讯细节,客户端代理负责向调用方法的方法名参数返回值包等信息根据通信协议组织成报文发送给服务端,服务端解析报文,根据客户端传递的信息执行对应的方法,然后将返回值安装协

  • Java中的拦截器、过滤器、监听器用法详解

    本文实例讲述了Java中的拦截器.过滤器.监听器用法.分享给大家供大家参考,具体如下: 一.拦截器 :是在面向切面编程的就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方 法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作. 1.Struts2拦截器是在访问某个Action或Action的某个方法,字段之前或之后实施拦截,并且Struts2拦截器是可插拔的,

  • Java RPC框架如何实现客户端限流配置

    关键资源 关键资源总是有限的,也就意味着处理能力也有限,所以当面对大量业务时,为了保障自己能够有序的提供服务最经济的做法就是限制同一时间处理的事务数.比如银行的工作人员,一个工作人员同时只能为一个客户服务,来多了根本处理不了,不光是一种浪费而且有可以造成混乱的局面导致工作人员无法工作. 网络请求漏斗 越上层的服务器处理的事务越轻,应付请求的能力也越强,也就意味着同一请求越上层处理时间越短.为了有效的保护下层服务器,就需要对发送给下层的请求量做限流,在下层服务器可接受的范围内.否则就可能会出现下层

  • Java实现布隆过滤器的方法步骤

    前言 记得前段时间的文章么?redis使用位图法记录在线用户的状态,还是需要自己实现一个IM在线用户状态的记录,今天来讲讲另一方案,布隆过滤器 布隆过滤器的作用是加快判定一个元素是否在集合中出现的方法.因为其主要是过滤掉了大部分元素间的精确匹配,故称为过滤器. 布隆过滤器 在日常生活工作,我们会经常遇到这的场景,从一个Excel里面检索一个信息在不在Excel表中,还记得被CTRL+F支配的恐惧么,不扯了,软件开发中,一般会使用散列表来实现,Hash Table也叫哈希表,哈希表的优点是快速准确

  • 分析JAVA中几种常用的RPC框架

    RPC是远程过程调用的简称,广泛应用在大规模分布式应用中,作用是有助于系统的垂直拆分,使系统更易拓展.Java中的RPC框架比较多,各有特色,广泛使用的有RMI.Hessian.Dubbo等.RPC还有一个特点就是能够跨语言,本文只以JAVA语言里的RPC为例. 对于RPC有一个逻辑关系图,以RMI为例: 其他的框架结构也类似,区别在于对象的序列化方法,传输对象的通讯协议,以及注册中心的管理与failover设计(利用zookeeper). 客户端和服务端可以运行在不同的JVM中,Client只

  • Java利用Sping框架编写RPC远程过程调用服务的教程

    RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样. RPC 可基于 HTTP 或 TCP 协议,Web Service 就是基于 HTTP 协议的 RPC,它具有良好的跨平台性,但其性能却不如基于 TCP 协议的 RPC.会两方面会直接影响 RPC 的性能,一是传输方式,二是序列化. 众所周知,TCP 是传输层协议,HTTP 是应用层协议,而传输层较应用层更加底层,在数据传输方面,越底层越快,因此,在一般情况下

  • Java RPC框架熔断降级机制原理解析

    熔断与降级 为什么在RPC环节中有熔断以及降级的需求,详细的原因这里不多解释,从网上搜索一张图做示意. 熔断 我理解熔段主要解决如下几个问题: 当所依赖的对象不稳定时,能够起到快速失败的目的快速失败后,能够根据一定的算法动态试探所依赖对象是否恢复 比如产品详细页获取产品的好评总数时,由于后端服务异常导致客户端每次都需要等到超时.如果短时间内服务不能恢复,那么这段时间内的所有请求时间都将是最大的超时时间,这类消费时间又得不到正确结果的现象是不能容忍的.所以遇到这类情况,就需要根据一定的算法判定服务

  • Java RPC框架过滤器机制原理解析

    过滤器 字面义上理解的过滤器类似下图,从一堆物品中筛选出符合条件的留下,不符合的丢弃. GOF 职责链 GOF中有一种设计模式叫职责链,或者叫责任链,常规的UML图如下: 正统的职责链是将一个请求发给第一个接收者,接收者判断是否属于自己能处理的,如果能处理则执行操作并中止请求下发,流程到此为止.如果不能处理则将请求下发给下一个接收者一直到最后一个接收者. 变体职责链 上面提到正统的职责链有一个特点:当找到符合条件的执行者后流程中止并不会将请求继续下发给后续的执行者.这类场景比较适合比如审核操作,

  • Java方法参数传递机制原理解析

    这篇文章主要介绍了Java方法参数传递机制原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Java方法中如果声明了形参,在调用方法时就必须给这些形参指定参数值,实际传进去的这个值就叫做实参. 这就涉及到Java中的参数传递机制,值传递. 基本数据类型 基本数据类型,值传递的体现是数值的传递. public class TransferTempTest { public static void main(String[] args) {

  • java的package和import机制原理解析

    这篇文章主要介绍了java的package和import机制原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在说package.import机制前我们先来了解下java的CLASSPATH. CLASSPATH顾名思义就是class的路径,当我们在系统中运行某个java程序时,它就会告诉系统在这些地方寻找这个class文件 CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar; 这是

  • Java和Dubbo的SPI机制原理解析

    SPI: 简单理解就是,你一个接口有多种实现,然后在代码运行时候,具体选用那个实现,这时候我们就可以通过一些特定的方式来告诉程序寻用那个实现类,这就是SPI. JAVA的SPI 全称为 Service Provider Interface,是一种服务发现机制.它是约定在 Classpath 下的 META-INF/services/ 目录里创建一个以服务接口命名的文件,然后文件里面记录的是此 jar 包提供的具体实现类的全限定名. 这样当我们引用了某个 jar 包的时候就可以去找这个 jar 包

  • Java CGLib动态代理机制(全面解析)

    一.首先说一下JDK中的动态代理: JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的 但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高. 二.使用CGLib实现: 使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高.唯一需要注意的

  • Spring整合Dubbo框架过程及原理解析

    这篇文章主要介绍了Spring整合Dubbo框架过程及原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Dubbo作为一个RPC框架,其最核心的功能就是要实现跨网络的远程调用.演示过程创建两个小工程,一个作为服务的提供者,一个作为服务的消费者.通过Dubbo来实现服务消费者远程调用服务提供者的方法. dubbo 的使用需要一个注册中心,这里以Zookeeper为例来演示 1.Dubbo架构 Dubbo架构图(Dubbo官方提供)如下: 节

  • SpringBoot服务监控机制原理解析(面试官常问)

    前言 任何一个服务如果没有监控,那就是两眼一抹黑,无法知道当前服务的运行情况,也就无法对可能出现的异常状况进行很好的处理,所以对任意一个服务来说,监控都是必不可少的. 就目前而言,大部分微服务应用都是基于 SpringBoot 来构建,所以了解 SpringBoot 的监控特性是非常有必要的,而 SpringBoot 也提供了一些特性来帮助我们监控应用. 本文基于 SpringBoot 2.3.1.RELEASE 版本演示. SpringBoot 监控 SpringBoot 中的监控可以分为 H

  • C++实现简单插件机制原理解析

    在我做的第一个页游项目中,服务器使用了插件的机制,但是当时的插件都是用C#写,而且如何实现的也不是很清楚.之后的几个页游项目都是自己一个人包揽服务器部分,所以一直没有写插件的需求.下一个页游项目服务器这边需要多人合作,因此我想把其他模块都独立的做成插件的模式,目前也是在探索阶段.通过网上资料查找以及自己的整理,实现了一个简单版本的插件机制.实现代码如下: 文件Object.hpp中实现了所有插件类的基类,所有插件都要继承该类. #ifndef __OBJECT_HPP__ #define __O

  • 新浪开源轻量级分布式RPC框架motan简单示例解析

    目录 前言 概述 功能 简单调用示例 在pom中添加依赖 为调用方和服务方创建公共接口 编写业务接口逻辑.创建并启动RPCServer 创建并执行RPCClient 集群调用示例 使用CONSUL作为注册中心 Motan-Consul配置 使用ZOOKEEPER作为注册中心 Motan-ZooKeeper配置 前言 好消息,支撑微博千亿调用的轻量级 RPC 框架 Motan 在2016年5月份正式开源了,业界现在除了Dubbo 和 DubboX典型的分布式RPC服务治理型框架外,又多了一个优秀的

  • java同步器AQS架构AbstractQueuedSynchronizer原理解析下

    目录 引导语 1.释放锁 1.1.释放排它锁release 1.2.释放共享锁releaseShared 2.条件队列的重要方法 2.1.入队列等待await 2.1.1.addConditionWaiter 2.1.2.unlinkCancelledWaiters 2.2.单个唤醒signal 2.3.全部唤醒signalAll 3.总结 引导语 AQS 的内容太多,所以我们分成了两个章节,没有看过 AQS 上半章节的同学可以回首看一下哈,上半章节里面说了很多锁的基本概念,基本属性,如何获得锁

随机推荐