首页 文章 精选 留言 我的

精选列表

搜索[国产神器],共5636篇文章
优秀的个人博客,低调大师

IYqueCode V2.0 已经发布,企业微信活码神器

源雀SCRM AI开源版|V2.0 智能化能力重磅升级! 全新上线:AI智能营销助手+AI智能导航 • 全案策划与策略生成:基于 LangChain4j 接入的大模型能力,输入活动目标(如新品上市、节日大促),AI 自动生成包含活动主题、目标客群、优惠机制、执行排期的完整营销方案,告别经验主义。 • 场景感知与功能推荐:基于 LangChain4j 的自然语言理解能力,用户只需描述当前业务场景(如“我想搞一场拉新活动”),系统即可自动推荐并跳转至【员工活码】、【获客外链】等相关功能模块。 架构升级:基于 LangChain4j 的多模型支持重构 • 统一接口,多模兼容:全面重构 AI 底层架构,基于 LangChain4j 框架,实现对 OpenAI、智谱AI、通义千问、文心一言 等 20+ 主流 LLM 厂商的统一封装。 • ⚙️ 配置驱动,灵活切换:支持多种底层大模型配置,无需改动业务代码,轻松实现模型迁移或 A/B 测试,满足不同场景下的成本与效果需求。 • ️ 稳定可靠,持续进化:利用 LangChain4j 的标准化特性,提升了 AI 功能的稳定性与可维护性,为后续接入更多 AI 能力奠定坚实基础。 感谢您一直以来的支持与信任! 本次 V2.0 大版本更新,标志着源雀SCRM AI开源版正式迈入“多模型驱动、全流程智能”的新阶段。我们将继续坚持 100% 开源的策略,拥抱 LangChain4j 等前沿技术,助力企业构建更智能的私域增长引擎。 • AI 智能营销助手 助您轻松策划,高效获客; • AI 智能导航 让您操作无忧,所想即所得; • 多模型架构 赋予您极致的灵活性与选择权。 【源雀 SCRM AI开源版】 团队 如有任何疑问、建议,或希望参与开源贡献,欢迎随时通过我们的 Gitee/GitCode 社区或仓库联系我们! 祝您工作顺利,客户运营事半功倍! 源雀SCRM AI开源版 团队 2026年3月6日 详情查看:https://gitee.com/iyque/iYqueCode/releases/V2.0

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

理工科 MCP Server 神器,补足人工智能幻觉短板

作者:子釉 为什么需要 WolframAlpha 随着人工智能技术的飞速发展,大语言模型(LLM)如 GPT-4 在自然语言处理和生成方面取得了显著的成就。然而,这些模型在处理复杂的科学和数学问题时,常常出现所谓的"幻觉"现象,即生成错误或不合理的结果。这种现象不仅影响了模型的可靠性,也限制了其在教育、科研等领域的广泛应用【1】。 大语言模型在生成文本时,往往依赖于其训练数据中的模式和关联,而缺乏对现实世界的物理规律和逻辑规则的深入理解。这导致在处理科学和数学问题时,模型可能会生成看似合理但实际上错误的答案。例如,在解决物理问题时,模型可能会忽略单位转换、物理定律的应用,甚至在数学计算中出现低级错误。这种幻觉现象不仅影响了问题解决的正确性,也使得模型在面对复杂问题时显得力不从心。 WolframAlpha 是一个强大的计算知识引擎,能够处理各种科学、数学和技术问题。它基于广泛的数学和科学知识库,能够进行精确的计算、符号操作和公式推导。与大语言模型相比,WolframAlpha 在处理复杂的数学公式、物理定律和科学概念时具有显著的优势。它能够准确地执行计算任务,避免因模型自身的局限性而导致的错误【2】。 Higress MCP 市场近期上线了 WolframAlpha LLM API,可以通过 WolframAlpha MCP 形式调用,支持如 Lobechat、Cline、Cherry Studio、DeepChat 等多种调用形式。目前提供单用户 10 次/月的免费试用限额,欢迎大家试用! 配置流程 获取 WolframAlpha MCP 工具 进入 Higress MCP 市场主页:https://mcp.higress.ai/,点击 WolframAlpha 在 WolframAlpha 中,使用自定义或试用的 API-KEY 来生成 URL 选择保存好生成的 Streamable HTTP URL,用于后续的配置。 在 Lobechat 中配置 Higress MCP 安装 Lobechat Lobechat 提供在线版本及开源版本,目前最新版均支持 MCP 工具的使用。 对于开源版本,需要用户在本地具有 docker 环境,执行以下命令,一键即可安装: docker run -d -p 3210:3210 \ --name lobe-chat \ lobehub/lobe-chat:1.82.4 在设置界面,可以配置不同模型供应商对应的 API-KEY。 配置 Higress MCP 在 lobechat 的插件商店中,选择自定义插件: 选择 MCP 插件,并指定 Streamable HTTP 模式,填写上一步中获取的 URL,然后安装插件。 在对话界面,启用 WolframAlpha 插件。 测试案例 对于一些基本的推理、常识性的知识,引入 WolframAlpha 能够很好解决推理过程中的幻觉问题。同时,对于计算、绘图等基本数学功能,WolframAlpha 也能获得较好的表现。 数学推理 对于推理较难的非常识性数学知识,比如以下问题: 2^136279841-1 是素数吗? 当没有调用 WolframAlpha 工具时,由于问题较为复杂,基于模型推理过程无法给出准确答案。 日常计算 对于一些日常数学问题,也可以借助 WolframAlpha 来进行计算: 假设当前有 10,000 元,未来 10 年的通货膨胀率以 0.5% 的连续复利增长,那么这笔钱的未来购买力相当于今天的多少钱? 图像绘制 除此之外,WolframAlpha 还支持一些基本的图像绘制及生成的功能: 获取 10000 以内素数的分布,使用合适的绘图方式表示出每千位素数数量的变化 此外,WolframAlpha LLM API 在调用出错的情况下,会根据返回结果来提示更优的提问方式;Agent 在接收到类似的指令返回后,也会优化提问方式再次调用工具,从而获取正确的结果。 展望 通过结合 WolframAlpha 工具,可以有效解决大语言模型在处理科学和数学问题时的幻觉现象。WolframAlpha 的精确计算能力和广泛的知识库能够弥补大语言模型的不足,提升其在复杂问题解决中的准确性和可靠性。未来,随着技术的进一步发展,这种结合方式有望在更多领域得到应用,推动人工智能技术的更广泛发展。 Higress MCP 市场目前已上架 40+ 款 MCP 服务,包含如搜索、沙盒工具、基础信息查询等多个服务,欢迎使用接入! 【1】https://arxiv.org/html/2308.05713v4 【2】https://products.wolframalpha.com/api

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

java ORM 神器 mybatis-mp,新框架、新设计、更好用!!!

官网 :https://mybatis-mp.cn 为什么推荐 mybatis-mp ?: mybatis-mp 是一款超级强大的 ORM 框架 1:可多表 join(不再只能单表了) 2:良好 resultMap 结果映射能力(不怕映射错误了) 3:良好的扩展能力:orm+sql 模板 (让 ORM 框架不再死板,扩展性极强) 4:就算 xml 也不用再增加什么插件了,自带 xml 分页 5:强大的各种数据库适配,可在一套代码中 实现多个数据库适配;真正的 ORM hibernate 都做不到 6:极简的 api 设计,让开发者 不再迷糊 7:还很多很多独特优良的设计,只有前往体验,才能感受了;不相信可以试试!!! 1.单表 +@Fetch 注解 + fetchFilter 方法 @Data @ResultEntity(SysUser.class) public class SysUserVo { private Integer id; private String userName; private String password; private Integer roleId; private LocalDateTime create_time; @Fetch(source = SysUser.class, property = "roleId", target = SysRole.class, targetProperty = "id") private List<SysRoleVo> sysRoles; } List<SysUserVO> list = QueryChain.of(sysUserMapper) .from(SysUser.class) .fetchFilter(SysUserVO::getRoles,where->where.eq(SysRole::getStatus,1)) .returnType(SysUserVO.class) .list(); fetchFilter 方法是对 @Fetch 注解的增强,没有特殊要求一般,可忽略 2. 单表查询 SysUser sysUser = QueryChain.of(sysUserMapper) .eq(SysUser::getId, 1) .eq(SysUser::getUserName,'admin') .get(); 3.VO 映射 @Data @ResultEntity(SysUser.class) public class SysUserVo { private Integer id; private String userName; //字段名字不一样时 @ResultEntityField(property = "password") private String pwd; } SysUserVO sysUserVO = QueryChain.of(sysUserMapper) .eq(SysUser::getId, 1) .eq(SysUser::getUserName,'admin') .returnType(SysUserVO.class) .list(); 4. join 查询 @Data @ResultEntity(SysUser.class) public class SysUserVo { private Integer id; private String userName; //字段名字不一样时 @ResultEntityField(property = "password") private String pwd; //映射一个对象 1对1 @NestedResultEntity(target = SysRole.class) prviate SysRole sysRole; //映射多个对象 1对多 @NestedResultEntity(target = SysRole.class) prviate List<SysRole> sysRoles; } List<SysUserRoleVO> list = QueryChain.of(sysUserMapper) .from(SysUser.class) .join(SysUser.class, SysRole.class) .returnType(SysUserRoleVO.class) .list(); 还有很多很多超级方便有趣的写法,欢迎大家来使用https://mybatis-mp.cn 例如: 1 . 多表 join A 内嵌 B B 内嵌 C 都可以 2 . 不使用 join 使用 @Fetch 注解 + fetchFilter 方法实现 将 A JOIN B 变成 query A + query B 3 . 使用 @Paging 注解 实现你的 xml 自动分页 4 . 使用 SQL 模板,让你 ORM 更简单更容易扩展,再也不怕被框架限制了

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

开源神器 LSPosed 宣布停更,作者称遭受大量恶意攻击

Android 平台 Xposed Framework 的替代实现版本 LSPosed 宣布停止维护,目前 LSPosed 的 GitHub 仓库已经设置为只读状态。 LSPosed 是一个 Magisk模块,尝试提供一个 ART hooking 框架,该框架提供与 OG Xposed 一致的 API,其利用 LSPlant hooking 框架。 https://github.com/LSPosed/LSPosed 对于停止开发的原因,开发者在其订阅频道中表示停止维护的主要原因是出现许多谣言、诽谤、种族主义和咒骂等。同时开发者也受到 Magisk 社区的一些咒骂,因此开发者决定休息,停止开发和维护。

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

DolphinDB丨金融高频因子流批一体计算神器

前言 量化金融的研究和实盘中,越来越多的机构需要根据高频的行情数据(L1/L2以及逐笔委托数据)来计算量价因子。这些因子通常是有状态的:不仅与当前的多个指标有关,而且与多个指标的历史状态相关。以国内的股票市场为例,每3秒收到一个快照,每个股票每天得到4800个快照,计算因子时可能会用到之前若干个快照的数据,甚至之前若干天的数据。若研发环境系统(例如Python)与生产环境系统(例如C++)不同,要维护两套代码,对用户是非常沉重的负担。 今天的推文为大家介绍如何使用DolphinDB发布的响应式状态引擎(Reactive State Engine)高效开发与计算带有状态的高频因子,实现流批统一计算。状态引擎接受在历史数据批量处理(研发阶段)中编写的表达式或函数作为输入,避免了在生产环境中重写代码的高额成本,以及维护研发和生产两套代码的负担。状态引擎确保流式计算的结果与批量计算完全一致,只要在历史数据的批量计算中验证正确,即可保证流数据的实时计算正确,这极大降低了实时计算调试的成本。 1、金融高频因子计算 我们通过一个具体的例子来引入金融高频因子计算的问题。下面这个因子表达式是用DolphinDB的脚本语言写的。它使用了自定义函数sum_diff和内置函数ema (exponential moving average)。sum_diff是一个无状态函数,ema是一个有状态的函数,依赖历史数据。更为棘手的是,如下面的计算分解图所示,计算需要使用ema函数的多重嵌套。 defsum_diff(x,y){ return(x-y)/(x+y)}ema(1000*sum_diff(ema(price,20),ema(price,40)),10)-ema(1000*sum_diff(ema(price,20),ema(price,40)),20) 面对此类场景,我们需要解决以下几个问题: 投研阶段能否使用历史数据快速为每只股票计算100~1000个类似的因子? 实盘阶段能否在每个行情tick数据到来时为每只股票计算100~1000个类似的因子? 批处理和流计算的代码实现是否高效?批和流能否统一代码?正确性校验是否便捷? 2、现有解决方案的优缺点 python pandas/numpy目前是研究阶段最常用的高频因子解决方案。pandas对面板数据处理有非常成熟的解决方案,而且内置了大部分高频因子计算需要用到的算子,可以快速实现这些高频因子。但性能是pandas实现的一个短板,尤其是算子需要用自定义函数实现的时候,速度较慢。一个解决办法是通过启动多个python进程来并行计算。python pandas的实现是针对历史数据的,面对生产环境中的流式数据,如果不修改代码,只能采用类似apache spark的处理方法,把数据缓存起来,划分成一个个数据窗口来计算。因此,性能的问题在生产环境中会更突出。 为解决上述方案在生产环境中的性能问题,很多机构会用C++重新实现研究(历史数据)代码。这样做,需要维护两套代码,开发成本(时间和人力)会极大增加。此外,还要耗费大量精力确保两套系统的结果完全一致。 类似Flink批流统一的解决方案应运而生。Flink支持SQL和窗口函数,高频因子用到的常见算子在Flink中已经内置实现。因此,简单的因子用Flink实现会非常高效,运行性能也会非常好。但Flink最大的问题是无法实现复杂的高频因子计算。如前一章中提到的例子,需要多个窗口函数的嵌套,无法直接用Flink实现。这也正是DolphinDB开发响应式状态引擎的动机所在。 3、响应式状态引擎(Reactive State Engine) 响应式状态引擎实际上是一个计算黑盒,在历史数据上已经验证的DolphinDB因子代码(表达式或函数)以及实时行情数据作为输入,输出实时因子值。由于在静态的历史数据集上开发和验证高频因子远比在流数据上开发更为简单,响应式状态引擎显著降低了流式高频因子的开发成本和难度。 defsum_diff(x,y){ return(x-y)/(x+y)}factor1=<ema(1000*sum_diff(ema(price,20),ema(price,40)),10)-ema(1000*sum_diff(ema(price,20),ema(price,40)),20)>sharestreamTable(1:0,`sym`price,[STRING,DOUBLE])astickStreamresult=table(1000:0,`sym`factor1,[STRING,DOUBLE])rse=createReactiveStateEngine(name="reactiveDemo",metrics=factor1,dummyTable=tickStream,outputTable=result,keyColumn="sym")subscribeTable(tableName=`tickStream,actionName="factors",handler=tableInsert{rse}) 以上代码在DolphinDB中实现前述因子的流式计算。factor1是前述因子在历史数据上的实现,不做任何改变,直接传递给响应式状态引擎rse,即可实现流式计算。通过订阅函数subscribeTable,我们将流数据表tickStream与状态引擎rse进行关联。任何实时数据的注入,都将触发状态引擎的计算,输出因子值到结果表result。以下代码产生100条随机数据,并注入到流数据表。结果与通过SQL语句计算的结果完全相同。 data=table(take("000001.SH",100)assym,rand(10.0,100)asprice)tickStream.append!(data)factor1Hist=selectsym,ema(1000*sum_diff(ema(price,20),ema(price,40)),10)-ema(1000*sum_diff(ema(price,20),ema(price,40)),20)asfactor1fromdatacontextbysymasserteach(eqObj,result.values(),factor1Hist.values()) 3.1 工作原理 如图1所示,一个有状态的高频因子计算过程实际上可以分解成有一个有向无环图(DAG)。图中的节点有3种: 1、数据源,如price。 2、有状态的算子,如a, b, d, e。 3、无状态的算子,如c和result。 从数据源节点开始,按照既定的路径,层层推进,得到最后的因子输出。这非常类似excel中的单元格链式计算。当一个单元格的数据发生变化时,相关联的单元格依次发生变化。响应式状态引擎的名称也是从这一点引申出来的。 无状态的算子比较简单,使用DolphinDB已有的脚本引擎,就可以表示和计算。因此,问题转化为两点: 1、如何解析得到一个优化的DAG。 2、如何优化每个有状态的算子的计算。 3.2 解析和优化 DolphinDB的脚本语言是支持向量化和函数化的多范式编程语言。通过函数的调用关系,不难得到计算步骤的DAG。在解析的时候,因为输入消息的schema是已知的,我们可以快速推断出每一个节点的输入数据类型和输出数据类型。输入参数类型确定,函数名称确定,每个状态算子的具体实例就可以创建出来。 每一个算子(有状态和无状态)在DolphinDB中都可以转化为一个唯一的字符串序列。据此,我们可以删除重复的算子,提高计算效率。 3.3 内置的状态函数 状态算子计算时需要用到历史状态。因此,如果每一次触发计算,都必须准备历史数据,全量计算,不但耗费内存,而且耗费CPU时间。状态函数的优化,也就是增量方式的流式实现非常关键。下列状态函在DolphinDB的响应式状态引擎均得到了优化实现。目前,状态引擎不允许使用未经优化的状态函数。 累计窗口函数:cumavg, cumsum, cumprod, cumcount, cummin, cummax, cumvar, cumvarp, cumstd, cumstdp, cumcorr, cumcovar, cumbeta, cumwsum, cumwavg 滑动窗口函数:ema, mavg, msum, mcount, mprod, mvar, mvarp, mstd, mstdp, mskew, mkurtosis, mmin, mmax, mimin, mimax, mmed, mpercentile, mrank, mcorr, mcovar, mbeta, mwsum, mwavg, mslr 序列相关函数:deltas, ratios, ffill, move, prev, iterate, ewmMean, ewmVar, ewmStd, ewmCovar, ewmCorr 上述函数除了mslr返回两个值以外,其余函数均只有一个返回值。在后续的版本中,DolphinDB将允许用户用插件来开发自己的状态函数,注册后即可在状态引擎中使用。 3.4 自定义状态函数 响应式状态引擎中可使用自定义状态函数。需要注意以下几点: 函数定义前,使用 @state 表示函数是自定义的状态函数。 自定义状态函数中只能使用赋值语句和return语句。return语句必须是最后一个语句,可返回多个值。 使用iif函数表示if...else的逻辑。 如果仅允许使用一个表达式来表示一个因子,会带来很多局限性。首先,在某些情况下,仅使用表达式,无法实现一个完整的因子。下面的例子返回线性回归的alpha,beta和residual。 @statedefslr(y,x){ a,b=mslr(y,x,12) residual=mavg(y,12)-b*mavg(x,12)-a returna,b,residual} 其次,很多因子可能会使用共同的中间结果,定义多个因子时,代码会更简洁。自定义函数可以同时返回多个结果。下面的函数multiFactors定义了5个因子。 @statedefmultiFactors(lowPrice,highPrice,volumeTrade,closePrice,buy_active,sell_active,tradePrice,askPrice1,bidPrice1,askPrice10,agg_vol,agg_amt){ a=ema(askPrice10,30) term0=ema((lowPrice-a)/(ema(highPrice,30)-a),50) term1=mrank((highPrice-a)/(ema(highPrice,5)-a),true,15) term2=mcorr(askPrice10,volumeTrade,10)*mrank(mstd(closePrice,20,20),true,10) buy_vol_ma=mavg(buy_active,6) sell_vol_ma=mavg(sell_active,6) zero_free_vol=iif(agg_vol==0,1,agg_vol) stl_prc=ffill(agg_amt\zero_free_vol\20).nullFill(tradePrice) buy_prop=stl_prc spd=askPrice1-bidPrice1 spd_ma=round(mavg(iif(spd<0,0,spd),6),5) term3=buy_prop*spd_ma term4=iif(spd_ma==0,0,buy_prop/spd_ma) returnterm0,term1,term2,term3,term4} 最后,某些表达式冗长,缺乏可读性。第一节中的因子表达式改为下面的自定义状态函数factor1后,计算逻辑简洁明了。 @statedeffactor1(price){ a=ema(price,20) b=ema(price,40) c=1000*sum_diff(a,b) returnema(c,10)-ema(c,20)} 3.5 输出结果过滤 状态引擎会对输入的每一条消息做出计算响应,产生一条记录作为结果,计算的结果在默认情况下都会输出到结果表,也就是说输入n个消息,输出n条记录。如果希望仅输出一部分结果,可以启用过滤条件,只有满足条件的结果才会输出。 下面的例子检查股票价格是否有变化,只有价格变化的记录才会输出。 sharestreamTable(1:0,`sym`price,[STRING,DOUBLE])astickStreamresult=table(1000:0,`sym`price,[STRING,DOUBLE])rse=createReactiveStateEngine(name="reactiveFilter",metrics=[<price>],dummyTable=tickStream,outputTable=result,keyColumn="sym",filter=<prev(price)!=price>)subscribeTable(tableName=`tickStream,actionName="filter",handler=tableInsert{rse}) 3.6 快照机制 为了满足生产环境业务持续性的需要,DolphinDB内置的流式计算引擎包括响应式状态引擎均支持快照(snapshot)输出。 响应式状态引擎的快照包括已处理的最后一条消息的ID以及引擎当前的状态。当系统出现异常,重新初始化状态引擎时,可恢复到最后一个快照的状态,并且从已处理的消息的下一条开始订阅。 defsum_diff(x,y){ return(x-y)/(x+y)}factor1=<ema(1000*sum_diff(ema(price,20),ema(price,40)),10)-ema(1000*sum_diff(ema(price,20),ema(price,40)),20)>sharestreamTable(1:0,`sym`price,[STRING,DOUBLE])astickStreamresult=table(1000:0,`sym`factor1,[STRING,DOUBLE])rse=createReactiveStateEngine(name="reactiveDemo",metrics=factor1,dummyTable=tickStream,outputTable=result,keyColumn="sym",snapshotDir="/home/data/snapshot",snapshotIntervalInMsgCount=400000)msgId=getSnapshotMsgId(rse)if(msgId>=0)msgId+=1subscribeTable(tableName=`tickStream,actionName="factors",offset=msgId,handler=appendMsg{rse},handlerNeedMsgId=true) 响应式状态引擎要启用快照机制,创建时需要指定两个额外的参数snapshotDir和snapshotIntervalInMsgCount。snapshotDir用于指定存储快照的目录。snapshotIntervalInMsgCount指定处理多少条消息后产生一个快照。引擎初始化时,系统会检查快照目录下是否存在一个以引擎名称命名,后缀为snapshot的文件。以上面的代码为例,如果存在文件/home/data/snapshot/reactiveDemo.snapshot,加载这个快照。函数getSnapshotMsgId可以获取最近一个快照对应的msgId。如果不存在快照,返回-1。 状态引擎要启用快照机制,调用subscribeTable函数也需相应的修改: 首先必须指定消息的offset。 其次,handler必须使用appendMsg函数。appendMsg函数接受两个参数,msgBody和msgId。 再次,参数handlerNeedMsgId必须指定为true。 3.7 并行处理 当需要处理大量消息时,可在DolphinDB消息订阅函数subscribeTable中指定可选参数filter与hash,让多个订阅客户端并行处理消息。 参数filter用于指定消息过滤逻辑。目前支持三种过滤方式,分别为值过滤,范围过滤和哈希过滤。 参数hash可以指定一个哈希值,确定这个订阅由哪个线程来执行。例如,配置参数subExecutors为4,用户指定了哈希值5,那么该订阅的计算任务将由第二个线程来执行。 下面是响应式状态引擎并行计算因子的例子。假设配置参数subExecutors=4,创建4个状态引擎,每个状态引擎根据流表的股票代码的哈希值来订阅不同股票的数据,并且指定不同的订阅线程来处理,最终将结果输出到同一个输出表中。 defsum_diff(x,y){ return(x-y)/(x+y)}factor1=<ema(1000*sum_diff(ema(price,20),ema(price,40)),10)-ema(1000*sum_diff(ema(price,20),ema(price,40)),20)>sharestreamTable(1:0,`sym`price,[STRING,DOUBLE])astickStreamsetStreamTableFilterColumn(tickStream,`sym)sharestreamTable(1000:0,`sym`factor1,[STRING,DOUBLE])asresultStreamfor(iin0..3){ rse=createReactiveStateEngine(name="reactiveDemo"+string(i),metrics=factor1,dummyTable=tickStream,outputTable=resultStream,keyColumn="sym") subscribeTable(tableName=`tickStream,actionName="sub"+string(i),handler=tableInsert{rse},msgAsTable=true,hash=i,filter=(4,i))}n=2000000tmp=table(take("A"+string(1..4000),n)assym,rand(10.0,n)asprice)tickStream.append!(tmp) 需要注意的是,如果多个状态引擎是同一个输出表,该输出表必须是一个共享表。没有共享的表不是线程安全的,并行写入可能会导致系统崩溃。 4、流批统一解决方案 金融高频因子的流批统一处理在DolphinDB中有两种实现方法。 第一种方法:使用函数或表达式实现金融高频因子,代入不同的计算引擎进行历史数据或流数据的计算。代入SQL引擎,可以实现对历史数据的计算;代入响应式状态引擎,可以实现对流数据的计算。这在第3章的序言部分已经举例说明。在这种模式下用DolphinDB脚本语言表示的表达式或函数实际上是对因子语义的一种描述,而不是具体的实现。因子计算的具体实现交由相应的计算引擎来完成,从而实现不同场景下的最佳性能。 第二种方法:历史数据通过回放,转变成流数据,然后使用流数据计算引擎来完成计算。我们仍然以教程开始部分的因子为例,唯一的区别是流数据表tickStream的数据源来自于历史数据库的replay。使用这种方法计算历史数据的因子值,效率会略逊与基于SQL的批量计算。 defsum_diff(x,y){ return(x-y)/(x+y)}factor1=<ema(1000*sum_diff(ema(price,20),ema(price,40)),10)-ema(1000*sum_diff(ema(price,20),ema(price,40)),20)>sharestreamTable(1:0,`sym`date`time`price,[STRING,DATE,TIME,DOUBLE])astickStreamresult=table(1000:0,`sym`factor1,[STRING,DOUBLE])rse=createReactiveStateEngine(name="reactiveDemo",metrics=factor1,dummyTable=tickStream,outputTable=result,keyColumn="sym")subscribeTable(tableName=`tickStream,actionName="factors",handler=tableInsert{rse})//从历史数据库dfs://TAQ的trades表中加载一天的数据,回放到流表tickStream中inputDS=replayDS(<selectsym,date,time,pricefromloadTable("dfs://TAQ","trades")wheredate=2021.03.08>,`date,`time,08:00:00.000+(1..10)*3600000)replay(inputDS,tickStream,`date,`time,1000,true,2) 5、性能测试 我们测试了响应式状态引擎计算因子的性能。测试使用模拟数据,并使用warmupStreamEngine函数模拟状态引擎已经处理部分数据的情况。测试共包括20个不同复杂度度的因子,其中两个自定义状态函数分别返回3个和5个因子。为方便测试,计算仅使用单线程处理。 @statedefslr(y,x){ a,b=mslr(y,x,12) residual=mavg(y,12)-b*mavg(x,12)-a returna,b,residual}@statedefmultiFactors(lowPrice,highPrice,volumeTrade,closePrice,buy_active,sell_active,tradePrice,askPrice1,bidPrice1,askPrice10,agg_vol,agg_amt){ a=ema(askPrice10,30) term0=ema((lowPrice-a)/(ema(highPrice,30)-a),50) term1=mrank((highPrice-a)/(ema(highPrice,5)-a),true,15) term2=mcorr(askPrice10,volumeTrade,10)*mrank(mstd(closePrice,20,20),true,10) buy_vol_ma=mavg(buy_active,6) sell_vol_ma=mavg(sell_active,6) zero_free_vol=iif(agg_vol==0,1,agg_vol) stl_prc=ffill(agg_amt\zero_free_vol\20).nullFill(tradePrice) buy_prop=stl_prc spd=askPrice1-bidPrice1 spd_ma=round(mavg(iif(spd<0,0,spd),6),5) term3=buy_prop*spd_ma term4=iif(spd_ma==0,0,buy_prop/spd_ma) returnterm0,term1,term2,term3,term4}metrics=array(ANY,14)metrics[0]=<ema(1000*sum_diff(ema(close,20),ema(close,40)),10)-ema(1000*sum_diff(ema(close,20),ema(close,40)),20)>metrics[1]=<mslr(high,volume,8)[1]>metrics[2]=<mcorr(low,high,11)>metrics[3]=<mstdp(low,15)>metrics[4]=<mbeta(high,value,63)>metrics[5]=<mcovar(low,value,71)>metrics[6]=<(close/mavg(close,1..6)-1)*100>metrics[7]=<mmin(high,15)>metrics[8]=<mavg(((high+low)/2+(mavg(high,2)+mavg(low,2))/2)*(high-low)/volume,7,2)>metrics[9]=<mslr(mavg(close,14),volume,63)[1]>metrics[10]=<mcorr(mavg(open,25),volume,71)>metrics[11]=<mbeta(high,mstdp(close,8),77)>metrics[12]=<slr(close,volume)>metrics[13]=<multiFactors(low,high,volume,close,numTrade,numTrade,close,value,close,open,volume,numTrade)>dummy=streamTable(10000:0,`symbol`market`date`time`quote_type`preclose`open`high`low`close`numTrade`volume`value`position`recvtime,[SYMBOL,SHORT,DATE,TIME,SHORT,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,LONG,DOUBLE,LONG,TIMESTAMP])defprepareData(tickNum,batch){ total=tickNum*batch data=table(total:total,`symbol`market`date`time`quote_type`preclose`open`high`low`close`numTrade`volume`value`position`recvtime,[SYMBOL,SHORT,DATE,TIME,SHORT,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,LONG,DOUBLE,LONG,TIMESTAMP]) data[`market]=rand(10,total) data[`date]=take(date(now()),total) data[`time]=take(time(now()),total) data[`symbol]=take("A"+string(1..tickNum),total) data[`open]=rand(100.0,total) data[`high]=rand(100.0,total) data[`low]=rand(100.0,total) data[`close]=rand(100.0,total) data[`numTrade]=rand(100,total) data[`volume]=rand(100,total) data[`value]=rand(100.0,total) data[`recvtime]=take(now(),total) returndata}dropStreamEngine("demo1")dropStreamEngine("demo2")dropStreamEngine("demo3")dropStreamEngine("demo4")//4000个股票,20个因子hisData=prepareData(4000,100)realData=prepareData(4000,1)colNames=["symbol"].append!("factor"+string(0..19))colTypes=[SYMBOL].append!(take(DOUBLE,20))resultTable=streamTable(10000:0,colNames,colTypes)engine1=createReactiveStateEngine(name="demo1",metrics=metrics,dummyTable=dummy,outputTable=resultTable,keyColumn="symbol")warmupStreamEngine(engine1,hisData)timer(10)engine1.append!(realData)dropAggregator("demo1")//1个股票,20个因子hisData=prepareData(1,100)realData=prepareData(1,1)colNames=["symbol"].append!("factor"+string(0..19))colTypes=[SYMBOL].append!(take(DOUBLE,20))resultTable=streamTable(10000:0,colNames,colTypes)engine2=createReactiveStateEngine(name="demo2",metrics=metrics,dummyTable=dummy,outputTable=resultTable,keyColumn="symbol")warmupStreamEngine(engine2,hisData)timer(10)engine2.append!(realData)dropAggregator("demo2")//4000个股票,1个因子hisData=prepareData(4000,100)realData=prepareData(4000,1)metrics3=metrics[0]colNames=["symbol","factor0"]colTypes=[SYMBOL,DOUBLE]resultTable=streamTable(10000:0,colNames,colTypes)engine3=createReactiveStateEngine(name="demo3",metrics=metrics3,dummyTable=dummy,outputTable=resultTable,keyColumn="symbol")warmupStreamEngine(engine3,hisData)timer(10)engine3.append!(realData)//200个股票,20个因子hisData=prepareData(200,100)realData=prepareData(200,1)colNames=["symbol"].append!("factor"+string(0..19))colTypes=[SYMBOL].append!(take(DOUBLE,20))resultTable=streamTable(10000:0,colNames,colTypes)engine4=createReactiveStateEngine(name="demo4",metrics=metrics,dummyTable=dummy,outputTable=resultTable,keyColumn="symbol")warmupStreamEngine(engine4,hisData)timer(10)engine4.append!(realData) 我们统计了10次的总耗时,取平均值作为单次的耗时。测试使用的服务器CPU为Intel(R) Xeon(R) Silver 4216 CPU @ 2.10GHz。单线程情况下,测试结果如下: 6、多个引擎的流水线处理 DolphinDB内置的流计算引擎包括响应式状态引擎,时间序列聚合引擎,横截面引擎和异常检测引擎。这些引擎均实现了数据表(table)的接口,因此多个引擎流水线处理变得异常简单,只要将后一个引擎作为前一个引擎的输出即可。引入流水线处理,可以解决更为复杂的因子计算问题。譬如,因子计算经常需要使用面板数据,完成时间序列和横截面两个维度的计算,只要把响应式状态引擎和横截面两个引擎串联处理即可完成。 下面的例子是World Quant 101个Alpha因子中的1号因子公式的流数据实现。rank函数是一个横截面操作。rank的参数部分用响应式状态引擎实现。rank函数本身用横截面引擎实现。横截面引擎作为状态引擎的输出。 #Alpha001公式:rank(Ts_ArgMax(SignedPower((returns<0?stddev(returns,20):close),2),5))-0.5//创建横截面引擎,计算每个股票的rankdummy=table(1:0,`sym`time`maxIndex,[SYMBOL,TIMESTAMP,INDEX])resultTable=streamTable(10000:0,`sym`time`factor1,[SYMBOL,TIMESTAMP,DOUBLE])ccsRank=createCrossSectionalAggregator(name="alpha1CCS",metrics=[<time>,<rank(maxIndex)\count(maxIndex)-0.5>],dummyTable=dummy,outputTable=resultTable,keyColumn=`sym,triggeringPattern='keyCount',triggeringInterval=3000,timeColumn=`time)@statedefwqAlpha1TS(close){ ret=ratios(close)-1 v=iif(ret<0,mstd(ret,20),close) returnmimax(signum(v)*v*v,5)}//创建响应式状态引擎,输出到前面的横截面引擎ccsRankinput=table(1:0,`sym`time`close,[SYMBOL,TIMESTAMP,DOUBLE])rse=createReactiveStateEngine(name="alpha1",metrics=<wqAlpha1TS(close)>,dummyTable=input,outputTable=ccsRank,keyColumn="sym") 在上面这个例子中,我们还是需要人工来区分哪一部分是横截面操作,哪一部分是时间序列操作。在后续的版本中,DolphinDB将以行函数(rowRank,rowSum等)表示横截面操作的语义,其它向量函数表示时间序列操作,从而系统能够自动识别一个因子中的横截面操作和时间序列操作,进一步自动构建引擎流水线。 流水线处理和多个流表的级联处理有很大的区别。两者可以完成相同的任务,但是效率上有很大的区别。后者涉及多个流数据表与多次订阅。前者实际上只有一次订阅,所有的计算均在一个线程中依次顺序完成,因而有更好的性能。 7、展望 响应式状态引擎内置了大量常用的状态算子,支持自定义状态函数,也可与其他流式计算引擎以流水线的方式任意组合,方便开发人员快速实现复杂的金融高频因子。 内置的状态算子全部使用C++开发实现,算法上经过了大量的优化,以增量方式实现状态算子的流式计算,因而在单个线程上的计算达到了非常好的性能。对于规模较大的任务,可以通过订阅过滤的方式,拆分成多个子订阅,由多个节点以及每个节点的多个CPU并行完成订阅计算。后续的版本将完善计算子作业的创建、管理和监控功能,从手动转变为自动。

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

开局一张图,学一学项目管理神器Maven!

Maven强大的Java工程构建工具,做Java开发时少了跟Maven打交道,之前在知乎上看到有人提问:“学Java开发需不需要学习Maven?”,个人认为是必需要学的,这和工欲善其事必先利其器是一个道理,开发软件也要先把工具学好才能事半功倍啊。所以最近花了一点时间,将Maven的基础知识整理成一张脑力: 这篇文件主要简单介绍一些Maven中的概念,文章大致内容如下: 安装maven 配置maven maven的命令语法 maven的构建征集周期其他的一些高级特性如依赖、插件、settings部分留待之后再整理成。 安装Maven 安装Maven的步骤非常简单、快速,安装之前先确认JAVA_HOME环境变量是否指向JDK主目录可以使用echo命令输出JAVA_HOME目录: echo $JAVA_HOME 如果输出为空说明JAVA_HOME未设置或指向不正确,可以使用export导出JAVA_HOME变量: export JAVA_HOME=/path/to/java_home/ 要使JAVA_HOME变量开机生效,可以将JAVA_HOME=/path/to/java_home/放入.profile或.bash_profile视机器环境而定。 设置好JAVA_HOME环境变量后,就可以安装Maven了: 下载 apache-maven-3.6.3-bin.zip 使用unzip命令解压apache-maven-3.6.3-bin.zip压缩文件 添加一个MAVEN_HOME环境变量,指向解压后的apache-maven-3.6.3-bin目录 将MAVEN_HOME/bin添加PATH环境变量中 下载 Maven wget http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.zip 解压 Maven unzip apache-maven-3.6.3-bin.zip 设置MAVEN_HOME环境变量 cd apache-maven-3.6.3 pwd #查看当前目录 export MAVEN_HOME=/Users/yjwfn/bin/apache-maven-3.6.3 设置PATH环境变量 export PATH=$PATH:$MAVEN_HOME/bin 以上配置只是当前shell终端生效,要每次开机自动设置需要将以上命令放入~/.bash_profile或~/.profile中,本文使用~/.bash_profile(不同的机器名称会不一样,Linux一般叫~/.profile): vi ~/.bash_profile 将以下命令复制到文件中: export MAVEN_HOME=/Users/yjwfn/bin/apache-maven-3.6.3 export PATH=$PATH:$MAVEN_HOME/bin 验证安装 使用which mvn命令验证安装是否正确,查看输出mvn位置是否正确: liuweideMacBook-Pro:bin yjwfn$ which mvn /Users/yjwfn/bin/apache-maven-3.6.3/bin/mvn 使用mvn -v查看安装的 Maven 版本是否正确(因为有些系统会自带 Maven): liuweideMacBook-Pro:bin yjwfn$ mvn -v Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) Maven home: /Users/yjwfn/bin/apache-maven-3.6.3 Java version: 1.8.0_191, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre Default locale: zh_CN, platform encoding: UTF-8 OS name: "mac os x", version: "10.14.6", arch: "x86_64", family: "mac" 运行 Maven 运行 Maven 命令的基本样式由options、goal(s)、phase(s)组成: mvn [options] [<goal(s)>] [<phase(s)>] 所有的options可以使用mvn -h查看: liuweideMacBook-Pro:bin yjwfn$ mvn -h usage: mvn [options] [<goal(s)>] [<phase(s)>] Options: -am,--also-make If project list is specified, also .... Maven 命令的重点是goal(s)、phase(s)这两个概念,字面意思注是目标、阶段的意思。他们的使用和另外一个概念life cycles有关,后面会详细解释。现在只需要明白一条Maven命令由多个options、goal(s)、phase(s)组成。 Maven配置 Maven 有三个可以修改配置的地方: MAVEN_OPTS 环境变量:向全局Maven提供额外的选项,如JVM配置参数-Xms256m -Xmx512m; settings.xml:文件位于USER_HOME/.m2目录中,向多个Maven项目提供统一的配置 .mvn目录:该目录位工程目录根目录中,是个隐藏的文件extensions.xmlmaven.configjvm.config MAVEN_OPTS 使用 MAVEN_OPTS是一个环境变量,默认是空的。为测试将MAVEN_OPTS设置成-h: export MAVEN_OPTS=-h #加个-h选项 然后执行mvn不带任务参数就打印出usage: 用法: java [-options] class [args...] (执行类) 或 java [-options] -jar jarfile [args...] (执行 jar 文件) 注意: 仔细一看这个usage其实是Java命令输出的java -h: iuweideMacBook-Pro:bin yjwfn$ java -h 用法: java [-options] class [args...] (执行类) 或 java [-options] -jar jarfile [args...] (执行 jar 文件) 测试java -h与将MAVEN_OPTS设置成-h然后执行mvn打印出来的效果一致,所以MAVEN_OPTS大家应该知道怎么用了吧!要往JVM传递参数可以通过MAVEN_OPTS变量设置。 settings.xml 文件 settings.xml 可以放在两个地址: $MAVEN_HOME/conf/settings.xml USER_HOME/.m2中$MAVEN_HOME就是安装步骤中设置的环境变量,settings.xml的加载可以打开--debug选项查看: mvn --debug # 部分控制台输出 [DEBUG] Reading global settings from /Users/yjwfn/bin/apache-maven-3.6.3/conf/settings.xml [DEBUG] Reading user settings from /Users/yjwfn/.m2/settings.xml 如控制台输出一样,会在$MAVEN_HOME和USER_HOME/.m2中加载两个settings.xml文件。settings.xml的配置项非常多,就不详细说明了可以查看官方文档。 .mvn目录 .mvn目录位于工程根目录中,是工程级的配置一般包含三个配置文件: extensions.xml maven.config jvm.config Maven Extensions extensions.xml是为了使开发者更方便的使用Extensions功能建立的配置文件,Extensions是一种添加库到Core Classloader的方式,Maven主要有四类System Classloader -> Core Classloader -> Plugin Classloaders -> Custom Classloaders由于CloassLoader都是双亲委派模式,所以添加到Core Classloader中的库可以在Plugin Classloaders和Custom Classloaders中使用: extensions.xml的配置演示: extensions.xml这个配置文件就是声明哪些库需要添加到Core Classloader中,如下声明将guava添加到Core Classloader中: <extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.0.0 http://maven.apache.org/xsd/core-extensions-1.0.0.xsd"> <extension> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>23.0</version> </extension> </extensions> maven.config maven.config主要用于添加通用选项,在执行mvn命令时会将maven.config中配置的命令options添加到命令中。 新建个maven.config文件,内容如下: -v 直接执行mvn不带任何选项,由于在maven.config中有-v选项,所以打出的内容就是mvn -v: liuweideMacBook-Pro:.mvn yjwfn$ mvn Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) Maven home: /Users/yjwfn/bin/apache-maven-3.6.3 Java version: 1.8.0_191, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre Default locale: zh_CN, platform encoding: UTF-8 OS name: "mac os x", version: "10.14.6", arch: "x86_64", family: "mac" liuweideMacBook-Pro:.mvn yjwfn$ jvm.config jvm.config是配置JVM参数的文件,很容易理解就不多说了。 Maven 构建生命周期 构建生命周期就Maven较核心的概念, Maven有三个内置的构建生命周期分别为:clean、default、site。 构建阶段(Build Phase) 在Maven中一个生命周期由一系列Build Phase组成,而每个生命周期都会有很多Build Phase如default生命周期由以下Build Phase组成: validate -确认项目正确并且所有必要的信息均可用 compile -编译项目的源代码 test-使用合适的单元测试框架测试编译后的源代码。这些测试不应要求将代码打包或部署 package -获取编译后的代码,并将其打包为可分发的格式,例如JAR。 verify -对集成测试的结果进行任何检查,以确保符合质量标准 install -将软件包安装到本地存储库中,以作为本地其他项目中的依赖项 deploy -在构建环境中完成后,将最终软件包复制到远程存储库中,以便与其他开发人员和项目共享。 这些生命周期阶段(加上此处未显示的其他生命周期阶段)将顺序执行以完成default生命周期。给定上面的生命周期阶段,这意味着当使用default生命周期时,Maven将首先验证项目,然后尝试编译源代码,针对测试运行源代码,打包二进制文件(例如jar),针对该源运行集成测试软件包,验证集成测试,将经过验证的软件包安装到本地存储库,然后将已安装的软件包部署到远程存储库。 插件目标(Plugin Goals) Build Phase只是定义一些软件构建的流程,它不会直接去构建工程,这些构建流程的实施都是由插件来做的,构建生命周期可以这样理解一个生命周期由多个构建阶段组成,每个构建阶段都会被多个插件目标绑定,用一张图表示他们之间的关系: 图中的jar:jar、install:install是plugin:goal的意思,冒号的前面部分是插件名称,后面是目标名称。由上图可知当执行mvn install的命令时,会执行default生命周期中的install阶段(同时在install之前的阶段也会执行),由于install:install目标绑定到了install phase,所以install:install目标也会执行,这样就通过install插件来完成打包功能。 总结 Maven是非常流行的构建工具,下一代构建工具Gradle也有一些点是借鉴了Maven。文中所提到的点只是Maven工具的一部分知识,Maven还有很多强大的功能如:依赖管理、插件管理、多工程等功能,后续再整理这些高级功能与大家分享。 推荐阅读: 知乎已读服务的前世今生与未来! 达达O2O后台架构演进实践:从0到4000高并发请求背后的努力! 爱奇艺的数据库选型大法,实用不纠结! OPPO百万级高并发MongoDB集群性能数十倍提升优化实践 这份阿里云 Redis 的开发规范,建议收藏! 我花 10 个小时,写出了小白也能看懂的阿里数据中台分析! Nginx 核心架构设计,揭秘其为何能支持高并发? 大厂面试必问的volatile关键字,这一篇文章搞定! 5 分钟快速学习,缓存一致性优化方案! -END-

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

用Python开发钉钉群机器人,自动办公神器

云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 最近疫情比较严重,很多公司依靠阿里旗下的办公软件钉钉来进行远程办公,虽然钉钉别的功能很鸡肋,但是机器人这个功能还是让人眼前一亮,属于比较极客的功能,它可以将第三方服务的信息聚合到钉钉群中,实现信息的自动化同步。 注意:很多人学Python过程中会遇到各种烦恼问题,没有人解答容易放弃。 例如:通过聚合Github、Gitlab等源码管理服务,实现源码更新同步;通过聚合Trello、JIRA等项目协调服务,实现项目信息同步;同时,支持Webhook协议的自定义接入,支持更多可能性,例如:将运维报警提醒、自动化测试的结果报告提醒、工作、生活日程安排(上班打卡、下班吃饭、健身、读书、生日、纪念日...)等等的提醒,通过自定义机器人聚合到钉钉中。 不过关于钉钉机器人网上的一些攻略年代都比较久远,代码很多都基于python2,为了与时俱进,我们尝试用python3.7来开发配置钉钉自定义机器人。 首先明确一点,钉钉自定义机器人早就不支持在手机端创建了,所以打开你的pc端或者mac端的钉钉客户端,在需要机器人的聊天群界面,点击智能群助手 随后点击添加机器人按钮 此时能看到很多已经封装好的第三方机器人,本次我们选择自定义机器人 值得一提的是,钉钉的机器人基于webhook协议,webhook呢是一个api概念,是微服务api的使用范式之一,也被称为反向api,即前端不主动发送请求,完全由后端推送,有机会会专独写一篇文章阐述webhook 在添加机器人界面里,填写一些机器人的信息 需要注意的是,在安全设置一栏里,我们选择加签的方式来验证,在此说明一下,钉钉机器人的安全策略有三种,第一种是使用关键字,就是说你推送的消息里必须包含你创建机器人时定义的关键字,如果不包含就推送不了消息,第二种就是使用加密签名,第三种是定义几个ip源,非这些源的请求会被拒绝,综合来看还是第二种又安全又灵活。 创建成功后,系统会分配给你一个webhook地址,这个地址需要保存一下,地址中有唯一的accesstoken ok,那么怎么利用这个地址让你的机器人推送消息呢?查看官方文档: ding-doc.dingtalk.com/doc#/server… 发现文档居然还是python2.0的版本,好吧,我们自己来翻译成3.0 import time import hmac import hashlib import base64 import urllib.parse timestamp = str(round(time.time() * 1000)) secret = 'SEC90485937c351bfaed41fea8eda5f1e155bbf22842d5f9d6871999e05822fd894' secret_enc = secret.encode('utf-8') string_to_sign = '{}n{}'.format(timestamp, secret) string_to_sign_enc = string_to_sign.encode('utf-8') hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest() sign = urllib.parse.quote(base64.b64encode(hmac_code)) # print(timestamp) # print(sign) import requests,json #导入依赖库 headers={'Content-Type': 'application/json'} #定义数据类型 webhook = 'https://oapi.dingtalk.com/robot/send?access_token=f0ca7636f5812fe4815c97a72de9a7cc780c414c258b6c9a631036b1d0f49e3b×tamp='+timestamp+"&sign="+sign #定义要发送的数据 #"at": {"atMobiles": "['"+ mobile + "']" data = { "msgtype": "text", "text": {"content": '都谁没加到群里来?小心升不了班'}, "isAtAll": True} res = requests.post(webhook, data=json.dumps(data), headers=headers) #发送post请求 print(res.text) 复制代码 推送效果是下面这样的: 【云栖号在线课堂】每天都有产品技术专家分享!课程地址:https://yqh.aliyun.com/live 立即加入社群,与专家面对面,及时了解课程最新动态!【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK 原文发布时间:2020-04-14本文作者:编程叫兽本文来自:“51CTO”,了解相关信息可以关注“51CTO”

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

代码神器:拒绝重复编码,这款IDEA插件了解一下

云栖号:https://yqh.aliyun.com第一手的上云资讯,不同行业精选的上云企业案例库,基于众多成功案例萃取而成的最佳实践,助力您上云决策! Easycode是idea的一个插件,可以直接对数据的表生成entity、controller、service、dao、mapper无需任何编码,简单而强大。 1、安装(EasyCode) 我这里的话是已经那装好了。 建议大家在安装一个插件,叫做Lombok。 Lombok能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。出现的神奇就是在源码中没有getter和setter方法,但是在编译生成的字节码文件中有getter和setter方法。 2、建立数据库 -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL, `username` varchar(20) DEFAULT NULL, `sex` varchar(6) DEFAULT NULL, `birthday` date DEFAULT NULL, `address` varchar(20) DEFAULT NULL, `password` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; SET FOREIGN_KEY_CHECKS = 1; 3、在IDEA配置连接数据库 在这个之前,新建一个Springboot项目,这个应该是比较简单的。 建好SpringBoot项目之后,如下图所示,找到这个Database 按照如下图所示进行操作: 然后填写数据库名字,用户名,密码。点击OK即可。这样的话,IDEA连接数据库就完事了。 4、开始生成代码 在这个里面找到你想生成的表,然后右键,就会出现如下所示的截面。 点击1所示的位置,选择你要将生成的代码放入哪个文件夹中,选择完以后点击OK即可。 勾选你需要生成的代码,点击OK。 这样的话就完成了代码的生成了,生成的代码如下图所示: 5、pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--热部署--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <!-- 这个需要为 true 热部署才有效 --> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--阿里巴巴连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.9</version> </dependency> 6、Application.yml server: port: 8089 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/database?useUnicode=true&characterEncoding=UTF-8 username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver mybatis: mapper-locations: classpath:/mapper/*Dao.xml typeAliasesPackage: com.vue.demo.entity 7、启动项目 在启动项目之前,我们需要先修改两个地方。 在dao层加上@mapper注解 在启动类里面加上@MapperScan("com.vue.demo.dao")注解。 启动项目 测试一下 EasyCode的用法就介绍到这里了,觉得有用的童鞋赶紧用起来试试吧~ 云栖号:https://yqh.aliyun.com第一手的上云资讯,不同行业精选的上云企业案例库,基于众多成功案例萃取而成的最佳实践,助力您上云决策! 原文发布时间:2020-02-09本文作者:HeloWxl本文来自:“互联网架构师公众号”,了解相关信息可以关注“互联网架构师”

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

《K8s监控神器——TSDB for Prometheus的入门与实践》

直播时间 2019年4月25日 19:00 - 19:40(30分钟分享+10分钟答疑) 主讲人 胡建洪(花名:莫趋)阿里云智能-数据库产品事业部技术专家 内容概要 Prometheus作为云原生监控的首选工具,其单机部署的架构设计在提供稳定性和易用性的同时,也使得数据存储受限于单节点的存储大小。阿里云TSDB针对这一痛点,为用户提供了易用性高,稳定性强,性价比优的存储技术方案。本次技术分享中,我们会展示如何配置阿里云TSDB作为Prometheus远程存储的流程,并量化展示本方案达到的查询吞吐高,延迟低,性能按需弹性扩展等优势。关注Prometheus的你,请千万不要错过本次技术分享。 直播地址 阿里数据库技术交流群扫码入群 观看直播

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

利用神器BTrace 追踪线上 Spring Boot应用运行时信息

概述 生产环境中的服务可能会出现各种问题,但总不能让服务下线来专门排查错误,这时候最好有一些手段来获取程序运行时信息,比如 接口方法参数/返回值、外部调用情况 以及 函数执行时间等信息以便定位问题。传统的日志记录方式的确可以,但有时非常麻烦,甚至可能需要重启服务,因此代价太大,这时可以借助一个牛批的工具:BTrace ! BTrace 可用于动态跟踪正在运行的 Java程序,其原理是通过动态地检测目标应用程序的类并注入跟踪代码 ( “字节码跟踪” ),因此可以直接用于监控和追踪线上问题而无需修改业务代码并重启应用程序。 BTrace 的使用方式是用户自己编写符合 BTrace使用语法的脚本,并结合btrace命令,来获取应用的一切调用信息,就像下面这样: <btrace>/bin/btrace <PID> <trace_script> 其中 <PID>为被监控 Java应用的 进程ID <trace_script> 为 根据需要监控的信息 而自行编写的 Java脚本 本文就来实操一波 BTrace工具的使用,实验环境如下: OS:CentOS 7.4 64bit BTrace版本:1.3.11.3 被追踪的 Java应用:Spring Boot 2.1.1 应用,这里使用我的文章《Spring Boot应用缓存实践之:Ehcache加持》一文中的 Spring Boot工程 注: 本文首发于 My Personal Blog:CodeSheep·程序羊,欢迎光临 小站 BTrace 安装部署 下载 二进制文件并解压 这里我解压到目录:/home/btrace 配置系统环境变量 vim /etc/profile BTRACE_HOME=/home/btrace export BTRACE_HOME export PATH=$PATH:$BTRACE_HOME/bin 验证 BTrace安装情况 btrace --version 编译 BTrace源码 克隆源码 git clone git@github.com:btraceio/btrace.git 编译源码 ./gradlew build 构建完成的生成物路径位于:build/libs目录下 我们取出构建生成的 jar包供下文使用。 利用btrace追踪 Spring Boot应用例析 首先我们得构造一个 Spring Boot的模拟业务 用于下文被追踪和分析,这里我就使用文章 《Spring Boot应用缓存实践之:Ehcache加持》中的实验工程。 我们在此工程里再添加一个 scripts包,用于放置 btrace 脚本文件: 由于 btrace脚本中需要用到 btrace相关的组件和函数库,因此我们还需要在工程的 pom.xml中引入 btrace的依赖,所使用的 jar包就是上文编译生成的 btrace-1.3.11.3.jar <dependency> <groupId>com.sun.btrace</groupId> <artifactId>btrace</artifactId> <version>1.3.11.3</version> </dependency> Talk is cheap ,Show you the code !接下来就用四五个实验来说明一切吧: 0x01 监控方法耗时情况 btrace 脚本: @BTrace public class BtraceTest2 { @OnMethod(clazz = "cn.codesheep.springbt_brace.controller.UserController", method = "getUsersByName", location = @Location(Kind.RETURN)) public static void getFuncRunTime( @ProbeMethodName String pmn, @Duration long duration) { println( "接口 " + pmn + strcat("的执行时间(ms)为: ", str(duration / 1000000)) ); //单位是纳秒,要转为毫秒 } } 接下来开始运行 btrace脚本来拦截方法的参数,首先我们用 jps命令取到需要被监控的 Spring Boot应用的进程 Id为 27887,然后执行: /home/btrace/bin/btrace 27887 BtraceTest2.java 这里我总共对 /getusersbyname接口发出了 12次 POST请求,情况如下: 接下来我们再看看利用btrace脚本监控到的 /getuserbyname接口的执行时间: 这样一对比很明显,从数据库取数据还是需要 花费十几毫秒的,但从缓存读取数据 几乎没有耗时,这就是为什么要让缓存加持于应用的原因!!! 0x02 拦截方法的 参数/返回值 btrace 脚本: @OnMethod( clazz = "cn.codesheep.springbt_brace.controller.UserController", method = "getUsersByName", location = @Location(Kind.ENTRY) ) public static void getFuncEntry(@ProbeClassName String pcn, @ProbeMethodName String pmn, User user ) { println("类名: " + pcn); println("方法名: " + pmn); // 先打印入参实体整体信息 BTraceUtils.print("入参实体为: "); BTraceUtils.printFields(user); // 再打印入参实体每个属性的信息 Field oneFiled = BTraceUtils.field("cn.codesheep.springbt_brace.entity.User", "userName"); println("userName字段为: " + BTraceUtils.get(oneFiled, user)); oneFiled = BTraceUtils.field("cn.codesheep.springbt_brace.entity.User", "userAge"); println("userAge字段为: " + BTraceUtils.get(oneFiled, user)); } 接下来开始运行 btrace脚本来拦截方法的参数,首先我们用 jps命令取到需要被监控的java应用的进程 Id为 27887,然后执行: /home/btrace/bin/btrace -cp springbt_brace/target/classes 27887 BtraceTest4.java 此时正常带参数 {"userName":"codesheep.cn"} 去请求业务接口:POST /getusersbyname,会得到如下输出: 很明显请求参数已经被 btrace给拦截到了 同理,如果想拦截方法的返回值,可以使用如下 btrace脚本: @OnMethod( clazz = "cn.codesheep.springbt_brace.controller.UserController", method = "getUsersByName", location = @Location(Kind.RETURN) //函数返回的时候执行,如果不填,则在函数开始的时候执行 ) public static void getFuncReturn( @Return List<User> users ) { println("返回值为: "); println(str(users)); } 运行 btrace命令后,继续请求想要被监控的业务接口,则可以得到类似如下的输出: 0x03 监控代码是否到达了某类的某一行 btrace 脚本如下: @BTrace public class BtraceTest3 { @OnMethod( clazz="cn.codesheep.springbt_brace.service.UserService", method="getUsersByName", location=@Location(value= Kind.LINE, line=28) // 比如拦截第28行, 28行是从数据库取数据操作 ) public static void lineTest( @ProbeClassName String pcn, @ProbeMethodName String pmn, int line ) { BTraceUtils.println("ClassName: " + pcn); BTraceUtils.println("MethodName: " + pmn); BTraceUtils.println("执行到的line行数: " + line); } } 执行 btrace追踪命令 /home/btrace/bin/btrace 28927 BtraceTest3.java 接着用 POSTMAN工具连续发出了对 /getuserbyname接口的 十几次POST请求,由于只有第一次请求没有缓存时才会从数据库读,因此也才会执行到 UserService类的第 28行 ! 0x04 监控指定函数中所有外部调用的耗时情况 btrace脚本如下: @BTrace public class BtraceTest5 { @OnMethod (clazz = "cn.codesheep.springbt_brace.service.UserService",method = "getUsersByName", location=@Location(value= Kind.CALL, clazz="/.*/", method="/.*/", where = Where.AFTER) ) public static void printMethodRunTime(@Self Object self,@TargetInstance Object instance,@TargetMethodOrField String method, @Duration long duration) { if( duration > 5000000 ){ //如果外部调用耗时大于 5ms 则打印出来 println( "self: " + self ); println( "instance: " + instance ); println( method + ",cost:" + duration/1000000 + " ms" ); } } } 执行监控命令: /home/btrace/bin/btrace 28927 BtraceTest5.java 然后再对接口 /getuserbyname发出POST请求,观察监控结果如下: 我们发现最耗时的外部调用来源于 MyBatis调用。 0x05 其他追踪与监控 除了上面四种典型的追踪场景之外,其他的 btrace追踪与监控场景还比如 查看谁调用了System.gc(),调用栈如何,则可以使用如下 btrace脚本进行监控 @BTrace public class BtraceTest { @OnMethod(clazz = "java.lang.System", method = "gc") public static void onSystemGC() { println("entered System.gc()"); jstack(); } } 很明显,因为btrace 内置了一系列诸如 jstack等十分有用的监控命令。 当然最后需要说明的是 btrace内置了很多语法和命令,可以应对很多线上 Java应用监控场景,大家可以去研究一下官方文档 后记 由于能力有限,若有错误或者不当之处,还请大家批评指正,一起学习交流! My Personal Blog:CodeSheep 程序羊 程序羊的 2018年终总(gen)结(feng)

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

撩妹神器,打造圣诞雪花特效附加线上热修复

相信去年圣诞节打开过手机淘宝的童鞋都会对当时的特效记忆犹新吧:全屏飘雪,旁边还有个小雪人来控制八音盒背景音乐的播放,让人有种身临其境的感觉,甚至忍不住想狠狠购物了呢(误),大概就是下面这个样子滴: 嗯,确实很炫,那么我们一步步去分析是如何实现的: 一、实现下雪的 View 首先,最上面一层的全屏雪花极有可能是一个顶层的View,而这个View是通过动态加载去控制显示的(不更新淘宝也能看到这个效果)。那么我们先得实现雪花效果的 View,人生苦短,拿来就用。打开 gank.io,搜索"雪花": 看样子第7个库就是我们想要的了,点进源码,直接 download 不解释,记得 star 一个支持作者。那么现在我们的项目中就有一个完整的下雪效果 View 了。 二、实现雪人播放器 View 这个一张雪人图片+一个按钮即可实现,就不多解释了。接下来需要一段圣诞节音频,直接进行在线音频播放无疑是节省空间的好方案。『我的滑板鞋』烘托出的寂寞而甜蜜的氛围无疑是最适合圣诞节的,因此我们得到了『神曲』URL 一枚: http://cdn.ifancc.com/TomaToDo/bgms/my_hbx.mp3 接下来要找一个小雪人的图片当作播放器的背景,那么阿姆斯特朗...不对,是这个: 嗯,相当可爱喜庆。那么播放器核心代码如下: packagecom.kot32.christmasview.player; importandroid.content.Context; importandroid.media.AudioManager; importandroid.media.MediaPlayer; importandroid.util.AttributeSet; importandroid.view.View; importandroid.widget.Toast; importcom.kot32.christmasview.R; importjava.io.IOException; /** *Createdbykot32on16/12/8. */ publicclassMyPlayerextendsView{ publicMediaPlayermediaPlayer; publicMyPlayer(Contextcontext){ super(context); init(); } publicMyPlayer(Contextcontext,AttributeSetattrs){ super(context,attrs); init(); } privatevoidinit(){ setBackgroundResource(R.drawable.pig); mediaPlayer=newMediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); playUrl("http://172.20.248.106/IXC5b415fcacfc3c439e25a3e74533d2239/TomaToDo/bgms/my_hbx.mp3"); Toast.makeText(getContext(),"开始播放",Toast.LENGTH_SHORT).show(); setOnClickListener(newOnClickListener(){ @Override publicvoidonClick(Viewv){ if(!mediaPlayer.isPlaying()){ mediaPlayer.start(); Toast.makeText(getContext(),"继续播放",Toast.LENGTH_SHORT).show(); }else{ mediaPlayer.pause(); Toast.makeText(getContext(),"暂停播放",Toast.LENGTH_SHORT).show(); } } }); } publicvoidplayUrl(StringvideoUrl){ try{ mediaPlayer.reset(); mediaPlayer.setDataSource(videoUrl); mediaPlayer.prepare();//prepare之后自动播放 mediaPlayer.start(); }catch(IllegalArgumentExceptione){ e.printStackTrace(); }catch(IllegalStateExceptione){ e.printStackTrace(); }catch(IOExceptione){ e.printStackTrace(); } } @Override protectedvoidonDetachedFromWindow(){ super.onDetachedFromWindow(); try{ media 三、动态加载思路 上面基本实现了在本地的雪花以及播放音乐效果,那么在不更新主程序的情况下,如何将这两个View动态加载到主程序当中去呢? 首先我们明白,Android 的DexClassloader 是拥有加载任意APK 中任意类的能力的,只是有以下限制: 加载出的Activity 由于不在宿主 Manifest 文件中声明,因此框架无法找到并初始化这个Activity。 加载出的Activity 不具备生命周期,理由同上。 加载出的类的Resource 文件id 会和主程序混淆在一起。 由于我们只是加载View,并不是加载整个Activity,所以前两个问题并不会遇到,而第三个问题可以想办法解决掉。 在主程序中我们也要做这三件事: 把能够装载View的ViewGroup 的空位留出来 去获取更新的patch包 把View 从apk包中加载出来之后,放进留好的ViewGroup 中。这样一来,不仅是圣诞节,在之后的各种活动上都可以在线去加载活动的View。 四、开始加载 在加载View 之前,首先要意识到这个View 是引用了图片资源的(小猪图片),因此我们要解决资源问题: privatevoidinitResource(){ Resourcesresources=getContext().getResources(); try{ AssetManagernewManager=AssetManager.class.newInstance(); MethodaddAssetPath=newManager.getClass().getMethod("addAssetPath",String.class); addAssetPath.invoke(newManager,DynamicViewManager.getInstance().getUpdateFileFullPath()); ResourcesnewResources=newResources(newManager, resources.getDisplayMetrics(),resources.getConfiguration()); Reflect.onObject(getContext()).set("mResources",newResources); }catch(Exceptione){ e.printStackTrace(); } } 上面代码的作用是:把添加了外部更新包路径的资源管理器赋值给了App原来的资源管理器,也就是说现在可以在宿主中访问插件资源了。 核心加载代码如下: DexClassLoaderclassLoader=newDexClassLoader(apkFile.getAbsolutePath() ,"dex_out_put_dir" ,null ,getClass().getClassLoader()); ClassnewViewClazz=classLoader.loadClass("view'spackagename"); Constructorcon=newViewClazz.getConstructor(Context.class); //firstuseActivity'sResourcelietoView if(dynamicView==null){ dynamicView=(View)con.newInstance(getContext()); } //ReplacetheView'smResourcesandrecoverytheActivity'savoiddisorderofResources Reflect.onObject(getContext()).set("mResources",null); getContext().getResources(); RelativeLayout.LayoutParamslayoutParams=newRelativeLayout.LayoutParams(DisplayUtil.dip2px(getContext(),viewInfo.layoutParams.width), DisplayUtil.dip2px(getContext(),viewInfo.layoutParams.height)); layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT,RelativeLayout.TRUE); addView(dynamicView,layoutParams); 中间对 mResources 的操作的作用是:将宿主的Activity 的mResources 重置,避免在Activity 中使用资源时和插件冲突。 然而机智的我已经把更新包下载、版本管理、动态加载都封装好了,所以正确的加载方式是: 引用它:https://github.com/kot32go/dynamic-load-view 然后: 1.宿主声明: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/tb_bg" > <com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup android:layout_width="match_parent" android:layout_height="match_parent" app:uuid="activity_frame"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="原始页面" /> </com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup> <com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup android:layout_width="60dp" android:layout_height="60dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" app:uuid="activity_player"> </com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup> </RelativeLayout> 以上声明了主界面的布局,当然,在动态加载之前除了原有的"原始页面"TextView,是不会有任何其他东西的,也就是圣诞节来临之前的程序。注意:uuid 会和在线包相匹配。 2.打插件包 其实就是把之前包含了我们所写的两个View(雪花和雪人)的程序打包成apk。可以不签名。 3.把插件包放到服务器 在服务器返回的JSON中声明插件包地址和动态View 的一些参数,这里的演示程序请求地址为: http://tomatodo.ifancc.com/php/dynamicView.php 返回值为: { "version":54, "downLoadPath":"http://obfgb7oet.bkt.clouddn.com/patch106.apk", "fileName":"patch106.apk", "viewInfo":[ { "packageName":"com.kot32.testdynamicviewproject.snow.widgets.SnowingView", "uuid":"activity_frame", "layoutParams":{ "width":-1, "height":-1 }}, { "packageName":"com.kot32.testdynamicviewproject.player.MyPlayer", "uuid":"activity_player", "layoutParams":{ "width":-1, "height":-1 }} ]} 我们声明了这次在线包的版本,每个View 的包名和布局参数, 以及最重要的 和宿主程序中声明对齐的uuid。 另外,Dynamic-load-view 能够动态加载外部apk中的View以及资源,能够热修复线上View,以及模块化更新。 屏幕截图 特点 插件程序完全独立于宿主。 以 View作为模块进行模块化开发更新。 你也可以把View 铺满整个Activity,相当于更新Activity。 副作用小,没有加载Activity 带来的生命周期等问题。 兼容性好。Android 4.0~6.0 都没有问题。 简单。核心代码不超过400行。可以自行下载源码,修改更新规则。 如何使用 下载库,并作为library 引用。 需要在宿主程序的Application 的onCreat 中初始化,代码如下:. DynamicViewConfigconfig=newDynamicViewConfig.Builder() .context(this) .getUpdateInfoApi("http://vpscn.ifancc.com/php/dynamicView.php") .build(); DynamicViewManager.getInstance(config).init(); getUpdateInfoApi 这个方法需要传入一个API地址,这个API地址给客户端提供更新的信息. 在上面的地址中,服务器返回了下面这样的JSON 串: { "version":39, "downLoadPath":"http://obfgb7oet.bkt.clouddn.com/patch101.apk", "fileName":"patch101.apk", "viewInfo":[ { "packageName":"com.kot32.testdynamicviewproject.MyButton", "uuid":"test", "layoutParams":{ "width":100, "height":100 } }, { "packageName":"com.kot32.testdynamicviewproject.MyButton1", "uuid":"test_activity", "layoutParams":{ "width":-1, "height":-1 } } ] } 上面的JSON 串定义了本次更新的版本以及更新包的地址,并且提供了对每个View 的详细更新信息。 packageName :插件APK 中View 的完整包名. uuid : 和宿主程序中待更新 View 相同的 UUID. layoutParams:布局参数. 你也可以自己修改服务器需要提供的参数,更改com.kot32.dynamicloadviewlibrary.model 包中的模型类即可。 待更新的View 需要xml 布局文件中如下声明.注意uuid 属性必须赋值。更新时会匹配uuid 相同的View。 <com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup android:id="@+id/dv" android:layout_width="200dp" android:layout_height="200dp" app:uuid="test" android:layout_centerInParent="true"> <!--defaultview--> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@mipmap/ic_launcher"/> </com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup> 对于插件程序,只需要定义View 就好了,之后直接打成APK 包即可。 更多详细信息,请直接下载示例源码查看,源码不多,也很好理解。 缺陷 现在可以加载插件程序中的string和drawable 资源,但是style.xml 和 dimens.xml 的加载还存在一些问题。 插件程序中的资源文件的名字最好不要和主程序中重复。 在插件中访问资源请使用:getContext().getResources() 作者:kot32 来源:51CTO

资源下载

更多资源
Mario

Mario

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

Nacos

Nacos

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

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

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

用户登录
用户注册