首页 文章 精选 留言 我的

精选列表

搜索[初体验],共236篇文章
优秀的个人博客,低调大师

JAVA性能测试初体验

序言:做自动化测试的时候,一直没想过要去做性能,等到现在做性能的时候,才明白这本身就是一个必须都要经历的过程,就像编程一样,编写小型软件的时候,我们不用过多关注架构和性能,但是等成长到一定时候,就会需要关注软件的可复用性(这是由开发成本决定,这点可以在软件架构上去改善,常说的自动化框架也是为了增强脚本的可复用性和可维护性)、性能瓶颈(这是由系统资源成本决定,空间和时间的调配)、可测试行(这能大大提高测试人员的测试效率,很多时候我们要求开发提供一种测试的接口来方便测试人员进行测试)、可部署性(利用make、ant或者maven,能够大大提高软件发布效率,这也是持续集成中的一种手段)等,因此,测试中的发展其实可以有很多的,不仅关注测试手段,还要关注如何在更多的途径上提高测试效率。下面是对本次性能测试项目至今的一些简单总结,欢迎指正。 一、性能测试项目的背景 性能测试缘起于产品存在大量背景数据时,程序响应时间过慢,而且在特定的情况下有可能会造成一些数据上报丢失,所以需要定位。 产品为C/S架构,采用的协议是snmp协议,运行在jvm上。 二、性能测试的策略 1、测试目的的确定 1)系统监控,包括cpu、内存、线程使用情况,在大数据情况下,发现问题,帮助修正代码结构,系统结构,提高系统的运行效率。 2)确定软件运行资源需求指标。 2、性能测试指标确定 1)确定指标来源,主要包括:产品规格、行业标准、客户需求与故障场景等 2)确定测试特性,例如:系统容量、及时性、稳定性、抗压性、资源利用性等,这些特性可以根据行业性能测试特性以及产品的相关特性来决定。 3)确定具体指标,包括数目和单位。 3、性能测试技术储备 其实性能测试可以算得上是自动化测试的一种大数据测试 1)测试场景准备:准备测试场景,可以理解为对背景数据的构造,其实可以将这种构造理解为另类的接口测试,例如:我们的软件服务器是应用SNMP协议进行通信,设备端有一个agent,专门用来与软件服务器端通信,那么可以虚拟出这么一个agent,保存相应的设备信息,虚拟过程可以通过对在网的实际设备进行录制,然后生成。 互联网中,客户端与服务器的交涉是基于http接口协议,其一般的性能测试都是发送大量的http请求,其实这种过程有一个问题就是无法模拟真实的背景数据,因为报文过于单一,而印象很深的是新浪一位朋友开发的tcpcopy工具,在传输层,将线上数据复制到测试场景下,从而成功模拟了真实场景环境,这是一种很好的测试方法。 (还有一种准备工作就是对测试服务器的选型,包括操作系统类型、CPU内核数目、内存数目等) 2)测试数据准备:这其实就是接口数据,在互联网中,这方面的模拟比较简单,用很多工具,例如LR、jmeter、soaupi等都可以成功构造模拟http报文,从而查看服务器的响应。因为我们采用的是snmp协议,所以业内没有这样的snmp接口工具,所以就自己基于snmp协议包开发了其snmp报文模拟工具。 3)性能测试监控:性能测试过程中,对软件系统服务器的监控是关键,例如:web测试中,往往会对web服务器和数据库服务器、操作系统的指标性能进行监控,因为我们的软件是运行在jvm上,所以直接采用jconsole或者jprofiler监控服务器的内存使用、cpu使用、各个线程使用情况,还有对数据库和操作系统的监控等。 4、性能测试方法 1)基于指标,进行测试数据构造测试,查看系统是否工作正常以及监测是否没有问题。 2)基于指标,在基于测试数据测试的同时,由测试人员参与进行操作,测试在特定环境下的系统工作情况。 3)客户场景模拟测试。 4)随机测试,利用算法进行大量随机数据构造。 三、性能测试调优 1、性能测试是一个不断探索和不断完善的一个测试过程。 调优步骤:衡量系统现状、设定调优目标、寻找性能瓶颈、性能调优、衡量是否到达目标(如果未到达目标,需重新寻找性能瓶颈)、性能调优结束 2、衡量现状,系统性能主要存在问题 1)内存泄露 2)内存占用过大,响应速率慢 3)线程数不断增加,出现死锁或空闲线程 4)某些类实例化数目过多,占用多余的内存空间 3、内存泄露 1)检验方式:内存泄露需要进行时长测试,既将监控界面及系统界面全部打开,进行长时间运行(如12小时),观察系统类的增长情况。 2)问题定位:若出现JVM的Heap持续增长或者Memory views经过时长测试,出现较大规模的红色部分(增长部分),且无法GC。 4、系统内存占用大 1)检验方式:进行某些特定的操作,系统进行大量内存占用或者数据读写操作。 2)问题定位:若系统内存数突发性的增长,且之后不回落,说明某些模块在持续性的占用系统资源。或者出现JVM的Heap有增长,虽不是持续增长,但一直无法回落。 5、线程数目过多或死锁 1)检验方式:进行某些特定的操作,可以使系统产生大量线程操作。 2)问题定位:若系统线程数突发性的增长或持续增长,且之后不回落,说明某些模块在持续性的占用线程。或者观察是否有许多线程来自同一个模块、长期处于waiting或block状态 6、性能调优原则 调优过程中,充分而不过分使用硬件资源、不要一遇到问题就去改善资源环境,然后,合理调整JVM,需要重点掌握一些JVM的参数,并且要善于分析系统架构和JVM的底层工作机制。 总结:性能测试是一个很漫长的过程,不管是做JVM性能测试、WEB架构方面的性能测试,其实道理是相通的,个人觉得,要做好性能测试,不仅要对测试理解,更要对软件架构和底层的服务器工作机制特别理解,不然,往往,你只能去简单做一些所谓的性能测试操作,但是却无法针对很多场景提供有效的测试策略和调优建议。好的性能测试工程师应该是能够快速搭建场景定位问题、提供指标,并且能够对软件系统架构提出有效建议。共勉之 ====================================分割线================================ 最新内容请见作者的GitHub页:http://qaseven.github.io/

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

OpenStack代码贡献初体验

OpenStack如今已成为开源云平台中的明星项目,得到广泛关注。OpenStack的优秀出众依赖于众多开发者的努力,在享受其带来的便利与快捷的同时,为其做一份贡献也是一个开发者的义务。 在前段时间的OpenStack的测试过程中,我发现Nova项目中的一个Bug,于是向社区提交了Bug报告,并提交代码修复了该Bug,从提交报告到代码入库经历近一月,下面重现整个过程。 一.发现Bug: Nova中的虚拟机软删除(soft-delete)功能,是指在一段时间内,仅将数据库中的某虚拟机记录做一个标记(status='SOFT-DELETE'),然后将虚拟化平台(kvm等)中对应的虚拟机实例置为关机状态,当超过某一时间段后才将虚拟机实例真正删除;该功能为云平台用户提供了“后悔时间”,可以在一定程度上挽回误操作。默认情况下,软删除功能是关闭的,其开启方式是在nova配置文件中添加"reclaim_instance_interval"选项,并将其值设置为"后悔时间"的毫秒数。 在描述具体Bug前,需要对openstack中的用户管理方面的基本概念简单介绍一下。 上图是openstack用户模型的简化版本,为了便于理解将不属于keystone管理的quota也拿了过来。 Bug就与软删除相关。具体场景是这样的:假设OpenStack中有两个项目和两个用户:普通项目A其用户a,管理员项目Admin其用户为admin(用户管理相关概念可以查阅keystone文档),用户a不慎将自己的一台虚拟机删除了,这时求助系统管理员看看有没有办法恢复,好在系统开启的软删除功能,而且被删除的虚拟机还在可回收的时间范围内,这时管理员便以admin的身份登录系统,为用户a恢复了虚拟机,但是细心的管理员却发现了一些不对:其Admin项目下并没有任何虚拟机,但是其配额却被使用了,难道这和刚才的操作有关?再来重试一下:普通用户删除虚拟机,admin用户来为其恢复,这时配额又发生了变化,果然如此:被恢复的虚拟机的配额错误的添加到了Admin项目下。该Bug在最新的kilo版本中仍然存在,感兴趣的同学可以实验一下。 二.定位Bug: 发现了Bug的存在,那就更进一步,到代码中找一下原因吧。 如何确定问题代码的位置呢?这需要对Nova的项目结构有大体的了解,我们来简单了解一下: 上图是nova架构的极简版本,与本问题无关的组件都没有画上去,恢复虚拟机的操作过程大致是这样: nova api接收到用户请求,到数据库中查询虚拟机详情,将该虚拟机所在的主机、名称等数据发送到消息队列中; nova compute服务在监听到相关消息后,开始执行具体操作,将虚拟机在数据库中的记录做些调整,调用底层驱动恢复虚拟机。 既然软删除的功能层面没有任何问题,虚拟机的删除和恢复过程都很顺利,可见不会是驱动的问题,顺着API层的代码调用往下找,很快就可以定位了。直接看出问题的代码片段: def restore(self, context, instance): # 该代码做了删减 flavor = instance.get_flavor() # 获取quotas对象 num_instances, quotas = self._check_num_instances_quota( context, flavor, 1, 1) self._record_action_start(context, instance, instance_actions.RESTORE) try: if instance.host: instance.task_state = task_states.RESTORING instance.deleted_at = None instance.save(expected_task_state=[None]) self.compute_rpcapi.restore_instance(context, instance) else: instance.vm_state = vm_states.ACTIVE instance.task_state = None instance.deleted_at = None instance.save(expected_task_state=[None]) # 更新quotas quotas.commit() 上面的这段代码就是API层面上进行虚拟机回收的主要方法,可以看到其中有明显的配额操作(quotas),在解读这段代码前有必要先对nova中"context"的概念做个简介。不仅是nova,在openstack其他项目中都随处可见这个"context",它是一个包装了用户请求信息的对象,包含用户的项目和认证信息等,通过它可以简便的进行各项目之间的API调用和用户信息的查询,API服务接收到用户的每一次HTTP请求,都会创建一个新的context。 回到这段代码,我们重点关注对quotas所作的操作:在方法的第二行,通过了一个方法获取了quotas,有在方法的结尾执行了quotas.commit(),能够获取到的信息不多,我们再看一下获取quotas的方法:_check_num_instances_quota # 这里只截取一部分 def _check_num_instances_quota(self, context, instance_type, min_count, max_count): req_cores = max_count * instance_type['vcpus'] vram_mb = int(instance_type.get('extra_specs', {}).get(VIDEO_RAM, 0)) req_ram = max_count * (instance_type['memory_mb'] + vram_mb) try: quotas = objects.Quotas(context) quotas.reserve(context, instances=max_count, cores=req_cores, ram=req_ram) ... return max_count, quotas 这里可以看到获取quotas的过程:通过当前的context创建quotas对象,并且执行了reserve操作; 我们知道context是由HTTP请求而来,里面保存的是发请求的用户的信息,所以这里的quotas对象的“所有者”也就是context中的用户。 结合Bug发生的场景来看:管理员还原用户a的虚拟机,发请求的是管理员,当前context中记录的是管理员的信息,这里的quotas理所当然的就是管理员的,然后操作了用户a的虚拟机,更新的却是管理员的quotas。嗯,真相大白! 三.修复Bug: Bug的原因是获取的quotas并不属于期望的用户,但是直接修改context显然不合适(会影响后续的操作),先了解一下quotas对象自身吧: class Quotas(base.NovaObject): # 部分代码 def __init__(self, *args, **kwargs): super(Quotas, self).__init__(*args, **kwargs) # Set up defaults. self.reservations = [] self.project_id = None self.user_id = None self.obj_reset_changes() ... def reserve(self, context, expire=None, project_id=None, user_id=None, **deltas): reservations = quota.QUOTAS.reserve(context, expire=expire, project_id=project_id, user_id=user_id, **deltas) self.reservations = reservations self.project_id = project_id self.user_id = user_id self.obj_reset_changes() def commit(self, context=None): if not self.reservations: return if context is None: context = self._context quota.QUOTAS.commit(context, self.reservations, project_id=self.project_id, user_id=self.user_id) self.reservations = None self.obj_reset_changes() 注意看reserve方法的参数,默认为None的project_id和user_id,这正是改变quotas属主的方便入口! 修改后的代码这里就不贴了,感兴趣的同学可以到这次提交中看:Code Review 四.代码提交和Review: openstack社区有着整套项目管理流程,这里有一张图能够较详细的描述工作流程: 由图可见bugfix是其中最简单的流程。 关于如何提交代码,这篇文章有详细的介绍: 向 OpenStack 贡献您的代码。另外需要注意一点,在国内向社区提交代码,经常会因为网络问题导致无法提交,幸好找到了大牛的博客介绍了该类问题的解决办法。 修改完代码的单元测试和pep8本地测试当然不能少,早就知道社区对单元测试要求很严格,这次才真正领教了,三行代码的修改,单元测试却写了30行,review期间多次因为单元测试的问题重提代码(哭)。社区里面的开发者,尤其是项目的core,对待项目有着像对自己孩子般的认真与细致:他们会在一个自己根本不会在意的地方提醒你、面对当前的问题他们会延伸的考虑类似的问题。他们的态度让我首先感受到的吃惊,然后是敬佩! 经历八次review、历时近一个月,我的代码总算是入库了!希望我的这篇记录能对你有帮助。 感谢休伦公司技术总监 孙琦 提供的英文支持,社区大牛Alex Xu给出的修改建议。 Launchpad上面的bug提交: Abnormal changes of quota usage after instance restored by admin 代码审查过程: Fix abnormal quota usage after restore by admin Git@OSC中的代码: Fix abnormal quota usage after restore by admin 文章转载自 开源中国社区[https://www.oschina.net]

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

Ubuntu 20.04 ZFS 快照初体验

借助 Canonical 的 Zsys 计划,Ubuntu 20.04 的 ZFS 改进的一部分是能够自动对 APT 操作进行快照,以便在软件包管理更改后根据需要进行系统回滚/还原。 在 Ubuntu 19.10 中,Canonical向其 Ubiquity 桌面安装程序添加了 ZFS 根文件系统安装选项,Ubuntu 20.04 的桌面安装程序则提供了简化的安装选项。 在 Ubiquity 的“高级功能”中,可以选择安装 ZFS 根文件系统,但该选项仍是实验性的,EXT4 还是默认的文件系统。 试用中可以看到,在继续使用带有 ZFS 的 Ubuntu 20.04 每日构建 ISO 并重新启动系统后,执行任何 APT 事务时,都会出现新的“正在保存系统状态”的消息。如果软件包升级/安装/删除出现问题,运行 APT 将触发 Zsys 拍摄 ZFS 快照。 引导加载程序 GRUB 中出现一个“历史记录”菜单。 从该历史记录菜单中,可以选择一个较早的快照进行引导。 快照的行为和 GRUB 处理类似于十年前的 Fedora Btrfs 系统回滚选项,以及 SUSE/openSUSE 上提供的功能。 消息来自:https://www.phoronix.com/scan.php?page=news_item&px=Trying-Ubuntu-20.04-ZFS-Snaps

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

Link Develop平台之初体验

最近拿公司的一个项目体验了一下阿里云lot的link develop平台,使用起来还是很方便的,创建设备模型,定义功能,接入设备,数据就上来了,展示方面平台也提供了很方便的工具,先上图看下结果。 实现方式 通过网关把服务现场PLC的数据接入阿里云Link Develop平台,然后通过移动应用将获取到的数据根据需要做页面分类展示,2个网关接入了三个设备的数据。

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

Android测试之Monkey初体验

什么是Monkey? Monkey是Android中自带的用来进行压力测试的一个命令行工具。 用Monkey进行App压力测试的结果有三种 正常 Crash :程序崩溃 ANR:程序无响应 Monkey简单测试步骤 1.手机与电脑进行USB连接,并在开发者选项中选中USB调试 2.确认手机与电脑连接:打开cmd命令行或者使用Android Studio的朋友可以打开Terminal视图,输入adb devices查看已连接的设备。 如果设备连接成功会显示 注意:如果输入命令显示adb 不是内部或外部命令,也不是可运行的程序或批处理文件的话,就需要配置环境变量。 3.安装Apk到手机上 adb install package.apk 4.执行Monkey指令: monkey 500 结果我们发现这样执行的结果是对手机上的所有APP随机进行操作的并没有对特定的APP进行操作,那么下一步我们就以计算器APP来演示下如何对于特定的APP进行测试。 1)获取手机计算器的包名:需要在adb shell模式下输入logcat | grep START,会显示出当前启动的APP的信息 2)划到最底部,然后打开手机的计算器应用 在打印出的信息里我们可以查看到计算器的包名为com.android.calculator2 3)输入monkey -p com.android.calculator2 500(表示对计算器APP执行500次的随机事件) 我们会发现所有的操作都在计算器里执行的 补充 如果存在多个手机连接,输入adb shell会有如下提示。 如果想进入某一个设备需要执行adb -s 设备名 shell 个人博客:https://myml666.github.io

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

Spark2.1.0之初体验

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/beliefer/article/details/80042366 在《Spark2.1.0之运行环境准备》一文中,已经介绍了如何准备好基本的Spark运行环境,现在是时候实践一下,以便于在使用过程中提升读者对于Spark最直接的感触!本文通过Spark的基本使用,让读者对Spark能有初步的认识,便于引导读者逐步深入学习。 运行spark-shell 在《Spark2.1.0之运行环境准备》一文曾经简单运行了spark-shell,并用下图进行了展示(此处再次展示此图)。 图1执行spark-shell进入Scala命令行 图1中显示了很多信息,这里进行一些说明: 在安装完Spark 2.1.0后,如果没有明确指定log4j的配置,那么Spark会使用core模块的org/apache/spark/目录下的log4j-defaults.properties作为log4j的默认配置。log4j-defaults.properties指定的Spark日志级别为WARN。用户可以到Spark安装目录的conf文件夹下从log4j.properties.template复制一份log4j.properties文件,并在其中增加自己想要的配置。 除了指定log4j.properties文件外,还可以在spark-shell命令行中通过sc.setLogLevel(newLevel)语句指定日志级别。 SparkContext的Web UI的地址是:http://192.168.0.106:4040。192.168.0.106是笔者安装Spark的机器的ip地址,4040是SparkContext的Web UI的默认监听端口。 指定的部署模式(即master)为local[*]。当前应用(Application)的ID为local-1497084620457。 可以在spark-shell命令行通过sc使用SparkContext,通过spark使用SparkSession。sc和spark实际分别是SparkContext和SparkSession在Spark REPL中的变量名,具体细节已在《Spark2.1.0之剖析spark-shell》一文有过分析。 由于Spark core的默认日志级别是WARN,所以看到的信息不是很多。现在我们将Spark安装目录的conf文件夹下的log4j.properties.template以如下命令复制出一份: cp log4j.properties.template log4j.properties 并将log4j.properties中的log4j.logger.org.apache.spark.repl.Main=WARN修改为log4j.logger.org.apache.spark.repl.Main=INFO,然后我们再次运行spark-shell,将打印出更丰富的信息,如图2所示。 图2 Spark启动过程打印的部分信息 从图2展示的启动日志中我们可以看到SecurityManager、SparkEnv、BlockManagerMasterEndpoint、DiskBlockManager、MemoryStore、SparkUI、Executor、NettyBlockTransferService、BlockManager、BlockManagerMaster等信息。它们是做什么的?刚刚接触Spark的读者只需要知道这些信息即可,具体内容将在后边的博文给出。 执行word count 这一节,我们通过word count这个耳熟能详的例子来感受下Spark任务的执行过程。启动spark-shell后,会打开Scala命令行,然后按照以下步骤输入脚本: 步骤1 输入val lines =sc.textFile("../README.md", 2),以Spark安装目录下的README.md文件的内容作为word count例子的数据源,执行结果如图3所示。 图3 步骤1执行结果 图3告诉我们lines的实际类型是MapPartitionsRDD。 步骤2 textFile方法对文本文件是逐行读取的,我们需要输入val words =lines.flatMap(line => line.split(" ")),将每行文本按照空格分隔以得到每个单词,执行结果如图4所示。 图4 步骤2执行结果 图4告诉我们lines在经过flatMap方法的转换后得到的words的实际类型也是MapPartitionsRDD。 步骤3 对于得到的每个单词,通过输入val ones = words.map(w => (w,1)),将每个单词的计数初始化为1,执行结果如图5所示。 图5 步骤3执行结果 图5告诉我们words在经过map方法的转换后得到的ones的实际类型也是MapPartitionsRDD。 步骤4 输入val counts = ones.reduceByKey(_ + _),对单词进行计数值的聚合,执行结果如图6所示。 图6 步骤4执行结果 图6告诉我们ones在经过reduceByKey方法的转换后得到的counts的实际类型是ShuffledRDD。 步骤5 输入counts.foreach(println),将每个单词的计数值打印出来,作业的执行过程如图7和图8所示。作业的输出结果如图9所示。 图7 步骤5执行过程第一部分 图8 步骤5执行过程第二部分 图7和图8展示了很多作业提交、执行的信息,这里挑选关键的内容进行介绍: SparkContext为提交的Job生成的ID是0。 一共有四个RDD,被划分为ResultStage和ShuffleMapStage。ShuffleMapStage的ID为0,尝试号为0。ResultStage的ID为1,尝试号也为0。在Spark中,如果Stage没有执行完成,就会进行多次重试。Stage无论是首次执行还是重试都被视为是一次Stage尝试(Stage Attempt),每次Attempt都有一个唯一的尝试号(AttemptNumber)。 由于Job有两个分区,所以ShuffleMapStage和ResultStage都有两个Task被提交。每个Task也会有多次尝试,因而也有属于Task的尝试号。从图中看出ShuffleMapStage中的两个Task和ResultStage中的两个Task的尝试号也都是0。 HadoopRDD则用于读取文件内容。 图9 步骤5输出结果 图9展示了单词计数的输出结果和最后打印的任务结束的日志信息。 笔者在本文介绍的word count例子是以SparkContext的API来实现的,读者朋友们也可以选择在spark-shell中通过运用SparkSession的API来实现。 有了对Spark的初次体验,下面可以来分析下spark-shell的实现原理了,请看——《Spark2.1.0之剖析spark-shell》 关于《Spark内核设计的艺术 架构设计与实现》经过近一年的准备,基于Spark2.1.0版本的《 Spark内核设计的艺术 架构设计与实现》一书现已出版发行,图书如图: 纸质版售卖链接如下: 京东: https://item.jd.com/12302500.html 电子版售卖链接如下: 京东: https://e.jd.com/30389208.html

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

Java10 初体验(实战)

最近 IDEA 发布支持 java10的新版本。 Java10 简介: 详细版本更新特性请查看国外的一篇文章:https://www.azul.com/109-new-features-in-jdk-10/ 我在这里只简单的介绍 最热的一个特性:局部变量的类型推断 简单demo: var list = new ArrayList<String>(); // infers ArrayList<String> var stream = list.stream(); // infers Stream<String> 是不是很像js?但是我们要知道,java依旧是强类型语言,只是jvm帮助我们做了变量类型推断。 好了开始正文,java10需要最新版本的IDEA支持。否则JDK你都加不进去。 所以我们先下载最新版的idea: 最新IDEA下载地址:https://www.jetbrains.com/idea/download/#section=windows 安装好后,启动IDEA。 随便进一个项目,然后打开项目架构 快捷键 ctrl + shift + alt + s 添加SDK 给项目适配JDK10 测试 我们都听说过java10的新特性吧。最热的一个特性是 用var 来声明变量,是的,就像js一样。 那接下来直接进入让java粉迫不及待的场面。 /** * Created by Fant.J. */ public class NewJavaTest { public static void main(String[] args) { var list = new ArrayList<>(); list.add(1); list.add("fantj"); list.add(1.00); list.forEach(System.out::println); } } 控制台输出: 1 fantj 1.0 我在这里故意不给ArrayList 赋泛型,因为它默认就是Object,这样我可以给list赋任意类型的变量,给人感觉很像弱类型语言,但是我们应该清楚是因为jvm帮我们猜测了类型。 最后附上java10的官方更新文档:http://openjdk.java.net/jeps/286

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

VDI架构—Remote FX 初体验

引言 随着微软的VDI架构在企业中的实施原来越多,企业相关的需求也在随着市场的需要而在变化,声音、图像等多媒体元素在VDI中的展现,也称为了当今虚拟化所关注的主题之一。 VDI-RemoteFX概览 随着Windows 2008 R2以及Windows 7的SP1 Beta包的发布,我得以在虚拟化的VDI部署上使用新的功能来进行测试,其中尤为关注的就是最新的Remote FX的特性。 Windows 7 SP1是面向消费级用户和IT专业人员的累积更新,包含先前通过Windows Update提供的更新,以及根据客户和合作伙伴的反馈意见为Windows 7平台持续开发的增量式更新。它可以为PC提供支持,为操作系统提供持续改进,并且便于组织以单一更新集的形式进行部署。 Windows Server 2008 R2 SP1则是面向企业和IT专业人员的累积更新,包含虚拟化增强功能(动态内存和RemoteFX)、先前通过Windows Update提供的改进,并解决了客户反馈的问题。RemoteFX描述了一组富媒体功能,使用远程桌面服务平台的用户可利用它们来部署基于虚拟机或会话的远程桌面基础结构。具体而言,RemoteFX增强了远程桌面服务中的远程桌面协议(RDP),使远程办公人员可以访问任意类型的应用程序或屏幕内容,包括富媒体和3D应用程序,从而提高最终用户的工作效率。 根据微软官方的文档显示Remote FX在硬件方面有着那么几项很关键的要求,而我的测试也是因为我的CPU不满足其中的EPT技术而宣告失败。不过整个架构是能实现的,就先展示给大伙看看啦。 RemoteFX server hardware requirements There are several hardware requirements that must be met when deploying a RemoteFX server: SLAT-enabled processor – The processor in the RemoteFX server must support Second-Level Address Translation (SLAT). In virtualization scenarios, hardware-based SLAT support improves performance. On Intel-based processors, this is called Extended Page Tables (EPT), and on AMD-based processors, it is called Nested Page Tables (NPT). GPU - At least one graphics processing unit (GPU) is required on the RemoteFX server. The GPU driver must support DirectX 9.0c and DirectX 10.0. If more than one GPU is installed in the RemoteFX server, the GPUs must be identical. The GPU must have sufficient dedicated video memory that is separate from system memory. RemoteFX encoder - The RemoteFX encoder is optional and can be installed for additional scalability on the RemoteFX server. The hardware encoder card must be installed in an x4 speed PCI-express slot or greater. Hyper-V – The Hyper-V hardware requirements must be supported on the server. The Hyper-V hardware requirements for Windows Server 2008 R2 are available on the Windows Server 2008 Technical Library (http://go.microsoft.com/fwlink/?LinkID=180919). Streaming SIMD Extensions (SSE2) processor – If you are using RemoteFX on an RD Session Host server, the processor on the RD Session Host server must support SSE2. Remote FX测试架构示意 本次测试的架构图: VDI-Remote部署测试 1.部署Remote FX服务器 根据微软的原文步骤,在VDI的基础上部署Remote FX: In this step, you will do the following: lEnable RemoteFX. lConfigure the custom RDP settings. lInstall the RemoteFX 3D video adapter on the personal virtual desktop. First, you must enable RemoteFX on the RDVH-SRV computer. To enable RemoteFX 1.Log on to RDVH-SRV as CONTOSO\Administrator. 2.Open Server Manager. To open Server Manager, click Start, point to Administrative Tools, and then click Server Manager. 3.Under the Roles Summary heading, click Add Roles. 4.On the Before You Begin page of the Add Roles Wizard, click Next. 5.On the Select Server Roles page, select the Remote Desktop Services check box, and then click Next. 6.On the Introduction to Remote Desktop Services page, click Next. 7.On the Select Role Services page, select the RemoteFX check box. The Core Services check box will be automatically selected and installed when RemoteFX is installed. 8.On the Confirm Installation Selections page, verify that the RD Virtualization Host role service will be installed, and then click Install. 9.On the Installation Results page, you are prompted to restart the server to finish the installation process. Click Close, and then click Yes to restart the server. 10.After the server restarts and you log on to the computer as CONTOSO\Administrator, the remaining steps of the installation finish. When the Installation Results page appears, confirm that installation of the RD Virtualization Host role service succeeded, and then close Server Manager. 2. 配置Remote FX 接下来还有比较重要的这几步: Next, you must configure the custom RDP settings for the virtual desktop pool to always use a LAN connection speed. To configure the custom RDP settings 1.Log on to RDCB-SRV as the CONTOSO\Administrator user account. 2.Click Start, point to Administrative Tools, point to Remote Desktop Services, and then click Remote Desktop Connection Manager. 3.Expand RD Virtualization Host Servers. 4.Right-click Personal Virtual Desktops, and then click Properties. 5.Click the Custom RDP Settings tab. 6.In the Custom RDP settings box, type connection type:i:6 and then click OK. Instead of configuring the custom RDP settings, you can also give your users the option to configure their connection speed when logging on to the RD Web Access server. This is configured by editing the web.config file on the RDCB-SRV server. To show the connection speed check box 1.Log on to RDCB-SRV as the CONTOSO\Administrator user account. 2.Navigate to C:\Windows\Web\RDWeb\Pages. 3.Double-click the web.config file. 4.Under AppSettings, change the following settings to true. <add key="ShowOptimizeExperience" value="true" /> <add key="OptimizeExperienceState" value="true" /> 5.Save the file, and then close Notepad. Finally, install the 3D video adapter on the personal virtual desktop. To install the 3D video adapter 1.Shut down the PVD1-CLNT virtual desktop. 2.Open Hyper-V Manager. To open Hyper-V Manager, click Start, point to Administrative Tools, and then click Hyper-V Manager. 3.Under Virtual Machines, right-click pvd1-clnt.contoso.com, and then click Settings. 4.Click Add Hardware. 5.In the Select the devices you want to add box, click Synthetic 3D Video Adapter, and then click Add. 6.Click OK to add the 3D Video Adapter. 7.Under Virtual Machines, right-click pvd1-clnt.contoso.com, and then click Start. 8.Under Virtual Machines, right-click pvd1-clnt.contoso.com, and then click Connect. 9.Log on to the PVD1-CLNT computer as a member of the local Administrators group. 10.The RemoteFX 3D Video Adapter driver will install. When you dialog box asking you to restart the computer appears, Restart Now. 就这样,可以在VDI的架构前提下安装成功Remote FX,可是由于我的CPU不支持Intel的EPT技术,所以没有办法开启虚拟机展示给大家,听说只有i7的全系列以及至强的X5500以上的CPU才支持此技术,也不知道是真是假。 相关报错的截图如下: 微软的问题解答描述也是如我所料,是因为CPU不支持EPT技术所导致的,如下: Q:An error occurred while attempting to start the selected virtual machine(s): Failed to Power on with Error ‘Unspecified error’ A:It is likely that you are attempting to start RemoteFX virtual machines on a server that does not have a SLAT-enabled processor. PS:SLAT-enabled processor – The processor in the RemoteFX server must support Second-Level Address Translation (SLAT). In virtualization scenarios, hardware-based SLAT support improves performance. On Intel-based processors, this is called Extended Page Tables (EPT), and on AMD-based processors, it is called Nested Page Tables (NPT). 若在将来在VDI架构中彻底的都部署了Remote FX,那么很多企业的Call Center或者是研发、设计的部门,都可以顺利地运行在Hyper-V上了,而无需借助WYSE或者思杰等第三方来完成声音、视频的高性能双向传输啦,这是节省成本的有效途径,也是一大创举啊!我们都将拭目以待!! 本文转自xury 51CTO博客,原文链接:http://blog.51cto.com/xury007/351497,如需转载请自行联系原作者

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

Windows Phone 8 开发初体验

Windows Phone 8 是当前除了Android、IPhone之外,第3大智能手机运行平台。作为微软技术的忠实fans,一直关注和跟进微软技术的最新进展。这里就给大家简单介绍一下,如何进行Windows Phone 8 的开发。 开发Windows Phone 8应用或者游戏,首先得搭建开发环境。 操作系统必须为64位Windows 8或以上,开发工具VS版本为VS2012或以上。这里的VS推荐安装VS2012 Ultimate版本,http://www.microsoft.com/zh-cn/download/details.aspx?id=30678。当然你也可以安装VS2013,对应操作系统Windows 8.1。 安装好VS2012 Ultimate之后,我们找到Windows平台安装程序,如果本机没有找到,也可以从这里下载安装:http://www.microsoft.com/web/downloads/。 安装好Windows平台安装程序之后我们运行它,显示如下界面。 我们在产品—工具里面选择Windows Phone SDK 8.0进行安装。当然这里面有很多其它的工具,感兴趣的可以自行添加。 安装好之后我们打开VS2012。 选择Windows Phone,可以看到里面已经安装好了各种类型的项目选项。 好的, 赶紧体验一下Windows Phone 8开发吧。我们选择Windows Phone应用程序,点击确定。VS会自动为我们创建一个可运行的默认的Windows Phone 8应用程序。 可以看到项目的结构。这不就是我们所熟悉的WPF项目的风格吗?是的,Windows Phone 8 也是采用XAML语言进行页面的布局配合C#等后台语言进行编程。如果有过WPF或者SilverLight开发经验的程序员能够很平滑的过渡到Windows Phone 8开发。因此WPF(桌面)—SilverLight(浏览器)—Windows Phone 8(智能手机),一下子掌握3门技术,而且从桌面到浏览器到智能手机,3大平台的技术都掌握了,是不是很有成就感?呵呵。 好了,创建好的默认的项目我们直接运行,即可运行手机模拟器。吐槽一下,这个Windows Phone 8 模拟器运行可比Android等运行快多了,而且感觉很流畅。 呵呵,这不是我们所熟悉的Metro风格吗?和Windows 8 一样吧?可以理解为Windows 8 的移动版,呵呵。 下面我们赶紧准备一个例子吧? 这个例子,我选择微软官方的例子来演示。源码地址为:http://code.msdn.microsoft.com/wpapps/Local-Database-Sample-57b1614c。可以自行下载,有C#和VB两种语言的版本。 这是一个本地数据库操作的例子,我们用VS2012打开这个项目。 看到了吗?设计器默认为我们进行了分栏,左侧是显示效果,右侧是代码区域。 好,我们运行这个程序。 如果你要安装到真机,可以找到这个项目Bin\Debug目录下的Xap文件(类似于Android的Apk以及IPhone的Ipa),拷贝到你的真机上或者放到微软的云盘SkyDriver上下载安装即可。 这就是这个例子所带给我们的。还有很多小例子可以从官方网站下载,感兴趣的读者赶紧动手试一试激动人心的Windows Phone 8 开发吧。 本文转自 guwei4037 51CTO博客,原文链接:http://blog.51cto.com/csharper/1356949

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

CloudDBA初体验:SQL优化建议

数据库诊断和优化过程具有相当的复杂性,通常需要专业的DBA来解决。但在云计算的今天,人力运维和支撑已经变得不可能,自动化,智能化运维和服务支持日益迫切。 阿里云数据库团队在这方面不断的探索和积累,产出了CloudDBA。其目的就是要把我们已知问题和最佳实践能够以最简单的方式告诉用户,把我们多年使用数据库的经验传承给用户,方便客户使用云上数据库,给客户带来直接的价值。CloudDBA同时也在服务着内部业务,4000+的数据库实例之前需要一个team的运维人员,到现在我们只有一个同学,运维效率大幅提升。 CloudDBA一期给客户输出的功能包含数10项,迫不及待先给大家介绍SQL优化建议功能。 SQL优化方法 SQL对技术人员来说再熟悉不过,但开发人员和数据库人员对其却有不同的理解。比如开发人员看到的如下SQL语句, 在数据库中却是另外一种视图: CloudDBA的优化功能就帮助数据库寻找最佳执行路径,将其优化成更为简洁和高效的视图: 示例1 SQL条件下推是多数开发人员忽视的问题,详细介绍及解法说明参见MySQL · 性能优化 · 条件下推到物化表 以及 MySQL · 性能优化 · MySQL常见SQL错误用法。 该例子中的SQL在开发人员工作中经常出现: 聚合子查询其实是先定义的一个视图,之后用的时候再加条件出结果(SQL看起来简洁^^); 条件是拼接出来的,或许还出了bug,匹配符号位置放错了; 这样的写法性能肯定是好不了的。 有了CloudDBA后,开发人员会得到直接的提示: 根据提示,创建索引,重写SQL性能大幅提升: 示例2 再贴一个复杂点SQL语句。继续体验一下CloudDBA的自动化建议: CloudDBA是数据库自动化运维的一个分水岭。我们会不断努力,致力于阿里云数据库用户体验的提升!CloudDBA等待你的加入,实现更多闪耀的功能 。

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

Macaca初体验-Android端(Python)

前言: Macaca 是一套面向用户端软件的测试解决方案,提供了自动化驱动,周边工具,集成方案。由阿里巴巴公司开源:http://macacajs.github.io/macaca/ 特点: 同时支持PC端和移动端(Android、iOS)自动化测试。 支持JavaScript(Node.js)、Java、Python。 周边工具:支持用例录制的UI Recorder。 本次教程将介绍如何使用Macaca进行Android端自动化测试。使用编程语言为Python3.5(Macaca只支持Python3.4以上版本) 环境安装: 1、Macaca环境+Android SDK环境+Java环境+Node环境见:Android环境配置 2、通过macaca doctor可以检查环境是否配置成功,如下图所示则表示环境均配置正常,如果有标红提示,则需要对应处理。 >>macaca doctor 3、安装Macaca Python Client,支持pip安装。 >>python3 -m pip install wd 用例编写: 项目目录F:\workspace\macaca-android\macaca-test下创建测试用例:macaca-android-sample.test.py,其中macaca-test为测试目录集。 https://github.com/macaca-sample/sample-python/blob/master/tests/macaca-android-sample.test.py 代码如下: API详解: driver.init() 初始化 driver.quit() 退出 driver.back() 返回上一步 driver.element_by_id 根据id来查找元素 driver.element_by_name 跟据name来查找元素 driver.elements_by_class_name 跟据class_name来查找元素 driver.accept_alert() alert弹框确认 driver.touch('tap', {'x':100,'y':100}) 在设备上应用触摸操作,例如:tap/doubleTap/press/pinch/rotate/drag ,操作后面填写对应坐标x,y值 driver.save_screenshot 保存截图 备注:与appium的API极为相似,熟悉appium的同学可以快速上手,定位元素的方法一致。 详细API见官网:https://macacajs.github.io/wd.py/api.html 执行用例: 1、启动macaca服务: >>macaca server --verbose //加--verbose可以看到详细的执行过程 2、执行用例: >>python3 macaca_test\macaca-android-sample.test.py 以上 作者:搁浅 出处: http://www.cnblogs.com/xiaoxi-3-/ 如果对您有帮助,请关注我的同名简书:https://www.jianshu.com/u/da1677475c27 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

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

安装运行Appium初体验

最近有空玩了一下 Appium,记录一下 1.下载Appium for windows,现在是0.12.3版本 解压后如下图 双击Appium.exe就能启动Appium界面 点击Launch开启服务 2. 下载AndroidSDK 解压后 3. 配置系统环境变量 ANDROID_HOME: C:\adt-bundle-windows-x86_64-20131030\sdk Path添加: %ANDROID_HOME%\tools;%ANDROID_HOME%\platform-tools 4. 启动AVD,耗资源啊,这时候我T400的CPU已经100%了 5. 编写Test,使用ADT安装好Maven插件,创建一个Maven项目,添加一个文件夹apps用来存放被测的app,这里测试的是ContactManager.apk pom.xml添加如下依赖 1 <dependencies> 2 <dependency> 3 <groupId>junit</groupId> 4 <artifactId>junit</artifactId> 5 <version>4.11</version> 6 <scope>test</scope> 7 </dependency> 8 <dependency> 9 <groupId>org.seleniumhq.selenium</groupId> 10 <artifactId>selenium-java</artifactId> 11 <version>LATEST</version> 12 <scope>test</scope> 13 </dependency> 14 </dependencies> 编写AndroidContactsTest 1 package com.guowen.appiumdemo; 2 3 import org.junit.After; 4 import org.junit.Before; 5 import org.junit.Test; 6 import org.openqa.selenium.*; 7 import org.openqa.selenium.interactions.HasTouchScreen; 8 import org.openqa.selenium.interactions.TouchScreen; 9 import org.openqa.selenium.remote.CapabilityType; 10 import org.openqa.selenium.remote.DesiredCapabilities; 11 import org.openqa.selenium.remote.RemoteTouchScreen; 12 import org.openqa.selenium.remote.RemoteWebDriver; 13 import java.io.File; 14 import java.net.URL; 15 import java.util.List; 16 17 public class AndroidContactsTest { 18 private WebDriver driver; 19 20 @Before 21 public void setUp() throws Exception { 22 // set up appium 23 File classpathRoot = new File(System.getProperty("user.dir")); 24 File appDir = new File(classpathRoot, "apps/ContactManager"); 25 File app = new File(appDir, "ContactManager.apk"); 26 DesiredCapabilities capabilities = new DesiredCapabilities(); 27 capabilities.setCapability("device","Android"); 28 capabilities.setCapability(CapabilityType.BROWSER_NAME, ""); 29 capabilities.setCapability(CapabilityType.VERSION, "4.4"); 30 capabilities.setCapability(CapabilityType.PLATFORM, "WINDOWS"); 31 capabilities.setCapability("app", app.getAbsolutePath()); 32 capabilities.setCapability("app-package", "com.example.android.contactmanager"); 33 capabilities.setCapability("app-activity", ".ContactManager"); 34 driver = new SwipeableWebDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities); 35 } 36 37 @After 38 public void tearDown() throws Exception { 39 driver.quit(); 40 } 41 42 @Test 43 public void addContact(){ 44 WebElement el = driver.findElement(By.name("Add Contact")); 45 el.click(); 46 List<WebElement> textFieldsList = driver.findElements(By.tagName("textfield")); 47 textFieldsList.get(0).sendKeys("Some Name"); 48 textFieldsList.get(2).sendKeys("Some@example.com"); 49 driver.findElement(By.name("Save")).click(); 50 } 51 52 public class SwipeableWebDriver extends RemoteWebDriver implements HasTouchScreen { 53 private RemoteTouchScreen touch; 54 55 public SwipeableWebDriver(URL remoteAddress, Capabilities desiredCapabilities) { 56 super(remoteAddress, desiredCapabilities); 57 touch = new RemoteTouchScreen(getExecuteMethod()); 58 } 59 60 public TouchScreen getTouch() { 61 return touch; 62 } 63 } 64 } 6. 运行Test,注意AVD里的Android如果没有解锁需要先解锁 这时候我们可以看到AVD在运行了, 同时Appium的命令行有对应的输出 7. 更多信息请参考Appium的Github 最新内容请见作者的GitHub页:http://qaseven.github.io/

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

TiDB 之 TiCDC6.0 初体验

作者: JiekeXu 原文来源:https://tidb.net/blog/2dc4482b TiCDC 是一款 TiDB 增量数据同步工具,通过拉取上游 TiKV 的数据变更日志,具有将数据还原到与上游任意时刻一致的能力,同时提供开放数据协议(TiCDC Open Protocol),支持其他系统订阅数据变更,TiCDC 可以将数据解析为有序的行级变更数据输出到下游。 TiCDC 的系统架构如下图所示: TiCDC 运行时是一种无状态节点,通过 PD 内部的 etcd 实现高可用。TiCDC 集群支持创建多个同步任务,向多个不同的下游进行数据同步。 系统角色 TiKV CDC 组件:只输出 key-value (KV) change log。 o内部逻辑拼装 KV change log。 o提供输出 KV change log 的接口,发送数据包括实时 change log 和增量扫的 change log。 capture:TiCDC 运行进程,多个 capture 组成一个 TiCDC 集群,负责 KV change log 的同步。 o每个 capture 负责拉取一部分 KV change log。 o对拉取的一个或多个 KV change log 进行排序。 o向下游还原事务或按照 TiCDC Open Protocol 进行输出。 原理 原理:TiDB Server 负责接收 SQL,然后调用 TiKV 各个节点,然后输出自己节点的改变日志,然后将日志传到 TiCDC 集群,每个集群的 Capture 实际上为 TiCDC 节点,TiCDC 在内部逻辑拼装接收到的日志,提供输出日志的接口,发送到下游的 MySQL、Kafka 等。 每个 Capture 负责拉取一部分日志,然后自己排序,各个 capture 协同将自己接收的日志发送给capture 选择出来的owner,owner 进一步将日志排序,发送给目标下游端。 TiCDC 适用场景 TiCDC 适合源数据库为 TiDB,目标数据库支持 MySQL 兼容的任何数据库和 Kafka,同时 TiCDC Open Protocol 是一种行级别的数据变更通知协议,为监控、缓存、全文索引、分析引擎、异构数据库的主从复制等提供数据源。 数据库灾备:TiCDC 可以用于同构数据库之间的灾备场景,能够在灾难发生时保证主备集群数据的最终一致性,目前该场景仅支持 TiDB 作为主备集群。 数据集成:TiCDC 提供 TiCDC Canal-JSON Protocol,支持其他系统订阅数据变更,能够为监控、缓存、全文索引、数据分析、异构数据库的主从复制等场景提供数据源。 生产环境推荐配置 一般生产环境最低需要两台 16c 64G SSD 硬盘 万兆网卡的机器资源,如果是测试、学习环境,配置不需要这么高,也可以使用一个节点。 TiCDC环境部署 一般分两种情况:可以前期随 TiDB 一起部署,也可以后期进行扩容部署。 前期使用 tiup 部署 可以在 topology.yaml 文件中增加 tiup cluster deploy jiekexu-tidb v6.0.0 ./topology.yaml --user root -p cdc_servers 约定了将 TiCDC 服务部署到哪些机器上,同时可以指定每台机器上的服务配置。 gc-ttl:TiCDC 在 PD 设置的服务级别 GC safepoint 的 TTL (Time To Live) 时长,单位为秒,默认值为 86400,即 24 小时。 port:TiCDC 服务的监听端口,默认 8300 后期扩容 TiCDC 根据 保姆级分布式数据库 TiDB 6.0 集群安装手册 检查集群状态 tiup cluster status jiekexu-tidb 如果没有启动集群,需要先启动 tiup cluster start jiekexu-tidb 编辑扩容配置文件,准备将 TiCDC 节点 192.168.75.15/16 加入到集群中去. vim scale-out.yaml cdc_servers: - host: 192.168.75.15 gc-ttl: 86400 data_dir: /tidb-data/cdc-data/cdc-8300 - host: 192.168.75.16 gc-ttl: 86400 data_dir: /tidb-data/cdc-data/cdc-8300 加入 2 个 TiCDC 节点,IP 为 192.168.75.15/16,端口默认 8300,软件部署默认在 /tidb-deploy/cdc-8300 中,日志部署在 /tidb-deploy/cdc-8300/log 中,数据目录在 /tidb-data/cdc-data/cdc-8300 中。 使用 tiup 为原有 TiDB 数据库集群扩容 TiCDC 节点。 tiup cluster scale-out jiekexu-tidb scale-out.yaml -uroot -p [root@jiekexu1 ~]# tiup cluster scale-out jiekexu-tidb scale-out.yaml -uroot -p tiup is checking updates for component cluster ... Starting component `cluster`: /root/.tiup/components/cluster/v1.10.2/tiup-cluster scale-out jiekexu-tidb scale-out.yaml -uroot -p Input SSH password: + Detect CPU Arch Name - Detecting node 192.168.75.15 Arch info ... Done - Detecting node 192.168.75.16 Arch info ... Done + Detect CPU OS Name - Detecting node 192.168.75.15 OS info ... Done - Detecting node 192.168.75.16 OS info ... Done Please confirm your topology: Cluster type: tidb Cluster name: jiekexu-tidb Cluster version: v6.0.0 Role Host Ports OS/Arch Directories ---- ---- ----- ------- ----------- cdc 192.168.75.15 8300 linux/x86_64 /tidb-deploy/cdc-8300,/tidb-data/cdc-data/cdc-8300 cdc 192.168.75.16 8300 linux/x86_64 /tidb-deploy/cdc-8300,/tidb-data/cdc-data/cdc-8300 Attention: 1. If the topology is not what you expected, check your yaml file. 2. Please confirm there is no port/directory conflicts in same host. Do you want to continue? [y/N]: (default=N) y + [ Serial ] - SSHKeySet: privateKey=/root/.tiup/storage/cluster/clusters/jiekexu-tidb/ssh/id_rsa, publicKey=/root/.tiup/storage/cluster/clusters/jiekexu-tidb/ssh/id_rsa.pub + [Parallel] - UserSSH: user=tidb, host=192.168.75.16 + [Parallel] - UserSSH: user=tidb, host=192.168.75.14 + [Parallel] - UserSSH: user=tidb, host=192.168.75.15 + [Parallel] - UserSSH: user=tidb, host=192.168.75.13 + [Parallel] - UserSSH: user=tidb, host=192.168.75.12 + [Parallel] - UserSSH: user=tidb, host=192.168.75.11 + [Parallel] - UserSSH: user=tidb, host=192.168.75.17 + [Parallel] - UserSSH: user=tidb, host=192.168.75.17 + [Parallel] - UserSSH: user=tidb, host=192.168.75.17 + [Parallel] - UserSSH: user=tidb, host=192.168.75.17 + Download TiDB components - Download cdc:v6.0.0 (linux/amd64) ... Done + Initialize target host environments + Deploy TiDB instance - Deploy instance cdc -> 192.168.75.15:8300 ... Done - Deploy instance cdc -> 192.168.75.16:8300 ... Done + Copy certificate to remote host ……………………省略中间信息…………………… + Refresh components conifgs - Generate config pd -> 192.168.75.12:2379 ... Done - Generate config pd -> 192.168.75.13:2379 ... Done - Generate config pd -> 192.168.75.14:2379 ... Done - Generate config tikv -> 192.168.75.15:20160 ... Done - Generate config tikv -> 192.168.75.16:20160 ... Done - Generate config tikv -> 192.168.75.17:20160 ... Done - Generate config tidb -> 192.168.75.11:4000 ... Done - Generate config cdc -> 192.168.75.15:8300 ... Done - Generate config cdc -> 192.168.75.16:8300 ... Done - Generate config prometheus -> 192.168.75.17:9090 ... Done - Generate config grafana -> 192.168.75.17:3000 ... Done - Generate config alertmanager -> 192.168.75.17:9093 ... Done + Reload prometheus and grafana - Reload prometheus -> 192.168.75.17:9090 ... Done - Reload grafana -> 192.168.75.17:3000 ... Done + [ Serial ] - UpdateTopology: cluster=jiekexu-tidb Scaled cluster `jiekexu-tidb` out successfully 部署完成后检查集群状态,发现 TiCDC 已经部署到两节点了。我们看到 TiCDC 集群的 ID 为 192.168.75.15:8300, 192.168.75.16:8300,Status(状态)为 UP,表示 TiCDC 部署成功。 执行缩容操作 tiup cluster scale-in jiekexu-tidb --node 192.168.75.15:8300 这里仅下掉 75.15其中 --node 参数为需要下线节点的 ID。预期输出 Scaled cluster jiekexu-tidb in successfully 信息,表示缩容操作成功。 TiCDC 管理工具初尝试 cdc cli 是指通过 cdc binary 执行 cli 子命令,在以下接口描述中,通过 cdc binary 直接执行 cli 命令,PD 的监听 IP 地址为 192.168.75.12,端口2379。 使用 tiup ctl:v6.0.0 cdc 检查 TiCDC 的状态,如下: tiup ctl:v6.0.0 cdc capture list --pd=http://192.168.75.12:2379 命令中 --pd==http://192.168.75.12:2379,可以是任何一个 PD 节点,“is-owner”: true 代表当 TiCDC 节点为 owner 节点。为 false 代表备节点。 如果使用 TiUP 工具部署 TiCDC,那么则使用 TiUP 管理,命令可以写成 tiup cdc cli 数据同步准备 首先下游需要 MySQL 数据库,并为 MySQL 数据库( 端口号为 3306 )加入时区信息,创建数据库 jiekexu,并创建表 T1 ,注意不插入数据,如下操作: 192.168.75.12 已经安装好 MySQL5.7.38 数据库实例。 su – mysql mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql -S /mysql/data/mysql3306/socket/mysql3306.sock mysql -uroot -p -P 3306 -S /mysql/data/mysql3306/socket/mysql3306.sock create database jiekexu; use jiekexu; create table T1(id int primary key, name varchar(20)); select * from T1; 然后 TiDB 端数据库准备 create database jiekexu; use jiekexu; create table T1(id int primary key, name varchar(20)); select * from T1; 创建同步任务 cd /tidb-deploy/cdc-8300/bin ./cdc cli changefeed create --pd=http://192.168.75.12:2379 --sink-uri="mysql://root:root@192.168.75.12:3306/" --changefeed-id="simple-replication-task" --sort-engine="unified" [WARN] some tables are not eligible to replicate, []model.TableName{model.TableName{Schema:"test", Table:"t1", TableID:0, IsPartition:false}} Could you agree to ignore those tables, and continue to replicate [Y/N] Y Create changefeed successfully! ID: simple-replication-task Info: {"sink-uri":"mysql://root:root@192.168.75.12:3306/","opts":{"_changefeed_id":"sink-verify"},"create-time":"2022-06-30T00:14:25.821140534+08:00","start-ts":434246584426037251,"target-ts":0,"admin-job-type":0,"sort-engine":"unified","sort-dir":"","config":{"case-sensitive":true,"enable-old-value":true,"force-replicate":false,"check-gc-safe-point":true,"filter":{"rules":["*.*"],"ignore-txn-start-ts":null},"mounter":{"worker-num":16},"sink":{"dispatchers":null,"protocol":"","column-selectors":null},"cyclic-replication":{"enable":false,"replica-id":0,"filter-replica-ids":null,"id-buckets":0,"sync-ddl":false},"scheduler":{"type":"table-number","polling-time":-1},"consistent":{"level":"none","max-log-size":64,"flush-interval":1000,"storage":""}},"state":"normal","error":null,"sync-point-enabled":false,"sync-point-interval":600000000000,"creator-version":"v6.0.0"} 说明: –changefeed-id:同步任务的 ID,格式需要符合正则表达式 [1]+(-[a-zA-Z0-9]+)*$。如果不指定该 ID,TiCDC 会自动生成一个 UUID(version 4 格式)作为 ID。 –sink-uri:同步任务下游的地址,需要按照以下格式进行配置,目前 scheme 支持 mysql/tidb/kafka/pulsar。 –sort-engine:指定 changefeed 使用的排序引擎。因 TiDB 和 TiKV 使用分布式架构,TiCDC 需要对数据变更记录进行排序后才能输出。该项支持 unified(默认)/memory/file: unified:优先使用内存排序,内存不足时则自动使用硬盘暂存数据。该选项默认开启。 memory:在内存中进行排序。 不建议使用,同步大量数据时易引发 OOM。 file:完全使用磁盘暂存数据。已经弃用,不建议在任何情况使用。 查看同步任务 ./cdc cli changefeed list --pd=http://192.168.75.12:2379 [ { "id": "simple-replication-task", "summary": { "state": "normal", "tso": 434246659203137537, "checkpoint": "2022-06-30 00:19:03.469", "error": null } } ] 注意:“state”: “normal” : 表示任务状态正常。 “tso”: 434246659203137537: 表示同步任务的时间戳信息。 “checkpoint”: “2022-06-30 00:19:03.469” :表示同步任务的时间。 详细查询复制任务信息 { "info": { "sink-uri": "mysql://root:root@192.168.75.12:3306/", "opts": { "_changefeed_id": "sink-verify" }, "create-time": "2022-06-30T00:14:25.821140534+08:00", "start-ts": 434246584426037251, "target-ts": 0, "admin-job-type": 0, "sort-engine": "unified", "sort-dir": "", "config": { "case-sensitive": true, "enable-old-value": true, "force-replicate": false, "check-gc-safe-point": true, "filter": { "rules": [ "*.*" ], "ignore-txn-start-ts": null }, "mounter": { "worker-num": 16 }, "sink": { "dispatchers": null, "protocol": "", "column-selectors": null }, "cyclic-replication": { "enable": false, "replica-id": 0, "filter-replica-ids": null, "id-buckets": 0, "sync-ddl": false }, "scheduler": { "type": "table-number", "polling-time": -1 }, "consistent": { "level": "none", "max-log-size": 64, "flush-interval": 1000, "storage": "" } }, "state": "normal", "error": null, "sync-point-enabled": false, "sync-point-interval": 600000000000, "creator-version": "v6.0.0" }, "status": { "resolved-ts": 434246739838369793, "checkpoint-ts": 434246739313819649, "admin-job-type": 0 }, "count": 0, "task-status": [ { "capture-id": "9163a533-97e2-4b64-838a-139c70ea89f3", "status": { "tables": null, "operation": null, "admin-job-type": 0 } }, { "capture-id": "6155ee47-1e22-4369-b2b7-670c43b11b46", "status": { "tables": null, "operation": null, "admin-job-type": 0 } } ] } 数据同步测试 对于同步任务进行验证,登录 TiDB 数据库,查询刚刚创建的 jiekexu 数据库下面的表 T1,并且插入三行数据,如下所示: 源端插入数据 insert into T1 values(1,'jiekexu'); insert into T1 values(2,'jiekexu dba'); insert into T1 values(2,'jiekexu tidb'); select * from T1; 登录 MySQL 数据库,查询 jiekexu 数据库下面的表 T1,发现数据库已经同步过去,如下所示: mysql> select * from T1; +----+--------------+ | id | name | +----+--------------+ | 1 | jiekexu | | 2 | jiekexu dba | | 3 | jiekexu tidb | +----+--------------+ 3 rows in set (0.00 sec) 源端更新、删除数据 mysql> update T1 set name='jiekexu tidb dba' where id=3; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from T1; +----+------------------+ | id | name | +----+------------------+ | 1 | jiekexu | | 2 | jiekexu dba | | 3 | jiekexu tidb dba | +----+------------------+ 3 rows in set (0.00 sec) mysql> delete from T1 where id=1; Query OK, 1 row affected (0.01 sec) mysql> select * from T1; +----+------------------+ | id | name | +----+------------------+ | 2 | jiekexu dba | | 3 | jiekexu tidb dba | +----+------------------+ 2 rows in set (0.01 sec) 目标端 MySQL 端查看 mysql> select * from T1; +----+------------------+ | id | name | +----+------------------+ | 1 | jiekexu | | 2 | jiekexu dba | | 3 | jiekexu tidb dba | +----+------------------+ 3 rows in set (0.00 sec) mysql> select * from T1; +----+------------------+ | id | name | +----+------------------+ | 2 | jiekexu dba | | 3 | jiekexu tidb dba | +----+------------------+ 2 rows in set (0.00 sec) 源端添加、修改、删除列 alter table T1 add addr varchar(50); alter table T1 modify name varchar(32); alter table t1 add changeTime datetime default now(); Alter table t1 drop column changeTime; 目标端查看也能正常同步。 源端新建表数据插入测试 CREATE TABLE `Test` ( `id` int(11) NOT NULL, `name` varchar(32) DEFAULT NULL, `addr` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ); insert into Test values(1,'jiekexu','beijing'); 目标端 MySQL 端查看 停止同步任务 ./cdc cli changefeed --help Manage changefeed (changefeed is a replication task) Usage: cdc cli changefeed [flags] cdc cli changefeed [command] Available Commands: create Create a new replication task (changefeed) cyclic (Experimental) Utility about cyclic replication list List all replication tasks (changefeeds) in TiCDC cluster pause Pause a replication task (changefeed) query Query information and status of a replication task (changefeed) remove Remove a replication task (changefeed) resume Resume a paused replication task (changefeed) statistics Periodically check and output the status of a replication task (changefeed) update Update config of an existing replication task (changefeed) ./cdc cli changefeed pause --pd=192.168.75.12:2379 --changefeed-id simple-replication-task ./cdc cli changefeed pause --pd=http://192.168.75.12:2379 --changefeed-id simple-replication-task 注意:pause 停止任务时,pd 后面也可以不跟 http:// 协议,不会报错。 恢复同步任务 ./cdc cli changefeed resume --pd=http://192.168.75.12:2379 --changefeed-id simple-replication-task 注意:Pd 后面需要跟 http 协议,不然会报错。 删除同步任务 ./cdc cli changefeed remove --pd=http://192.168.75.12:2379 --changefeed-id simple-replication-task TiCDC 的限制 有效索引的相关要求 TiCDC 只能同步至少存在一个有效索引的表,有效索引的定义如下: 主键 (PRIMARY KEY) 为有效索引。 同时满足下列条件的唯一索引 (UNIQUE INDEX) 为有效索引: 索引中每一列在表结构中明确定义非空 (NOT NULL)。 索引中不存在虚拟生成列 (VIRTUAL GENERATED COLUMNS)。 TiCDC 从 4.0.8 版本开始,可通过修改任务配置来同步没有有效索引的表,但在数据一致性的保证上有所减弱。具体使用方法和注意事项参考同步没有有效索引的表。 暂不支持的场景 目前 TiCDC 暂不支持的场景如下: 暂不支持单独使用 RawKV 的 TiKV 集群。 暂不支持在 TiDB 中创建 SEQUENCE 的 DDL 操作和 SEQUENCE 函数。在上游 TiDB 使用 SEQUENCE 时,TiCDC 将会忽略掉上游执行的 SEQUENCE DDL 操作/函数,但是使用 SEQUENCE 函数的 DML 操作可以正确地同步。 对上游存在较大事务的场景提供部分支持,详见 TiCDC 是否支持同步大事务?有什么风险吗? 参考链接: https://docs.pingcap.com/zh/tidb/v6.0/ticdc-overview https://learn.pingcap.com/learner/course/30002

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

Android DataBinding使用(一):DataBinding初体验

目录 前言 MVVM ( Model — View — ViewModel )最初是在2005年由微软提出的一个UI架构概念 。 相比MVP模式,MVVM将Presenter改为了 ViewModel,同时实现View和VievvModel的双向绑定。 View层的变化会自动导致ViewMmlel发生变化,ViewModel的数据变化也会自动实现View的 刷新,开发者可以不用直接处理View和数据的更新操作,MVVM框架会完成这一切,MVVM 模式不同层之间关系如图所示。 在Google I/O 2015大会上,Android幵发闭队发布了官方 的MVVM模式支持函数库 Data Binding Library。Data Binding Library 是一个兼容函数库,可以在 Android 2.1 ( API Level 7 )及之 后的Android系统上面使用在使用Data Binding之前 , 需要确保Gradle的Android Studio插 件 版本大于或等于1.5.0-alphal,而且Amlmid Studio的版本号应该大于或等于1.3。 使用步骤 首先需要在module的build.gradle文件中添加如下代码,Android Studio会自动为我们集成需要的依赖库。 android { ... dataBinding { enabled = true } } 新建一个JavaBean对象,这里我把它命名为UserBean。 public class UserBean { private String userName; private int age; public UserBean(String userName, int age) { this.userName = userName; this.age = age; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } 编辑使用了DataBinding的Activity布局文件(这里是activity_main.xml),它与普通的布局文件有些不同,它的根标签是layout里面包含了data和以前常用的布局,其中data标签是用来实现数据绑定的,在data标签内定义了一个名为user的属性变量,类型是UserBean,并在控件中使用@{user.userName}的方式将数据绑定到控件上。 <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <data> <variable name="user" type="com.itfitness.databindingdemo.bean.UserBean"/> </data> <LinearLayout android:layout_width="match_parent" android:orientation="vertical" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:text="@{user.userName}" android:layout_gravity="center_horizontal" android:layout_height="wrap_content" /> <TextView android:layout_width="wrap_content" android:text='@{user.age+""}' android:layout_gravity="center_horizontal" android:layout_height="wrap_content" /> </LinearLayout> </layout> 在Activity中添加数据 public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); binding.setUser(new UserBean("<张三>",18)); } } 这里的ActivityMainBinding是自动生成的,它是根据布局文件的名字来生成的:如activity_main-->ActivityMainBinding 、fragment-->FragmentBinding。 运行结果 技术博客:https://myml666.github.io

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

Docker初体验,关于Dockerfile那点事

一、Dockerfile的格式 Dockerfile的格式如下: # Comment 以“#”开头的行为注释行。跨行注释也必须加“#”,Dockerfile不支持连续字符“”。命令解析指令也是以“#”开头,命令解析器是一个可选项,位于Dockerfile的首行,只允许出现一次,第二次出现则被认为是注释,在解析器中换行符同样是不被支持的,但是其中的非断行空格是允许的。 #directive=value # directive =value # directive= value # directive = value # escap Escape在dockerfile中被用作转义字符和换行符,如果不特别指定,系统默认的转义字符为: (反斜杠)。转义不能在RUN命令中执行,除非位于行末进行格式换行。作为换行符时,escape允许Dockerfile指令跨行执行。反引号在Windows下非常有用(举例可以参阅官方文档) # escape=\ (反斜杠) 或 # escape=` (反引号) INSTRUCTION arguments INSTRUCTION一般被称为指令或者命令,对大小写不敏感,为了与其他参数区别开,习惯大写。 .dockerfileignore file 使用Dockerfile构建镜像时最好是将Dockerfile放置在一个新建的空目录下。然后将构建镜像所需要的文件添加到该目录中。为了提高构建镜像的效率,你可以在目录下新建一个.dockerignore文件来指定要忽略的文件和目录。.dockerignore文件的排除模式语法和 Git的.gitignore文件相似。 二、相关指令详解 FROM 每个Dockerfile必须以FROM指令开头,FROM指明了当前镜像创建的基镜像,也就是说每个镜像必须基于一个已存在的镜像进行创建。FROM指令后直接跟基镜像的名称或者镜像名称加标签。镜像的名称和标签可以去Docker Hub或者使用命令docker search keyword 进行搜索。用法如下: FROM <image> 或 FROM <image>[:<tag>] ARG ARG指令定义了用户可以在创建镜像时或者运行时传递的变量,申明于调用类似于shell中的变量申明与定义。 ARG CODE_VERSION=latest FROM base:${CODE_VERSION} ENV ENV指令用来定义镜像的环境变量,并且可以引用已经存在的环境变量,例如:HOME、HOSTNAME、PATH。ENV的值跟ARG指令申明的变量一样可以传递、被引用,定义方法也基本一致。 FROM busybox ENV foo /bar # WORKDIR /bar WORKDIR ${foo} Dockerfile中的ENV支持以下变量的访问:ADD、COPY、ENV、EXPOSE、FROM、LABEL、STOPSIGNAL、USER、VOLUME、WORKDIR。 RUN RUN指令在当前镜像的顶层中执行命令并提交结果,新产生的镜像用于下一步的Dockerfile。分层执行指令和生成提交符号Docker的核心概念,提交很方便,容器可以从镜像历史中的任意点创建,类似于源码控制。在shell形式中,可以使用(反斜杠)将单个RUN指令继续到下一行。RUN指令有两种使用格式: RUN <command>(shell形式,该命令在shell中运行,默认情况下/bin/sh -c在Linux中运行,cmd /S /CWindows中运行) RUN ["executable", "param1", "param2"](exec执行形式) [root@ChatDevOps ~]# cat Dockerfile FROM centos RUN mkdir /chatdevops RUN ["touch","/chatdevops/chatdevops.log"] RUN /bin/bash [root@ChatDevOps ~]# docker run -it --name chatdevops chatdevops /bin/bash [root@99484f802e71 /]# ll /chatdevops/chatdevops.log -rw-r--r--. 1 root root 0 May 29 03:00 /chatdevops/chatdevops.log CMD CMD的主要是为一个正运行的容器提供默认执行命令。如果存在多个CMD指令,那么只有最后一个会被执行。如果在容器运行时指定了命令,则CMD指定的默认内容会被替代。CMD一共有三种格式: CMD ["executable","param1","param2"] #(执行形式,这是比较常见的一种形式) CMD ["param1","param2"] #(以json数组的形式将两个参数存储下来,在指定了ENTRYPOINT 指令后,用CMD指定具体的参数,此处必须用双引号将涉及到的变量引起来) CMD command param1 param2 #(shell形式) [root@ChatDevOps ~]# cat Dockerfile FROM centos CMD echo "chatdevops" CMD ["echo","Hello world"] [root@ChatDevOps ~]# docker run -it --rm --name chatdevops chatdevops Hello world ENTRYPOINT ENTRYPOINT的格式和RUN指令格式一样,分为exec格式和shell格式。ENTRYPOINT的目的和CMD一样,都是在指定容器启动程序及参数。ENTRYPOINT在运行时 也可以替代,不过比CMD要略显繁琐,需要通过docker run的参数--entrypoint来指定。当指定了ENTRYPOINT后,CMD的含义就发生了改变,不再是直接的运行其命令,而是将CMD的内容作为参数传给ENTRYPOINT指令。 [root@ChatDevOps ~]# cat Dockerfile FROM ubuntu:18.04 RUN apt-get update \ && apt-get install -y curl \ && rm -rf /var/lib/apt/lists/* ENTRYPOINT [ "curl","-s","http://ip.cn" ] [root@ChatDevOps ~]# docker run e317e4042076 当前 IP:71.184.25.21 来自:北京市 [root@ChatDevOps ~]# docker run e317e4042076 -i HTTP/1.1 200 OK Date: Tue, 29 May 2018 09:00:10 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive Set-Cookie: __cfduid=d4439884e43e37c36aac129e9f4d0507f1527584410; expires=Wed, 29-May-19 09:00:10 GMT; path=/; domain=.ip.cn; HttpOnly Server: cloudflare CF-RAY: 4227c4e460886d9c-SJC LABEL LABEL指令用于添加一个元数据到镜像,键和值配对存在。例如可以给容器添加辅助说明信息。值中支持换行字符斜杠()。如果Docker中出现重复的键,则新的值会覆盖原来的值。为了减少Docker的层数,可以在单一LABEL指令中指定多个标签: LABEL multi.label1="value1" \ multi.label2="value2" \ other="value3" MAINTAINER MAINTAINER在新版本中已经废弃,可以使用LABEL来替代MAINTAINER进行声明。 EXPOSE EXPOSE指定容器在运行中监听的端口。默认情况下,EXPOSE指定的是TCP端口,若要指定监听udp端口: EXPOSE 80/udp COPY COPY能够从构建上下文中复制文件到新的一层中镜像中,COPY指令有两种形式: COPY [--chown=<user>:<group>] <src>... <dest> COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] chown属性只支持Linux容器的构建。COPY命令支持通配符,可以把多个源文件复制到目标文件下。 ADD ADD的格式和用法基本与COPY一致,并在COPY的基础上新增了一些功能。ADD的源文件可以是一个URL。如果本地源路径的文件为一个tar压缩文件的话,压缩格式为gzip,bzip2以及xz的情况 下,ADD指令将会自动解压缩这个压缩文件到目标路径,来自于URL的远程文件则不会被解压。 VOLUME VOLUME旨在创建一个具有名称的挂载点。容器在运行时尽量保持存储层不发生数据写入操作。一个卷可以存在于一个或多个容器的特定目录,这个目录可以绕过联合文件系统,并提供数据共享或数据持久化功能。卷可以在容器间共享或重用,对卷的修改是及时生效的。对卷的修改不会对新的镜像产生影响,卷会一直存在直到没有容器使用它。可以使用数组的形式指定多个卷。使用方式如下: VOLUME /data VOLUME ["/data"] VOLUME ["data","test","chatdevops"] VOLUME也可以在创建容器时进行声明: [root@ChatDevOps docker]# docker run -it -v /myvolume --name myvolume chatdevops 以上命令创建一个名为myvolume的容器,同时挂载/myvolume。/myvolume在之前并不存在,在创建myvolume时同时创建了该目录。 USER USER指令为Dockerfile中全部RUN,CMD,ENTRYPOINT设置运行Image时使用的用户名或UID。这个用户或组必须事先在系统中存在。若不存在则下一层镜像以root用户进行执行。 USER <user>[:<group>] USER <UID>[:<GID>] WORKDIR WORKDIR用来为Dockerfile下文中的RUN, CMD, ENTRYPOINT, COPY和ADD等指令指定当前工作目录。如果存在多个WORKDIR则以指令钱最近的一条为参考。如果该目录不存在,则系统会自动创建该目录。如果要改变当前的工作目录,不能使用cd命令来切换,需要使用WORKDIR来进行切换。 ONBUILD 这是一个特殊的指令,它后面跟的是其它指令,比如 RUN , COPY等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。 STOPSIGNAL STOPSIGNAL指令设置唤醒信号并将其发送到容器后退出。后跟信号值(无符号整数)或者SIGNAME格式的信号名称,例如SIGKILL。 STOPSIGNAL signal HEALTHCHECK Docker提供了HEALTHCHECK指令,通过该指令指定一行命令,用这行命令来判断容器主进程的服务状态是否还正常,从而比较真实的反应容器实际状态。当在一个镜像指定了HEALTHCHECK指令后,用其启动容器,初始状态会为 starting ,在HEALTHCHECK指令检查成功后变为healthy,如果连续一定次数失败,则会变为unhealthy。格式如下: HEALTHCHECK [OPTIONS] CMD command (check container health by running a command inside the container) HEALTHCHECK NONE (disable any healthcheck inherited from the base image) HEALTHCHECK 支持下列选项: --interval=<间隔> :两次健康检查的间隔,默认为 30 秒; --timeout=<时长> :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒; --retries=<次数> :当连续失败指定次数后,则将容器状态视为 unhealthy ,默认3次。 HEALTHCHECK在Dockerfile中只能出现一次,如果出现多次则最后一个生效。 SHELL SHEELL指令允许默认的shell形式被命令形式覆盖。在Linux系统中默认shell形式为 [“/bin/sh”, “-c”], 在 Windows上是[“cmd”, “/S”, “/C”]。SHELL指令必须用Dockerfile中的JSON格式写入。SHELL指令在Windows上特别有用,其中有两个常用的和完全不同的本机shell:cmd和powershell,以及包括sh的备用shell。 SHELL指令可以出现多次。每个SHELL指令都会覆盖所有以前的SHELL指令,并影响所有后续指令。 三、参考文献 https://docs.docker.com/engine/reference/builder/#usage

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Spring

Spring

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

Rocky Linux

Rocky Linux

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

Sublime Text

Sublime Text

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

用户登录
用户注册