190102-Quick-Fix 从0到1构建一个应用内服务/数据访问订正工具包

文章目录
  1. I. 背景说明
    1. case1: 程序出bug了
    2. case2: 缓存数据有问题
    3. 3. 小结
  2. II. 方案设计
    1. 1. 设计
    2. 2. 技术
      1. a. 服务定位
      2. b. EndPoint支持
  3. III. 相关博文
  4. IV. 使用说明
    1. 1. 依赖管理
    2. 2. 引入依赖包
      1. a. 纯jar应用
      2. b. 纯Spring应用
      3. c. SpringMVC应用
      4. d. SpringCloud应用
  5. V. 其他
    1. 项目
    2. 其他
    3. 声明
    4. 扫描关注

I. 背景说明

Builder

case1: 程序出bug了

在我们的实际工作中,当我们遇到别人反馈代码出问题了吧,怎么返回的数据不对?

当应用持续跑了一段时间之后,这个时候我们的第一个反应基本是确认能复现么?如果能复现,那么调用的姿势是不是对的?如果确认姿势没问题,那么就是请求参数不对了!!! 如果请求参数还没有问题,卧槽,这下完了,真可能有bug了,这下怎么办?

接下来,一般的讨论是在测试环境复现一下,如果能复现,那么开启debug(或者远程debug),一行行调试,相信很快就能搞定了;

但是,最怕的就是但是,测试环境没法复现,至于线上环境才有问题,这下怎么搞?

case2: 缓存数据有问题

另外一个场景就是为了提升服务性能,缓存基本上会被大量的使用在各个系统之间;有缓存,那么就会有缓存不一致的问题,如果缓存用的是外部的如(redis/memcache)之类的,那么缓存数据的查询和订正,就相对简单了;但是,如果我们使用了内存作为数据的缓存,比如(hashmap, guava),这种时候,我想知道这个内存中的数据怎么办?我想修改这个内存的中的数据怎么办?

3. 小结

上面两个场景,归纳一下主要是两个问题

  • 如何知道线上应用中,某个服务的方法的执行结果;
  • 如何知道线上应用中,某些内存数据的结果

II. 方案设计

为了解决上面抛出的两个问题,我们要怎么做呢?

1. 设计

如何访问应用中的方法、数据,首先想到的就是反射;通过反射来执行某个实例的方法,或者获取实例的属性值,并没有太多的难度,有问题的是如何做到无侵入,如何与外部通信,如何做到通用

首先我们需要注入一个EndPoint,用于实现应用于外界的通信,这个是一切开始的基本条件,Fixer的Endpoint负责接收外部请求,并将请求转发给内部的解析器,执行应用内服务访问,并将结果输出给外部使用者

IMAGE

上图给出了EndPoint的结构设计,因为目前的java应用,直接以jar方式跑的不太多了,更常见的是将服务跑在其他的容器中,比如我们常见的tomcat应用,Spring应用等;不同的容器,对外暴露的方式不一样,怎么样才可以做到在不同的容器中,进行优雅的支持呢?

接下来请求到应用内之后,首先定位到访问的服务,其次则进行服务调用执行,其实现流程如下

IMAGE

2. 技术

从上面的结构设计出发,找到这个项目实现的关键点,然后看下可以怎么实现

a. 服务定位

如何通过传入的请求参数来定位需要执行的服务方法,一般来将,应用中提供的服务可以分为两种情况

  • 以实例的形式提供服务
    • 如Spring中以Bean的形式,一个Service就是一个Bean;我们可以借助Spring的ApplicationContext获取对应的bean
    • 这种类型的服务,要求应用本身持有所有服务,然后我们可以通过这个ServiceHolder来定位具体的服务
  • 一个类对应一个服务
    • 这种常见的是静态类或者单例,这个是以ClassLoader维度进行区分的;
    • 因此我们可以直接通过ClassLoader方式来加载对应的类

然后我们主要目标需要集中在第一种方式,不同的应用方式,获取ServiceHolder不一样,让我们自己去全部支持,显然是不太现实的,因此我们需要设计一个方案,让使用者,自己来根据应用中的ServiceHolder,来选择具体的Service方法

这种,就可以通过SPI机制来支持

b. EndPoint支持

提供与外部的交互,最常见的方案就是暴露一个http接口,然后接收外部的请求;非web服务怎么办?也可以开一个socket走tcp通信,那么问题来了

  • 对于web服务
    • 直接在已有的web服务上新增一个端点,并加上权限控制?
    • 另开一个端口提供服务
  • 对于非web服务
    • 新开端口提供服务

所以再具体的实现中,我们需要考虑以下几点

  • 如何复用已有的web服务?
  • 没有web服务时,自己怎么支持web服务?
  • 如何支持绑定端口的配置?
  • 当项目中引入了多种EndPoint支持方式时,怎么保证只有一个生效?

针对上面的问题,具体实现时,会用到下面一些机制

  • 引入优先级
  • 通过SPI来实现自定义扩展
  • 解析JVM参数,来获取对应的配置

III. 相关博文

从设计到实现,下面博文分别进行详细介绍说明

… (待补齐)

IV. 使用说明

1. 依赖管理

首先添加仓库,两种方式,一个是github的release版本的引入,优势是稳定;确定是更新及时问题;

1
2
3
4
5
6
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>

另一个是我个人的仓库

1
2
3
4
5
6
<repositories>
<repository>
<id>yihui-maven-repo</id>
<url>https://raw.githubusercontent.com/liuyueyi/maven-repository/master/repository</url>
</repository>
</repositories>

2. 引入依赖包

根据实际的应用场景,引入对应的依赖包,

a. 纯jar应用

1
2
3
4
5
<dependency>
<groupId>com.git.hui.fix</groupId>
<artifactId>fix-core</artifactId>
<version>1.0</version>
</dependency>

注意事项:

  • fix-core 内置了一个http服务器,默认绑定端口号 9999, 可以通过jvm参数 -Dquick.fix.port 来覆盖
  • 在应用的入口出,需要主动执行 FixEngine.instance(); 进行初始化
  • 因为fix-core 只提供了静态类的ServerLoader, 对于实例的加载,需要业务方自己来实现

使用姿势如下:

1
curl -X POST -H "Content-Type:application/json" http://127.0.0.1:9999/fixer/call -d '{"service": "com.git.hui.fix.example.jar.server.CalculateServer", "method": "getCache", "params": ["init"], "type":"static"}'

实例demo:

jar-example

b. 纯Spring应用

如我是一个纯Spring应用,没有使用SpringMVC,可以引入

1
2
3
4
5
<dependency>
<groupId>com.git.hui.fix</groupId>
<artifactId>spring-fixer</artifactId>
<version>1.0</version>
</dependency>

注意事项:

spring-fixer提供了访问Spring容器内bean的服务方式,因此除了获取默认提供的静态类之外,还可以访问bean;

  • 使用默认的http服务器,端口号为 9999, 通过jvm参数 -Dquick.fix.port 来覆盖
  • 与前面不同,不需要主动调用FixEngine.instance()
  • 内部提供bean的加载ServerLoader,可以直接通过beanName或者Bean的完整类名访问其内部方法/数据

使用姿势如下:

1
2
3
4
5
6
7
8
9
# 执行bean的某个方法
curl -X POST -H "Content-Type:application/json" http://127.0.0.1:8080/inject-fixer-endpoint/call -d '{"service": "demoBean", "method": "randName"}'
# 查看bean的属性值
curl -X POST -H "Content-Type:application/json" http://127.0.0.1:8080/inject-fixer-endpoint/call -d '{"service": "demoBean", "field": "name"}'
# 执行bean的属性的某个方法
curl -X POST -H "Content-Type:application/json" http://127.0.0.1:8080/inject-fixer-endpoint/call -d '{"service": "demoBean", "field": "values", "method":"add", "params": ["autoInsertByQuickFixer!"]}'

# 测试静态类的静态成员的方法调用
curl -X POST -H "Content-Type:application/json" http://127.0.0.1:9999/fixer/call -d '{"service": "com.git.hui.fix.example.spring.server.StaticBean", "method": "getCache", "params": ["init"], "type":"static"}'

实例demo:

c. SpringMVC应用

如是一个SpringMVC应用,可以引入

1
2
3
4
5
<dependency>
<groupId>com.git.hui.fix</groupId>
<artifactId>spring-mvc-fixer</artifactId>
<version>1.0</version>
</dependency>

使用说明:

  • 利用mvc本身提供的http服务,访问路径为/inject-fixer-endpoint/call; 因此需要做好安全校验

使用姿势&实例:

d. SpringCloud应用

如果是一个SpringCloud服务,且开启了 actuator 应用监测,可以引入

1
2
3
4
5
<dependency>
<groupId>com.git.hui.fix</groupId>
<artifactId>spring-cloud-fixer</artifactId>
<version>1.0</version>
</dependency>

使用说明:

  • 将FixEndPoint端口集成在SpringCloud的Actuator中,因此在实际使用时,需要在配置中开启,设置参数 management.endpoints.web.exposure.include
  • 访问路径为:/actuator/inject-fixer-endpoint/call, 前面的 actuator路径与应用监测配置的路径一致

使用姿势&实例:

V. 其他

项目

其他

拒绝单机,欢迎start或者加好友支持

声明

尽信书则不如,已上内容,一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

  • 微博地址: 小灰灰Blog
  • QQ: 一灰灰/3302797840
  • WeChat: 一灰/liuyueyi25

扫描关注

公众号&博客

QrCode

打赏码

pay

知识星球

goals

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×