程序员留的后门,怎么能管它叫热部署呢?

news2024/12/24 2:57:49

前言

这篇文章给大家分享一个热部署相关的知识点。

这可是一个好东西啊,当年在学习学 JSP 的时候,哐哐哐一顿操作,发现服务没重启,我在 JSP 里面写的东西就直接生效了。

当场我就是一个大大的震惊,而旁边教我的人只是说着一些什么热部署,字节码,classloader 重写之类的我根本就听不懂的话。

但是听不懂不重要,我会使用就行了。随即就是一顿疯狂练习,把这玩意用的炉火纯青,什么 pageContext 上下文,redirect 重定向,forward 转发,九大内置对象,完整的生命周期啥的,手到擒来。

可惜这玩意不争气,在前后端分离的大潮下,自己慢慢的没落了。搞得我空有一身 JSP 本领,无处施展。

后来啊,

后来,我就自己悄悄的在程序里面基于它做了很多后门嘛,但是我发誓,我没有一点点其他的想法,真的是出于好心,出于责任心,想要加快线上问题响应效率,就在里面各种线上骚操作,刀尖跳舞,可好用了...

好了,说回热部署。

首先我们明确下什么叫做热部署,热部署是在不重启 java 虚拟机的前提下,自动更新 class 的行为,从而更新整个运行时的逻辑。

在 java 开发领域,热部署一直是一个难以解决的问题,java 虚拟机理论上只能实现方法体的修改热部署,对于整个类结构的更改,仍然需要重启虚拟机,对类重新加载才能完成更新操作。

OSGI

其实 java 业界有一些解决方案,比如 osgi 架构,这玩意时间比较长了,但一直没火起来。

osgi 架构的出现,可以让 java 系统变成模块化的形式,让模块重启成为可能。从一定程度上也算是个热部署的方案。

可惜这玩意以前开发起来就觉得很反人类,配置文件一大堆不说,学习成本也很大。

和 spring 结合起来,居然是一个模块一个 spring 上下文体系。并且如果模块之间有调用关系的话,重启相关的模块会让应用出现短暂的功能性休克,也就说,整个热启动过程不平滑。

这项技术现在估计很多小伙伴都没听说过,目前也渐渐的退出历史舞台,用的企业估计很少。

ASM

ASM 是一款修改字节码的框架,同类型的框架还有 Cglib。

这些框架能加载一个 class 信息,用户可以按照自己的需求增强修改这些信息,最后输出成一个新的 class。

具体实现过程,这里就不展开了。大家可以搜索下,相关技术实现文章不少。

但单纯修改字节码一般要和其他技术结合起来,单靠这个也无法完成热更新,虽然 ASM 类的框架能够修改类,但是这些 ASM 的修改逻辑也是用 java 写的,这段代码也需要执行的。

如果你把 ASM 的代码写在 java 里,也无法实现从外部来热更新。

Javaagent&Attach API结合ASM

这就是上面一段说的 ASM 要结合其他技术才能实现热更新的方案,也是目前很多开源框架采用的方案。

比如大名鼎鼎的 Arthas,就是利用 javaagent 通过 Attach API 运行时加载目标 Java 程序,最终利用 Instrumention API 或者 ASM 增强 class,来实现代码跟踪,以及代码热修改的特性。

但我认为用 Arthas 来完成线上代码的热更新,只能用于一些很紧急的场景。不能替代日常业务逻辑修改。而且操作起来也挺复杂。

你需要先修改 java 代码,然后把 java 代码放到服务器上,在 arthas 里查找这个类的类加载器的 hashcode,然后利用 arthas 提供的命令编译 java 代码输出成新的 class 文件,最用利用 redefine 命令进行热更新。

试想下,如果大量逻辑的更改。这得有多麻烦。

所以更多的是利用 arthas 对线上应用进行诊断,追踪,热更改代码其实就是 arthas 众多功能中其中一个,并不是主要功能。

换一种思路

以上方案都是基于修改 class 本身,然后让 JVM 的类加载器重新加载来实现的。

那么有没有更好的方案呢?

其实 java 代码可以运行一些脚本的,jdk 本身就支持调用脚本,从 JDK 1.6 开始,java 就支持 JSR223,可以用一致的形式在 JVM 上执行一些脚本语言,而且可实时编译,运行的效率和 java 不相上下的。

有的同学看到这里可能会拍砖了:利用脚本只能更改部分逻辑,不可能把所有的逻辑都用脚本写吧,你这篇文章探讨的不是“有没有可能所有的逻辑都可热更新”么?

别急,首先我们来确定一个事情。

你的 java 应用系统需不需要所有的逻辑都是可以热更新的?

很多代码都是大致固定不变的,比如 util 类,一些 vo 的定义也不大变更的,一些固定的业务也不需要热更新需求的。只有一些经常变更的决策部分,可能需要热更新。

那么我们只要把需要经常变的部分用脚本来定义不就可以了么。

业界有没有类似的开源框架呢?

还真有,而且是高 star 的热门开源项目,能够帮你做到用脚本进行热更新业务。

开源编排规则引擎

可能有小伙伴又要说了:你不是介绍 java 代码热部署么,怎么话题转到规则引擎上去了?

这里要说下,规则引擎的一大特性就是把决策部分逻辑剥离到外面,能够实现逻辑的变动快速热变更。

而这次介绍的规则引擎框架则更为强大,除了能剥离逻辑,还能解耦系统,让你的所有的逻辑块均可随意变更。理论上能实现所有的逻辑都可变更,不是部分哦。

这就是业界现在很火的编排式规则引擎框架:LiteFlow。

LiteFlow 的理念很简单,就是把系统中的各个逻辑切分成一小块一小块的,称之为组件,这些组件可以由 java 代码来写,也可以用脚本来写。然后一个完整业务就是把各个组件组搭一起,形成一个完整业务链。

 这种模式的好处就是,不需要热更新的部分可以用java组件来写,需要经常变的部分可以用脚本来写。所有的组件均可混搭成为一个业务。

如何编排这些组件,LiteFlow 独创了 ELF 语法,拥有非常好上手的编排语法。程序员的话,十分钟就可以上手。上图粉色部分就是最简单的一种串联形式。

业务链路中组件可实时更换,也可实时增加,形成一个新的业务链。同时定义好的组件也可复用在其他的链路中。

​LiteFlow 的脚本方案也是利用 JSR223 来实现的,目前已经实现的脚本有三种:

​为什么说利用 LiteFlow 编排引擎框架,你的所有逻辑都是可以变更的呢?

因为你完全可以把所有的逻辑都用脚本组件来实现,LiteFlow 提供了非常强大的脚本支持,完全和 Java 底层打通,你可以在脚本中 import java 的类,也可以调用 java 的类方法,甚至于可以在脚本中去定义方法,定义类,一切写法和java中完全一样。

更夸张的是,LiteFlow 允许你在脚本中调用 spring 上下文的 bean,你可以在脚本中调用 DAO 取数据,可以在脚本中发送 rpc 给其他微服务。

只要你愿意,你可以一行 java 业务代码不写,完全把业务搬到脚本组件中去。

而且连逻辑块的顺序你也可以随意变动,因为 LiteFlow 的编排规则和脚本均可实现热变更。

LiteFlow 为经常用的存储中间件也提供了原生支持:

​LiteFlow 支持所有的关系型数据库,另外 zk,etcd,nacos 均可支持,还提供了额外的扩展接口,供你自己扩展成其他的存储方式。

有想过么,你所有的逻辑和规则编排语法,都是存在于系统之外的。只要更改其脚本和逻辑,你所有节点的系统不需要做任何事,实时的进行热变更。

而这一切,LiteFlow 做到了非常平滑,所谓平滑的意思是,不用担心在热变更的时候你的业务会受到任何的中断,也不会因为热变更造成正在执行的链路产生任何的异常。

LiteFlow 编排能力有多强大呢,简单的几个关键字就可以编排出超乎想象的效果:

​最后放出 LiteFlow 的官网和仓库地址,如果你觉得这款开源框架对你的业务有帮助,可以深入了解一下。

项目官网:https://liteflow.yomahub.com

gitee托管仓库:https://gitee.com/dromara/liteFlow

github托管仓库:https://github.com/dromara/liteflow

JSR223

刚刚提到了 LiteFlow 这个支持多种脚本语言的规则引擎框架就是基于 JSR223 规范来做的。

那么 JSR223 是个啥玩意呢?

有了这个规范,我们就可以在 Java 的平台里,执行其他的语言,包括且不仅限于 jvm 发展出来的语言。

有的同学可能会说:在java项目里执行其他语言,这不吃饱了撑着么,java体系那么庞大,各种工具一应俱全,放着好好的java不写,还要去执行其他语言干嘛?

写 java 的都知道,java 是需要事先编译的。

如果你想要在运行中改变编译好的 class 信息,怎么办?

前面说了,如果你用字节码等技术栈,是需要很大的成本的。

所以要想在运行中很方便的改变业务逻辑,其实用 java 去执行其他的脚本语言是一个好办法。况且有的脚本语言有着比 java 更简洁的语法特性。

而由于 JSR223 规范的存在,在 java 平台中调用其他脚本语言,其实一点都不复杂,你无需关心每种语言的实际api。

JSR223 规范最初在 Java6 平台被提出,提供了一套标准的 API 为脚本语言的执行提供了内置支持。

也就是说,你只要熟悉这一套 API 就能执行大部分的脚本语言。

而且这套 API 的使用也是非常方便的,几个核心方法仔细看个 10 分钟就能明白如何使用。

来个最简单的例子:

//获得javascript的脚本引擎
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("javascript");
//进行脚本编译
String script = "function process(){\n" +
  "var a=10\n" +
  "var b=3\n" +
  "return a*b-c\n" +
  "}\n" +
  "process()";
CompiledScript compiledScript = ((Compilable) scriptEngine).compile(script);
//绑定java的参数
Bindings bindings = new SimpleBindings();
bindings.put("c", 5);
//执行并打印结果
Object result = compiledScript.eval(bindings);
System.out.println(result);

上述代码演示的是用 JSR223 API 去执行 javascript 语言。

值得一提的是,java 内置了 javascript 引擎,你无需引入任何第三方包依赖就可以获得这个引擎:

 整个过程分 4 块,分别是:

  • 获得引擎

  • 脚本编译

  • 绑定 java 参数

  • 执行

在实际业务中,建议在系统启动的时候去编译脚本,然后把编译好的脚本对象 compiledScript 对象给缓存起来,因为编译过程相对比较耗时,运行时每次去编译是个糟糕的设计。

如果在运行中改变了脚本,只需要重新去编译这个脚本并缓存其编译后的对象即可。

你只需要掌握以上代码,那几乎就已经掌握了 JSR223 规范的使用了。是不是很简单?

如果你想换成 groovy 脚本语言,那你需要依赖第三方依赖

<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-jsr223</artifactId>
      <version>3.0.8</version>
</dependency>

然后在上述的代码里获得引擎这块换成groovy即可:



ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("groovy");

如果你想换成 python,需要依赖第三方依赖:

<dependency>
    <groupId>org.python</groupId>
    <artifactId>jython-standalone</artifactId>
    <version>2.7.3</version>
</dependency>

然后在上述的代码里获得引擎这块换成 python 即可:

ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("python");

其实现在很多的语言在 java 平台都推出了自己的 java 三方执行依赖包,而且很多的包都支持了 JSR223 规范。

只要支持了 JSR223 规范的语言,都可以利用上述的代码来执行。

JSR223 规范的 API 可以支持 java 和其他语言的绑定和互通,一个 java 对象通过 bindings 对象也是可以传到脚本语言中的,在脚本语言中,你可以获得 java 的对象,来进行调用其方法和进行逻辑计算。

不过不同的语言调用操作符也许有所不同,比如 groovy,javascript 都是通过点操作符,和 java 很像,Lua 脚本的对 java 对象的操作符是冒号。

所以当你的项目支持相关的脚本语言之前,你先要熟悉下相关语言的语法。

最后,希望大家都能从 JSR223 规范中找到一些设计你们相关业务系统的灵感。

·············· END ··············

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/71814.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]Python计算机毕业设计Django在线教育系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

offsetWidth、offsetHeight、clientHeight、clientWidth、scrollHeight、scrollWidth详细对比

offsetWidth、offsetHeight、clientHeight、clientWidth、scrollHeight、scrollWidth详细对比 代码测试 box-sizing:content-box时 box-sizing:border-box时 总结 offsetHeight、offsetWidth其实就是元素在屏幕上所占的实际宽高&#xff08;包括border、padding&#xff0…

Java数据结构与Java算法学习Day11---有向图、拓扑排序、加权无向图、最小生成图、加权有向图、最短路径(简略笔记记录)

目录 一、有向图 157 1.1有向图的定义及相关术语 157 1.2有向图的实现 158 二、拓扑排序 159 2.1检测有向图中的环 160 2.1.1检测有向图中环API设计 160 2.1.2检测有向环实现 161 2.2基于深度优先顶点排序 162 2.2.1顶点排序API设计 2.2.2顶点排序实现的原理过程 2.…

【Day2】零基础学java--》牛客网刷题【字符集合】

大家好&#xff0c;我是良辰丫&#x1f49e;&#xff01;好久不见&#xff0c;一个多月没写博客了&#xff0c;都有些生疏了&#xff0c;学校的一些事情&#xff0c;考试&#xff0c;实训&#xff0c;各种实验&#xff0c;嘿嘿嘿&#xff01;其实也不能为自己找借口&#xff0c…

PDF文件怎么添加水印?一定要学会的两个方法

相信大家都遇到过这种难题&#xff0c;那就是自己制作PDF文件传输给别人之后&#xff0c;害怕文件内容被别人使用&#xff0c;窃取自己的劳动成果&#xff0c;所以需要对自己的文件进行保护&#xff0c;我们通常的做法是给PDF文件添加水印&#xff0c;这样就可以尽可能的保护文…

Redis这个内存回收,确实有点牛逼!!!

1. 过期 key 处理 Redis 之所以性能强&#xff0c;最主要的原因就是基于内存存储。然而单节点的 Redis 其内存大小不宜过大&#xff0c;会影响持久化或主从同步性能。 我们可以通过修改配置文件来设置 Redis 的最大内存&#xff1a; maxmemory 1gb当内存使用达到上限时&…

【LeetCode】1812. 判断国际象棋棋盘中一个格子的颜色

题目描述 给你一个坐标 coordinates &#xff0c;它是一个字符串&#xff0c;表示国际象棋棋盘中一个格子的坐标。下图是国际象棋棋盘示意图。 如果所给格子的颜色是白色&#xff0c;请你返回 true&#xff0c;如果是黑色&#xff0c;请返回 false 。 给定坐标一定代表国际象棋…

五、卷积神经网络CNN5(卷积相关问题2)

采用宽卷积的好处有什么 窄卷积和宽卷积 对于窄卷积来说&#xff0c;是从第一个点开始做卷积&#xff0c;每次窗口滑动固定步幅。比如下图左部分为窄卷积。那么注意到越在边缘的位置被卷积的次数越少。于是有了宽卷积的方法&#xff0c;可以看作在卷积之前在边缘用 0 补充&…

MySQL数据库学习(1)

一、概述 MySQL 是最流行的关系型数据库管理系统&#xff0c;在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System&#xff1a;关系数据库管理系统)应用软件之一。 在本教程中&#xff0c;会让大家快速掌握 MySQL 的基本知识&#xff0c;并轻松使用 M…

【畅购商城】微信支付模块之微信支付二维码

目录 流程分析 微信支付概述 账号申请 已有账号 ​​​​​​​支付接口和SDK(了解)​​​​​​​ ​​​​​​​入门案例 统一下单 ​​​​​​​查询订单 ​​​​​​​JS版二维码&#xff1a;QRCode.js ​​​​​​​概述 ​​​​​​​入门案例 ​​​​…

字节跳动3-2专家强烈推荐入广告核心部门,要学的12大技术实战PDF

前言 你了解过字节跳动的薪资和职级是什么样的吗&#xff1f; 想必大家都对字节跳动的薪资和职级都有一定的了解了&#xff0c;下面就给大家介绍字节3-1专家推荐的进入字节跳动需要储备知识技能&#xff01;&#xff01;&#xff01; 应届生进入字节跳动的是711的学习路线&am…

Innodb如何实现表--下篇

Innodb如何实现表--下篇Innodb数据页结构File HeaderPage HeaderInfimum和Supremum RecordUser Records和Free SpacePage DirectoryFile Trailer实例分析Innodb数据页结构 Innodb数据页由以下7个部分组成: File Header(文件头) Page Header(页头) Infimun和Supremum Records U…

Abaqus二次开发:局部坐标系的建立与应用

问题描述 在单向复材中&#xff0c;纤维的力学性能往往是横观各向同性的&#xff0c;于是需要规定材料方向。 通常需要新建局部坐标系用于材料方向的定义&#xff0c;而在实际建立坐标系中&#xff0c;坐标系会储存在对应的Part下&#xff1a; mdb.models[‘Model-1’].parts[…

GraphQL基础使用--mongoDB数据库操作

GraphQL hello world 首先我们要安装好执行GraphQL的环境 因为其是运行在node服务器端的&#xff0c;所以我们要安装 express express-graphql graphql mongoose 安装好后的package.json文件是这个样子的 其次我们就要开始配置端口为3000的node服务器 const express requir…

Leetcode---2.两数之和

目录题目分析链表最终代码实现&#xff08;内含注释&#xff09;题目 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和…

护眼灯对眼睛真的有作用吗?一文了解市面上的护眼灯是否真的管用

我们都知道&#xff0c;现在越来越多的人开始使用护眼台灯照明了&#xff0c;不为别的&#xff0c;只为眼睛健康&#xff0c;所以同样的也有许多人质疑护眼灯是否真的对眼睛有效果&#xff0c;今天就来聊聊护眼灯是否真的有护眼作用。 在我看来&#xff0c;人体眼睛看任何事物…

TS201的通过外部中断IRQ0控制DMA传输(含参考代码)

外部中断控制DMA传输 DMA的原理就不多说了&#xff0c;之前的文章里有写。 电路中的Interrupt Pin ADSP-TS201 EZ-KIT Lite评估板上每片DSP含有4个外部中断(IRQ3–0 )&#xff0c;其中IRQ0接到了一个按键上&#xff08;SW4和SW5&#xff09;。 当按键按下时&#xff0c;表现…

西湖论剑 Flagshop 分析复现

前言 比赛时候没能做出来&#xff0c;其实这道题就是一道pwn题。后面与p w n师傅讨论分析EXP分析还原了解题过程。学到了很多&#xff0c;也希望分享给大家。 任意文件读取 抓包或者看源码就会发现有一个SSRF&#xff0c;但是没有权限读flag&#xff0c;测试发现存在一个readf…

全网惟一面向软件测试人员的Python基础教程-为什么要学Python

全网惟一面向软件测试人员的Python基础教程 起点&#xff1a;python软件测试实战宝典》目录 第一章 为什么软件测试人员要学习Python 文章目录全网惟一面向软件测试人员的Python基础教程前言一、Python是什么&#xff1f;二、为什么要学二、测试人员如何学二、怎么从0开始学Py…

旅行路线可视化研究与实现(Java+Android+Eclipse实现的旅游APP)

目 录 1 概论 1 1.1 研究现状 1 1.2 系统开发意义 1 1.3 系统开发背景 2 2 开发环境以及相关技术 5 2.1 Eclipse 5 2.2 Adroid 5 2.2.1 基本概念 5 2.2.2 简介 6 2.2.3 系统架构 6 2.2.4 四大组件 7 2.3 Java语言 7 2.4 SQLite 7 2.4.1 简介 7 2.4.2 架构 7 2.4.3 特点 8 2.5 F…