soul框架简单介绍与设计模式分析

  • 1. 初识
    • 1.1 查看官网文档了解
    • 1.2. 目标:
  • 2. 分析源码
    • 2.1. 网关的实现原理
      • 2.1.1. 跨域请求问题
      • 2.1.2. 网关请求处理
      • 2.1.3. http网关插件
        • 2.1.3.1 插件数据获取(插件、选择器、规则)
          • 2.1.3.1.1 数据的订阅
        • 2.1.3.2 插件的执行(以http插件为例)
    • 2.2. 亮点
  • 3. 疑问
  • 其他

1. 初识

1.1 查看官网文档了解

针对API的网关 – 可以为每个接口配置负载均衡、限流等功能

有三个角色:

  • soul-admin 配置规则
  • soul-gateway 网关 – 用户请求会打到这
  • server 用户真实的服务 – 用户请求通过网关转发到这

流程:请求打到网关,依据用户的配置,转发到server。当然除了转发还有限流、负载均衡等~

1.2. 目标:

  1. 了解soul网关的实现原理
  2. 插件化的设计有没有什么玄机(现在看来就是不同的协议用的不同的module)
  3. 了解框架中使用的设计模式
  4. 分析框架的分层

2. 分析源码

2.1. 网关的实现原理

网关接收请求入口中哪,如何处理不同协议请求。网关接收到规则,做了什么?

2.1.1. 跨域请求问题

soul-web有个 CrossFilter 跨域过滤器,将response的Access-Control-Allow-Origin等设置为*

扩展知识1:
cors请求分为简单查询(请求方法为HEAD、GET、POST且HTTP头信息不超过以下五种[Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain])和非简单查询。
针对非简单查询,浏览器在正式通信之前,会先发起一次OPTIONS预检
http://www.ruanyifeng.com/blog/2016/04/cors.html

2.1.2. 网关请求处理

请求入口是soul-web的SoulWebHandler
SoulWebHandler 中持有一个插件列表List<SoulPlugin> plugins(根据order排序), 在WebHandler触发handle的时候,将插件放到一个chain里面,按顺序执行插件的execute方法。将请求上链后,soul-web无需关注请求的处理与传递,发送和处理解耦。【责任链模式】

扩展知识2:
Reactor,Java响应式编程。Spring5的WebFlux:与Spring WebMvc 同级,是一个支持反应式编程模型的新框架体系。反应式模型区别于传统的 MVC 最大的不同是异步的、事件驱动的、非阻塞的。提高应用程序的并发性能,单位时间能够处理更多的请求。
https://www.jianshu.com/p/8a2c9376bc11
https://www.jianshu.com/p/7ee89f70dfe5?from=singlemessage
https://learnku.com/articles/30263

扩展知识3:
Mono.just 和Mono.defer区别:
例如:Mono m2 = Mono.defer(()->Mono.just(new Date())); 每执行一次:m2.subscribe(System.out::println); 都会输出不同的date,因为他是在订阅的时候才去new Date的;如果执行是Mono.just(new Date()); 是声明的时候就new date, 订阅几次都是这个date

2.1.3. http网关插件

所有的网关继承一个抽象的网关类,抽象类定义模版方法(SoulWebHandler的handle方法调用的就是插件的这个Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain)方法,此方法是模板方法:先处理通用的选择器、规则等信息,最终调用了抽象方法abstract Mono<Void> doExecute(ServerWebExchange exchange, SoulPluginChain chain, SelectorData selector, RuleData rule),此方法每个插件有不同的实现。【模版方法模式】

2.1.3.1 插件数据获取(插件、选择器、规则)

插件数据都缓存中内存里BaseDataCache,以map的结构存储,分为:PLUGIN_MAPSELECTOR_MAPRULE_MAP
BaseDataCache类中代码很整洁,得益于Optional类的使用,对于map对象操作的方法,基本上一行代码就解决(不需要各种非空判断)。【使用Optional替代非空判断】

[其实这点在某本书(重构?码工?还是整洁?忘了哪本了)上也提到过,不过当时没重视起来。现在看来对代码整洁性有很大提升,至少看着这些代码很舒服,以后要注意使用]

2.1.3.1.1 数据的订阅

问题1:数据从缓存取,那么缓存数据哪里来

可以看到有个类CommonPluginDataSubscriber,类中方法subscribeDataHandler 处理了三类(插件、选择器、配置)数据的消息(修改、删除)。这块应该是【观察者模式的思想】。
CommonPluginDataSubscriber类整体设计不错,实现来PluginDataSubscriber接口,不同的订阅消息都是一个方法,对应三类数据的订阅操作等。收到消息后,基本上会调用一个subscribeDataHandler方法。个人认为此方法就封装等不太好, 如下代码: 就算不用策略模式[什么时候使用这个模式又不会有过度设计等嫌疑呢],至少把三个if xx instanceof xx 的分支内容都封装成一个方法。

    private <T> void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) {Optional.ofNullable(classData).ifPresent(data -> {if (data instanceof PluginData) {if (dataType == DataEventTypeEnum.UPDATE) {...} else if (dataType == DataEventTypeEnum.DELETE) {...}} else if (data instanceof SelectorData) {if (dataType == DataEventTypeEnum.UPDATE) {...} else if (dataType == DataEventTypeEnum.DELETE) {...}} else if (data instanceof RuleData) {if (dataType == DataEventTypeEnum.UPDATE) {...} else if (dataType == DataEventTypeEnum.DELETE) {...}}});}

问题2:什么时候订阅的,什么时候会回调onSubscribeonSelectorSubscribe等方法?

可以配置不同的数据同步方案,例如http、websocke、zookeeper等,这里结构清晰,易于扩展,不同等实现是一个module。例如:soul-sync-data-http、soul-sync-data-nacos、soul-sync-data-websocket等等。

  • http:项目启的时候(spring bean注解new 一个HttpSyncDataService 构造方法里面执行网络请求),做一次fetch。并有一个线程开启轮询。
  • websocket:同样用spring 的bean注解作为入口,建立websocket连接WebsocketSyncDataService

问题3:数据的可用性,如何保证数据高可用
以http拉取方式为例,soul做了异常(超时)重查。除了重查,看起来没有做进一步的操作保证数据拉取,但有日志,可以接入监控报警。基本上够了~
这里把代码贴出来的目的是:日志打的很细节,先是warn,后是error。(打日志要注意规范,什么时候是warn, 什么时候是error)

 catch (Exception e) {// print warnning log.if (time < retryTimes) {log.warn("Long polling failed, tried {} times, {} times left, will be suspended for a while! {}",time, retryTimes - time, e.getMessage());ThreadUtils.sleep(TimeUnit.SECONDS, 5);continue;}// print error, then suspended for a while.log.error("Long polling failed, try again after 5 minutes!", e);ThreadUtils.sleep(TimeUnit.MINUTES, 5);
}

2.1.3.2 插件的执行(以http插件为例)

  • 获取负载均衡器,这里是个spi,方便自定义复杂的负载均衡算法
  • 根据规则生成新的url
  • 转发请求
    其实这个转发有点没看明白, 就写了个exchange.getAttributes().put(Constants.HTTP_URL, realURL);

2.2. 亮点

  1. 代码module层次清晰。
  2. 类职责单一:例如数据同步例如soul-web中有很多过滤器,每个过滤器只做一件事(CrossFilter、ExcludeFilter、FileSizeFilter等)。
  3. 类的结构清晰:例如插件有接口、抽象,不同的协议不同的实现类。
  4. 数据接收等代码的处理有发布订阅的思想。
  5. 责任链模式:soul-web作为入口,通过WebHandler接收请求,请求处理放入chain中,插件实现请求处理。多个处理实现按序执行,请求接收无需关注链中的处理过程。
  6. 模版方法模式:抽象插件AbstractSoulPlugin有模版方法execute,定义execute需要做的事(比如:判断插件开关、选择器配置等),具体的插件实现,只需要关注正在的请求处理。
  7. 使用Optional代替非空判断
  8. WebFlux的使用(以前不认识它…所以对我来说也是亮点)
  9. 日志打得很规范

3. 疑问

1、直接中方法里面用断言是否合适?
例如非测试代码里有这种assert soulContext != null;

答:经了解,Java断言默认是关闭的(通过 -ea 开启),所以个人认为业务代码不应该使用断言做判断。

断言默认不执行测试:

    public static void main(String[] args) {boolean isOpen = false;// 如果开启了断言,会将isOpen的值改为trueassert isOpen = true;// 打印是否开启了断言,如果为false,则没有启用断言System.out.println(isOpen);}

输出:

false

2、什么时候用策略模式不会显得过度设计?
策略模式可以消除if else,但是也带来来代码的复杂性。if else封装的好,似乎也没那么大问题?

答:基本上,现在代码里没必要用原生的策略模式,可以用Spring的注解(同一个接口不同的bean实现类,注入的时候选择不同的beanName,也可以配合工厂)解决问题。代码设计应该考虑如何使代码简洁轻量,而不是一定想要使用哪种设计模式。

其他

对于责任链但选型需要考虑场景,过多的节点、层次结构深也会给整个链带来复杂性,发展成树形就变得难以维护。另外,引入责任链对于问题的排查也会带来难度。这个是今后自己在做选型要注意的点。


[1] soul官网 https://dromara.org/zh/

soul框架简单介绍与设计模式分析相关推荐

  1. Spring框架的设计理念与设计模式分析

    spring设计原理 Spring框架的设计理念与设计模式分析 2016-1-27 by Damon 摘要:Spring作为现在最优秀的框架之一,被广泛的使用并有很多对其分析的文章.本文将从另外一个视 ...

  2. Django - Django框架 简单介绍

    Django框架 简单介绍 本文地址: http://blog.csdn.net/caroline_wendy/article/details/29172271 1. 介绍 Django是一个开放源码 ...

  3. Rebound动画框架简单介绍

    Rebound动画框架简单介绍 Android菜鸟一枚,有不对的地方希望大家指出,谢谢. 最近在接手了一个老项目,发现里面动画框架用的是facebook中的Rebound框架,由于以前没听说过,放假时 ...

  4. 【修真院Java小课堂】Tiles框架简单介绍

    大家好,我是IT修真院上海分院第6期的学员,一枚正直纯洁善良的程序员 今天给大家分享一下,Tiles框架简单介绍 Tiles框架简单介绍 背景介绍 什么是Tiles Tiles 是一种JSP布局框架, ...

  5. Spring 框架简单介绍

    考虑到你可能不熟悉 Spring,我这里对它做下简单介绍.我们常说的 Spring 框架,是指 Spring Framework 基础框架.Spring Framework 是整个 Spring 生态 ...

  6. 玩转人工智能(3)常用的大数据框架简单介绍

    时光不老,我们不散. 讲大数据框架前,简单的介绍下大数据的文化.信息时代人类社会的进步得益于分享和开源.大数据时代属于信息时代的第三代发展阶段(2001年到2011年可以认为是CT行业的黄金期,200 ...

  7. 朝花夕拾之socket的基本使用以及mina框架简单介绍

    工欲善其事,必先利其器,从互联网诞生到现在,基本上所有的程序都是网络程序,很少有单机版的程序了. 而网络编程的本质是两个设备之间的数据交换,当然,在计算机网络中,设备主要指计算机.我们现在进行网络编程 ...

  8. Dbutil框架简单介绍

    Dbutil框架 Dbutil介绍 l commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化 ...

  9. Spring 框架的设计理念与设计模式分析

    Spring 的骨骼架构 Spring 总共有十几个组件,但是真正核心的组件只有几个,下面是 Spring 框架的总体架构图: 图 1 .Spring 框架的总体架构图 从上图中可以看出 Spring ...

最新文章

  1. python众数问题给定含有n个元素的多重集合s_2-1 问题描述:给定含有n个元素的多重集合S - 下载 - 搜珍网...
  2. 系统实施基础:系统实施的相关知识介绍
  3. 豆瓣评分9.4!这一部纪录片,探秘中国的未至之境!
  4. mysql bundle 安装_阿里云服务器下安装MySQL (Bundle压缩包 安装)
  5. cmd中输入net start mysql 提示:服务名无效或者MySQL正在启动 MySQL无法启动
  6. 重新启动计算机的方法有,电脑重新启动怎么办 重新启动解决方法介绍【详解】...
  7. python sqlite3 增删改查(最基本的增删改查)
  8. [VNC] 云服务器 Ubuntu 16.04 安装 gnome 桌面并配置 VNC
  9. SSLRobot:适用于HttpWatch的免费SSL / TLS测试工具
  10. python3pygame 游戏程序_python3 pygame实现接小球游戏
  11. (转)Django ==== 实战学习篇十三 分页(Paginator)处理;Django使用内置的admin
  12. NULL指针的奇妙之旅
  13. 网络通信框架 HP-Socket v5.5.1,支持可靠 UDP
  14. ai怎么平均排列_一篇AI打麻将的论文,理科生眼中的麻将是这样的
  15. rhel5中查询设备上采用的未知文件系统
  16. 三/五/七/九点二次平滑法
  17. 你绝对不知道 Vue 也有生老病死
  18. 推荐几个全网最全的程序员接私活地方法或完整攻略或常用平台以及接单的注意事项(以免被雇主坑),比如国内的程序员客栈、CODING 码市,国外的Upwork、Freelancer、Dribbble等。
  19. python递归函数
  20. Uniswap社区3号提案近200万美元预算昨日到账,这笔钱要怎么花?

热门文章

  1. HTML5贪吃蛇代码
  2. java全栈系列之JavaSE--Arrays类详解027
  3. 2022大数据产业年度“国产化优秀代表厂商”榜单发布,亚信科技AntDB数据库位列其中
  4. 滤波电容为什么要靠近放置,去耦半径是什么?滤波电容如何打孔?(转)
  5. Android Camera2 CameraCharacteristics Key 详细解说
  6. python调用海康威视工业相机SDK实现图片采集
  7. Go 使用IP纯真库获取IP对应的国家、省、市
  8. Swift - lazy 修饰符和lazy 方法
  9. LaTeX插图命令使用教程(简单例子+清晰代码)(论文排版)
  10. 让人癫狂的24号,请你慢点离开