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]。
Groovy 特性如下:
Groovy 优势如下:
我将介绍如下几种常用的适合 Groovy 脚本热更新的场景,供您学习
风控的规则引擎非常适合用 groovy 来实现,对抗黑产,策略人员每天都都会产出拦截规则,如果每次都需要发版,可能发完观测完后,该薅的羊毛都被黑产薅没了。
所以利用 groovy 脚本引擎的动态解析执行,使用规则脚本将查拦截规则抽象出来,快速部署,提升效率。
大型互联网系统,伴随着海量数据进入,各个层级的人员需要时时刻刻关注业务的各个维度指标,此时某个指标异常光靠人肉是没办法实现的。此时需要监控中心介入,提前部署好异动规则,当异常发生时,监控中心发出告警通知到对应的规则创建人员,从而尽快查明原因,挽回资损。
此时要保证监控中心异常灵活,可以随时随地满足业务人员或者研发人员配置监控指标,测试我们可以使用 Groovy 条件表达式,满足灵活监控规则配置需求。
营销活动配置是我个人觉得最复杂的业务之一。活动模板多样,千人千面,不同人群看到的活动样式或者“奖品”不一。且活动上线要快,效果回收,投入产出比等要能立即观测。
此时需要工程侧抽象出整个活动模板,在需要变化的地方嵌入 Groovy 脚本,这样就减少了测试和发版的时间,做到活动可线上配置化。
代码实现展示:
/** * 加载脚本 * @param script * @return */ public static GroovyObject buildScript(String script) { if (StringUtils.isEmpty(script)) { throw new RuntimeException("script is empty"); } String cacheKey = DigestUtils.md5DigestAsHex(script.getBytes()); if (groovyObjectCache.containsKey(cacheKey)) { log.debug("groovyObjectCache hit"); return groovyObjectCache.get(cacheKey); } GroovyClassLoader classLoader = new GroovyClassLoader(); try { Class<?> groovyClass = classLoader.parseClass(script); GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance(); classLoader.clearCache(); groovyObjectCache.put(cacheKey, groovyObject); log.info("groovy buildScript success: {}", groovyObject); return groovyObject; } catch (Exception e) { throw new RuntimeException("buildScript error", e); } finally { try { classLoader.close(); } catch (IOException e) { log.error("close GroovyClassLoader error", e); } } }
重点关注:
// 程序内部需要关联出待执行的脚本即可 try { Map<String, Object> singleMap = GroovyUtils.invokeMethod2Map(s.getScriptObject(), s.getInvokeMethod(), params); data.putAll(singleMap); } catch (Throwable e) { log.error(String.format("RcpEventMsgCleanScriptGroovyHandle groovy error, guid: %d eventCode: %s", s.getGuid(), s.getEventCode()), e); } // 三种执行方式,看 脚本内部返回的结果是什么 public static Map<String, Object> invokeMethod2Map(GroovyObject scriptObject, String invokeMethod, Object[] params) { return (Map<String, Object>) scriptObject.invokeMethod(invokeMethod, params); } public static boolean invokeMethod2Boolean(GroovyObject scriptObject, String invokeMethod, Object[] params) { return (Boolean) scriptObject.invokeMethod(invokeMethod, params); } public static String invokeMethod2String(GroovyObject scriptObject, String invokeMethod, Object[] params) { log.debug("GroovyObject class: {}", scriptObject.getClass().getSimpleName()); return (String) scriptObject.invokeMethod(invokeMethod, params); }
都说 Groovy 能完美兼容 Java 语法,即直接复制 Java 代码到 Groovy 文件内,亦能编译成功。
事实真的如此么,我们看如下执行的代码:
Set<String> demo = new HashSet<>(); demo.add("111"); demo.add("222"); for (String s : demo) { executor.submit({ -> println "submit: " + s; }); } for (String s in demo) { executor.submit({ -> println "sp submit: " + s; }); } // 输出结果 // submit: 222 // sp submit: 222 // submit: 222 // sp submit: 222
此时代码并没有按照预期的结果输出 111, 222,这是为什么呢?
答:lambda 语法在 Groovy 中语义和在Java 中不一致,虽然编译不出错,但表达的语义不一致
在 Groovy 中表示闭包概念,此处不熟悉的可以 Google 详细了解 Groovy 语法。
通常加载 Groovy 类代码如下:
GroovyClassLoader groovyLoader = new GroovyClassLoader(); Class<Script> groovyClass = (Class<Script>) groovyLoader.parseClass(groovyScript); Script groovyScript = groovyClass.newInstance();
每次执行 groovyLoader.parseClass(groovyScript),Groovy 为了保证每次执行的都是新的脚本内容,会每次生成一个新名字的Class文件,这个点已经在前文中说明过。当对同一段脚本每次都执行这个方法时,会导致的现象就是装载的Class会越来越多,从而导致PermGen被用满。
标签:Java,Groovy,Apache,语言,引擎,互联网,系统, 来源:
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。