`
lixw
  • 浏览: 200940 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

持续集成简单总结

阅读更多

一、理论篇:

 

       持续集成鼓励尽量短周期内项目团队的代码提交,同时保证每次check in都不会损害我们的构建通过。它跟每日构建的区别就在于代码提交频率更高(一般为一个小时),构建的频率也更高,这样做的目的就是为了快速反馈,使得BUG越早被发现,并能以邮件或者消息(甚至短信)的形式快速反馈给开发人员,从而快速解决问题,并保证构建成功。

 

二、工具篇:

 

       持续集成重在COC(Conversion Over Configuration:约定由于配置),这样选择合适的支持持续集成的工具就相当重要。庆幸的是我们有许多开源的选择,但是首先我们需要了解持续集成的实现架构:

CI架构图

 

       从上图中我们看到,客户端提交代码更改到源代码仓库,CI服务器会检测到代码库的修改,它会检出代码,在本地构建,构建成功,会将构建结果反馈回客户端,同时可能将构建的可运行代码发布到WEB服务器上。

       所以,我们就需要各个节点的工具支持:

       对于SCM工具,我们的选择的开源工具有CVS、SVN等,这也没有特殊的取舍,就自己的爱好和公司的已有平台而定。我们这里假设使用SVN作为版本管理工具,它的中文站是:http://www.subversion.org.cn/

       对于构建工具,我们的选择的开源工具有Ant和Maven等,Ant通过一些内置的和扩展的Task来实现包括文件操作、编译、测试、代码检查、打包等操作,Eclipse默认提供了对Ant的支持。通过在build.xml中配置一系列相互依赖的任务来实现我们定义的构建过程。Maven是一个以项目为模型的构建,项目管理工具,注意它并不是为了替代Ant(同时支持运行Ant脚本),而是以另一种视觉提供了对软件生命周期的管理,它通过插件的方式提供了类似于Ant任务的功能,它的特色之处在于对项目依赖组件的统一管理,同时它的生成站点功能也是一个不错的特性,具体不再赘述,后面的构建我们会分别用Ant和Maven来说明。

        对于持续集成工具,我们的选择的开源工具有CruiseControl(后面简称CC)和Hudson。BuildLoop是CC的核心, 这个BuildLoop包含插件支持,详细介绍可以参照附件中的电子书。CC的实施结构如下图所示:

 

        为了完成上面的结构,CC提供的插件按照下图所示的流程完成构建:

 

        通过对CC的cruisecontrol项目的配置,支持图形化显示checkstyle, pmd, findbugs, cobertura, javadoc等报告。同时CC-Config也提供了对项目的图形化配置,比较方便。

        Hudson也提供了持续集成服务器的大多数功能,详细参考官方站点:https://hudson.dev.java.net/

 

三、实践篇:

 

         我们模拟了两个项目,一个AntBasedCI是基于Ant构建的客户端应用程序,一个MavenBasedCI是基于Maven构建的Web应用程序,我们的SCM由SVN自带的Server提供,启动这个服务,我们需要在命令行运行: svnserve -d -r D:\repos 其中-r指定repository磁盘位置。

        CC的项目配置:

<cruisecontrol>
   <project requiremodification="false" forceonly="false" name="MavenBasedCI">
    <modificationset QuietPeriod="30">
      <svn LocalWorkingCopy="${checkout.dir}\${project.name}" CheckExternals="false" UseLocalRevision="false" />
    </modificationset>
    <schedule Interval="300">
      <maven2 Goal="-e clean site install" MvnHome="D:\OpenSource\maven-2.0.4" PomFile="${checkout.dir}/${project.name}/pom.xml" ShowProgress="false" />
    </schedule>
    <bootstrappers>
      <svnbootstrapper LocalWorkingCopy="${checkout.dir}\${project.name}" />
    </bootstrappers>
    <listeners>
      <currentbuildstatuslistener File="${logs.dir}\${project.name}\status.txt" />
    </listeners>
    <log>
      <merge Dir="${logs.dir}\${project.name}" />
      <merge Dir="${checkout.dir}\${project.name}\target" Pattern="*.xml" />
    </log>
    <publishers>
      <onsuccess>
        <artifactspublisher Dest="${artifact.dir}\${project.name}" File="${checkout.dir}\${project.name}\target\${project.name}-1.0-SNAPSHOT.war" />
      </onsuccess>
      <artifactspublisher File="${checkout.dir}\${project.name}\target\${project.name}-1.0-SNAPSHOT.war" Dest="artifacts/${project.name}" />
      <artifactspublisher Dir="${checkout.dir}\${project.name}\target\site" Dest="artifacts/${project.name}" />
    </publishers>
    <property name="checkout.dir" value="${basedir}\checkout" />
    <property name="logs.dir" value="${basedir}\logs" />
    <property name="artifact.dir" value="${basedir}\artifacts" />
  </project>
  <project requiremodification="false" forceonly="false" name="AntBasedCI">
    <modificationset QuietPeriod="30">
      <svn LocalWorkingCopy="${checkout.dir}\${project.name}" CheckExternals="false" UseLocalRevision="false" />
    </modificationset>
    <schedule Interval="300">
      <ant AntHome="D:\OpenSource\apache-ant-1.7.1" BuildFile="${checkout.dir}\${project.name}\build.xml" Target="all" />
    </schedule>
    <bootstrappers>
      <svnbootstrapper LocalWorkingCopy="${checkout.dir}\${project.name}" />
    </bootstrappers>
    <listeners>
      <currentbuildstatuslistener File="${logs.dir}\${project.name}\status.txt" />
    </listeners>
    <log>
      <merge Dir="${logs.dir}\${project.name}" />
      <merge Dir="${checkout.dir}\${project.name}\target" Pattern="*.xml" />
    </log>
    <publishers>
      <onsuccess>
        <artifactspublisher Dest="${artifact.dir}\${project.name}" File="${checkout.dir}\${project.name}\target\${project.name}.jar" />
      </onsuccess>
      <artifactspublisher Dest="artifacts/${project.name}" File="${checkout.dir}\${project.name}\target\${project.name}.jar" />
      <artifactspublisher Dir="${checkout.dir}\${project.name}\target" Dest="artifacts/${project.name}" />
    </publishers>
  </project>
  <dashboard />
  <property name="basedir" value="E:\CI\ccworkspace" />
  <property name="checkout.dir" value="${basedir}\checkout" />
  <property name="logs.dir" value="${basedir}\logs" />
  <property name="artifact.dir" value="${basedir}\artifacts" />
</cruisecontrol>

         上面的配置是通过CC-Config图形配置自动生成的,包含我们的两个工程。

         Ant配置build.xml

<?xml version="1.0" encoding="UTF-8"?>
<project name="AntBasedCI" default="all">

	<property name="default.target.dir" value="target" />
	<property name="classes.dir" value="${default.target.dir}/classes" />
	<property name="test.classes.dir" value="${default.target.dir}/test-classes" />
	<property name="test.report.dir" value="${default.target.dir}/test-reports" />
	<property name="lib.dir" value="${basedir}/lib" />
	<property name="javadoc.dir" value="${default.target.dir}/apidocs" />

	<property name="source.dir" value="src" />
	<property name="test.source.dir" value="test" />

	<property name="test.pattern" value="**/**Test.java" />

	<!-- Coverage reports are deposited into these directories -->
	<property name="cobertura.dir" value="${default.target.dir}/cobertura"/>
	<!-- Instrumented classes are deposited into this directory -->
	<property name="instrumented.dir" value="instrumented" />
	
	<path id="cobertura.classpath">
		<fileset dir="${lib.dir}">
			<include name="*.jar" />
		</fileset>
	</path>
	
	<taskdef classpathref="cobertura.classpath" resource="tasks.properties"/>
	
	<target name="clean">
		<delete dir="${classes.dir}"/>
		<delete dir="${test.classes.dir}"/>
		<delete dir="${default.target.dir}"/>
	</target>
	
	<target name="init" depends="clean">
		<mkdir dir="${classes.dir}" />
		<mkdir dir="${test.classes.dir}" />
		<mkdir dir="${javadoc.dir}" />
		<mkdir dir="${default.target.dir}"/>
		<mkdir dir="${instrumented.dir}"/>		
		<path id="build.classpath">
			<fileset dir="${lib.dir}">
				<include name="**/*.jar" />
			</fileset>
			<fileset dir="${default.target.dir}">
				<include name="**/*.jar" />
			</fileset>
		</path>
	</target>
	
	<target name="compile-source" depends="init" description="compiles all .java files in source directory ">
		<javac destdir="${classes.dir}" srcdir="${source.dir}" classpathref="build.classpath" />
	</target>
	
	<target name="instrument" depends="compile-source">
		<delete file="cobertura.ser"/>
		<delete dir="${instrumented.dir}" />
		
		<!--Instrument the application classes, writing the instrumented classes into ${build.instrumented.dir}.-->
		<cobertura-instrument todir="${instrumented.dir}">
			<ignore regex="org.apache.log4j.*" />
			<fileset dir="${classes.dir}">
				<!-- Instrument all the application classes, but don't instrument the test classes.-->
				<include name="**/*.class" />
				<exclude name="**/*Test.class" />
			</fileset>
		</cobertura-instrument>
	</target>
		
	<target name="jar" depends="instrument">
		<jar jarfile="${default.target.dir}/${ant.project.name}.jar" basedir="${classes.dir}" />
	</target>
	
	<target name="compile-tests" depends="jar" description="compiles all .java files in test directory ">
		<javac destdir="${test.classes.dir}" srcdir="${test.source.dir}" classpathref="build.classpath" />
	</target>

	<target name="javadoc" depends="init">
		<javadoc author="true" charset="gbk" classpathref="build.classpath" 
			destdir="${javadoc.dir}" version="true" use="true" sourcepath="${source.dir}"></javadoc>
	</target>
	
	<target name="test" depends="compile-tests" description="runs JUnit tests">
		<mkdir dir="${test.report.dir}" />
		<junit dir="${basedir}" printSummary="on" fork="true" haltonfailure="true">
			<sysproperty key="basedir" value="${basedir}" />
			<formatter type="xml" />
			<classpath>
				<path refid="build.classpath" />
				<pathelement path="${test.classes.dir}" />
				<pathelement path="${classes.dir}" />
			</classpath>
			<batchtest todir="${test.report.dir}">
				<fileset dir="${test.source.dir}">
					<include name="${test.pattern}" />
				</fileset>
			</batchtest>
		</junit>
	</target>
			
	<target name="coverage-check">
		<cobertura-check branchrate="40" totallinerate="100" />
	</target>

	<target name="coverage-report">
		<cobertura-report srcdir="${source.dir}" destdir="${cobertura.dir}" format="html" />
	</target>
	
	<target name="alternate-coverage-report">
		<!--
                Generate a series of HTML files containing the coverage
                data in a user-readable form using nested source filesets.
            -->
		<cobertura-report destdir="${cobertura.dir}">
			<fileset dir="${source.dir}">
				<include name="**/*.java"/>
			</fileset>
		</cobertura-report>
	</target>
			
	<target name="coverage" depends="jar,instrument,test,coverage-report,alternate-coverage-report"/>	
	
	<target name="pmd" depends="test">
		<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" classpathref="build.classpath"/>
		<pmd>
			<ruleset>rulesets/basic.xml</ruleset>
			<ruleset>rulesets/braces.xml</ruleset>
			<ruleset>rulesets/javabeans.xml</ruleset>
			<ruleset>rulesets/unusedcode.xml</ruleset>
			<ruleset>rulesets/strings.xml</ruleset>
			<ruleset>rulesets/design.xml</ruleset>
			<ruleset>rulesets/coupling.xml</ruleset>
			<ruleset>rulesets/codesize.xml</ruleset>
			<ruleset>rulesets/imports.xml</ruleset>
			<ruleset>rulesets/naming.xml</ruleset>
			<formatter type="xml" toFile="${default.target.dir}/pmd_report.xml" />
			<fileset dir="${source.dir}">
				<include name="**/*.java" />
			</fileset>
		</pmd>
	</target>
	
	<target name="findbugs" depends="jar">
		<taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
	    classpathref="build.classpath" />
		<findbugs classpathref="build.classpath" pluginlist="${lib.dir}/coreplugin-1.0.jar"
	    output="xml" outputFile="${default.target.dir}/findbugs.xml">
			<sourcePath path="${source.dir}" />
			<class location="${default.target.dir}/${ant.project.name}.jar" />
		</findbugs>
	</target>

	<target name="all" depends="coverage,pmd,findbugs,javadoc" />
	
</project>

         从上面的配置我们看到这个构建包括:编译、测试、测试覆盖率统计、代码检查、BUG查找、生成Javadoc和打包。

          Maven的配置pom.xml

<?xml version="1.0" encoding="GBK"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.samueli.webapp</groupId>
	<artifactId>MavenBasedCI</artifactId>
	<packaging>war</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>MavenBasedCI</name>
	<description>持续集成测试项目</description>
	<url>http://www.samueli.com</url>
	<licenses>
		<license>
			<name>Fake License</name>
			<url>http://127.0.0.1:8080/dc-license.txt</url>
			<distribution>repo</distribution>
		</license>
	</licenses>
	<!-- 问题管理 -->
	<issueManagement>
		<system>jira</system>
		<url>http://169.254.11.166:8088/secure/Dashboard.jspa</url>
	</issueManagement>
	<!-- 源代码管理 -->
	<scm>
		<connection>scm:svn://127.0.0.1/repos/MavenBasedCI</connection>
		<developerConnection>
			scm:svn://127.0.0.1/repos/MavenBasedCI
		</developerConnection>
		<tag>head</tag>
		<url>svn://127.0.0.1/repos/MavenBasedCI</url>
	</scm>

	<!-- 开发人员以及角色定义 -->
	<developers>
		<developer>
			<name>aaa</name>
			<id>aaa</id>
			<email>aaa@samueli.com</email>
			<organization>samueli</organization>
			<roles>
				<role>PM</role>
			</roles>
		</developer>
		<developer>
			<name>bbb</name>
			<id>bbb</id>
			<email>bbb@samueli.com</email>
			<organization>samueli</organization>
			<roles>
				<role>设计人员</role>
				<role>开发人员</role>
			</roles>
		</developer>
	</developers>

	<!-- 依赖定义 -->
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.3</version>
		</dependency>
	</dependencies>

	<!--依赖版本管理-->
	<dependencyManagement />
	<organization>
		<name>XXX有限责任公司</name>
		<url>http://www.samueli.com</url>
	</organization>

	<!--项目模块管理-->
	<modules />

	<build>
		<plugins>
			<plugin>
				<artifactId>maven-site-plugin</artifactId>
				<configuration>
					<locales>zh_CN</locales>
					<outputEncoding>GBK</outputEncoding>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.5</source>
					<target>1.5</target>
					<encoding>GBK</encoding>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-checkstyle-plugin</artifactId>
				<configuration>
					<outputEncoding>GBK</outputEncoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-pmd-plugin</artifactId>
				<executions>
					<execution>
						<goals>
							<goal>check</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<linkXref>true</linkXref>
					<sourceEncoding>GBK</sourceEncoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>cobertura-maven-plugin</artifactId>
				<version>2.0</version>
				<configuration>
					<instrumentation>
						<excludes>
							<exclude>
								com/samueli/MavenBasedCI/util/*.class
							</exclude>
						</excludes>
					</instrumentation>
					<check>
						<haltOnFailure>true</haltOnFailure>
						<totalLineRate>40</totalLineRate>
						<totalBranchRate>100</totalBranchRate>
					</check>
					<outputEncoding>GBK</outputEncoding>
				</configuration>
				<executions>
					<execution>
						<id>clean</id>
						<goals>
							<goal>clean</goal>
						</goals>
					</execution>
					<execution>
						<id>check</id>
						<goals>
							<goal>check</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
		<extensions>
			<extension>
				<groupId>org.apache.maven.wagon</groupId>
				<artifactId>wagon-ftp</artifactId>
				<version>1.0-alpha-6</version>
			</extension>
		</extensions>
	</build>

	<distributionManagement>
		<repository>
			<id>ftp-repository</id>
			<url>ftp://192.168.3.241</url>
		</repository>
		<site>
			<id>lmss.site</id>
			<name>MavenBasedCI</name>
			<url>file:///Y:\</url>
		</site>
	</distributionManagement>

	<!--各种报告-->
	<reporting>
		<plugins>
			<plugin>
				<artifactId>maven-site-plugin</artifactId>
				<configuration>
					<locales>zh_CN</locales>
					<outputEncoding>GBK</outputEncoding>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-surefire-report-plugin</artifactId>
				<configuration>
					<showSuccess>false</showSuccess>
					<outputEncoding>GBK</outputEncoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jxr-plugin</artifactId>
				<configuration>
					<outputEncoding>GBK</outputEncoding>
					<inputEncoding>GBK</inputEncoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-javadoc-plugin</artifactId>
				<configuration>
					<aggregate>true</aggregate>
					<charset>UTF16</charset>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-pmd-plugin</artifactId>
				<configuration>
					<rulesets>
						<ruleset>/rulesets/basic.xml</ruleset>
						<ruleset>/rulesets/imports.xml</ruleset>
						<ruleset>/rulesets/unusedcode.xml</ruleset>
						<ruleset>/rulesets/finalizers.xml</ruleset>
					</rulesets>
					<outputEncoding>GBK</outputEncoding>
					<linkXref>true</linkXref>
					<excludes>
						<exclude>
							com/samueli/MavenBasedCI/util/*.java
						</exclude>
					</excludes>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-checkstyle-plugin</artifactId>
				<configuration>
					<configLocation>lmss_checks.xml</configLocation>
					<outputEncoding>GBK</outputEncoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>taglist-maven-plugin</artifactId>
				<configuration>
					<tags>
						<tag>TODO</tag>
						<tag>@todo</tag>
						<tag>FIXME</tag>
						<tag>XXX</tag>
					</tags>
					<outputEncoding>GBK</outputEncoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>cobertura-maven-plugin</artifactId>
				<version>2.0</version>
				<configuration>
					<outputEncoding>GBK</outputEncoding>
				</configuration>
			</plugin>
<!--			<plugin>-->
<!--				<groupId>org.codehaus.mojo</groupId>-->
<!--				<artifactId>changes-maven-plugin</artifactId>-->
<!--			</plugin>-->
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>changelog-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>findbugs-maven-plugin</artifactId>
				<configuration>
					<xmlOutput>true</xmlOutput>
				</configuration>
			</plugin>

		</plugins>
	</reporting>
</project>

 

 

          Maven详细的站点生成可以参考这里:http://www.duduwolf.com/wiki/2008/766.html

          Maven生成的站点例子:

          CC生成的集成报告截图如下:

 

  四、总结篇:

 

           通过上面的简单介绍,我们基本掌握了持续集成的目的和基本理论,在Martin Fowler的文章中提到了一些最佳实践也值得参考。当然持续集成是一个在实践中不断发展和完善的过程,对于一个团队而言,引入持续集成对于提高开发效率和规范开发过程是必需的,不过在整个持续集成中,我们信赖的依据就是构建,其中的单元测试可靠性就会有一定的要求,这样对于我们开发人员,如何保证写出高质量的单元测试便是一个挑战,TDD是一个不错的实践,它完全从需求出发,逐步完善测试用例,不断减少与需求的偏差来尽量满足需求。同时引入测试覆盖率也利于我们审查我们的单元测试。CC提供的统一出口的各种报告和图表,可以更加直观和快捷的从整体上把握我们代码在构建中表现出来的健壮性(代码检查)和满足需求性(单元测试通过率、测试覆盖率),同时对于出现的问题,能够责任到人,快速反馈也是很有利于问题的解决,对于持续集成的学习刚刚开始,错误偏颇在所难免,越是深入的学习,越会有更多的感悟和思考。

 

五、参考:

 

1、Martin Fowler的文章

原文:http://martinfowler.com/articles/continuousIntegration.html
翻译:http://dev.csdn.net/develop/article/12/12286.shtm

2、Juven的一篇原创文章

http://juvenshun.spaces.live.com/blog/cns!CF7D1BC903C111E1!284.entry

3、IBM DW上的一篇教程,以Hudson为例

https://www6.software.ibm.com/developerworks/cn/education/java/j-cq11207/index.html

4、满江红开源上提供的CruiseControl教程下载

http://www.xiaxin.net/blog/OpenDoc-CruiseControl.zip

5、IBM DW上的另外一篇文章讲解如何实现持续集成

http://www.ibm.com/developerworks/cn/rational/rationaledge/content/nov05/lee/

分享到:
评论
4 楼 surpass_li 2009-08-12  
不错,学习了
3 楼 flashcloud 2009-07-07  
现象分析请参考:http://flashcloud.iteye.com/admin/blogs/421355
2 楼 flashcloud 2009-07-07  
谢谢你的好文,在实际测试过程中,build.xml中有两点疏漏,导致不能得到正确的覆盖报告:
1.任务compile-source中的javac必须增加“debug="yes"”的属性
2.在任务test的junit中,必须在配置<classpath>前插入节点“<classpath location="${instrumented.dir}" />
具体原因分析可以参考我写的文章:
1 楼 juvenshun 2008-09-17  
约定“优于”配置
持续集成,理论,工具,最佳实践,缺一不可。

相关推荐

    知攻善防-应急响应靶机-web2.z18

    知攻善防-应急响应靶机-web2.z18

    知攻善防-应急响应靶机-web2.z09

    知攻善防-应急响应靶机-web2.z09

    白色简洁风格的影视众筹平台整站网站源码下载.zip

    白色简洁风格的影视众筹平台整站网站源码下载.zip

    HTTP请求流程深入解析与性能优化技术指南

    内容概要:本文详细解析了HTTP请求的整个流程,包括用户请求发起、请求报文构建、服务器处理请求、响应报文生成、网络传输响应和浏览器接收响应六个阶段。每个阶段的内容均涵盖了关键步骤和技术细节,如DNS解析、TCP连接、缓存策略、HTTP/2性能提升、HTTPS加密等。通过这些内容,读者可以全面理解HTTP请求的完整流程。 适合人群:具备一定网络基础知识的前端、后端开发人员及IT运维人员。 使用场景及目标:适用于希望深入了解HTTP协议及其优化技术的技术人员,有助于提升系统的性能和安全性,优化用户体验。 阅读建议:本文内容详尽且涉及多个关键技术点,建议读者结合实际案例进行学习,逐步理解和掌握各个阶段的技术细节和优化方法。

    白色简洁风格的电话通讯公司模板下载.zip

    白色简洁风格的电话通讯公司模板下载.zip

    白色简洁风格的日历当日事件提醒整站网站源码下载.zip

    白色简洁风格的日历当日事件提醒整站网站源码下载.zip

    RX8 专业消人声 乐器 软件

    一键制作 歌曲伴奏! 可以消人声 吉他 鼓 等 多轨道声音。相当好用。

    知攻善防-应急响应靶机-web2.z04

    知攻善防-应急响应靶机-web2.z04

    NSDocumentError如何解决.md

    NSDocumentError如何解决.md

    白色宽屏风格的大气冲浪运动整站网站模板.rar

    白色宽屏风格的大气冲浪运动整站网站模板.rar

    白色简洁风格的婴儿用品商城网站模板.zip

    白色简洁风格的婴儿用品商城网站模板.zip

    罗兰贝格2023未来营养趋势报告21页

    罗兰贝格2023未来营养趋势报告21页

    html+css 圣诞树代码html

    预览地址:https://blog.csdn.net/qq_42431718/article/details/144749829 html+css 圣诞树代码html

    小学生出题软件v6.3.3.zip

    1-100加减乘除出题生成器

    白色简洁风格的网络实验室CSS模板.zip

    白色简洁风格的网络实验室CSS模板.zip

    白色简洁风格的企业产品展示整站网站源码下载.zip

    白色简洁风格的企业产品展示整站网站源码下载.zip

    etcd服务器性能指标与状态监控数据

    内容概要:《etcd-metrics-latest.txt》文档记录了 etcd(一个分布式键值存储系统)的多个指标数据,包括但不限于集群版本、认证修订版、后端磁盘操作延时分布、租赁管理、键值操作统计、快照保存、网络通信、Go 运行时指标、gRPC 请求处理、操作系统资源使用以及进程资源使用等。这些指标提供了详细的性能监测数据,帮助运维人员和开发人员理解和优化 etcd 集群的运行状态。 适合人群:具有基础计算机科学知识的运维人员或开发人员,尤其是负责维护或开发基于 etcd 技术系统的专业人员。 使用场景及目标:主要用于监控 etcd 集群的健康状况,评估性能瓶颈,辅助故障排查,支持集群的持续优化和技术决策。 其他说明:文档中大量使用了指标和术语,建议读者对 etcd、Go 语言、gRPC 和操作系统基础知识有一定的了解,以便更好地解读文档中的数据。对于不熟悉这些技术的读者来说,可能需要额外查阅相关资料来辅助理解。

    (1866400)java编的计算器程序

    Java编写的计算器程序是一种基于Java编程语言实现的计算工具,常用于教学或个人项目中,以帮助用户执行基本的数学运算。在这个简单的计算器程序中,我们可能会遇到以下几个关键的Java知识点: 1. **基础语法与控制结构**:Java的基础语法包括变量声明、数据类型(如int、double等)、条件语句(if-else)和循环语句(for, while)。在计算器程序中,这些元素用于读取用户输入、判断操作类型以及重复执行某些计算过程。 2. **面向对象编程**:Java是一种面向对象的语言,因此计算器程序可能包含多个类,如Calculator类、Button类(模拟图形界面的按钮)和Display类(显示计算结果)。类之间可能存在继承关系,例如Button类可能继承自一个抽象的UIComponent类。 3. **输入/输出处理**:在命令行计算器中,Java的Scanner类用于获取用户输入,如数字和运算符。在图形用户界面(GUI)计算器中,可能使用事件监听器处理用户的点击事件,获取按钮上的文字信息。 4. **异常处理**:为了确保程序的健壮性,计算器可能包含异常处理代码,比如当

    SystemExit.md

    SystemExit.md

    NavigationGuardError解决办法.md

    NavigationGuardError解决办法.md

Global site tag (gtag.js) - Google Analytics