关于cxf:CXF动态调用WebService时Class缓存的实现

起因CXF 2.7版本和最新的3.5版本,当应用 动静调用WebService性能时,如果返回值联合应用Jackson转json性能,那么存在 非堆内存透露问题。详情参见:一波三折!记一次非堆内存透露(CXF+Jackson)的排查 透露起因CXF动静调用WebService,个别蕴含以下步骤: 下载WSDL文件将WSDL文件解析成Java代码模型生成Java源码编译源码->Class文件创立ClassLoader加载Class创立数据绑定&类型初始化应用这些Class实现WebService交互废除这些Class,期待JVM闲暇时GC回收然而,如果在对WebService返回值进行解决时,应用了Jackson的toJson性能,那么Jackson会持有CXF动静生成的Class,导致每次动静产生的Class无奈回收,Class数量一直增长,最终导致 非堆内存溢出。 解决办法解决办法有很多种,咱们这里提供将webservice动静调用时产生的class/classloader缓存起来复用,一则解决class透露问题,二则因为缩小了每次wsdl文件解析、java代码生成、java代码编译、class加载的过程,对webservice动静调用还有一些性能上的晋升。当然,为了避免wsdl变动引起缓存变成脏数据的问题,能够通过对wsdl的MD5来判断缓存有效性。 因为不确定CXF的ClientImpl对象是否为重型对象,不确定此对象是否适合进行缓存应用、以及在并发场合下应用,革新计划的次要缓存内容为ClassLoader。 参考实现/** * 动静调用webservice缓存内容 * * @author sswhsz * @CreateDate 2022-11-18 */public class CacheItem { /** * http://*?wsdl、或者 file:/*.wsdl 目前只起备用阐明作用,不作为唯一性辨认 */ private String wsdlLocation; /** * wsdl文档的MD5,用于wsdl的唯一性判断 */ private String wsdlMD5; /** * wsdl文档的长度,疾速判断用 */ private int wsdlSize; /** * wsdl解析的Java模型(仅办法原型、不含实现代码) */ private JavaModel javaModel; private S2JJAXBModel intermediateModel; private JAXBDataBinding databinding; /** * wsdl解析的定义文档 (因为不缓存Client,每次创立Client时均须要解析definition对象) */ private Definition definition; /** * 已加载 webservice class的classLoader,缓存备用; * * 另外,为了避免此classLoader应用平台bundle作为parent,从而阻止平台bundle的卸载/更新; * * 动静生成的classloader将应用 零碎classloader作为parent (ClassLoader.getSystemClassLoader) */ private ClassLoader classLoader; //getter、setter略}/** * 反对WebService动静调用缓存 * * @author sswhsz * @CreateDate 2022-11-18 */public class CacheDynamicClientFactory extends DynamicClientFactory { protected CacheDynamicClientFactory(Bus bus) { super(bus); } /** * * @param wsdlUrl * wsdl本地文件门路,例如:tempFile.getAbsolutePath() * @param cacheItem * 缓存内容,如果是第一次创立classloader/definition,创立的内容将写入 cacheItem,否则从cacheItem读取 * @return */ public Client createClientUseCache(String wsdlUrl, CacheItem cacheItem) { LOG.log(Level.FINE, "Creating client from WSDL " + wsdlUrl); Definition definition = cacheItem.getDefinition(); if (definition == null) { definition = createDefinition(wsdlUrl); cacheItem.setDefinition(definition); } Service svc = createService(definition); ClientImpl client = new ClientImpl(bus, svc, /*port*/null, getEndpointImplFactory()); ClassLoader classLoader = cacheItem.getClassLoader(); if (classLoader == null) { // our hashcode + timestamp ought to be enough. String stem = toString() + "-" + System.currentTimeMillis(); File src = new File(tmpdir, stem + "-src"); File classes = new File(tmpdir, stem + "-classes"); //1、生成java代码 Object[] models = generateJavaCode(wsdlUrl, svc, /*bindingFiles*/null, src); JCodeModel codeModel = (JCodeModel) models[0]; S2JJAXBModel intermediateModel = (S2JJAXBModel) models[1]; //2.编译 compilerJavaCode(src, classes); //3.创立classloader classLoader = createClassLoader(classes); //4.创立数据绑定 & 加载class JAXBDataBinding databinding = createDataBinding(codeModel, classLoader); svc.setDataBinding(databinding); //5. 类型初始化? 原始代码照搬-->没太看懂 typeClassInit(classLoader, client, intermediateModel); // delete the classes files FileUtils.removeDir(classes); cacheItem.setIntermediateModel(intermediateModel); cacheItem.setDatabinding(databinding); cacheItem.setClassLoader(classLoader); } else { JAXBDataBinding databinding = cacheItem.getDatabinding(); svc.setDataBinding(databinding); S2JJAXBModel intermediateModel = cacheItem.getIntermediateModel(); typeClassInit(classLoader, client, intermediateModel); } //设置上下文ClassLoader的目标是为了后续 invoke办法时,从上下文中获取加载的class //但此处 CXF设计不佳。 更正当的做法是 将classloader存储在ClientImpl上,在invoke办法前后 try{ 设置} finally {勾销设置} ClassLoaderUtils.setThreadContextClassloader(classLoader); return client; } /** * java代码生成 * * @param src * 生成Java代码的根目录 * @return Object[] 返回元素2:[0]=JCodeModel, [1]=intermediateModel */ protected Object[] generateJavaCode(String wsdlUrl, Service svc, List<String> bindingFiles, File src) { //all SI's should have the same schemas SchemaCollection schemas = svc.getServiceInfos().get(0).getXmlSchemaCollection(); SchemaCompiler compiler = JAXBUtils.createSchemaCompilerWithDefaultAllocator(new HashSet<String>()); InnerErrorListener listener = new InnerErrorListener(wsdlUrl); Object elForRun = ReflectionInvokationHandler.createProxyWrapper(listener, JAXBUtils.getParamClass(compiler, "setErrorListener")); compiler.setErrorListener(elForRun); OASISCatalogManager catalog = bus.getExtension(OASISCatalogManager.class); hackInNewInternalizationLogic(compiler, catalog); addSchemas(compiler.getOptions(), compiler, svc.getServiceInfos(), schemas); addBindingFiles(bindingFiles, compiler); S2JJAXBModel intermediateModel = compiler.bind(); listener.throwException(); JCodeModel codeModel = intermediateModel.generateCode(null, elForRun); if (!src.mkdir()) { throw new IllegalStateException("Unable to create working directory " + src.getPath()); } try { Object writer = JAXBUtils.createFileCodeWriter(src); codeModel.build(writer); } catch (Exception e) { throw new IllegalStateException("Unable to write generated Java files for schemas: " + e.getMessage(), e); } return new Object[] { codeModel, intermediateModel }; } protected void compilerJavaCode(File src, File classes) { if (!classes.mkdir()) { throw new IllegalStateException("Unable to create working directory " + classes.getPath()); } StringBuilder classPath = new StringBuilder(); try { ClassLoader parent = ClassLoader.getSystemClassLoader(); setupClasspath(classPath, parent); } catch (Exception ex) { throw new RuntimeException(ex); } List<File> srcFiles = FileUtils.getFilesRecurse(src, ".+\\.java$"); if (!compileJavaSrc(classPath.toString(), srcFiles, classes.toString())) { LOG.log(Level.SEVERE, new Message("COULD_NOT_COMPILE_SRC", LOG, src).toString()); } FileUtils.removeDir(src); } protected ClassLoader createClassLoader(File classes) { URL[] urls = null; try { urls = new URL[] { classes.toURI().toURL() }; } catch (MalformedURLException mue) { throw new IllegalStateException("Internal error; a directory returns a malformed URL: " + mue.getMessage(), mue); } //留神:parentClassLoader应用 system class loader, 防止无心中阻止 bundle class loader的卸载和更新 ClassLoader parent = ClassLoader.getSystemClassLoader(); ClassLoader cl = ClassLoaderUtils.getURLClassLoader(urls, parent); return cl; } protected void typeClassInit(ClassLoader classLoader, ClientImpl client, S2JJAXBModel intermediateModel) { //5. 应用线程上下文ClassLoader加载class ClassLoaderHolder holder = ClassLoaderUtils.setThreadContextClassloader(classLoader); try { ServiceInfo svcfo = client.getEndpoint().getEndpointInfo().getService(); TypeClassInitializer visitor = new TypeClassInitializer(svcfo, intermediateModel, allowWrapperOps()); visitor.walk(); } finally { holder.reset(); } } protected JAXBDataBinding createDataBinding(JCodeModel codeModel, ClassLoader cl) { JAXBContext context; Map<String, Object> contextProperties = jaxbContextProperties; if (contextProperties == null) { contextProperties = Collections.emptyMap(); } StringBuilder sb = new StringBuilder(); boolean firstnt = false; for (Iterator<JPackage> packages = codeModel.packages(); packages.hasNext();) { JPackage jpackage = packages.next(); if (!isValidPackage(jpackage)) { continue; } if (firstnt) { sb.append(':'); } else { firstnt = true; } sb.append(jpackage.name()); } JAXBUtils.logGeneratedClassNames(LOG, codeModel); String packageList = sb.toString(); try { if (StringUtils.isEmpty(packageList)) { context = JAXBContext.newInstance(new Class[0], contextProperties); } else { context = JAXBContext.newInstance(packageList, cl, contextProperties); } } catch (JAXBException jbe) { throw new IllegalStateException("Unable to create JAXBContext for generated packages: " + jbe.getMessage(), jbe); } JAXBDataBinding databinding = new JAXBDataBinding(); databinding.setContext(context); return databinding; } protected Definition createDefinition(String wsdlUrl) { try { // use wsdl manager to parse wsdl or get cached definition Definition definition = bus.getExtension(WSDLManager.class).getDefinition(wsdlUrl); return definition; } catch (WSDLException ex) { throw new ServiceConstructionException(new Message("SERVICE_CREATION_MSG", LOG), ex); } } protected Service createService(Definition definition) { WSDLServiceFactory sf = new WSDLServiceFactory(bus, definition); sf.setAllowElementRefs(allowRefs); Service svc = sf.create(); return svc; } public static CacheDynamicClientFactory newInstance() { Bus bus = CXFBusFactory.getThreadDefaultBus(); return new CacheDynamicClientFactory(bus); }}

January 16, 2023 · 4 min · jiezi

RPC框架是啥之Apache-CXF一款WebService-RPC框架入门教程

本博客 猫叔的博客,转载请申明出处学习系列RPC框架是啥?RPC框架是啥之Java自带RPC实现,RMI框架入门Apache CXF一款WebService RPC框架入门教程CXF官网:http://cxf.apache.org/Apache CXF是一个开源的WebService RPC框架,是由Celtix和Codehaus XFire合并而成的。它可以说是一个功能齐全的集合。 功能特性: 支持Web Service标准,包括SOAP(1.1、1.2)规范、WSI Basic Profile...等等我也不了解的,这里就不一一举例了。支持JSR相关规范和标准,包括....同上。支持多种传输协议和协议绑定(SOAP、REST/HTTP、XML)、数据绑定(JAXB2.X、Aegis、Apache XML Beans)。还是先从案例入手吧项目源码地址:RPC_Demo,记得是项目里面的comgithubcxf1、使用IDEA构建一个maven项目,我选择了maven-archetype-webapp构建基本框架。当然你可能还需要创建一些目录 2、我想是时候先配置好主要的pom文件了。<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cxf</groupId> <artifactId>comgithubcxf</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>comgithubcxf Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> <cxf.version>3.1.7</cxf.version> <spring.version>4.0.9.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>comgithubcxf</finalName> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> </build></project>3、构建Server端还有其服务实现,接口使用@WebService注解,标明是一个WebService远程服务接口。package com.github.cxf.server;import javax.jws.WebService;/** * Create by UncleCatMySelf in 21:57 2019\4\23 0023 */@WebServicepublic interface CxfService { String say(String someOne);}在实现类上也同样加上,并通过endpointInterface标明对接的接口实现 ...

April 24, 2019 · 3 min · jiezi

SAP CX Upscale Commerce : SAP全新推出的电商云平台

大家好,我是Andy Chen,是SAP成都研究院年轻的SAP CX Upscale Commerce (后面将会以Upscale简称)开发团队的一名产品经理。CX的全称是Customer Experience。今天很高兴能够从一个产品经理的视角,给大家初步介绍一下这个SAP全新推出的面向中端市场的电商云平台。SAP CX Upscale Commerce是SAP专为零售商,B2C品牌商,CPG(快速消费品)和Direct-to-Consumer(直接面向消费者)行业而设计的下一代中端市场电商SaaS云平台。当前目标市场是美国(未来会逐步推向全球其他区域),目标客户主要包含以下特征:目标公司的软件采购和实施预算低于25万美元总收入(total revenues)低于10亿美元的General Business公司大型公司(包括SAP Enterprise Commerce Platform和SAP Commerce Cloud客户)寻求快速部署上线某些子品牌的快闪店(popo-upstore),小型的online或者in-store应用商店。在选择Upscale方案之前,这些公司通常会选择部署在其他电商平台上,比如Shopify Plus或Magento2018年10月,SAP的两位大佬, CX总裁Alex Atzberge以及SAP Upscale Commerce的高级副总裁Charles Nicholls,在巴塞罗那CX Live会议上宣布 SAP CX Upscale Commerce上线。2019年1月,SAP Upscale Commerce被评选为SAP Labs China 2018最佳产品之一,本人也很荣幸代表成都Upscale团队上台领取该奖项。Upscale研发部门分布在全球多个城市,波士顿主要负责前端应用研发,成都、蒙特利尔、慕尼黑和伦敦主要负责后端微服务交付。接下来开始我们分三个大的章节进行的产品介绍,首先介绍目前美国零售行业数字化转型的挑战,接下来是Upscale产品概念与部分特征,最后介绍如何通过Upscale在30分钟内快速部署一个iOS移动原生电商应用。如果大家有任何关于Upscale产品以及技术架构的问题,可以留言讨论。零售行业数字化转型面临的挑战(以美国为例)1. 电商移动用户体验的提升势在必行根据comScore关于移动端的统计报告,尽管占据了全网约70%的流量,但是只有20%的交易发生在移动端。这份报告的详细地址:https://www.comscore.com/Insi…市场主流的在线商店应用都是为PC桌面为出发点设计的,而移动设备应用只是PC端购物体验的延伸和扩展,用户体验没有针对移动应用进行深度定制,体验较差。2. 品牌就是体验用户体验越来越成为用户是否下单的关键要素,个性化服务体验是其中非常关键的因素之一。大规模的个性化功能实现是非常挑战和困难的:今天大多数终端个性化呈现都是商家手动配置的。弊端非常明显 – 人工工作量大、效果差、伪个性化、无法及时响应用户需求。3. Channel-less vs Cross Channel消费者希望在线上、线下渠道都可以购买该品牌的同一个商品,并且能够享受高度一致的用户体验和服务。为了给消费者提供一致性的良好体验,构建强大的分布式订单管理系统是许多品牌的首要任务。4. 敏捷性品牌商和零售商需要能够快速投放商品进入市场试错。新产品如何才能快速同步线上线下推向市场,并且响应市场需求变化?传统电商方案的平台搭建是一项艰巨的任务,需要大量的自定义代码,花费数月时间(甚至更长)和巨大的财务支出来实施。即便如此,有时也很难满足业务快速部署和调整的需求。面对以上挑战,我们需要一个全新的零售策略和支持这个策略的电商云解决方案 - SAP Upscale!SAP Upscale和传统本地部署的电商解决方案相比,有哪些优势?1. Upscale极大地简化了传统电商的数字化转型之路,开启了基于速度和敏捷的新时代。从商家tenant的创建到上线,只需要几天到几周的配置时间。2. Upscale专为移动消费者量身定做,包含移动端原生iOS、Android应用和PWA应用(Progressive Web App, 渐进性网页应用, 一种新的网页开发技术)。Upscale模糊了渠道之间的界限,使线上或者门店购物变得轻松有趣。3. Upscale内嵌的AI功能可以自动帮助商家为每个用户提供个性化服务,真正做到“千人千面”。4. 通过提供优秀的用户体验和个性化、线上线下一致性服务,商家将会从中得到最大化的收入和利润。快速部署 & 灵活响应市场动态Upscale深深刻着零售DNA的烙印,团队主要成员拥有数十年的零售行业领域知识的深厚沉淀。通过将传统的零售产品(Product)、品类(Category)管理经验和深度学习、人工智能的高效结合,Upscale模糊了传统意义上的三个独立学科:商品推销(Merchandising),个性化(Personalization)和分布式订单管理(Distributed Order Management)之间的界限。通过实时记录和分析用户在App上的每次点击、浏览、收藏等事件,结合历史销售订单、商品数据为每个客户提供最佳购物体验,同时为每个商家自动优化销售利润和收入。另外与大多数传统商务平台不同,Upscale使用最先进的微服务构建,一切都是API优先。商家可以根据自己业务和数字化转型的需求,灵活订阅和部署需要的前端应用和后端服务。在获得灵活快速的市场响应能力的同时,TCO(总体拥有成本)将会明显下降。The MobigramMobigrams是基于AI的1对1个性化商店应用。我们的目标是:1. 为每一个终端用户提供自动化的个性化服务,真正做到“千人千面”;2. 为每一个零售商或者品牌商自动化地优化收入和利润。每个消费者打开App都有自己独特的Mobigram,在消费者整个浏览商品和下单购物的过程中,App会通过后端AI实时计算的结果,为每个用户动态组装个性化的产品集合、品类、内容等模块。AI实时计算考虑的变量包括但不限于零售商想要销售的产品,产品库存位置,库存可用性等。使用AI和深度学习技术来平衡零售商的需求(出售库存,利润率,产品周转速度和曝光度)与客户的体验(访问速度,便利性,产品相关性,购物愿望),从而推动能为零售商或品牌商带来最大利润的客户体验。目前Upscale在3个领域已经上线的AI功能:Continuity CommerceSAP Upscale Commerce具有内置的AI预测订单连续性引擎,可自动甄别潜在可能从单次商品购买转换为连续性订单的客户。比如,用户以相对固定的周期购买某一品牌洗发露,那么系统可以识别此类购物行为。Upscale会向此类客户自动发送邀请,用户只需要在邀请里面点击一下购买,那么就可以立刻加入Continuity Program。商家将会在未来按照用户喜好的周期,定期的自动递送该商品给客户)。 对于客户而言,这意味着他/她们“永远不会买不到”喜欢的产品,也不会因为疏忽而遗忘购买某些商品。在这个过程中,用户自然也成为了该品牌最忠实的会员。对于商家而言,科学和准确的预测潜在购买需求有了有效的输入, 从而可以进一步研究和实现库存自动管理优化。延伸阅读:Getting Started with Continuity Ecommerce, Part 1: Approaches, Expectationshttps://www.practicalecommerc…30分钟快速搭建一个SAP Upscale移动原生iOS应用本章节将会为大家简单介绍如何在30分钟快速搭建一个移动原生iOS应用。1. Upscale tenant订阅SAP Upscale为商家提供一键on-boarding tenant的工具,只需要提供tenant的名称、subDomain信息以及需要创建tenant的系统环境,后台就会为用户订阅专属的tenant。2. 移动零售应用配置完成了tenant的订阅,商家即可配置自己的电商移动原生应用。SAP Upscale Commerce为商家提供了工作台作为配置的界面,几乎所有跟搭建一个电商网店的相关工作都可以在这个界面完成,其中:Configuration该部分包括一个电商应用所需的最基本配置,比如库存(仓库或者门店)、收款、税务、发货、用户账号管理、客户服务、订单管理(取消、退货)等。产品、产品与品类的关联关系,库存初始化信息等相关工作我们都是通过后台作业来执行。商家准备好产品CSV文件,可以配置周期性或者一次性作业来导入主数据。Upscale提供非常灵活的字段匹配、自定义属性的功能来帮助客户完成主数据集成。另外一个基本配置是App,商家可以在这里配置原生App(iOS,Android)以及PWA等应用,并且可以按照商家的策略,关联同样或者不一样的用户体验配置。Inventory作为Channel-less的电商解决方案,线上线下库存透明化是基础。在该配置目录下,商家可以看到全局库存、单品库存以及库存调拨等功能。EditionEdition是一组对象组合,包括产品品类(Category)、单品(product)以及AI自动推荐的Trend category。这些对象在一段时间内可用并在特定渠道上供消费者购买,例如2017年秋季Edition。商家可以灵活的配置多个Editions,并按照自身策略激活发布其中一个,关联到一个渠道然后开放给消费者。Experience用于定义一个App的布局、模块和模板。Upscale提供基于模板的一键生成功能,商家只需要按照自己业务需求一键生成默认的experience,所见即所得。然后根据自己品牌的风格,变更配色、logo等主题,即可关联edition来搭建原生应用。3. 全渠道用户体验完成以上配置后,商家即可部署App到应用商店供用户或者门店使用。全渠道的用户体验让客户能够在任何地方购物,并且真正得到全渠道个性化体验。下图是通过Xcode 模拟iPhone应用的一个截图:我们可以看到,每个客户拥有自己量身定制的个性化界面,系统会动态清除客户不喜欢的内容和产品,改变商品推销的内容和方式,引导客户进入跟自身最贴切并且最容易下单购买的类别。用户得到最佳体验的同时,Upscale也能最大限度地提高零售商的收入和利润。Upscale支持的应用平台有:Mobile App – iOS & AndroidMobile Web (PWA)Tablet (PWA)Desktop (PWA)Kiosk & In Store通过本文对SAP Upscale Commerce的基本介绍,相信大家应该对于这款SAP全新的SaaS电商云解决方案有了初步的了解。限于篇幅,如果有任何问题和更多的兴趣,欢迎讨论,感谢阅读。要获取更多Jerry的原创文章,请关注公众号"汪子熙": ...

February 24, 2019 · 1 min · jiezi