Groovy 是什么?
Apache 的 Groovy 是 Java 平台上设计的面向对象编程语言。这门动态语言拥有类似 Python、Ruby 和 Smalltalk 中的一些特性,可以作为 Java 平台的脚本语言使用,Groovy 代码动态地编译成运行于 Java 虚拟机(JVM)上的 Java 字节码,并与其他 Java 代码和库进行互操作。
由于其运行在 JVM 上的特性,Groovy 可以使用其他 Java 语言编写的库。Groovy 的语法与 Java 非常相似,大多数 Java 代码也符合 Groovy 的语法规则,尽管可能语义不同。 Groovy 1.0 于 2007 年 1 月 2 日发布,并于 2012 年 7 月发布了 Groovy 2.0。从版本 2 开始,Groovy 也可以静态编译,提供类型推论和 Java 相近的性能。Groovy 2.4 是 Pivotal 软件赞助的最后一个主要版本,截止于 2015 年 3 月。Groovy 已经将其治理结构更改为 Apache 软件基金会的项目管理委员会(PMC)[1]。
Java 为何需要 Groovy ?
Groovy 特性如下:
- 语法上支持动态类型,闭包等新一代语言特性
- 无缝集成所有已经存在的 Java 类库
- 既支持面向对象编程也支持面向过程编程
- 执行方式可以将 groovy 编写的源文件编译成 class 字节码文件,然后交给 JVM 去执行,也可以直接将 groovy 源文件解释执行。
- Groovy 可以与 Java 完美结合,而且可以使用 java 所有的库
Groovy 优势如下:
- 敏捷
- groovy 在语法上加入了很多语法糖,很多 Java 严格的书写语法,在 Groovy 中只需要少量的语法糖即可实现
- Groovy 的灵活性是的它既可以作为变成语言,亦可作为脚本语言
- 0 成本学习 Groovy,完美适配 Java 语法
热部署技术设计及实现
使用场景
我将介绍如下几种常用的适合 Groovy 脚本热更新的场景,供您学习
风控安全——规则引擎
风控的规则引擎非常适合用 groovy 来实现,对抗黑产,策略人员每天都都会产出拦截规则,如果每次都需要发版,可能发完观测完后,该薅的羊毛都被黑产薅没了。
所以利用 groovy 脚本引擎的动态解析执行,使用规则脚本将查拦截规则抽象出来,快速部署,提升效率。
监控中心
大型互联网系统,伴随着海量数据进入,各个层级的人员需要时时刻刻关注业务的各个维度指标,此时某个指标异常光靠人肉是没办法实现的。此时需要监控中心介入,提前部署好异动规则,当异常发生时,监控中心发出告警通知到对应的规则创建人员,从而尽快查明原因,挽回资损。
此时要保证监控中心异常灵活,可以随时随地满足业务人员或者研发人员配置监控指标,测试我们可以使用 Groovy 条件表达式,满足灵活监控规则配置需求。
活动营销
营销活动配置是我个人觉得最复杂的业务之一。活动模板多样,千人千面,不同人群看到的活动样式或者“奖品”不一。且活动上线要快,效果回收,投入产出比等要能立即观测。
此时需要工程侧抽象出整个活动模板,在需要变化的地方嵌入 Groovy 脚本,这样就减少了测试和发版的时间,做到活动可线上配置化。
技术实现
脚本加载/更新
代码实现展示:
1 | /** |
重点关注:
- 脚本开启缓存处理:否则多次会更新可能会导致 Metaspace OutOfMemery
脚本执行
1 | // 程序内部需要关联出待执行的脚本即可 |
生产踩坑指南
Java8 lambda 与 Groovy 语法问题
都说 Groovy 能完美兼容 Java 语法,即直接复制 Java 代码到 Groovy 文件内,亦能编译成功。
事实真的如此么,我们看如下执行的代码:
1 | Set<String> demo = new HashSet<>(); |
此时代码并没有按照预期的结果输出 111, 222,这是为什么呢?
答:lambda 语法在 Groovy 中语义和在 Java 中不一致,虽然编译不出错,但表达的语义不一致
在 Groovy 中表示闭包概念,此处不熟悉的可以 Google 详细了解 Groovy 语法。
GroovyClassLoader 加载机制导致频繁 gc 问题
通常加载 Groovy 类代码如下:
1 | GroovyClassLoader groovyLoader = new GroovyClassLoader(); |
每次执行 groovyLoader.parseClass(groovyScript),Groovy 为了保证每次执行的都是新的脚本内容,会每次生成一个新名字的 Class 文件,这个点已经在前文中说明过。当对同一段脚本每次都执行这个方法时,会导致的现象就是装载的 Class 会越来越多,从而导致 PermGen 被用满。
同时这里也存在性能瓶颈问题,如果去分析这段代码会发现 90%的耗时占用在 Class。
如上实战过程中,已经给出了解决办法:
- 对于 parseClass 后生成的 Class 对象进行 cache,key 为 groovyScript 脚本的 md5 值
脚本首次执行耗时高
在初期方案上线时,压测后显示,首次加载脚本性能较慢,后续脚本执行速度非常快,猜测可能是 Groovy 内部在首次脚在脚本时还做了其他的校验(本人还没跟进这块,如果有读者感兴趣,可以断点详细看下链路耗时在哪里)
正对首次加载缓慢问题,解决方法如下:
1 | // 1.加载脚本,并缓存 |
往期精彩
欢迎关注公众号:咕咕鸡技术专栏
个人技术博客:https://jifuwei.github.io/ >
参考:
[1] Groovy Wiki