首页 文章 精选 留言 我的

精选列表

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

Apache DolphinScheduler 技术详解进阶(资源中心)

本文介绍了Apache DolphinScheduler的内置参数及其用于调度的应用,包括基础和衍生参数的使用方法。 此外,文章还详细阐述了如何在 DolphinScheduler 中引用依赖资源,例如使用资源中心管理文件和引用脚本,以 Shell 任务为例进行说明。 内置参数 DolphinScheduler提供了一些时间相关的系统参数,方便定时调度使用。 1)基础内置参数 变量名 参数 说明 system.biz.date ${system.biz.date} 定时时间前一天,格式为 yyyyMMdd system.biz.curdate ${system.biz.curdate} 定时时间,格式为 yyyyMMdd system.datetime ${system.datetime} 定时时间,格式为 yyyyMMddHHmmss 2)衍生内置参数 可通过衍生内置参数,设置任意格式、任意时间的日期。 (1)自定义日期格式 可以对[yyyyMMdd],[yyyyMM-dd]。 (2)使用add_months() 函数 该函数用于加减月份, 第一个入口参数为[yyyyMMdd],表示返回时间的格式 第二个入口参数为月份偏移量,表示加减多少个月。 参数 说明 $[add_months(yyyyMMdd,12*N)] 后 N 年 $[add_months(yyyyMMdd,-12*N)] 前 N 年 $[add_months(yyyyMMdd,N)] 后 N 月 $[add_months(yyyyMMdd,-N)] 前 N 月 (3)直接加减数字 在自定义格式后直接“+/-”数字,单位为“天”。 参数 说明 $[yyyyMMdd+7*N] 后 N 周 $[yyyyMMdd-7*N] 前 N 周 $[yyyyMMdd+N] 后 N 天 $[yyyyMMdd-N] 前 N 天 $[HHmmss+N/24] 后 N 小时 $[HHmmss-N/24] 前 N 小时 $[HHmmss+N/24/60] 后 N 分钟 $[HHmmss-N/24/60] 前 N 分钟 3)配置示例 若执行的脚本需要一个格式为 yyyy-MM-dd 的前一天日期的参数,进行如下配置即可 ${system.biz.date} 日志结果如下 引用依赖资源 有些任务需要引用一些额外的资源,例如 MR、Spark 等任务须引用 jar 包,Shell 任务需要引用其他脚本等。DolphinScheduler 提供了资源中心来对这些资源进行统一管理。资源中心存储系统可选择本地文件系统或者 HDFS 等。资源中心除了提供文件资源管理功能,还提供了 Hive 自定义函数管理的功能。 下面以 Shell 任务为例,演示如何引用资源中心的其他脚本。 1)点击资源中心,点击创建文件 2)创建文件 3)确保HDFS可以查询到 hello.sh 脚本 4)编辑Node-A中的内容,保存并执行 5)查看任务实例中Node-A结果 ApacheDolphinScheduler 作为一个高效的任务调度和管理平台,通过其强大的内置参数和资源中心,为用户提供了灵活的时间调度和资源管理功能。通过掌握这些功能,用户可以更加高效地管理和执行各种计算任务,从而提高整体的工作效率和准确性。 本文由 白鲸开源科技 提供发布支持!

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

Pipeline模式应用 | 京东云技术团队

本文记录Pipeline设计模式在业务流程编排中的应用 前言 Pipeline模式意为管道模式,又称为流水线模式。旨在通过预先设定好的一系列阶段来处理输入的数据,每个阶段的输出即是下一阶段的输入。 本案例通过定义PipelineProduct(管道产品),PipelineJob(管道任务),PipelineNode(管道节点),完成一整条流水线的组装,并将“原材料”加工为“商品”。其中管道产品负责承载各个阶段的产品信息;管道任务负责不同阶段对产品的加工;管道节点约束了管道产品及任务的关系,通过信号量定义了任务的执行方式。 依赖 工具依赖如下 <!-- 工具类大全 --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>最新版本</version> </dependency> 编程示例 1. 管道产品定义 package com.example.demo.pipeline.model; /** * 管道产品接口 * * @param <S> 信号量 * @author * @date 2023/05/15 11:49 */ public interface PipelineProduct<S> { } 2. 管道任务定义 package com.example.demo.pipeline.model; /** * 管道任务接口 * * @param <P> 管道产品 * @author * @date 2023/05/15 11:52 */ @FunctionalInterface public interface PipelineJob<P> { /** * 执行任务 * * @param product 管道产品 * @return {@link P} */ P execute(P product); } 3. 管道节点定义 package com.jd.baoxian.mall.market.service.pipeline.model; import java.util.function.Predicate; /** * 管道节点定义 * * @param <S> 信号量 * @param <P> 管道产品 * @author * @date 2023/05/15 11:54 */ public interface PipelineNode<S, P extends PipelineProduct<S>> { /** * 节点组装,按照上个管道任务传递的信号,执行 pipelineJob * * @param pipelineJob 管道任务 * @return {@link PipelineNode}<{@link S}, {@link P}> */ PipelineNode<S, P> flax(PipelineJob<P> pipelineJob); /** * 节点组装,按照传递的信号,判断当前管道的信号是否相等,执行 pipelineJob * * @param signal 信号 * @param pipelineJob 管道任务 * @return {@link PipelineNode}<{@link S}, {@link P}> */ PipelineNode<S, P> flax(S signal, PipelineJob<P> pipelineJob); /** * 节点组装,按照传递的信号,判断当前管道的信号是否相等,执行 pipelineJob * * @param predicate 信号 * @param pipelineJob 管道任务 * @return {@link PipelineNode}<{@link S}, {@link P}> */ PipelineNode<S, P> flax(Predicate<S> predicate, PipelineJob<P> pipelineJob); /** * 管道节点-任务执行 * * @param product 管道产品 * @return {@link P} */ P execute(P product); } 4. 管道产品、任务,节点的实现 4.1 管道产品 package com.example.demo.pipeline.factory; import com.example.demo.model.request.DemoReq; import com.example.demo.model.response.DemoResp; import com.example.demo.pipeline.model.PipelineProduct; import lombok.*; /** * 样例-管道产品 * * @author * @date 2023/05/15 14:04 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class DemoPipelineProduct implements PipelineProduct<DemoPipelineProduct.DemoSignalEnum> { /** * 信号量 */ private DemoSignalEnum signal; /** * 产品-入参及回参 */ private DemoProductData productData; /** * 异常信息 */ private Exception exception; /** * 流程Id */ private String tradeId; @Data @Builder @NoArgsConstructor @AllArgsConstructor public static class DemoProductData { /** * 待验证入参 */ private DemoReq userRequestData; /** * 待验证回参 */ private DemoResp userResponseData; } /** * 产品-信号量 * * @author * @date 2023/05/15 13:54 */ @Getter public enum DemoSignalEnum { /** * */ NORMAL(0, "正常"), /** * */ CHECK_NOT_PASS(1, "校验不通过"), /** * */ BUSINESS_ERROR(2, "业务异常"), /** * */ LOCK_ERROR(3, "锁处理异常"), /** * */ DB_ERROR(4, "事务处理异常"), ; /** * 枚举码值 */ private final int code; /** * 枚举描述 */ private final String desc; /** * 构造器 * * @param code * @param desc */ DemoSignalEnum(int code, String desc) { this.code = code; this.desc = desc; } } } 4.2 管道任务(抽象类) package com.example.demo.pipeline.factory.job; import cn.hutool.core.util.ClassUtil; import cn.hutool.json.JSONUtil; import com.example.demo.pipeline.factory.DemoPipelineProduct; import com.example.demo.pipeline.model.PipelineJob; import lombok.extern.slf4j.Slf4j; /** * 管道任务-抽象层 * * @author * @date 2023/05/15 19:48 */ @Slf4j public abstract class AbstractDemoJob implements PipelineJob<DemoPipelineProduct> { /** * 公共执行逻辑 * * @param product 产品 * @return */ @Override public DemoPipelineProduct execute(DemoPipelineProduct product) { DemoPipelineProduct.DemoSignalEnum newSignal; try { newSignal = execute(product.getTradeId(), product.getProductData()); } catch (Exception e) { product.setException(e); newSignal = DemoPipelineProduct.DemoSignalEnum.BUSINESS_ERROR; } product.setSignal(newSignal); defaultLogPrint(product.getTradeId(), product); return product; } /** * 子类执行逻辑 * * @param tradeId 流程Id * @param productData 请求数据 * @return * @throws Exception 异常 */ abstract DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) throws Exception; /** * 默认的日志打印 */ public void defaultLogPrint(String tradeId, DemoPipelineProduct product) { if (!DemoPipelineProduct.DemoSignalEnum.NORMAL.equals(product.getSignal())) { log.info("流水线任务处理异常:流程Id=【{}】,信号量=【{}】,任务=【{}】,参数=【{}】", tradeId, product.getSignal(), ClassUtil.getClassName(this, true), JSONUtil.toJsonStr(product.getProductData()), product.getException()); } } } 4.3 管道节点 package com.example.demo.pipeline.factory; import cn.hutool.core.util.ClassUtil; import cn.hutool.json.JSONUtil; import com.example.demo.pipeline.model.PipelineJob; import com.example.demo.pipeline.model.PipelineNode; import lombok.extern.slf4j.Slf4j; import java.util.function.Predicate; /** * 审核-管道节点 * * @author * @date 2023/05/15 14:32 */ @Slf4j public class DemoPipelineNode implements PipelineNode<DemoPipelineProduct.DemoSignalEnum, DemoPipelineProduct> { /** * 下一管道节点 */ private DemoPipelineNode next; /** * 当前管道任务 */ private PipelineJob<DemoPipelineProduct> job; /** * 节点组装,按照上个管道任务传递的信号,执行 pipelineJob * * @param pipelineJob 管道任务 * @return {@link DemoPipelineNode} */ @Override public DemoPipelineNode flax(PipelineJob<DemoPipelineProduct> pipelineJob) { return flax(DemoPipelineProduct.DemoSignalEnum.NORMAL, pipelineJob); } /** * 节点组装,按照传递的信号,判断当前管道的信号是否相等,执行 pipelineJob * * @param signal 信号 * @param pipelineJob 管道任务 * @return {@link DemoPipelineNode} */ @Override public DemoPipelineNode flax(DemoPipelineProduct.DemoSignalEnum signal, PipelineJob<DemoPipelineProduct> pipelineJob) { return flax(signal::equals, pipelineJob); } /** * 节点组装,上个管道过来的信号运行 predicate 后是true的话,执行 pipelineJob * * @param predicate * @param pipelineJob * @return */ @Override public DemoPipelineNode flax(Predicate<DemoPipelineProduct.DemoSignalEnum> predicate, PipelineJob<DemoPipelineProduct> pipelineJob) { this.next = new DemoPipelineNode(); this.job = (job) -> { if (predicate.test(job.getSignal())) { return pipelineJob.execute(job); } else { return job; } }; return next; } /** * 管道节点-任务执行 * * @param product 管道产品 * @return */ @Override public DemoPipelineProduct execute(DemoPipelineProduct product) { // 执行当前任务 try { product = job == null ? product : job.execute(product); return next == null ? product : next.execute(product); } catch (Exception e) { log.error("流水线处理异常:流程Id=【{}】,任务=【{}】,参数=【{}】", product.getTradeId(), ClassUtil.getClassName(job, true), JSONUtil.toJsonStr(product.getProductData()), product.getException()); return null; } } } 5. 业务实现 通过之前的定义,我们已经可以通过Pipeline完成流水线的搭建,接下来以“审核信息提交”这一业务场景,完成应用。 5.1 定义Api、入参、回参 package com.example.demo.api; import com.example.demo.model.request.DemoReq; import com.example.demo.model.response.DemoResp; import com.example.demo.pipeline.factory.PipelineForManagerSubmit; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * 演示-API * * @author * @date 2023/08/06 16:27 */ @Service public class DemoManagerApi { /** * 管道-审核提交 */ @Resource private PipelineForManagerSubmit pipelineForManagerSubmit; /** * 审核提交 * * @param requestData 请求数据 * @return {@link DemoResp} */ public DemoResp managerSubmit(DemoReq requestData) { return pipelineForManagerSubmit.managerSubmitCheck(requestData); } } package com.example.demo.model.request; /** * 演示入参 * * @author * @date 2023/08/06 16:33 */ public class DemoReq { } package com.example.demo.model.response; import lombok.Data; /** * 演示回参 * * @author * @date 2023/08/06 16:33 */ @Data public class DemoResp { /** * 成功标识 */ private Boolean success = false; /** * 结果信息 */ private String resultMsg; /** * 构造方法 * * @param message 消息 * @return {@link DemoResp} */ public static DemoResp buildRes(String message) { DemoResp response = new DemoResp(); response.setResultMsg(message); return response; } } 5.2 定义具体任务 假定审核提交的流程需要包含:参数验证、加锁、解锁、事务提交 package com.example.demo.pipeline.factory.job; import cn.hutool.json.JSONUtil; import com.example.demo.model.request.DemoReq; import com.example.demo.pipeline.factory.DemoPipelineProduct; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * 加锁-实现层 * * @author * @date 2023/05/17 17:00 */ @Service @Slf4j public class CheckRequestLockJob extends AbstractDemoJob { /** * 子类执行逻辑 * * @param tradeId 流程Id * @param productData 请求数据 * @return * @throws Exception 异常 */ @Override DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) throws Exception { DemoReq userRequestData = productData.getUserRequestData(); log.info("任务[{}]加锁,线程号:{}", JSONUtil.toJsonStr(userRequestData), tradeId); return DemoPipelineProduct.DemoSignalEnum.NORMAL; } } package com.example.demo.pipeline.factory.job; import cn.hutool.json.JSONUtil; import com.example.demo.model.request.DemoReq; import com.example.demo.pipeline.factory.DemoPipelineProduct; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * 解锁-实现层 * * @author * @date 2023/05/17 17:00 */ @Service @Slf4j public class CheckRequestUnLockJob extends AbstractDemoJob { /** * 子类执行逻辑 * * @param tradeId 流程Id * @param productData 请求数据 * @return * @throws Exception 异常 */ @Override DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) throws Exception { DemoReq userRequestData = productData.getUserRequestData(); log.info("任务[{}]解锁,线程号:{}", JSONUtil.toJsonStr(userRequestData), tradeId); return DemoPipelineProduct.DemoSignalEnum.NORMAL; } } package com.example.demo.pipeline.factory.job; import cn.hutool.json.JSONUtil; import com.example.demo.model.request.DemoReq; import com.example.demo.pipeline.factory.DemoPipelineProduct; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * 审核-参数验证-实现类 * * @author * @date 2023/05/15 19:50 */ @Slf4j @Component public class ManagerCheckParamJob extends AbstractDemoJob { /** * 执行基本入参验证 * * @param tradeId * @param productData 请求数据 * @return */ @Override DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) { /* * 入参验证 */ DemoReq userRequestData = productData.getUserRequestData(); log.info("任务[{}]入参验证,线程号:{}", JSONUtil.toJsonStr(userRequestData), tradeId); // 非空验证 // 有效验证 // 校验通过,退出 return DemoPipelineProduct.DemoSignalEnum.NORMAL; } } package com.example.demo.pipeline.factory.job; import cn.hutool.json.JSONUtil; import com.example.demo.model.request.DemoReq; import com.example.demo.model.response.DemoResp; import com.example.demo.pipeline.factory.DemoPipelineProduct; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * 审核-信息提交-业务实现 * * @author * @date 2023/05/12 14:36 */ @Service @Slf4j public class ManagerSubmitJob extends AbstractDemoJob { /** * 子类执行逻辑 * * @param tradeId 流程Id * @param productData 请求数据 * @return * @throws Exception 异常 */ @Override DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) throws Exception { DemoReq userRequestData = productData.getUserRequestData(); try { /* * DB操作 */ log.info("任务[{}]信息提交,线程号:{}", JSONUtil.toJsonStr(userRequestData), tradeId); productData.setUserResponseData(DemoResp.buildRes("成功")); } catch (Exception ex) { log.error("审核-信息提交-DB操作失败,入参:{}", JSONUtil.toJsonStr(userRequestData), ex); throw ex; } return DemoPipelineProduct.DemoSignalEnum.NORMAL; } } 5.3 完成流水线组装 针对入回参转换,管道任务执行顺序及执行信号量的构建 package com.example.demo.pipeline.factory; import com.example.demo.model.request.DemoReq; import com.example.demo.model.response.DemoResp; import com.example.demo.pipeline.factory.job.CheckRequestLockJob; import com.example.demo.pipeline.factory.job.CheckRequestUnLockJob; import com.example.demo.pipeline.factory.job.ManagerCheckParamJob; import com.example.demo.pipeline.factory.job.ManagerSubmitJob; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.Objects; import java.util.UUID; /** * 管道工厂入口-审核流水线 * * @author * @date 2023/05/15 19:52 */ @Slf4j @Service @RequiredArgsConstructor public class PipelineForManagerSubmit { /** * 审核-管道节点 */ private final DemoPipelineNode managerSubmitNode = new DemoPipelineNode(); /** * 审核-管道任务-提交-防刷锁-加锁 */ private final CheckRequestLockJob checkRequestLockJob; /** * 审核-管道任务-提交-防刷锁-解锁 */ private final CheckRequestUnLockJob checkRequestUnLockJob; /** * 审核-管道任务-参数验证 */ private final ManagerCheckParamJob managerCheckParamJob; /** * 审核-管道任务-事务操作 */ private final ManagerSubmitJob managerSubmitJob; /** * 组装审核的处理链 */ @PostConstruct private void assembly() { assemblyManagerSubmit(); } /** * 组装处理链 */ private void assemblyManagerSubmit() { managerSubmitNode // 参数验证及填充 .flax(managerCheckParamJob) // 防刷锁 .flax(checkRequestLockJob) // 事务操作 .flax(managerSubmitJob) // 锁释放 .flax((ignore) -> true, checkRequestUnLockJob); } /** * 审核-提交处理 * * @param requestData 入参 * @return */ public DemoResp managerSubmitCheck(DemoReq requestData) { DemoPipelineProduct initialProduct = managerSubmitCheckInitial(requestData); DemoPipelineProduct finalProduct = managerSubmitNode.execute(initialProduct); if (Objects.isNull(finalProduct) || Objects.nonNull(finalProduct.getException())) { return DemoResp.buildRes("未知异常"); } return finalProduct.getProductData().getUserResponseData(); } /** * 审核-初始化申请的流水线数据 * * @param requestData 入参 * @return 初始的流水线数据 */ private DemoPipelineProduct managerSubmitCheckInitial(DemoReq requestData) { // 初始化 return DemoPipelineProduct.builder() .signal(DemoPipelineProduct.DemoSignalEnum.NORMAL) .tradeId(UUID.randomUUID().toString()) .productData(DemoPipelineProduct.DemoProductData.builder().userRequestData(requestData).build()) .build(); } } 总结 本文重点为管道模式的抽象与应用,上述示例仅为个人理解。实际应用中,此案例长于应对各种规则冗杂的业务场景,便于规则编排。 待改进点: 各个任务其实隐含了执行的先后顺序,此项内容可进一步实现; 针对最后“流水线组装”这一步,可通过配置描述的方式,进一步抽象,从而将变动控制在每个“管道任务”的描述上,针对规则项做到“可插拔”式处理。 作者:京东保险 侯亚东 来源:京东云开发者社区 转载请注明来源

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

前端CodeReivew实践 | 京东云技术团队

把Code Review变成一种开发文化而不仅仅是一种制度 把Code Review 作为开发流程的必选项后,不代表Code Review这件事就可以执行的很好,因为Code Review 的执行,很大部分程度上依赖于审查者的认真审查,以及被审查者的积极配合,两者缺一不可! 如果仅仅只是当作一个流程制度,那么就可能会流于形式。最终结果就是看起来有Code Review,但没有人认真审查,随便看下就通过了,或者发现问题也不愿意修改。 真要把Code Review这件事做好,必须让Code Review变成团队的一种文化,开发人员从心底接受这件事,并认真执行这件事。 要形成这样的文化,不那么容易,也没有想象的那么难,比如这些方面可以参考: 首先,得让开发人员认识到Code Review这件事为自己、为团队带来的好处 然后,得要有几个人做好表率作用,榜样的力量很重要 还有,对于管理者来说,你激励什么,往往就会得到什么 最后,像写自动化测试一样,把Code Review要作为开发任务的一部分,给审查者和被审查者都留出专门的时间去做这件事,不能光想着马儿跑得快又舍不得给马儿吃草 如何形成这样的文化,有心的话,还有很多方法可以尝试。只有真正让大家都认同和践行,才可能去做好Code Review这件事。 自动化工具及规则 前端代码规范 eslint config coding里增加eslint的扫描 扫出来问题很多:后端也在改,每个迭代修改几类。这个需要大家看看是否参考后端的方式,定期梳理一批 提交MR前的必要条件 先设计再编码 建议大家在做复杂功能设计之前可以内部先简单进行一轮头脑碰撞,看一下是否思路可行,或者大家可以一起看一下是否有更好的实现方案。否则review的时候大部分逻辑都写完了,如果有更好的方案,不一定能在review的时候再提出修改了,就算提出了,也可能会涉及大量代码的重写,最终耽误工期。 本地代码通过eslint审查 在提交代码前,请确保error、warning都已处理完毕。有必要忽略eslint校验时可以考虑使用 /* eslint-disable */ /* eslint-disable no-param-reassign */ // eslint-disable-next-line, 否则肉眼的力量是有限的 PR要小 在做Code Review的时候,如果有大量的文件修改,那么Review起来是很困难的,但如果PR比较小,相对就比较容易Review,也容易发现代码中可能存在的问题。 有必要的话,可以拆分功能点,分阶段提交MR,提交的时候标注仅review还是review+merge 开发要预留时间,你的Reviewer也要提前周知预留时间 如何review 对评论进行分级 在做Code Review时,需要针对审查出有问题的代码行添加评论,如果只是评论,有时候对于被审查者比较难甄别评论所代表的含义,是不是必须要修改。 建议可以对Review的评论进行分级,不同级别的结果可以打上不同的Tag,比如说: [blocker]在评论前面加上一个blocker标记,表示这个代码行的问题必须要修改 [optional]:在评论前面加上一个[optional]标记,表示这个代码行的问题可改可不改 [question]:在评论前面加上一个[question]标记,表示对这个代码行不理解,有问题需要问,被审查者需要针对问题进行回复澄清 类似这样的分级可以帮助被审查者直观了解Review结果,提高Review效率。 文件结构的检查 是否符合代码工程目前形成的常用文件结构 文件命名规范 文件放置的位置是否合理(比如demand的组件不能放到teamspace下) 书写风格 变量 驼峰式命名 let cardList; let cardListButton; function getCardList(){} 常量 全部大写,使用下划线来分割单词 const TIMEOUT = 10000; const MAX_LENGHT = 10; Function 对应的方法应该使用对应的动词,例如: get/set, add/remove, create/destroy, start/stop, insert/delete, begin/end; 驼峰式命名; 构造函数的函数名,采用首字母大写(InitialCap);其他函数名,一律首字母小写。 css规范 命名规范:BEM+ Utility-first No全局污染 <style lang="less">这种不带scoped标签下的样式是全局生效的,慎写影响面积广的样式,比如 模块中禁止书写第一类,标签选择器 html,body{} div{} ul{} input{} 减少对通用样式的修改 .el-menu{} .jacp-header{} // 如需修改需要加前缀修饰,以免影响他处 .card-list .el-menu{} No过度修饰的选择器 过度修饰的选择器并不理想。 过度修饰的选择器是指像 div.promo 这样的。很可能你只用 .promo 也能得到相同的效果。当然你可能偶尔会需要用元素类型来修饰 class 例如你写了一个 .error 而且想让它在不同的元素类型中显示效果不一样,例如 .error{ color:red; } div.error{ color: pink;} span.error{ color: pink; padding:14px; } 但是大多数时候还是应当尽量避免。 再举一个修饰过度的选择器例子,ul.nav li a{}。如前文所说,我们马上就可以删掉 ul 因为我们知道 .nav 是个列表,然后我们就可以发现 a 一定在 li 中,所以我们就能将这个选择器改写成 .nav a{}。 No浏览器前缀 无需手写浏览器前缀,因为我们是通过打包的时候处理的,autoprefixer了解一下 // 源码中只需书写 ::placeholder { color: gray; } ​ // 打包后会自动加上前缀 ::-webkit-input-placeholder { color: gray; } :-ms-input-placeholder { color: gray; } ::-ms-input-placeholder { color: gray; } ::placeholder { color: gray; } 合并重复代码 如果有重复的样式需要书写的时候,请不要直接拷贝粘贴到其他位置,尽量合二为一,不同的部分再行单独处理,减少代码冗余。 // 以下代码 .el-upload{ display: none; } .el-upload-list__item{ display: none; } // 请合并成为 .el-upload, .el-upload-list__item{ display: none; } 合理使用HTML标签 语义化:请不要通篇div Bad Semantics <div class="article"> <div class="article_title">这里是标题</div> <div class="the_content">这里是内容 <div class="darkbold">这里加粗</div> 然后是普通文字.</div> </div> Good Semantics <article> <h1>这里是标题</h1> <p>这里是内容 <b>这里加粗</b> 然后是普通文字.</p> </article> 选择功能恰当的标签 如需行内元素可以使用<span>, 而不是设置成inline的<div> 图标icon使用<i class="icon-add">, 不要使用<span>+</span> 给img加上alt: 让禁用图片或者使用特殊设备的用户无障碍得了解你的网页信息,并且对图像搜索引擎友好 书写HTML标签注意 保持标签闭合 保持标签名小写 元素对齐,适当缩进 属性书写顺序 [建议遵循以下顺序] 同一 rule set 下的属性在书写时,应按功能进行分组,并以 Formatting Model(布局方式、位置) > Box Model(尺寸) > Typographic(文本相关) > Visual(视觉效果) 的顺序书写,以提高代码的可读性。 布局定位属性:display / position / float / clear / visibility / overflow 自身属性:width / height / margin / padding / border / background 文本属性:color / font / text-decoration / text-align / vertical-align / white- space / break-word 其他属性(CSS3):content / cursor / border-radius / box-shadow / text-shadow / background:linear-gradient … 参考文章: https://zhuanlan.zhihu.com/p/73809355 作者:京东科技 李贝贝 来源:京东云开发者社区 转载请注明来源

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

Tomcat目录结构 | 京东云技术团队

Tomcat目录结构图如下: 1、bin目录 存放一些可执行的二进制文件,****.sh 结尾的为linux下执行命令,****.bat 结尾的为windows下执行命令。 catalina.sh:真正启动tomcat文件,可以在里面设置jvm参数。 startup.sh:启动tomcat(需事先配置好JAVA_HOME环境变量才可启动,该命令源码实际执行的为catalina.sh start)。 shutdown.sh:关闭tomcat。 version.sh:查看tomcat版本相关信息。 2、conf目录 存放tomcat相关配置文件的。 2.1、catalina.policy 项目安全文件,用来防止欺骗代码或JSP执行带有像System.exit(0)这样的命令,可能影响容器的破坏。 只有当Tomcat用-security命令行参数启动时这个文件才会被使用,即启动tomcat时, startup.sh -security 。 2.2、catalina.proterties 配置tomcat启动相关信息文件 2.3、context.xml 监视并加载资源文件,当监视文件发生变化时,自动加载,通常不会去配置 2.4、jaspic-providers.xml和jaspic-providers.xsd 不常用文件 2.5、logging.properties tomcat日志文件配置,包括输出格式、日志级别等。 2.6、server.xml 核心配置文件:修改端口号,添加编码格式等 核心组件介绍: <1>Server:最顶层元素,而且唯一,代表整个tomcat容器。一个Server元素包含一个或者多个Service元素; <2>Service:对外提供服务的。一个Service元素包含多个Connector元素,但是只能包含一个Engine元素; <3>Connector:接收连接请求,创建Request和Response对象用于和请求端交换数据;然后分配线程让Engine来处理这个请求,并把产生的Request和Response对象传给Engine <4>Engine:Engine组件在Service组件中有且只有一个;Engine是Service组件中的请求处理组件。Engine组件从一个或多个Connector中接收请求并处理,并将完成的响应返回给Connector,最终传递给客户端。 <5>Host:代表特定的虚拟主机。 <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> **name:**虚拟主机的主机名。比如 localhost 表示本机名称,实际应用时应该填写具体域名,比如 www.dog.com ,当然如果该虚拟主机是给内部人员访问的,也可以直接填写服务器的 ip 地址,比如 192.168.1.101; **appBase:**设置 Web 应用程序组的路径。appBase 属性的值可以是相对于 Tomcat 安装目录的相对路径,也可以是绝对路径,需要注意的是该路径必须是 Tomcat 有权限访问的; **unpackWARs:**是否自动展开war压缩包再运行Web应用程序,默认值为true; **autoDeplay:**是否允许自动部署,默认值是 true,表示 Tomcat 会自动检测 appBase 目录下面的文件变化从而自动应用到正在运行的 Web 应用程序; **deployOnStartup:**为true时,表示Tomcat在启动时检查Web应用,且检测到的所有Web应用视作新应用; <6>Context:该元素代表在特定虚拟主机Host上运行的一个Web应用,它是Host的子容器,每个Host容器可以定义多个Context元素。静态部署Web应用时使用。 <Context path="/" docBase="E:\Resource\test.war" reloadable="true"/> **path:**浏览器访问时的路径名,只有当自动部署完全关闭(deployOnStartup和autoDeploy都为false)或docBase不在appBase中时,才可以设置path属性。 **docBase:**静态部署时,docBase可以在appBase目录下,也可以不在;本例中,不在appBase目录下。 **reloadable:**设定项目有改动时,重新加载该项目。 2.7、tomcat-users.xml和tomcat-users.xsd tomcat-users.xml:tomcat用户配置文件,配置用户名,密码,用户具备权限 tomcat默认没有配置任何用户,只有配置好用户后才能使用以下Tomcat Manager三个功能: <role rolename="manager-gui"/> <role rolename="manager-script"/> <user username="tomcat" password="tomcat" roles="manager-gui"/> <user username="admin" password="123456" roles="manager-script"/> tomcat-users.xsd:对tomcat-users.xml文件的描述和约束 2.8、web.xml web应用相关通用配置,可以做下面这些事情。 配置servlet 添加过滤器,比如过滤敏感词汇 设置session过期时间,tomcat默认30分钟 注册了很多MIME类型,即文档类型。这些MIME类型是客户端与服务器之间说明文档类型的,如用户请求一个html网页,那么服务器还会告诉客户端浏览器响应的文档是text/html类型的,这就是一个MIME类型 配置系统欢迎页 3、lib目录 存放tomcat依赖jar包的。 其中ecj-x.x.x.jar起到了将.java文件编译成.class字节码文件的作用。 4、logs目录 存放tomcat运行时产生的日志文件。 在windows环境中,日志文件输出到catalina.xxxx-xx-xx.log文件中。 在linux环境中,日志文件输出到catalina.out文件中。 大体有以下几类: catalina.xxxx-xx-xx.log windows下日志文件输出内容 host-manager.xxxx-xx-xx.log 访问webapps下host-manager项目日志 localhost.xxxx-xx-xx.log tomcat启动时,自身访问服务,只记录tomcat访问日志,而非业务项目日志 localhost_access_log.xxxx-xx-xx.txt 表示访问tomcat下所有项目日志记录 manager.xxxx-xx-xx.log 访问webapps下manager项目日志 5、temp目录 用户存放tomcat在运行过程中产生的临时文件(清空不会对tomcat运行带来影响)。 6、webapps目录 用来存放应用程序,可以以文件夹、war包、jar包的形式发布应用。当然也可以将应用程序放在磁盘的任意位置,在配置文件中映射好即可。 默认自带以下5个项目: 7、work目录 用于存放tomcat在运行时的编译后文件(清空该目录下所有内容,重启tomcat,可达到清除缓冲的作用) 作者:京东科技 杨建 来源:京东云开发者社区 转载请注明来源

资源下载

更多资源
腾讯云软件源

腾讯云软件源

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

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部分的功能。

用户登录
用户注册