一种面向业务配置基于JSF广播定时生效的工具

作者:京东物流 王北永 姚再毅 李振

1 背景

目前,ducc实现了实时近乎所有配置动态生效的场景,但是配置是否实时生效,不能直观展示每个机器上jvm内对象对应的参数是否已变更为准确的值,大部分时候需要查看日志确认是否生效。

2 技术依赖

1)Jsf:京东RPC框架,用作机器之间的通讯工具

2)redis/redisson:redis,用作配置信息的存储

3)ZK/Curator: Zookeeper,用作配置信息的存储 和redis二选一

3)clover:定时任务集群,用作任务延迟或周期性执行

3 实现原理

1)接入方:

各个接入系统通过接入管理模块获取token,并指定所在系统发布的的服务器ip,用作后续的ip鉴权。当系统启动时,自动在各个系统生成接口提供方,并注册到JSF注册中心。别名需各个系统唯一不重复。鉴权为统一服务端做IP鉴权。

2)统一配置服务端:

提供按不同接入方、不同系统、不同环境的配置界面。业务人员可设定自动生效时间或者立即生效时间。如果是立刻生效,则通过JSF广播或者指定机器生效配置。如果是定时生效,则新增定时器并指定生效规则,达到时间后触发广播通知。

整个接入方和统一配置服务端的架构如下图

4 实现步骤

1)重写JSF类ConsumerConfig类方法refer,将其中的轮训调用客户端改为广播调用客户端BroadCastClient。

this.client= new BroadCastClient(this);
this.proxyInvoker = new ClientProxyInvoker(this);
ProtocolFactory.check(Constants.ProtocolType.valueOf(this.getProtocol()), Constants.CodecType.valueOf(this.getSerialization()));
this.proxyIns = (T) ProxyFactory.buildProxy(this.getProxy(), this.getProxyClass(), this.proxyInvoker);

2)广播调用客户端方法分别获取当前注册中心有心跳的服务提供者和已失去连接的机器列表。对统一配置来讲,要么同时失败,要么同时成功,判断如果存在不正常的服务提供方,则不同步。只有全部提供方存在才可以开始广播配置信息

 ConcurrentHashMap<Provider, ClientTransport>  concurrentHashMap = this.connectionHolder.getAliveConnections();
        ConcurrentHashMap<Provider, ClientTransportConfig>  deadConcurrentHashMap = this.connectionHolder.getDeadConnections();
        if(deadConcurrentHashMap!=null && deadConcurrentHashMap.size()>0){
            log.warn("当前别名{}存在不正常服务提供方数量{},请关注!",msg.getAlias(),deadConcurrentHashMap.size());
            throw new RpcException(String.format("当前别名%s存在不正常服务提供方数量%s,请关注!",msg.getAlias(),deadConcurrentHashMap.size()));
        }
        if(concurrentHashMap.isEmpty()){
            log.info("当前别名{}不存在正常服务提供方",msg.getAlias());
            throw new RpcException(String.format("当前别名%s不存在正常服务提供方",msg.getAlias()));
        }
        Iterator aliveConnections = concurrentHashMap.entrySet().iterator();
        log.info("当前别名{}存在正常服务提供方数量{}",msg.getAlias(),concurrentHashMap.size());
        while (aliveConnections.hasNext()) {
            Entry<Provider, ClientTransport> entry = (Entry) aliveConnections.next();
            Provider provider = (Provider) entry.getKey();
            log.info("当前连接ip={}、port={}、datacenterCode={}",provider.getIp(),provider.getPort(),provider.getDatacenterCode());
            ClientTransport connection = (ClientTransport) entry.getValue();
            if (connection != null && connection.isOpen()) {
                try {
                    result = super.sendMsg0(new Connection(provider, connection), msg);
                } catch (RpcException rpc) {
                    exception = rpc;
                    log.warn(rpc.getMessage(), rpc);
                } catch (Throwable e) {
                    exception = new RpcException(e.getMessage(), e);
                    log.warn(e.getMessage(), e);
                }
            }
        }

3)服务配置端,当业务人员配置及时生效或者任务达到时,则根据配置,生成服务调用方,通过统一刷新接口将配置同步刷新到对应的接入系统中,如下图为操作界面,当增删改查时,会将属性增量同步。

服务端在上面操作增删改时,通过以下方式获取服务调用方

 public static ExcuteAction createJsfConsumer(String alias, String token) {
        RegistryConfig jsfRegistry = new RegistryConfig();
        jsfRegistry.setIndex("i.jsf.jd.com");
        BroadCastConsumerConfig consumerConfig = new BroadCastConsumerConfig<>();
        Map<String, String> parameters = new HashMap<>();
        parameters.put(".token",token);
        consumerConfig.setParameters(parameters);
        consumerConfig.setInterfaceId(RefreshRemoteService.class.getName());
        consumerConfig.setRegistry(jsfRegistry);
        consumerConfig.setProtocol("jsf");
        consumerConfig.setAlias(alias);
        consumerConfig.setRetries(2);
        return  new ExcuteAction(consumerConfig);
    }

通过以上的配置的客户端,调用服务提供方方法refreshRemoteService#refresh,将配置信息进行同步到各个接入系统

public void call(Map<String,Object> propertiesValue){
        try{
            RefreshRemoteService refreshRemoteService = (RefreshRemoteService)consumerConfig.refer();
            if(refreshRemoteService!=null){
                refreshRemoteService.refresh(propertiesValue);
            }
        }catch (Exception e){
            log.error(e.getMessage());
            throw new EasyConfigException(e);
        }finally {
            consumerConfig.unrefer(); ;
        }
    }

4)接入方启动时,需要根据自己配置,将存在redis或者zk的配置一次加载到实例变量中。并注册刷新接口到JSF注册中心。

其中注册刷新接口到JSF注册中心代码如下

 @Bean(name = "refreshPorpertiesService")
    public ProviderConfig createJsfProvider() throws Exception {
        ProviderConfig providerConfig = new ProviderConfig();
        providerConfig.setId("refreshPorpertiesService");
        providerConfig.setInterfaceId(RefreshRemoteService.class.getName());
        providerConfig.setRef(new RefreshRemoteServiceDelage(applicationContext));
        providerConfig.setTimeout(30000);
        providerConfig.setAlias(EasyConfigure.getAppCode()+EasyConfigure.getEnv());
        providerConfig.setServer(serverConfig);
        providerConfig.setRegistry(jsfRegistry);
        providerConfig.setParameter("token", MD5Util.md5(EasyConfigure.getAppCode()));
        providerConfig.export();
        return providerConfig;
    }

其中
RefreshRemoteServiceDelage类提供刷新接口的实际逻辑如下,需判断当前实例是jdk动态代理还是cglib代理

判断逻辑如下

 if(AopUtils.isJdkDynamicProxy(object)) {
         object= AopUtil.getJdkDynamicProxyTargetObject(object);
 } else if(AopUtils.isCglibProxy(object)){ //cglib
         object= AopUtil.getCglibProxyTargetObject(object);
   }

实例对象变量值根据自定义的参数转换方式转换后赋值实例变量

if(autoValue.convert()!=null && !autoValue.getClass().isInterface()){
         if(!autoValue.convert().newInstance().getInClassType().isAssignableFrom(newVal.getClass()) ){
                    continue;
             }
           newVal = autoValue.convert().newInstance().convert(newVal);
            if(newVal!=null){
                  if(!autoValue.convert().newInstance().getOutClassType().isAssignableFrom(newVal.getClass()) ){
                            continue;
                      }
              field.setAccessible(true);
               Object value = ReflectionUtils.getField(field,object);
              log.info("change  properties{} for object {} before value {}",field.getName(),object.getClass().getName(),value);
               ReflectionUtils.setField(field,object,newVal);
                log.info("change  properties{} for object {} after value {}",field.getName(),object.getClass().getName(),newVal);
         }              
}

5 实践

1)pom引入

<dependency>
   <groupId>com.jdl</groupId>
   <artifactId>easyconfig</artifactId>
   <version>1.0-SNAPSHOT</version>
</dependency>

2)配置存储配置(比如redis方式)

refresh:
  config:
    appCode: zdzq-worker-appcode
    redisUrl: redis://:@127.0.0.1

3)类全局变量需要实时刷新配置,需在类统一指定注解PropertiryChangeListener,实例变量需要增加注解AutoValue并指定数据格式转换器

@PropertiryChangeListener
public class ChanceServiceImpl implement ChanceService{
@AutoValue(convert = DateConvert.class,alias = "config-id")
private Date  signDate;
@AutoValue(convert = SpmKaApply Convert.class,alias = "config-id")
private SpmKaApply  spmKaApply;
}

以上convert方法自定义,支持各种复杂配置对象,举例数据转换为List如下

public  class Convert2 implements Convert<Map<String, String>, Set<String>> {
    public Convert2(){}
    @Override
    public Set<String> convert(Map<String, String> siteInfoMap) {
        ....你的对象值转换
    }
    @Override
    public Class<?> getInClassType() {
        return Map.class;
    }
    @Override
    public Class<?> getOutClassType() {
        return Set.class;
    }

接入应用服务启动后,可访问/refreshUI 可查看应用在集群中为自动配置的实例,并显示当前实例中变量值参数。key为实例变量名。

6 总结

1、支持jdk动态代理的实例对象和cglib代理对象的参数动态配置

2、支持定时刷新配置

3、直接查看和验证应用集群中实例变量是否一致

本文转载于网络 如有侵权请联系删除

相关文章

  • 如何使用 Deepfakes 换脸

    大家好,又见面了,我是你们的朋友全栈君。转载:https://www.cnblogs.com/zackstang/p/9011753.html1.获取deepfakes工具包gitclonehttps://github.com/deepfakes/faceswap.git2.补齐依赖包:pipinstalltqdmpipinstallcv2pipinstallopencv-contrib-pythonpipinstalldlibpipinstallkeraspipinstalltensorflowpipinstalltensorflow-gpu(如机器带有gpu)pipinstallface_recognition3.收集样本:这里我选用的是新垣结衣的样本,费了好半天,下了100张图片:另外一个人的样本是凯瑞穆里根,由于实在是找图片麻烦,所以直接截取了《TheGreatGatsby》里的视频,然后用ffmpeg转化为图片,大概有70张的样子。3.面部抓取在收集完样本后,使用./faceswap.pyextract–iinput_folder/–ooutput_folder/命令对样本图

  • Linux系统下安装MySQL

    最近学习SpringBoot需要用到数据库,想着在自己服务器上搭建一个MySQL好方便用,记录一下。首先检查机器上是否已经安装有或安装过MySQL,有的话需要将其卸载,把相关的文件删除掉。rpm-qa|grepmysql我安装的是5.7.24版本,网上说高于这个版本有些配置有变化,不适用这个方法。下载MySQLwgethttps://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz 复制下载后解压,将解压出来的文件夹移动到/usr/local/,并改名为mysqltar-zxvfmysql-5.7.24-linux-glibc2.12-x86_64.tar.gz mvmysql-5.7.24-linux-glibc2.12-x86_64/usr/local/ mvmysql-5.7.24-linux-glibc2.12-x86_64mysql 复制在/usr/local/mysql目录下创建data文件夹更改mysql目录下所有的目录及文件夹所属的用户组和用户,以及权限c

  • ERROR org.apache.hadoop.hdfs.server.namenode.NameNode: Failed to start namenode. java.net.BindExcept

    从这个报错的异常内容我们先翻译一下,大概就是指在集群启动的时候,namenode因为出现了端口占用的情况,导致nameNode不可用,导致的集群无法正常启动! 从具体的日志中我们可以看出,我在sbin目录下执行了stop-all.sh关闭集群的指令之后,在开启集群之后,出现了nameNode启动失败的情况,并且是因为地址已经被使用。 说明我们在上一次关闭集群的时候出现了"假死"的状态!总结:真死——进程不存在且不可用(processinformationunavailable) 假死——进程存在但不可用(processinformationunavailable) 因此我们再尝试关闭集群./stop-all.sh,然后用jps查看进程 发现果然进程还在,每个进程名左边显示的是进程号。既然系统无法关闭进程,那么我们只能使用kill-9[进程号]的形式强行把进程干掉! 执行kill-9794737960679766817048020480303 然后我们重新启动集群,发现可以对文件系统执行更新操作后说明NameNode已经成功启动了!

  • 三言两语记录mysql for update锁

    FORUPDATE中文直译的意思是:用于更新。理解:这次查询的数据我要用于更新操作,所以麻烦Mysql帮我加锁,其他进程在我更新完成之前不能发起forupdate请求(可以发起普通select请求,用于前端展示)用途:防止高并发情况下,比如用户连续快速点击两次购买,导致商品数量超卖为负数等情况必要条件mysqlinnodb引擎在事务中启用forupdate(直到commit或者rollback此次更新操作结束释放锁)mysql暂无forupdatenowait需要封装,增加控制超时时间的逻辑,这样子伪nowaitselect命中索引或者主键,则为行锁,没有命中则为表锁(需要注意避免影响业务)测试步骤1.一个连接A发起事务,执行selectforupdateSTARTTRANSACTION; select*fromtestwhereid="Auth"forupdate;复制2.另一个连接B发起普通select请求,正常返回结果3.连接B发起selectforupdate请求,由于第一个步骤的事务还没有结束,所以不能获取,会一直堵塞,直到超时或者锁被释放后返回4.没有n

  • 空间转录组听课收获

    前些天,《百奥智汇》在我们《单细胞天地》宣传了他们的公开课,见:线上讲座|单细胞空间转录组专题——实验技术和前沿应用,已经圆满结束.“空间转录组”专题讲座已经结束,回看请访问网页版地址:https://ke.qq.com/webcourse/index.html#cid=2728654&term_id=102835765&lite=1&from=800021724 image-20200820183050735如果你没有时间看,也可以下载保存一份,参考:什么,腾讯课堂的免费单细胞公开课录屏马上就要删除了下面是我们生信技能树转录组讲师张娟的听课笔记产品介绍产品为:10XVisium空间转录组技术优势:获得多样本类型中完整组织切片的无偏和高通量的基因表达分析了解局部细胞在正常和病变组织中的相互作用无需进行组织解离即可进行基因表达研究分析和了解基因表达一致性及其对生物系统的影响高灵敏度,高分辨率,周期短及组织兼容性强应用方向:肿瘤学,免疫学,发育生物学,神经科学,病理学技术原理:主要使用Poly-A捕获和唯一空间barcodes,示意图如下实验流程-组织透化: 样本准

  • MongoDB 安装及文档的基本操作

    前言MongoDB是一个基于分布式文件存储的半结构化的非关系型数据库。在海量数据中,可以较高性能的处理存取操作。它是以BSON格式进行数据存储(类似JSON格式,但类型更为丰富),因此对于复杂的数据类型,可以较轻松的保存和处理。同时,在非关系型数据库阵容中,相比其他数据库产品,它拥有更丰富的功能,并且与关系型数据库类型,所以对于新手使用也能快速上手。安装环境:CentOS7 版本号:4.2.6企业版 版本:免安版(TGZ)安装包访问官网链接下载链接:https://www.mongodb.com/download-center/enterprise我这里使用的是企业版,下载选项如图:下载后得到压缩包mongodb-linux-x86_64-enterprise-rhel70-4.2.6.tgz将下载的压缩包上传至对应目录,然后进行解压>tar-zxvfmongodb-linux-x86_64-enterprise-rhel70-4.2.6.tgz复制配置文件当前使用的是免安版,所以mongoDB的配置文件需要自己手动创建。如果使用的是安装版,安装后配置文件会在/etc/mongo

  • 聊聊rocketmq的sendHeartbeatToAllBrokerWithLock

    序本文主要研究一下rocketmq的sendHeartbeatToAllBrokerWithLocksendHeartbeatToAllBrokerWithLockrocketmq-client-4.6.0-sources.jar!/org/apache/rocketmq/client/impl/factory/MQClientInstance.javapublicclassMQClientInstance{ privatefinalstaticlongLOCK_TIMEOUT_MILLIS=3000; privatefinalInternalLoggerlog=ClientLogger.getLog(); //...... publicvoidsendHeartbeatToAllBrokerWithLock(){ if(this.lockHeartbeat.tryLock()){ try{ this.sendHeartbeatToAllBroker(); this.uploadFilterClassSource(); }catch(finalExceptione){ log.er

  • 每日算法题:Day 28(数据结构)

    作者:TeddyZhang,公众号:算法工程师之路 Day28,数据结构知识点走起~1编程题【剑指Offer】删除链表中重复的节点在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。例如,链表1->2->3->3->4->4->5处理后为1->2->5思路: 首先题目说了这是一个排序链表,因此所有重复的数一定会是在一起的,这样我们改变指针的指向,就可以将这些重复的一次性去除完,在遍历的时候,如果相邻两个数重复,我们要进入一个循环进行判断,由于需要这些重复数的边界,因此我们需要使用pre和last指针用来得到其边界,然后进行删除!/* structListNode{ intval; structListNode*next; ListNode(intx): val(x),next(NULL){ } }; */ classSolution{ public: ListNode*deleteDuplication(ListNode*pHead) { ListNode*dummy=newListNode()

  • 【leetcode刷题】T95-查找和替换模式

    【题目】你有一个单词列表words和一个模式 pattern,你想知道words中的哪些单词与模式匹配。如果存在字母的排列p,使得将模式中的每个字母x替换为p(x)之后,我们就得到了所需的单词,那么单词与模式是匹配的。(回想一下,字母的排列是从字母到字母的双射:每个字母映射到另一个字母,没有两个字母映射到同一个字母。)返回words中与给定模式匹配的单词列表。你可以按任何顺序返回答案。示例: 输入:words = ["abc","deq","mee","aqq","dkd","ccc"], pattern = "abb" 输出:["mee","aqq"] 解释: "mee" 与模式匹配,因为存在排列 {a -> m, b -> e, ...}。 "ccc" 与模式不匹配,因为 {a -> c, b -> c, ...} 不是排列。 因为 a 和

  • 聊聊flink的RestartStrategies

    序本文主要研究一下flink的RestartStrategiesRestartStrategiesflink-core-1.7.1-sources.jar!/org/apache/flink/api/common/restartstrategy/RestartStrategies.java@PublicEvolving publicclassRestartStrategies{ ​ /** *GeneratesNoRestartStrategyConfiguration. * *@returnNoRestartStrategyConfiguration */ publicstaticRestartStrategyConfigurationnoRestart(){ returnnewNoRestartStrategyConfiguration(); } ​ publicstaticRestartStrategyConfigurationfallBackRestart(){ returnnewFallbackRestartStrategyConfiguration(); } ​ /** *

  • Swift学习:闭包

    本篇将详细总结介绍Swift闭包的用法; 闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift中的闭包与C和Objective-C中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。主要内容: 1.闭包表达式 2.闭包的使用与优化 3.值捕获 4.逃逸闭包 5.自动闭包一、闭包表达式Swift闭包的三种存在形式: 1.全局函数是一个有名字但不会捕获任何值的闭包 2.嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包 3.闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包 闭包表达式的语法一般有如下的一般形式:{(parameters)->returnTypein statements }复制说明: 1.闭包的外层是一个大括号,先写的参数和返回值,然后操作部分之前使用in; 2.闭包就相当于OC中的block,也可以看做是匿名函数; 3.闭包表达式参数可以是in-out参数,但不能设定默认值; 4.闭包的函数体部分由关键字in引入,该关键字表示闭包参数和返回值类型已经完成,闭包函数体开始;二、闭包的使用与优化下面,我们使用Swi

  • Java每日一练(2017/8/10)

    最新通知●回复"每日一练"获取以前的题目!●【新】Android视频更新了!(回复【安卓视频】获取下载链接)●【新】Ajax知识点视频更新了!(回复【学习视频】获取下载链接)●【新】HTML5知识点视频更新了!(回复【前端资料】获取下载链接)●答案公布时间:为每期发布题目的第二天★【新】回复“测试题”获取昨天发布的软件工程师初级阶段测试题答案★【新】回复“学习资料”获取java学习电子文档★【新】需要求职简历模板的可以加小编微信xxf960513聊天系统●我希望大家积极参与答题!有什么不懂可以加小编微信进行讨论★珍惜每一天,拼搏每一天,专心每一天,成功每一天如果你是初学者,或者是自学者!你可以加小编微信!小编可以给你建议以及给你提供学习资料!你在学习上有什么问题都可以咨询小编!小编都会为你解答!注:本公众号纯属个人公众号!不存在任何培训机构招生信息本期题目:(单选题)1、下列选项中,用于在定义子类时声明父类名的关键字是:()AinterfaceBpackageCextendsDclass(单选题)2、Continue语句跳出整个循环 A正确B错误(单选题)3、jav

  • 游戏服务器之内存数据库redis客户端应用(上)

    本文主要介绍游戏服务器的对redis的应用。介绍下redisc++客户端的一些使用。存储结构设计:(1)装备道具的redis存储结构为例(Hashes存储类型)存储结构为:key:EQUIPMENTBAG角色idfrield:装备位置value:装备信息存储一个装备道具到redis(使用hset命令) 一次存储玩家的装备背包里的所有道具(使用命令hmset) 一次获取一个玩家的装备包裹的所有道具(一次获取键的所有field和value(使用命令hgetall)) (2)角色基础属性的redis存储结构为例(字符串存储类型)存储结构:key:BASE角色id,value:角色基础信息 获取一个角色基础属性(使用命令get)存储一个角色基础属性(使用命令set)(3)过期时间设置过期时间30天.访问时需要判断key是否还存在。 本文目录:1、redis命令介绍 (1)基本命令 (2)应用介绍 2、redis存储结构之应用解析3、写入redis的应用 (1)存储一个装备道具到redis(使用hset命令) 存储结构key:EQUIPMENTBAG角色idfrield:装备位置value:装备

  • 2017 HDR技术动态

    2017年是HDR发展突飞猛进的一年,这一年里,HDR不仅仅在技术层面取得了巨大的进步,在消费市场也取得了极大的成功,在其他相关领域中也得到了广泛的应用。在消费市场上,具备HDR功能的电视无疑已经成为当下的热点,几乎每个主要的电视生产商都推出了数款HDR电视,并且覆盖了多个价位以供消费者选择。事实上,因为HDR技术的不断成熟以及用户对高品质图像显示设备的需求,现在已经很难找到不兼容HDR模式的4K电视了。随着HDR电视在不断普及,性能评价标准也变得多样了起来,但主要的评价目标仍然是色彩容量。色彩容量是指显示设备能够产生的颜色和亮度的3D范围。而传统的色度图表示的是显示设备可以产生的2D色域。将2D的色域与第三个维度亮度(以坎德拉每平方米测量,或简称为“尼特”)结合,就形成了完整的色彩容量描述。今年的一部分消费电视只能产生峰值在几百尼特数量级的亮度,而另外一些性能较好的则能产生高达2000尼特的峰值亮度。在色彩方面,许多电视厂商的长期目标是能够百分百地复制ITU-RRecommendationBT.709中的色彩。短期内,大多电视厂商的目标是实现DCI-P3色域,而有些研究的更加深入厂商

  • Linux乱码问题解决方案

    linux系统中文件名内容为urf8编码,windows系统中文件名默认为gbk编码,多数文档使用gbk编码,系统采用utf8编码无中文输入法导致的乱码1、ibus输入法Ubuntu系统安装后已经自带了ibus输入法,在英语环境下默认不启动。配置ibus自动启动可以在ubuntu系统菜单上选择System---Preferences---StartupApplications,在该窗口中增加一个程序:Name:ibus-daemon Command:ibus-daemon-d-x-r复制ibus默认提供的中文输入法比较弱智,需要额外安装ibus-pinyin,命令如下:sudoapt-getinstallibus-pinyin复制这时,还需要将ibus-pinyin输入法启动。在ubuntu系统菜单上选择System---Preferences---IBusPreferences,在InputMethod页中的“Selectaninputmethod”下拉框中选择增加Chinese–Pinyin,就是图标中有个一个大大的“拼”字的那一个,然后点击Add按钮,最后通过Up按钮将该输入法移

  • 概览:可视化前端测试

    作者:莫卓颖可视化前端测背景相信进行过前端开发的同学都知道,前端测试不仅仅涉及到功能的测试,而且也需要考虑到界面样式测试、多浏览器兼容性测试、性能测试。本文主要讨论分析目前前端测试的现状,并讨论目前流行的测试工具,下篇文章将会介绍工具的使用方法。前端测试分类前端测试主要分三大方向测试,而这三大方向也分很多小方向测试,首先简单的介绍每个方向的概念。界面样式测试 固定界面样式测试:主要针对文字内容不变的区域,例如页面的页头,页脚这类结构、内容不变的区域,而测试一般通过截图对比解决。 结构不变界面样式测试:主要针对结构不变的区域,例如新闻区域这类结构不变,内容变化的区域,这类测试一般通过DOM元素对比解决。 计算样式测试:主要针对计算样式不变的区域,这类测试一般通过比较计算样式解决,但是这种测试不推荐,因为测试成本比较大。功能测试 服务器数据预期测试:主要针对用户在前端界面进行某种操作后,提交数据给后台后,测试后台能否返回预期的数据 界面功能测试:主要针对用户在前端界面进行某种交互性操作后,测试能否获取预期的功能、界面交互多浏览器测试 多浏览器测试:基于界面样式测试、功能测试的基础上来进行不

  • java collections.sort_java中

    大家好,又见面了,我是你们的朋友全栈君。 importjava.awt.BorderLayout;importjava.awt.GridLayout;importjava.awt.image.BufferedImage;importjavax.swing.ImageIcon;importjavax.swing.JButton;importjavax.swing.JFrame;importjavax.swing.JLabel;importjavax.swing.JPanel;publicclassMain{ publicstaticvoidmain(String[]args){ JPanelgui=newJPanel(newBorderLayout(2,2));BufferedImagebi=newBufferedImage(600,200,BufferedImage.TYPE_INT_RGB);gui.add(newJLabel(newImageIcon(bi)));JFramemyframe=newJFrame();JPanelmyPanel=newJPanel();gui.add(

  • 腾讯云DNSPod修改记录

    1.接口描述接口请求域名:dnspod.tencentcloudapi.com。 修改记录 默认接口请求频率限制:500次/秒。 APIExplorer提供了在线调用、签名验证、SDK代码生成和快速检索接口等能力。您可查看每次调用的请求内容和返回结果以及自动生成SDK调用示例。 2.输入参数以下请求参数列表仅列出了接口请求参数和部分公共参数,完整公共参数列表见公共请求参数。 参数名称 必选 类型 描述 Action 是 String 公共参数,本接口取值:ModifyRecord。 Version 是 String 公共参数,本接口取值:2021-03-23。 Region 否 String 公共参数,本接口不需要传递此参数。 Domain 是 String 域名 RecordType 是 String 记录类型,通过API记录类型获得,大写英文,比如:A。 RecordLine 是 String 记录线路,通过API记录线路获得,中文,比如:默认。 Value 是 String 记录值,如IP:200.200.200.200,C

  • 有关C# 8.0、.NET Framework 4.8与NET Standard 2.1的一个说明

    早在本月12日,微软官方的.NETBlog发布了一篇名为《BuildingC#8.0》的文章,介绍了很多C#8.0的新特性。不过本文主要讨论的并不是C#8.0的新特性,而是存在于这篇文章中的一段文字: MostoftheC#8.0languagefeatureswillrunonanyversionof.NET.However,afewofthemhaveplatformdependencies. Asyncstreams,indexersandrangesallrelyonnewframeworktypesthatwillbepartof.NETStandard2.1.AsImmodescribesinhispostAnnouncing.NETStandard2.1,.NETCore3.0aswellasXamarin,UnityandMonowillallimplement.NETStandard2.1,but.NETFramework4.8willnot.Thismeansthatthetypesrequiredtousethesefeatureswon’tbeavailabl

  • 邻接矩阵无向图(二)之 C++详解

    本章是通过C++实现邻接矩阵无向图。 目录 1.邻接矩阵无向图的介绍 2.邻接矩阵无向图的代码说明 3.邻接矩阵无向图的完整源码 转载请注明出处:http://www.cnblogs.com/skywang12345/ 更多内容:数据结构与算法系列目录 邻接矩阵无向图的介绍 邻接矩阵无向图是指通过邻接矩阵表示的无向图。 上面的图G1包含了"A,B,C,D,E,F,G"共7个顶点,而且包含了"(A,C),(A,D),(A,F),(B,C),(C,D),(E,G),(F,G)"共7条边。由于这是无向图,所以边(A,C)和边(C,A)是同一条边;这里列举边时,是按照字母先后顺序列举的。 上图右边的矩阵是G1在内存中的邻接矩阵示意图。A[i][j]=1表示第i个顶点与第j个顶点是邻接点,A[i][j]=0则表示它们不是邻接点;而A[i][j]表示的是第i行第j列的值;例如,A[1,2]=1,表示第1个顶点(即顶点B)和第2个顶点(C)是邻接点。 邻接矩阵无向图的代码说明 1.基本定义 classMatrixUDG{ private: charmVexs[MAX];//顶点集合

  • 开发人员和测试人员面对面沟通的重要性

    原文最早发表于百度空间2008-01-09     国内规模较大的软件公司A为了规范管理开发团队和测试团队,于是把两个团队的人员分开在不同的楼层管理,这样便于开发不受测试“打扰”,潜心开发出更好的产品。开发和测试人员最常用的联系方式便是即使通讯工具或电子邮件,面对面的交流可以说是非常少,除了每周固定一天会有测试组长和开发组长等的例会外,其他时候两个团队几乎不怎么见面的,普通测试人员想和开发人员交流更是困难,所以,测试人员发现问题或者有疑问时会纪录在一个笔记本或别的工具中,当开例会时交由测试组长一次性向开发人员提问……     另一个软件公司B采用的是另外一种管理方法,按项目划分,同一个项目的开发和测试工作在同一个楼层,便于项目在测试过程中发现的问题能够及时反馈给开发,同时开发可以及时了解产品测试的流程根据开发经验提出测试流程需要改进的地方。开发人员和测试人员最常用的联系方式除了即使通讯工具和邮件,面对面交流是非常重要的一种手段,这种交流不需要经由双方组长预先安排,随时可以互相的“拜访”……&nbs

相关推荐

推荐阅读