首页 文章 精选 留言 我的

精选列表

搜索[快速],共10000篇文章
优秀的个人博客,低调大师

使用秘籍|如何实现图数据库 NebulaGraph 的高效建模、快速导入、性能优化

本文整理自 NebulaGraph PD 方扬在「NebulaGraph x KubeBlocks」meetup 上的演讲,主要包括以下内容: NebulaGraph 3.x 发展历程 NebulaGraph 最佳实践 建模篇 导入篇 查询篇 NebulaGraph 3.x 的发展历程 NebulaGraph 自 2019 年 5 月开源发布第一个 alpha 版本以来,陆陆续续发布了 2.0 GA,到现在 v3.6.0,已经是 v3.x 版本中比较后期的版本了。从 3.x 开始,基本上保持了三个月(一个季度)发一个 y 版本的节奏,从 v3.1 到 v3.2,到现在的 v3.6。(演讲时 v3.6 尚未发布,所以没有相关内容展示) 而这几个版本主要是在完备性、性能,稳定性以及易用性上的优化。从 NebulaGraph 作为一款图数据库的产品定位上来说,核心应用场景是 TP 场景,而对 TP 型数据库产品来说,有几个特性是非常重要的: 稳定:数据库作为底层的基础设施,许多业务是基于数据库运行的,是在线的系统,因此稳定性是一个非常重要的特性。在 NebulaGraph v3.4 版本,包括最新版本的 v3.6 版本中,系统的稳定性得到了非常大的提升和改善; 性能:因为 NebulaGraph 定位是一个 TP 的数据库产品,因此性能,包括高吞吐量是至关重要的; 易用:一个好的产品要如何让用户更好地用起来,降低用户的学习成本,也是需要考虑到的; 针对这些特性,我们在 v3.x 这 5 个版本中做了这些尝试: 稳定性 在 v3.x 开始,NebulaGraph 引入 fuzzer,极大提升测试效率。fuzzer 可基于 nGQL(NebulaGraph 的查询语言)的语法进行灵活组合,生成人为不能拟定的查询语句,由此让测试更加完善,从而提高了稳定性。 此外,版本新特性部分还新增 Memory Tracker。图数据库不同于其他数据库,数据一直处于持续地遍历、迭代中,因为即便是数据量不大的情况下,数据的迭代会导致它的结果异常大,这就造成了内存的管理压力。在 v3.4 版本中,NebulaGraph 引入了 Memory Tracker 机制,从论坛的用户反馈上,可以看得出来相关的 OOM 问题大幅度减少了。这里可以阅读下《内存管理实践之 Memory Tracker》; 性能提升 图的经典查询一般包括 K 跳(K-hop)、子图和路径查询。K 跳就是从一个点出发,比如说从我出发,去找寻我好友(一跳)的好友(两跳),这种查询,可能社交或者反欺诈的场景中使用会比较多。此外,就是子图,比如说我现在从一个点出发,找到他的周围的关联的一群人,以及这一群人关联的另外一群人,这时候就可能会用到子图的功能。还有就是路径查询,像是企业和企业之间的关联关系之类的,就比较适合用路径,来找寻二者的关联。在 v3.x 中,这几个图的经典查询性能都有大幅度的提升的,具体大家可以看论坛的性能报告:自测和他测报告合集; 上面提到过内存管理的难点,除了 Memory Tracker 机制之外,属性裁剪能提升内存的利用率,在 NebulaGraph 中如果查询不需要用到某个属性,就会将其裁剪掉,从而提升内存利用率。 易用性 NebulaGraph 在 v2.x 开始支持 openCypher,一开始是比较基础的 openCypher 语法;在 v3.x 开始,NebulaGrpah 做了一个语法完善,像是 OPTIONAL MATCH、多 MATCH 等语法支持,全面覆盖了国际图基准测试之一的 LDBC-SNB 支持的图查询。 此外,在 v3.5 开始支持了 UDF 功能,这个功能是由社区用户 zhaojunnan 提供支持的,它可以用来帮助实现一些内核暂时不支持的功能。这里就不详细展开 UDF 的说明了,具体大家可以看《NebulaGraph UDF 功能的设计与背后的思考》; 最后一点是全文索引优化,这个在后面章节会详细讲述。 NebulaGraph 的最佳实践 在这个部分主要分为:建模、数据导入、查询等三大内容。 数据建模 数据膨胀 这是社区用户在交流群里反馈的一个问题: 数据导入之后占用硬盘空间极大,60 MB 的文件导入之后,Storage 占用了 3.5 G; Storage 服务占用内存很高 并发量大的时候,会出现 ConnectionPool 不够的情况; 3 跳查询本来就很慢么?我看官方的 Benchmark 数据规模大的时候会慢,但是当前我们数据量也不是很大; 目前的数据情况:点 594,952,边 798,826。同时,边和点均建了 1 个索引: 所以它有什么问题呢? 在图数据库 NebulaGraph 中,有个概念叫做 Space,Space里有一个概念是 VID type,VID 是 NebulaGraph 中非常关键、重要的概念,所有的数据字段都是通过 VID 来进行唯一索引,类似主键的概念。比如上图的中间部分:点结构和边结构,可以看到结构中都有 VID,用来进行字段查询;边结构还分起点 srcId 和终点 dstId,就是上图的两处 VertexID。 如上图所示,这个 Space 中配置的 VID 类型是 String,而 NebulaGraph 支持的 VID 类型有两种:一种是 INT,数值类型,像手机号之类的可以用 INT 来存储;一种是 String,比如说人名之类的,当然你要用 String 来存储像是身份证号之类的数值信息也可以。但是,用 VID 的查询效率从经验看是 INT 类型是远高于用 String 作为 VID 类型的。 回到上面的这个例子,一开始用户创建 VID 时,直接选取了 FIXED_STRING 类型,设定为了 256 位的定长 String。但是这里会导致一个问题: 594,952(点数)*256 (VID 大小)* (1 + 1) + 798,826(边数)* 256(VID 大小) * (2 + 2 + 2 + 2) = 1.80 GB 上面的例子是数据存储的大小计算过程,点的数量乘以定长的长度(这里是 256),再乘以占据的字节大小,以及边的数量乘以对应 VID 的长度,再乘以对应边 VID 占据的空间大小,算出来是 1.8 GB。由此,我们可以想到一个事情:是不是可以精简下 VID 的定长长度,设置一个合理的数值,比如说是 32,那它空间占据量就是: 594,952(点数)*32(VID 大小)* (1 + 1) + 798,826(边数)* 32(VID 大小)* (2 + 2 + 2 + 2) = 0.23 GB 修改 VID 的定长长度之后,整个空间使用量就是之前的 1/8,还是非常可观的一个磁盘容量优化。如果是更多的点和边数据量的话,缩减的磁盘空间会更客观。由此,我们有个建议:VID 的定长长度尽可能短,同理,属性类型设置亦如是。 超级节点 图数据库实践中,超级节点是一个比较常遇到的性能问题。那么,什么是超级节点(稠密点)呢?图论给出的解释是:一个点有着超级多的相邻边,相邻边就是出边(从这个点指向另外一个点)或者是入边(某个点指向这个点)。像是社交网络中 KOL、网红大V 之类的人,或是证券市场的热门股票,交通网络中的枢纽站、银行系统中的四大行、互联网中的高流量站点,或者是电商平台的爆款商品,等等都是整个关系网络中的超级节点。一旦查询中有超级节点,查询速率就会变得异常的缓慢,甚至有时候内存都耗尽了,查询结果还没跑出来。 下面就来讲讲,现阶段你要用 NebulaGraph 可以如何解决或是绕开超级节点: 要在建模环节规避掉超级节点的问题,“拆点”是可行的方式之一。如上图左侧所示,在未优化建模之前,A 通过 transfer 边关系连接到 B1、B2,如果 A 频繁的转账,势必会导致它成为一个超级节点。这时候,你可以将 A 拆分成 A1 和 A2,按照某种约定的方式,比如说转账的日期,或者是由单一客户拆分成对公客户、对私客户,从而达到拆点、避开超级节点形成的目的。不过,这里会涉及到一个 VID 变更的问题,将 A 拆分成 A1 和 A2,会导致对应的 VID 发生变化,当然你可以命名 A1 为 A0721,A2 为 A0722,加上日期数字来标识它们。 相对应拆点,还有拆/合边的方式。在两个点之间,有许多同一类型的边,比如说转账关系,这时候,可以根据业务的逻辑来进行判断,比如取最短边、最新边、最大边、最小边等,在一些不需要明细的场景里,只体现关系出来,这样就能提升查询效率。除了合并之外,拆边也是一种方式,如上图右侧所示,两个点之前有非常多的关系,它们都是交易类型,可能有一部分是发红包,有一部分是转账,这时候,你就可以按照拆点的逻辑,将边进行拆解。 此外,还有截断,NebulaGraph 有个配置参数是 max_edge_returned_per_vertex,用来应对多邻边的超级节点问题。比如我现在 max_edge_returned_per_vertex 设置成 1,000,那系统从点 A 出发,遍历 1,000 个点之后就不再遍历了,便将结果返回给系统。这里会存在一个问题,加入 A 和 B1 之间存在 1 千多条边,A 和 B2 存在 3 条边,按照这种遍历 1,000 条边之后就不再遍历的设定,可能返回结果中 A 和 B2 的关系边就不会返回了,因为这个遍历返回是随机的。 其他的话,同相关的社区用户交流,我发现在许多业务场景中,超级节点并没有太大的实际业务价值。这里就要提下“超级节点的检测”,比如:通过度中心性算法(DegreeCentrality)计算出出入度大小,这个图算法 nebula-algorithm 和 nebula-analytics 都支持。当这个算法跑完之后,得到的二维表就能告诉你哪些是超级节点,提早让用户知道哪些点会影响查询效率。 此外,假如现在你有一个已知的超级节点,且不方便处理,那查询的时候就要尽量避免逆向查询,即从这个超级节点出发,查询其他节点。 数据导入 社区用户经常遇到的还有一类问题:数据导入慢的问题。一般新的社区用户都会问:你们的导入性能如何?这时候我们一般会说:导入性能老牛逼了,而且我们是直接用 INSERT 方式导入的,速度贼快,之前遇到最快的是 600MB/s。 这时候用户一般会反问:为什么我测试出来,导入速度没有官方说的那么快。 这里就展开说说如何提升你的数据导入性能。 熟悉 NebulaGraph 的小伙伴都知道,它的底层存储是基于 RocksDB 实现的,而 RocksDB 有 wal_ttl 这么一个配置项,如果你的导入数据量非常大,对应的 wal 日志也会相对应的变大。因此,建议在进行数据导入时,将 wal_ttl 时间设短一点,以防止膨胀的 wal 日志过度地占用磁盘,可以得到及时的清理。此外,就是 Compaction 相关的配置项,主要是 max_subcompactions 和 max_background_jobs 这两个参数项,一般建议将其设置为 CPU 核数的一半。而这个一半的参数建议,主要来源于用户的反馈以及一些经验数据,不同的场景还是需要不同的配置,HDD 和 SSD 的配置也有所不同,大家可以后面看着情况进行调试。 除了配置参数之外,在做数据导入之前,建议大家执行下 SHOW HOSTS 操作,查看 leader 是否分布均匀:NebulaGraph 会将数据分为若干个 partition,每个 partition 会随机分布在节点上,理想状态自然是 partition 的 leader 是均匀地分布在各个节点的。假如 leader 分布不均的话,可以执行 BALANCE LEADER 操作,确保其均匀分布。 在工具配置方面,可能就是数据导入的重头戏了,配置你的数据导入工具参数: 配置项 concurrency,表示导入工具连接多少个 graphd(查询)节点,一般设置为导入工具 nebula-importer 所在机器的 CPU 核数; manager.batch,虽然 NebulaGraph 支持你通过 INSERT 来一个个点插入到数据库中,但是这个有些低效。因此,设立了 batch 字段用来将一批数据导入到数据库中,默认参数设置是 128,不过这里要根据你自身的数据特性来进行优化。假如你的属性值很多,那么建议将 batch 调小;反之,将 batch 值调大即可。整个 batch 的大小,建议小于 4MB; manager.readerConcurrency 是数据读取的并发数,即,从数据源读取数据的并发数。默认参数是 50,一般建议设置为 30-50 即可; manager.importerConcurrency,数据读取之后,会根据一定的规则拼接成 batch 块,这里就涉及到这个参数项。manager.importerConcurrency 指的是生成待执行的 nGQL 语句的协程数,一般来说它会设置成 manager.readerConcurrency 字段的 10 倍,默认值是 512; 软件说完了,来说下硬件方面的配置。NebulaGrpah 优先推荐使用 SSD,当然 HDD 也是可以的,不过性能相对会差点。此外,在 data_path 下多配置几块盘,每个路径配置一个盘,这个也是之前的实践经验总结出来的。而机器和机器之间,推荐使用万兆网卡。最后一点是,nebula-importer 之类的导入工具有条件的话尽量单独部署,和集群隔离开,不然的话在一台机器人会存在资源抢占的问题。 软硬件都说完了,剩下就是数据本身的问题。图数据库的定位是关系分析,同此无关的事情,例如:全文搜索(ES 擅长的场景),要看情况是否将该部分数据放入到 NebulaGraph 中。由于 NebulaGraph 进行数据导入时,不存在导入的先后顺序,即点和边一起混合导入,这样设计的好处是,数据无需做预处理,坏处是数据导入之后可能会产生悬挂边,不利于后续的查询。最后要留意起点,或终点为空的数据,或者是异常数据,这些数据在异常处理时很容易一不小心形成超级节点。 查询指南 下面来讲讲如何搞定 NebulaGraph 的查询篇。这里是一些 tips: MATCH 性能比 GO 略慢,但 MATCH 是我们优化的重点。如果没有强性能需求的话,推荐还是尽量使用 MATCH,表达能加丰富之外,它同将要出炉的 ISO GQL(图查询语言)是匹配的; 慎用函数(无法下推),在 NebulaGraph 中并没有将函数下推到 storage。因此,像 src(edge)、dst(edge)、rank(edge)、properties($$) 之类的函数,性能都不如 edge_.src、edge._dst、edge._rank、$$.tag.prop 这些下推到 storage 的表达; 遇到聚合且需要取属性的情况,先聚合再取属性,因为取属性耗时较长; MATCH 如果只是最后需要返回 count,那么对于 count 的变量最好采用 count(id(v)) 类似的形式,这样会应用到属性裁剪,减少内存消耗; 能不带路径尽量不要带路径,带路径需要进行路径构造,属性裁剪会失效,此外,还会增加很多额外的内存开销。 总的来说,减少模糊、增加确定,越早越好。 内存保护试试 Memory Tracker 在 v3.4 版本中,引入的一个大功能是:Memory Tracker,用来保护内存,防止内存占用过大导致的 OOM 问题。 预留内存:memory_tracker_untracked_reserved_memory_mb(默认 50 MB)。Memory Tracker 机制会管理通过 new/delete 申请内存,但进程除了通过此种方式申请内存外,还可能存在其他方式占用的内存;比如通过调用底层的 malloc/free 申请,这些内存通过此 flag 控制,在计算时会扣除此部分未被 track 的内存,所以这里预留了 50 MB; 内存比例:memory_tracker_limit_ratio,就是实际可用内存的比例占用多少的情况下,会限制它再申请使用内存。一般默认是 0.8,就是这个内存占用小于 0.8 的情况下,是可以随意使用内存的;当系统内存占用超过 80% 时,系统便会拒绝掉新的查询语句; 数值范围:(0,1],默认 0.8 且为开启状态。大多数的用户的 storage 和 graph 节点都存在混部情况,这时候就会建议调低 memory_tracker_limit_ratio,顺便说一句,这个参数项是支持在线调整的; 数值配置成 2,则会对其进行动态调整,这个动态分配的内存占用比例可能会不大精准; 数值配置成 3,则关闭 Memory Tracker 功能; 此外,你如果要调试 Memory Tracker 的话,可以开启 memory_tracker_detail_log 来获得调试日志,这个参数项默认是关闭的。 经测试,Memory Tracker 对性能有 1% 左右的影响,但是对于上层为平台类产品或者交互式分析类产品,强烈建议打开。为什么呢?因为上层的业务同学不大了解 NebulaGrpah 运行机制的情况下,容易将服务打满,导致内存爆炸,因此开启这个功能之后,至少能保证系统的稳定运行。 最后,如果动态申请内存时,返回报错 GRAPH_MEMORY_EXCEEDED/STORAGE_MEMORY_EXCEEDED 说明这个内存已经不够用,这条查询语句将不会执行(被杀掉)。 语句调试得用 PROFILE 在任意一条 nGQL 查询语句前面加入 PROFILE,并能得到这条语句的执行计划。 上图一条语句的整个生命周期,Planner 是执行计划(Execution Plan)生成器,它会根据 Validator 校验过、语义合法的查询语法树生成可供执行器(Executor)执行的未经优化的执行计划,而该执行计划会在之后交由 Optimizer 生成一个优化的执行计划,并最终交给 Executor 执行。执行计划由一系列节点(PlanNode)组成。而下图则是一些常见的算子,上图每一个 Plan 节点对应了一个算子: 算子 介绍 GetNeighbor 根据指定的 vid ,从存储层获取起始点和边的属性 Traverse 仅用于 MATCH 匹配 ()-[e:0..n]-() 模式,获取拓展过程中的起始点和边的属性 AppendVertices 供 MATCH 使用,同算子 Traverse 配合获取点的属性 GetEdge 获取边的属性 GetVertices 获取点的属性,FETCH PROP 或者 GO 语句中。 ScanEdge 全表扫描边,例如 MATCH ()-[e]->() RETURN e LIMIT 3 ScanVertices 全表扫描点,例如 MATCH (v) return v LIMIT 3 IndexScan MATCH 语句中找到起始点的索引查询 TagIndexPrefixScan LOOKUP 语句中前缀扫描 LOOKUP ON player where player.name == "Steve Nash" YIELD player.name TagIndexRangeScan LOOKUP 语句中范围扫描 LOOKUP ON player where player.name > "S" YIELD player.name TagIndexFullScan LOOKUP 语句中全扫描 LOOKUP ON player YIELD player.name Filter 按条件过滤,例如 WHERE 语句 Project 获取上一步算子的列 Dedup 去重 LeftJoin 合并结果 LIMIT 限制输出行数 下面这个是一个例子,我们可以结合例子讲解下。一般来说 PROFILE 会生成一个执行计划,同 EXPLAIN 生成执行计划不同,PROFILE 生成的执行计划中会有相对应的执行时间在里面,比如说下面这张图: 一般来说,我们看执行计划不只是看上下的调用关系,还需要去看里面的具体执行细节: execTime:graphd 的处理时间; totalTime:graphd 算子起到到算子退出时间; total_rpc_time:graphd 调用 storage client 发出请求到接收到请求时间; exec:storaged 的处理时间,同上面的 graphd 处理时间的 execTime; total:storage client 接收到 graphd 请求到 storage client 发送请求的时间,即 storaged 本身的处理时间加上序列化和反序列化的时间; 除了查看时间之外,我们还要查看 row 就能看到 graphd 和 storaged 的具体通信量大小。上图有 3 个 Partition,每个 Partition 返回 1 个 limit 1,总共就 3 条数据。 此外,还得查看执行计划中是否包含计算下推: 上面两条查询语句的差异,上面提到过,就是将函数改成其他调用方式,将 properties(edge).degree 改为 follow.degree,很明显地看到计算下推了。 某个功能不支持array 因为产品规划的问题,NebulaGraph 可能有些功能没法直接支持。比如用户反馈的: 当前属性仅支持基本类型 long、string,来构建索引。是否可以支持多值,比如:long[]、string[] 来构建索引? 的确目前不支持 array,有什么曲线救国的法子?这里提供一些方法,仅供参考: 把数组放到 string 里,进行查询时,将数据读取出来进行解析,虽然有点不优雅,但是能解决问题; 转化成 bitmap,将不同的类型组成 bitmap,虽然导致代码会复杂点,但可以获得比较快的过滤; 边上的 array 转换成两点之间的平行边,相当于一条边就是一个属性,可以方便地进行属性过滤,当然它会带了额外的边数量增加问题; 点上的 array 转化成自环边,弊端第 3 种方式,会产生大量自己指向自己的平行边; 把属性作为 tag,比如我现在有个商品,它在北京、上海、杭州都有仓库,这时候可以将这个货点变成一个 tag 属性,从而方便地对其进行查询。这里需要注意的是,这个方式容易产生超级节点,这里就需要注意避免超级节点的产生。 功能更强了 UDF 用户自定义函数(User-defined Function,UDF),用户可以在 nGQL 中调用函数。与从 nGQL 中调用的内置函数一样,UDF 的逻辑通常扩展或增强了 nGQL 的功能,使其具有 nGQL 没有或不擅长处理的功能。UDF 被定义后可以重复使用。 这里简述下 UDF 的使用过程: 准备编译环境 & 下载源码:https://docs.nebula-graph.com.cn/3.5.0/4.deployment-and-installation/1.resource-preparations/ 进入到 NebulaGraph 代码仓库,创建 UDF 相关源码文件。当前有两个示意文件 standard_deviation.cpp / standard_deviation.h 可以参考 编译 UDF g++ -c -I ../src/ -I ../build/third-party/install/include/ -fPIC standard_deviation.cpp -o standard_deviation.o g++ -shared -o standard_deviation.so standard_deviation.o 加载 UDF 至 graphd 服务 编辑 graphd 服务配置文件:打开 /usr/local/nebula/etc/nebula-graphd.conf 文件,添加或修改以下配置项: # UDF C++ --enable_udf=true # UDF .so --udf_path=/home/foobar/dev/nebula/udf/ 重启 graphd udo /usr/local/nebula/scripts/nebula.service restart graphd 连接到 graphd 后验证 GO 1 TO 2 STEPS FROM “player100” OVER follow YIELD properties(edge).degree AS d | yield collect($-.d) AS d | yield standard_deviation($-.d) 不过,目前 UDF 有些问题: so 包位置只支持扫描本地,也就是如果你是分布式集群的话,每个机器上都得有个包; 函数只在 graphd 层,无法下推到存储; 暂不支持 Java(性能考虑),未来版本会支持; 待解决的问题 这里罗列下未来的产品可优化点: 全文索引:v3.4 之前的全文索引功能都不太好用,约束比较多,且有些 bug。v3.4 版本做了精简和优化,更加稳定。但实际上,v3.4 及之前的全文索引功能准确讲并不是真正意义的全文索引,主要是支持前缀搜索、通配符搜索、正则表达式搜索和模糊搜索等。并不支持分词、以及查询的分数,v3.6 版本(即将发布)做了全文索引的优化,重新设计了全文索引功能(可以更好的支持 Neo4j 替换)。不过与原有的全文索引不兼容,需要重建索引; 关于悬挂边的产生:设计理念,不隐式的对数据进行变更导致(删除点的时候不隐式删除边),当然由此会带来悬挂边和孤儿点的问题,后面这块会考虑进行相关的优化; 事务的支持,大多数人对 NebulaGraph 的事务需求来源于,他认为 NebulaGraph 是一款 TP 产品,TP 产品是一定具备事务性的,这并非是业务场景的需求。当然,事务这块撇开这种某款产品必须具备的特性之外这点,一些生产链路上面,事务还是一个强需求,因此在后续的开发中也会新增事务特性。 其他问题交流 下面问题整理自本次分享的 QA 部分: Q:上文提到 VID 的设定,是越短越好。短的 VID 会带来什么后果么? 方扬:VID 理论上是越短越好,没有任何的副作用。不过它的设定是在满足你既有的业务需求,不要出现重复的 VID 情况下,尽可能的短即可; Q:截断的话,是对返回的数据量做限制,这个返回的话是有序的么? 训焘:目前数据的返回是 random,随即返回的,随机根据你的 range 来返回一些数据量。 谢谢你读完本文 (///▽///) 如果你想尝鲜图数据库 NebulaGraph,记得去 GitHub 下载、使用、(^з^)-☆ star 它 -> GitHub;和其他的 NebulaGraph 用户一起交流图数据库技术和应用技能,留下「你的名片」一起玩耍呀~

优秀的个人博客,低调大师

JeeSite V5.4.1 和 V4.7.0 发布,一个 Java 快速开发平台

V5.4.1 升级内容 新增 对象存储 增加 expires 参数,生成私有对象地址的有效期 去掉 对象存储 publicUrl 中的桶名称(阿里云、七牛云、腾讯云、MinIO) 增加 配置参数 web.xssFilterExcludeUri 方便排除一些特殊地址不过滤 优化 文件预览 禁用pdfobject,直接使用pdfjs,防止pdf中的js运行 优化 数据源 createDruidDataSource 接口,可以自定义xa数据源 优化 支持自定义 j2Cache 的二级缓存,可以将 Redis 替换为同类产品 优化 文件管理 groupType 参数,文件柜编码开头为 self 的自动过滤自己的,开头是 office 自动过滤本部门的 优化 主题风格细节、多语言国际化译文 修正 因修改右上角消息溢出,导致国际化en英文环境下的,用户下拉换行的问题 修正 tree 删除问题 #I7GCVK Cloud 新增消息模块独立微服务 Cloud 新增文件模块独立微服务 Cloud 新增扩展模块独立微服务 Cloud 升级 seata 1.6.1 Vue分离端 功能国际化语言包完善 升级方法 修改pom.xml文件中的jeesite-parent版本号为5.4.1-SNAPSHOT 如果你修改了parent、common、core项目源码,请与git上的代码进行同步 如果你是跨版本升级,请注意每一个版本的升级方法,业务上有调整的地方进行修改 对象存储 MinIO 公共访问地址中不再增加桶名,请在 publicUrl 中添加桶名 执行root/package.bat(sh)打包脚本,强制更新依赖。 V4.7.0 升级内容 升级 springboot 2.7.12、mybatis 3.5.13、jsqlparser 4.6、druid 1.2.18、commons-io 2.13 升级 poi 5.2.3、groovy 3.0.17、thumbnailator 0.4.19、twelvemonkeys 3.9.3、zxing 3.5.1 升级 UReport 的POI5.2.3、Antlr4.7.2,并增加slf4j日志、修正日期类型格式化的问题 DataGrid 防止有冻结列的时候进行全部隐藏和显示后出现重复列问题 #I6Y5SQ 升级方法 修改pom.xml文件中的jeesite-parent版本号为4.7.0-SNAPSHOT 如果你修改了parent、common、core项目源码,请与git上的代码进行同步 如果你是跨版本升级,请注意每一个版本的升级方法,业务上有调整的地方进行修改 本版本升级了众多第三方依赖包,请进行系统功能完整测试。 执行root/package.bat(sh)打包脚本,强制更新依赖。 了解更多 JeeSite 官网地址:http://jeesite.com JeeSite 在线文档:http://docs.jeesite.com JeeSite 演示地址:http://demo.jeesite.com JeeSite Vue 演示地址:http://vue.jeesite.com JeeSite 源码仓库:https://gitee.com/thinkgem/jeesite4 JeeSite Vue 前端源码:https://gitee.com/thinkgem/jeesite-vue JeeSite 跨平台手机端:https://gitee.com/thinkgem/jeesite4-uniapp JeeSite Cloud 微服务:https://gitee.com/thinkgem/jeesite4-cloud JeeSite 客户端安装程序:https://gitee.com/thinkgem/jeesite-client

优秀的个人博客,低调大师

JeeSite 平台 Spring Boot 3 体验版发布,一个 Java 快速开发平台

引言 是时候为 Spring Boot 3 做准备了,2018年2月 Spring Boot 进入 2.0 时代,距今已经 5 年了。2022 年 11 月 Spring Boot 3.0 正式发布,它将基于 Spring Framework 6.0,并且需要 Java 17 版本,同时它也将是 Jakarta EE 9 的第一个 Spring Boot 版本。如今 Spring Boot 3.1 已经发布,也带来了非常好的新特性,同时也支持了 Java 20。 为跟近技术时代,周边生态依赖库大部分也对 Spring Boot 3 做了相应支持,所以JeeSite 是时候发布Spring Boot 3 版本了。 同时 JeeSite 对 Spring Boot 2 的版本将持续更新,请大家放心使用。 技术栈 JDK 17+ Servlet 5.0+ Jakarta 6.0.0 Spring Boot 3.1.1 Apache Shiro 2.0.0 Spring Cloud 2022.0.3 Spring Cloud Alibaba 2022.0.0 Spring Framework 6.0.10 OpenAPI 3 and Springdoc 2.0 Atomiko Transactions 6.0 Spring Boot Admin 3.1 Flowable 7.0.0 等等众多依赖更新 Vue分离端 Ant Design Vue 4.0.0 功能国际化语言包完善 等等众多依赖更新 下载地址 JeeSite:https://gitee.com/thinkgem/jeesite4/tree/v5.springboot3/ JeeSite Cloud:https://gitee.com/thinkgem/jeesite4-cloud/tree/v5.springboot3.cloud/ JeeSite Vue:https://gitee.com/thinkgem/jeesite-vue/tree/next/ 我可以升级上来吗? 可以,自JeeSite 4版本开始,JeeSite团队就非常重视从旧版本升级的问题,除了向下兼容,我们也抛弃一些必要的变更。如果你对JeeSite改动不大,业务代码上升级是比较容易的,后面我们将在官方文档补充升级方法。

优秀的个人博客,低调大师

投放视频广告时,如何快速与第三方播放器兼容?

新媒体时代,广告样式越来越丰富。相较于传统的图文信息,视频类广告更具有直观性,能够让消费者在了解产品知识和功能的同时加深对产品的印象。 因此在各类网站或App上投放视频类广告是个很好的宣传方式,但广告商们如果想在网站上展示视频广告,必须确保视频广告投放协议与发布渠道的播放器兼容;如果不能兼容,广告商或者视频广告投放平台必须创建不同的响应,来与特定视频播放器兼容。为此,互联网广告署(IAB)开发了视频广告投放模板 (VAST)这种 XML 模式,允许通过视频广告服务器提供流内视频广告,并在多个网站和设备上的视频播放器中播放,不仅解决兼容性问题,还提高了视频广告投放的效率和可扩展性。 为帮助广告商们更好投放广告,华为鲸鸿动能平台提供了HUAWEI VAST SDK的集成,目前仅支持符合VAST协议的线性广告(投放形式为前、中、后贴片广告)。 开发准备 集成SDK 使用VAST协议时需要集成HUAWEI VAST SDK。同时如果您需要使用默认的播放器,请集成HUAWEI Player SDK。 配置Maven仓地址。 Android Studio的代码库配置在Gradle 插件7.0以下版本、7.0版本和7.1及以上版本有所不同。请根据您当前的Gradle 插件版本,选择对应的配置过程。 以7.0版本为例: a) 打开Android Studio项目级“build.gradle”文件。 b) 添加Maven代码库。 在“buildscript > repositories”中配置Maven仓地址。 buildscript { repositories { google() jcenter() // 配置鲸鸿动能SDK的Maven仓地址 maven {url 'https://developer.huawei.com/repo/'} } } c) 打开项目级“settings.gradle”文件,配置Maven仓地址。 dependencyResolutionManagement { ... repositories { google() jcenter() // 配置鲸鸿动能SDK的Maven仓地址 maven {url 'https://developer.huawei.com/repo/'} } } 配置网络权限 在targetSdkVersion 28及以上的手机上允许HTTP(S)网络请求,在“AndroidManifest.xml”做以下配置。 <application ... android:usesCleartextTraffic="true" > ... </application> 配置混淆脚本 您编译APK前需要配置混淆配置文件,避免混淆HUAWEI VAST SDK导致功能异常。 打开Android工程应用级根目录下的“proguard-rules.pro”混淆配置文件,加入排除HUAWEI VAST SDK的混淆配置。 -keep class com.huawei.openalliance.ad.** { *; } -keep class com.huawei.hms.ads.** { *; } 初始化SDK 初始化HUAWEI VAST SDK 集成HUAWEI VAST SDK后,在应用的DemoApplication类中,初始化HUAWEI VAST SDK。 // 当用户同意广告服务获取数据时,调用下面的方法 SdkFactory.userAcceptAdLicense(true); 开发步骤 实现VAST广告 使用默认播放器播放线性广告 播放线性广告可以使用默认的播放器,也可使用您实现的自定义播放器。 在XML文件中,初始化播放器的布局。示例代码如下: <FrameLayout android:id="@+id/fl_linear_ad" android:layout_width="match_parent" android:layout_height="250dp" android:background="@android:color/black" android:visibility="gone" android:layout_alignParentEnd="true" android:layout_alignParentStart="true" android:layout_alignParentTop="true" /> ``` 2. 生成默认的广告播放控制器,注册线性广告视图。示例代码如下: private void registerLinearAdView() { mLinearAdView = findViewById(R.id.fl_linear_ad); mProgressBar = findViewById(R.id.progress); VastAdPlayer.getInstance().registerLinearAdView(mLinearAdView, getController()); } private BaseVideoController getController() { return mAdConfig.isCustomVideoPlayer() ? new DefaultVideoController(this) : new CustomVideoController(this); } 3. 请求播放线性广告。示例代码如下: VastAdPlayer.getInstance().startLinearAd(linearAdSlot, new AdsRequestListener() { @Override public void onSuccess(View view, int responseCode) { } @Override public void onFailed(View view, int responseCode) { } @Override public void playAdReady() { mLinearAdView.setVisibility(View.VISIBLE); } @Override public void playAdFinish() { mLinearAdView.setVisibility(View.INVISIBLE); } @Override public void onBufferStart() { mProgressBar.setVisibility(View.VISIBLE); } @Override public void onBufferEnd() { mProgressBar.setVisibility(View.INVISIBLE); } }); #### 使用自定义播放器播放线性广告 1. 在XML文件中,初始化播放器的布局。示例代码如下: <FrameLayout android:id="@+id/fl_linear_ad" android:layout_width="match_parent" android:layout_height="250dp" android:background="@android:color/black" android:visibility="gone" android:layout_alignParentEnd="true" android:layout_alignParentStart="true" android:layout_alignParentTop="true" /> ``` 实现自定义的广告播放器控制器。示例代码如下: public class CustomVideoController extends BaseVideoController implements VastPlayerListener { public CustomVideoController(Context context) { this(context, null); } public CustomVideoController(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomVideoController(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(); } @Override public int getLayoutId() { return R.layout.activity_video_player; } private void initView(){ ... } } 生成自定义的广告播放器控制器,注册线性广告视图。示例代码如下: private void registerLinearAdView() { mLinearAdView = findViewById(R.id.fl_linear_ad); mProgressBar = findViewById(R.id.progress); VastAdPlayer.getInstance().registerLinearAdView(mLinearAdView, getController()); } private BaseVideoController getController() { return mAdConfig.isCustomVideoPlayer() ? new DefaultVideoController(this) : new CustomVideoController(this); } 请求播放线性广告。示例代码如下: VastAdPlayer.getInstance().startLinearAd(linearAdSlot, new AdsRequestListener() { @Override public void onSuccess(View view, int responseCode) { } @Override public void onFailed(View view, int responseCode) { } @Override public void playAdReady() { mLinearAdView.setVisibility(View.VISIBLE); } @Override public void playAdFinish() { mLinearAdView.setVisibility(View.INVISIBLE); } @Override public void onBufferStart() { mProgressBar.setVisibility(View.VISIBLE); } @Override public void onBufferEnd() { mProgressBar.setVisibility(View.INVISIBLE); } }); 播放广告荚 广告荚的播放与线性广告的播放,只在使用自定义播放器请求播放广告步骤4中有区别,请求播放广告荚的示例代码如下: VastAdPlayer.getInstance().startAdPods(linearAdSlot, new AdsRequestListener() { @Override public void onSuccess(View view, int responseCode) { } @Override public void onFailed(View view, int responseCode) { } @Override public void playAdReady() { mLinearAdView.setVisibility(View.VISIBLE); } @Override public void playAdFinish() { mLinearAdView.setVisibility(View.INVISIBLE); } @Override public void onBufferStart() { mProgressBar.setVisibility(View.VISIBLE); } @Override public void onBufferEnd() { mProgressBar.setVisibility(View.INVISIBLE); } }); 测试VAST广告 您需要使用专门的测试广告位ID来获取测试广告,以避免在测试过程中产生无效的广告点击量。测试广告位ID,仅可用于调测广告功能,不可用于广告变现,在应用正式发布前需替换为正式的广告位ID。您应在应用发布前先进入流量变现官网,点击“开始变现”,登录鲸鸿动能媒体服务平台,申请正式的广告位ID并替换测试广告位ID,具体操作详情请参见展示位创建。以下表格中提供了VAST广告的专用测试广告位ID: 下载VAST广告的示例代码并运行,可以看到如下效果图: 了解更多详情>> 访问华为开发者联盟官网 获取开发指导文档 华为移动服务开源仓库地址:GitHub、Gitee 关注我们,第一时间了解 HMS Core 最新技术资讯~

优秀的个人博客,低调大师

ShopWind V3.4.4 更新,一键采集淘宝天猫京东商品快速迁移

ShopWind 是一款开源多商户电商系统,支持商家入驻开店,可以做社区团购系统,拼团系统,搭建积分商城系统等。展示端有PC电脑端、手机H5端、可发布微信小程序、上架APP安卓端和iOS端、搭建自己的微信公众号号等。各种营销功能完备,可以作为基础源码进行二次开发,减少重复造轮子的烦恼。系统现在已经更新到V3.4.4,从淘宝、京东下来的商家支持一键采集淘宝京东等平台的产品,可以通过ShopWind系统搭建自己的电商系统平台。 官网网址:https://www.shopwind.net 开发文档:https://developer.shopwind.net API 接口文档:http://docs.shopwind.net 开发者社区:https://forum.shopwind.net PC 体验 前台体验:http://test.shopwind.net买家测试账号:buyer 密码:123456 支付密码:123456 后台体验:http://test.shopwind.net/admin平台管理员账号:admin 密码:123456 商家体验:http://test.shopwind.net/seller/login.html商家测试账号:seller 密码:123456 移动端体验(商业版) H5 端体验:https://h5.shopwind.net买家测试账号:18978189192 密码:111111 支付密码:111111 移动端体验 商品采集功能介绍 ShopWind 电商系统在 v3.4.x 版本加入了数据采集功能,支持淘宝、天猫、京东、1688、拼多多商品一键采集到平台的功能,你只需要输入商品详情页链接,就可以批量实现导入,导入的数据包括:商品标题,价格,库存,主图,规格(包括规格图),描述等字段。 一、使用采集功能前,您需要先配置数据采集组件秘钥,秘钥申请:https://www.99api.com/Login?log=5&referee=19843 二、进入商家后台 -》商品管理 -》采集商品 三、输入淘宝京东等商品详情页地址(支持批量),选择数据来源平台后提交 四、导入后如图 3 所示 页面展示 首页 / 用户中心 / 分类页 产品详情 / 购物车 / 下单页 平台后台管理 丰富的功能插件 可视化模板编辑 / DIY 页面布局 更新内容 【新增】数据采集淘宝天猫京东商品模块 【新增】商品长图和主图视频功能 【新增】详情页显示最近买家评价信息 【新增】即时提现到支付宝余额功能 【新增】查看买家评价页 【新增】商品新品标识 【优化】商品规格字段 【优化】移动端支付方式传值 【优化】商品价格货币化显示方式 【优化】在线支付订单不在额外新增财务收支记录 【修复】移动端商品详情页商品属性弹窗显示异常 【修复】商品评价统计数据有误的问题 【修复】优惠券在多个订单可重复使用的问题 【修复】详情页商品规格图显示有误等问题 【修复】退款模块一处错误

优秀的个人博客,低调大师

TLv8 IDE v2.4.3 已经发布,基于 Eclipse 的快速开发工具

TLv8 IDE v2.4.3 已经发布,此版本更新内容包括: 1、Studio透视图默认展示项目资源管理器和包资源管理器; 2、放弃自定义资源视图,直接使用项目资源管理器; 3、补充源码说明; 4、表编辑器增加编辑默认值功能; 5、优化功能树编辑器:添加layui图标属性编辑; 6、解决win10及以下版本无法运行cef的问题(报表编辑器)。 --------------------一些已知bug-------------------

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册