编者注: AWS Elastic Beanstalk是Amazon Web Services提供的一项编排服务,用于部署基础架构,该基础架构协调了各种AWS服务,包括EC2,S3,简单通知服务(SNS),CloudWatch,自动扩展和Elastic Load Balancer。

Elastic Beanstalk在裸服务器和OS上提供了一个附加的抽象层。 用户会看到操作系统和平台的预构建组合。

部署需要定义许多组件:“应用程序”作为项目的逻辑容器,“版本”是应用程序可执行文件的可部署构建,“配置模板”包含两个Beanstalk环境的配置信息和产品。 (来源: 维基百科 )

现在,我们提供了全面的指南,以便您可以开发自己的基于Amazon Elastic Beanstalk的应用程序。 我们涵盖了广泛的主题,从部署和配置到Java集成和命令行接口。 有了本指南,您将能够在最短的时间内启动并运行自己的项目。 请享用!

目录

1.简介 2.概念
2.1 Web服务器环境 2.2工人环境 2.3设计注意事项
3. Java Web应用程序与Tomcat
3.1简单的REST-API 3.2使用Web界面进行部署 3.3使用CLI进行部署 3.4使用DynamoDB 3.5使用RDS
4.具有Spring Boot的Java Web应用程序 5.下载源代码

1.简介

Amazon Elastic Beanstalk是一项服务,可让您使用一组现有的Amazon AWS服务托管您的应用程序。 与更通用的服务(如EC2)相比,您不必提供已部署到云中的计算机的映像,而只需提供一个可运行的应用程序,该应用程序托管在Amazon AWS的预定义环境中。

Amazon让您在不同的预定义环境之间进行选择,并设置代表您运行应用程序所需的一切。 因此,如果您只想专注于应用程序本身而不是基础操作系统和服务器,那么此服务非常适合您。 但是,由于Elastic Beanstalk在后台使用现有服务,例如EC2,S3,CodeCommit或Route 53,因此您可以完全控制应用程序。

2.概念

Amazon Elastic Beanstalk定义了在整个服务中使用的一组术语,因此必须从头开始理解。 应用程序是一组组件,这些组件不仅包含应用程序的不同版本,而且还包含应用程序部署到的环境的配置。 应用程序版本是可部署的工件,带有标签并存储在Amazon S3存储桶中。 因此,可以在以后的某个时间点还原它。 环境是一组Amazon AWS资源,用于运行特定版本的应用程序。 可以配置不同的环境,并且应用程序的版本可以在不同的环境中运行。

不同的环境可以同时存在,也可以为同一应用程序提供不同的版本。 环境由两层组成: Web服务器环境工作环境 。 当Web服务器环境提供HTTP请求时,工作环境从队列中读取消息并进行处理。 这样,应用程序可以使用“工作队列”模式将业务逻辑与服务HTTP请求分离。 环境配置包含环境的设置。 应用此配置将使Amazon AWS创建相应的资源。 现有模板可用于创建配置,因此称为配置模板

2.1 Web服务器环境

“ Web服务器环境”提供了运行应用程序的所有资源。 它由一台或多台用于部署应用程序的Web服务器组成。 这些Web服务器托管在Amazon AWS云内运行的EC2计算机上。 由于这些计算机位于Elastic Load Balancer的后面,因此可以通过CNAME名称(例如myapp.us-west-2.elasticbeanstalk.com 。 使用Route 53服务将此CNAME别名为内部负载平衡URL。 通过使用域名系统(DNS),此Amazon服务提供了高可用性。 您环境中的EC2计算机可以属于“ Auto Scaling”组。

这意味着如果负载增加,Amazon会自动增加实例数,另一方面,如果负载消失,则将机器数减少到至少一台机器。 在每台计算机上运行的“主机管理器”负责部署应用程序以及收集指标和事件。 它还会监视应用程序服务器并在必要时轮换日志文件。 如果环境的一个组件需要修补程序或更新,则“主机管理器”可以代表您执行此更新。 “安全组”为一组实例定义防火墙规则。 Web服务器环境中的基本安全组允许访问应用程序的端口80(HTTP)。 如果您需要使用数据库,则可以自由定义更多具有更细粒度控制的安全组。

2.2工人环境

“工作人员环境”提供所有资源来运行消耗来自Amazon SQS队列的消息的工作人员应用程序。 因此,它不仅提供运行您的工作程序的EC2机器,还提供一个SQS队列,可用于将消息从“ Web服务器环境”传输到“工作环境”,然后再传输回来。 在“工作人员环境”的每个实例上运行的守护程序从队列中提取请求,并将其传递到您的应用程序。

2.3设计考虑

在开始应用程序之前,必须仔细考虑它的设计。 十分重要的一方面是可伸缩性。 基本上,有两种方法可以处理应用程序上不断增加的负载。 第一种方法是增加每台计算机的可用硬件资源,使其可以处理更多负载。 尽管这种方法可用于负载增加很小的范围,但它不能满足任意需求。

第二种方法是增加正在运行的服务的数量(水平扩展),因为它允许在必要时添加更多计算机。 第二种方法是Amazon Elastic Beanstalk遵循的方法,因为对可用EC2实例的严格监控允许在必要时在自动扩展组中设置新实例。 但这也意味着该应用程序的设计和编写可以扩展。 水平扩展不是构建需要更多硬件的单片应用程序,而是将负载分配到尽可能无状态的任意数量的较小服务上,以便可以在运行时添加新服务。

Web服务器环境前面的负载平衡器将在可用服务之间分配传入请求,要求每个服务可以处理其中一个。 开发适用于AWS Elastic Beanstalk的应用程序时的另一个重要点是安全性。 可以使用SSL对传入和传出Web服务器环境的数据进行加密。 因此,必须从外部认证机构之一(例如VeriSign或Entrust)获得有效的证书。 请注意,SSL加密在环境的负载均衡器处结束,并且它与Web服务器之间的流量通常不加密。

为了能够在必要时启动和停止其他实例,您的应用程序不应在每个节点的本地存储上存储任何数据,因为一旦关闭实例,该数据将被擦除,而在启动另一个实例时,该数据将不存在。 。 因此,必须关注持久性存储。 Amazon AWS提供了可用于您的应用程序以存储状态的不同服务:

  • Amazon S3:此服务可用于在云中存储任意数量的数据。
  • Amazon Elastic File System:EFS可以安装在您的EC2实例上,并且可以像普通文件系统一样使用。
  • Amazon Elastic Block Store:EBS卷已附加到EC2实例,可用于与文件系统或数据库结合使用。
  • Amazon DynamoDB:此服务在Amazon云中提供NoSQL数据库。
  • Amazon Relational Database Service:RDS管理可以由您的应用程序使用的六个不同的关系数据库引擎(Amazon Aurora,PostgreSQL,MySQL,MariaDB,Oracle和Microsoft SQL Server)。

如果世界各地的用户遇到不同的延迟时间,您可以使用Amazon CloudFront使其自动在世界各地分发您的应用程序和存储,并将用户路由到最近的可用站点。 最后但并非最不重要的一点是,如果有必要,您可以让Amazon更新和修补环境。 这些更新以“滚动批次”的形式执行,即Amazon获取第一批EC2实例并终止它们。

新实例出现后,可以处理下一批。 是否应处理下一批的条件可以基于计时策略或新计算机的运行状况。 通过此过程可以使站点保持运行状态,而应用程序的新旧服务将在短时间内运行。 为此,您当然必须设计应用程序,以使旧版本的服务可以读取新版本已写入的数据。

3. Java Web应用程序

既然我们已经学到了很多有关Amazon Elastic Beanstalk背后的概念的知识,我们应该动手实践,并开发一个小型应用程序,该应用程序使用Apache Tomcat公开一个简单的REST接口。

3.1简单的REST-API

首先,我们使用以下命令行创建一个新的maven项目:

mvn archetype:generate -DgroupId=com.javacodegeeks.ultimate.aws.eb -DartifactId=tomcat-web-service -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

之后,我们将使用以下结构创建一个名为tomcat-web-service的新目录:

|-- pom.xml
`-- src|-- main|   `-- webapp|       `-- index.jsp|       `-- WEB-INF|           `-- web.xml

该原型已经为我们创建了一个web.xml和一个index.jsp文件。 后者可用于稍后在云中简单地测试第一个版本,因此我们尚未删除JSP页面。 web.xml文件需要进行一些编辑:

<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>tutorial-webapp</display-name><servlet><servlet-name>RestServlet</servlet-name><servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class><init-param><param-name>jersey.config.server.provider.packages</param-name><param-value>com.javacodegeeks.ultimate.aws.eb</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>RestServlet</servlet-name><url-pattern>/tutorial-service/*</url-pattern></servlet-mapping>
</web-app>

使用XML元素display-name我们定义如何在Apache Tomcat服务器内部标记应用程序。 servletservlet-mapping元素定义了正在侦听请求的Servlet类及其应侦听的URL模式。 在我们的例子中,我们使用模式/tutorial-service/* ,即所有看起来像http://://tutorial-service/* URL都将由该servlet处理。

上下文名称是通过我们部署到tomcat中的战争档案的名称定义的。 参数jersey.config.server.provider.packages告诉我们将用于实现REST-API的JAX-B实现,它应该扫描哪个Java包以查找注释。 为此,我们必须在maven项目中创建以下目录结构: src/main/java/com/javacodegeeks/ultimate/aws/eb 。 为了告诉maven我们要使用哪个版本的JAX-B实现,我们将以下依赖性信息块复制到您的pom.xml文件中:

<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><jersey.version>2.26</jersey.version><junit.version>4.12</junit.version><commons-logging>1.1.3</commons-logging><log4j.version>1.2.17</log4j.version><javax-ws-rs-api.version>2.1</javax-ws-rs-api.version><aws-sdk.version>1.11.106</aws-sdk.version><db.dynamodb.local-endpoint>false</db.dynamodb.local-endpoint>
</properties><dependencies><dependency><groupId>javax.ws.rs</groupId><artifactId>javax.ws.rs-api</artifactId><version>${javax-ws-rs-api.version}</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>${commons-logging}</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency><dependency><groupId>org.glassfish.jersey.containers</groupId><artifactId>jersey-container-servlet</artifactId><version>${jersey.version}</version></dependency><dependency><groupId>org.glassfish.jersey.inject</groupId><artifactId>jersey-hk2</artifactId><version>${jersey.version}</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version><scope>test</scope></dependency><dependency><groupId>org.glassfish.jersey.core</groupId><artifactId>jersey-client</artifactId><version>${jersey.version}</version></dependency><dependency><groupId>org.glassfish.jersey.media</groupId><artifactId>jersey-media-json-jackson</artifactId><version>${jersey.version}</version></dependency>
</dependencies><dependencyManagement><dependencies><dependency><groupId>com.amazonaws</groupId><artifactId>aws-java-sdk-bom</artifactId><version>${aws-sdk.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

在仅API合同( javax.ws.rs-api )旁边,我们定义使用jersey-container-servlet作为JAX-B实现,并使用jersey-hk2进行对jersey的依赖项注入。 由于Amazon SDK使用commons-logging我们也要这样做。 作为日志记录服务,我们选择了经典的log4j实现。 目前, junitjersey-client依赖项仅用于我们的集成测试。

拥有可以在本地执行的集成测试大大简化了开发,因为我们不必每次都将新版本的应用程序上载到AWS云中。 当前不需要aws-java-sdk-bom依赖关系,但是由于在接下来的步骤中将使用SDK,因此我们已经在其中包含了它。 pom.xmlbuild部分再次有点长:

<build><finalName>tomcat-web-service</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.7.0</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.12.1</version><configuration><excludes><exclude>**/*IntegrationTest*</exclude></excludes></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-failsafe-plugin</artifactId><version>2.12.4</version><configuration><includes><include>**/*IntegrationTest*</include></includes></configuration><executions><execution><goals><goal>integration-test</goal><goal>verify</goal></goals></execution></executions></plugin><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version><configuration><url>http://localhost:8080/manager</url><server>localhost</server><path>/${project.build.finalName}</path><contextFile>${project.basedir}/src/test/tomcat7-maven-plugin/context.xml</contextFile></configuration><executions><execution><id>start-tomcat</id><phase>pre-integration-test</phase><goals><goal>run-war</goal></goals><configuration><fork>true</fork></configuration></execution><execution><id>stop-tomcat</id><phase>post-integration-test</phase><goals><goal>shutdown</goal></goals></execution></executions></plugin></plugins>
</build>

maven-compiler-plugin用于定义构建的源版本和目标版本。 这是必需的,因为所提供的服务安装了特定的Java版本,因此我们必须编译可在目标环境中执行的工件。 surefire和failsafe插件用于执行本地junit和集成测试。 最后, tomcat7-maven-plugin允许我们在构建中启动和停止嵌入式Apache Tomcat服务器,以执行集成测试。

此步骤缩短了开发和测试之间的往返时间,因为我们不必每次都启动或重新启动外部安装的服务器。 由于我们将不使用任何Tomcat 8特定功能,因此tomcat7-maven-plugin应该足够了。 正如tomcat7-maven-plugin的配置已经表明的那样,我们需要一个专用的context.xml文件来进行tomcat集成测试。 因此,以下内容被放入src/test/tomcat7-maven-plugin/context.xml

<?xml version='1.0' encoding='utf-8'?>
<Context><WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>

完成上述所有准备工作之后,我们就可以准备第一堂课:

package com.javacodegeeks.ultimate.aws.eb;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;@Path("/tutorial")
public class TutorialResource {private static final Log LOGGER = LogFactory.getLog(TutorialResource.class);@GET@Produces("text/json")@Path("/list-all-courses")public Response listAllCourses() {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Listing all courses.");}List tutorials = new ArrayList<>();tutorials.add(new Tutorial("Linus Meyer", "Linux"));tutorials.add(new Tutorial("Bill Famous", "Microsoft"));tutorials.add(new Tutorial("Josh Hotspot", "Java"));return Response.status(200).entity(tutorials).build();}
}

@Path注释该类,以告知JAX-RS框架,该类本身中使用的所有相对URL路径均应以/tutorial前缀。 为了简单起见,我们目前只有一个方法: listAllCourses() 。 URL的一部分在方法级别用另一个@Path注释表示。 使用注释@Produces和媒体类型text/json指定此REST资源的调用将返回JSON字符串。

最后,我们通过使用专用注释@GET告诉框架此方法是GET请求。 当前,我们没有任何持久性存储,因此我们无法从数据源加载数据。 因此,我们对课程列表进行了硬连线,并返回了Tutorial对象的列表。 Tutorial类是一个简单的值对象:

public class Tutorial {private String author;private String title;public Tutorial() {}public Tutorial(String author, String title) {this.author = author;this.title = title;}public String getAuthor() {return author;}public String getTitle() {return title;}public void setAuthor(String author) {this.author = author;}public void setTitle(String title) {this.title = title;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Tutorial tutorial = (Tutorial) o;if (author != null ? !author.equals(tutorial.author) : tutorial.author != null) return false;if (title != null ? !title.equals(tutorial.title) : tutorial.title != null) return false;return true;}@Overridepublic int hashCode() {int result = author != null ? author.hashCode() : 0;result = 31 * result + (title != null ? title.hashCode() : 0);return result;}@Overridepublic String toString() {return "Tutorial{" +"author='" + author + '\'' +", title='" + title + '\'' +'}';}
}

我们需要有一个默认的构造函数,以便JSON库可以从字符串表示形式反序列化Tutorial类型的对象。 getter和setter方法允许JSON库设置相应的属性值。 在此示例中,我们还实现了equals()hashCode()因为我们希望稍后在集成测试中比较Tutorial的实例。 如前所述,现在可以将应用程序已经构建和部署到AWS上,但这是需要一段时间的,直到部署可用。

因此,我们只编写了一个集成测试,以验证我们的实施是否按预期工作,以将不必要的上载保存到AWS云(并节省时间)。 在上面的pom.xml文件中,我们配置了集成测试,并在其类名称中使用IntegrationTest命名。 因此,我们在src/test/java下创建一个具有以下内容的类:

public class TutorialIntegrationTest {@Testpublic void testListAllCourses() {Client client = ClientBuilder.newClient();WebTarget target = client.target("http://localhost:8080").path("/tomcat-web-service/tomcat-web-service/tutorial/list-all-courses");Response response = target.request().get();assertThat(response.getStatus(), is(200));List tutorials = response.readEntity(new GenericType>(){});assertThat(tutorials.size(), is(3));assertThat(tutorials, hasItem(new Tutorial("Linus Meyer", "Linux")));assertThat(tutorials, hasItem(new Tutorial("Bill Famous", "Microsoft")));assertThat(tutorials, hasItem(new Tutorial("Josh Hotspot", "Java")));}
}

方法testListAllCourses()的第一行创建一个新的REST客户端,而第二行提供服务器上的主机,端口和路径。 对于我们的本地测试, localhost是合适的。 Tomcat默认在端口8080上运行。URL作为部署到Tomcat的war文件名称的第一部分组成。 我们已经使用XML元素finalNamepom.xml文件中定义了它。

使用request()返回的对象上的get()方法发出GET请求。 如果一切正常,则Web服务应返回状态码200。在这种情况下,它还应返回Tutorial对象的列表。 方法readEntity()允许我们使用具有两个泛型类型的GenericType实例来定义它。 一种用于List ,一种用于Tutorial内部类型。 结果列表应恰好包含三个条目,每个课程一个条目。 要在本地测试所有内容,我们在本地终端上发出以下命令:

mvn clean verify

由于verify阶段在integration-test阶段之后,因此此调用还将运行先前编写的集成测试:

[INFO] --- tomcat7-maven-plugin:2.2:run-war (start-tomcat) @ tomcat-web-service ---
[INFO] Running war on http://localhost:8080/tomcat-web-service
[INFO] Creating Tomcat server configuration at D:\development\glassfish\glassfish-5.1\awseb\awseb\tomcat-web-service\target\tomcat
[INFO] create webapp with contextPath: /tomcat-web-service
[...]
[INFO]
[INFO] --- maven-failsafe-plugin:2.12.4:integration-test (default) @ tomcat-web-service ---
[INFO] Failsafe report directory: D:\development\glassfish\glassfish-5.1\awseb\awseb\tomcat-web-service\target\failsafe-reports-------------------------------------------------------T E S T S
-------------------------------------------------------
Running com.javacodegeeks.ultimate.aws.eb.TutorialIntegrationTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.443 secResults :Tests run: 1, Failures: 0, Errors: 0, Skipped: 0[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO]
[INFO] --- tomcat7-maven-plugin:2.2:shutdown (stop-tomcat) @ tomcat-web-service ---
[...]
[INFO]

我们可以看到,tomcat服务器是在集成测试之前和之后启动的。 在构建结束时,文件tomcat-web-service.war驻留在我们项目的target目录中。 这是我们现在要上载到Amazon Elastic Beanstalk的应用程序。

3.2使用Web界面进行部署

如果您尚未创建Amazon AWS账户,则现在应该这样做,方法是将您的浏览器指向以下URL ,然后单击标记为“创建AWS账户”的链接。 在以下步骤中,您将必须提供典型的个人信息以及有效的信用卡。 后者是必需的,以便允许Amazon为您使用的资源计费。 创建新帐户时,您就有资格获得“免费套餐”。

在前12个月中,您目前可以在EC2上使用多达750个小时的计算能力,在S3上使用5 GB的标准存储。 这对于我们的教程来说绰绰有余。 设置可用的AWS账户后,即可创建第一个应用程序。 因此,将浏览器指向以下URL,并填写应用程序的名称以及可选的描述:

下一页询问我们是要设置Web服务器环境还是工作环境。 我们的示例REST应用程序最适合第一类环境。 因此,我们单击“创建Web服务器”。

对于Web服务器环境,我们必须设置配置和类型。 我们选择“ Tomcat”作为预定义配置,选择“单个实例”作为类型。 通过这种方式,Amazon为我们提供了一个已安装Apache Tomcat服务器的EC2实例。 此时我们不选择自动缩放,因为对于我们的示例而言,一个实例就足够了。

以下页面要求指定应用程序版本。 因此,我们选择第二个选项,并选择我们的Maven构建之前生成的war文件。

另外,我们还可以提供指向之前已上传到Amazon S3或选择的Amazon示例应用程序之一的工件的链接。

如前所述,我们的应用程序获得了自己的CNAME。 因此,可以使用以下页面提供CNAME。 “检查可用性”使我们可以验证名称是否仍然可用。 如果我们打算使用关系数据库或虚拟私有云网络。 在我们的简单示例中,我们不需要这两种资源,因此只需单击“下一步”

在“配置详细信息”部分中,我们可以选择服务器类型。 一个t2.micro实例足以进行我们的实验,但是如果您愿意,可以选择一个更大的实例。 EC2文档更详细地描述了可用的实例类型。 其余输入字段可以保留,因为我们对实例光盘或运行状况报告没有任何特定要求。 也不需要EC2密钥对。

环境标签可用于标识成本分配报告中的环境,或通常可用于管理环境和权限。 对于我们的第一个示例应用程序,标签不是必需的,但是您可以自由提供它们。

“权限”页面允许定义实例配置文件和服务角色。 实例配置文件是IAM角色,您的应用程序使用该IAM角色与其他AWS服务进行通信,而服务角色则是用于监视环境。

最后,概述页面会显示所有信息以供验证。 如果您对选择感到满意,则可以单击“启动”,然后让Amazon AWS为您创建所有资源。 该过程完成后,您可以在控制台中看到一个新环境:

单击此环境将导致以下仪表板:

在这里,您可以查看环境的所有事件,运行版本和配置。 现在应用程序已启动并运行,我们可以将浏览器指向以下URL:

正如预期的那样,浏览器将显示一个包含三个Tutorial项目的JSON数组。

3.3使用CLI进行部署

当您不必经常使用Web控制台时,就足够了。 如果您正在争取自动化,则可以使用awccli工具从命令行界面(CLI)创建环境。 为此,您首先必须安装python和pip 。 这两个网站均包含有关如何为通用操作系统安装和配置这两个工具的信息。 一旦您运行了python和pip,就可以使用以下命令简单地安装awscli

pip install awsebcli --upgrade --user

为了能够直接运行eb命令,必须将其包含在路径中。 在Windows系统上,您必须将以下路径添加到PATH环境变量: %USERPROFILE%\AppData\Roaming\Python \Python36\scripts 。 在基于Linux的系统和基于macOS的系统上,您无需修改​​任何环境变量。 第一步是通过在maven项目的根文件夹中调用命令eb init来初始化默认设置:

~/eb $ eb init
Select a default region
1) us-east-1 : US East (N. Virginia)
2) us-west-1 : US West (N. California)
3) us-west-2 : US West (Oregon)
4) eu-west-1 : EU (Ireland)
5) eu-central-1 : EU (Frankfurt)
6) ap-south-1 : Asia Pacific (Mumbai)
7) ap-southeast-1 : Asia Pacific (Singapore)
8) ap-southeast-2 : Asia Pacific (Sydney)
9) ap-northeast-1 : Asia Pacific (Tokyo)
10) ap-northeast-2 : Asia Pacific (Seoul)
11) sa-east-1 : South America (Sao Paulo)
12) cn-north-1 : China (Beijing)
13) us-east-2 : US East (Columbus)
14) ca-central-1 : Canada (Central)
15) eu-west-2 : EU (London)
(default is 3): 5

接下来,您必须提供您的AWS凭证:

You have not yet set up your credentials or your credentials are incorrect
You must provide your credentials.
(aws-access-id):
(aws-secret-key):

下一个问题是每次要部署软件的新版本时,都要创建一个新的应用程序版本。 由于我们确实想创建一个新版本,因此选择1:

Select an application to use
1) [ Create new Application ]
(default is 1): 1

现在,您可以输入应用程序的名称:

Enter Application Name
(default is "eb"): tomcat-web-service
Application eb has been created.

与在Web控制台中一样,我们必须为我们的应用程序选择一个预配置的平台。 我们在这里选择Tomcat,而不是Java(!),否则我们的应用程序将不得不打开自己的端口并处理请求。

Select a platform.
1) Node.js
2) PHP
3) Python
4) Ruby
5) Tomcat
6) IIS
7) Docker
8) Multi-container Docker
9) GlassFish
10) Go
11) Java
(default is 1): 5

最后,您必须说是否要使用SSH连接到EC2实例。 在这个简单的示例中,我们不想这样做:

Do you want to set up SSH for your instances?
(y/n): n

由于eb工具不知道工件的位置,因此我们必须使用存储在.elasticbeanstalk/config.yml下的YAML文件来指定它,其内容如下:

deploy:artifact: target/tomcat-web-service.war

现在,您可以使用以下命令部署工件:

eb deploy --staged

3.4使用DynamoDB

到目前为止,我们的示例应用程序尚未存储任何数据。 如前所述,一旦当前计算机关闭,我们将丢失本地计算机上的所有状态,因为自动缩放机制不再需要它。 因此,我们必须将应用程序的状态存储在外部数据存储中。 经常使用的一种方便选择是Amazon的NoSQL数据库“ DynamoDB”。 在DynamoDB中,数据存储在表中,例如关系数据库中。

表格是项目的集合,每个项目都包含一个或多个属性。 属性是键/值对。 与关系数据库相反,没有模式,即每个项目可以具有不同的属性。 例如,您可以创建一个表来存储事件。 每个事件都是一个项目,描述特定事件的数据使用其创建时间,事件类型等属性进行存储。每个项目都有一个哈希键和可选的附加排序键。 哈希键用于将大型表中的项目分布在不同的分区上。 如果表具有排序键,则该表用于以排序方式存储具有相同哈希键的项目。

这样可以简化以某种哈希键遍历所有项目的方式。 分区允许在不同的计算机上分发数据,因此是一种可水平扩展应用程序的机制。 了解了DynamoDB的基础知识之后,我们可以使用另一个资源扩展示例应用程序,该资源使我们可以创建和删除应用程序所需的表。 我们将通过使用Amazon的AWS SKD来做到这一点; 因此,我们必须在其中的DynamoDB部分定义一个依赖项:

com.amazonawsaws-java-sdk-dynamodb

具体版本是从aws-java-sdk-bom依赖关系推导出来的,我们在本教程的开始部分就已经在dependencyManagement中定义了该dependencyManagement 。 接下来,我们创建一个新的类DbResource

@Path("/db")
public class DbResource {private static final Log LOGGER = LogFactory.getLog(DbResource.class);@GET@Produces("text/json")@Path("/list-all-tables")public Response listAllTables() {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Listing all tables.");}AmazonDynamoDB dynamoDB = createClient();ListTablesResult listTablesResult = dynamoDB.listTables();List tableNames = listTablesResult.getTableNames();return Response.status(200).entity(tableNames).build();}public AmazonDynamoDB createClient() {String property = System.getProperty("tutorial.dynamodb.local-endpoint");Boolean localEndpoint = Boolean.valueOf(property);if (!localEndpoint) {return AmazonDynamoDBClientBuilder.standard().withRegion(Regions.EU_CENTRAL_1).build();} else {return AmazonDynamoDBClientBuilder.standard().withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("http://localhost:8000", "eu-central-1")).withCredentials(new AWSCredentialsProvider() {@Overridepublic AWSCredentials getCredentials() {return new AWSCredentials() {@Overridepublic String getAWSAccessKeyId() {return "dummy";}@Overridepublic String getAWSSecretKey() {return "dummy";}};}@Overridepublic void refresh() {}}).build();}}
}

此类公开REST URL /db/list-all-tables ,该URL返回DynamoDB实例上所有现有表的列表。 因此,它将创建AmazonDynamoDB的实例,该实例由方法createClient()返回。 此方法的代码查询系统属性tutorial.dynamodb.local-endpoint以了解是否使用具有给定端点和虚拟凭据的本地运行DynamoDB,还是使用DefaultAWSCredentialsProviderChain派生此信息。 此特定实施按以下顺序搜索AWS凭证:

  • 环境变量:AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY
  • Java系统属性:aws.accessKeyId和aws.secretKey
  • 配置文件存储在用户的主目录中
  • EC2容器提供的凭据
  • EC2元数据服务提供的凭证

由于我们的Web服务将部署在EC2实例上,因此凭据由AWS环境提供,因此不得存储在我们的应用程序内部。 如果我们使用本地端点,则可以使用虚拟值和我们选择的区域。 此处说明了如何安装和运行本地DynamoDB实例。 通过在maven插件tomcat7-maven-pluginconfiguration中放置以下片段,可以在启动Tomcat服务器时自动设置system属性:

true

为了在将功能部署到云之前测试上述功能,我们编写了以下小型集成测试:

@Test
public void testListAllTables() {Client client = ClientBuilder.newClient();WebTarget target = client.target("http://localhost:8080").path("/tomcat-web-service/tomcat-web-service/db/list-all-tables");Response response = target.request().get();assertThat(response.getStatus(), is(200));List tutorials = response.readEntity(new GenericType>(){});assertThat(tutorials.size(), is(0));
}

测试代码只需调用新的URL并验证是否返回了空字符串列表。 同样,我们可以编写代码来创建和删除表:

public Response removeAllTables() {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Removing all tables.");}AmazonDynamoDB dynamoDB = createClient();ListTablesResult listTablesResult = dynamoDB.listTables();List tableNames = listTablesResult.getTableNames();for (String table : tableNames) {dynamoDB.deleteTable(table);}return Response.status(200).entity(tableNames).build();
}@GET
@Produces("text/json")
@Path("/create-all-tables")
public Response createAllTables() {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Creating all tables.");}AmazonDynamoDB dynamoDB = createClient();List tableNames = new ArrayList();createTable(dynamoDB);return Response.status(200).entity(tableNames).build();
}private void createTable(AmazonDynamoDB amazonDynamoDB) {List attributeDefinitions = new ArrayList();attributeDefinitions.add(new AttributeDefinition().withAttributeName("tutorial").withAttributeType("S"));List keySchema = new ArrayList();keySchema.add(new KeySchemaElement().withAttributeName("tutorial").withKeyType(KeyType.HASH));CreateTableRequest request = new CreateTableRequest().withTableName("tutorials").withKeySchema(keySchema).withAttributeDefinitions(attributeDefinitions).withProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits(1L).withWriteCapacityUnits(1L));DynamoDB dynamoDB = new DynamoDB(amazonDynamoDB);Table table = dynamoDB.createTable(request);try {table.waitForActive();} catch (InterruptedException e) {LOGGER.error("Failed to wait for table to become active: " + e.getLocalizedMessage(), e);}
}

尽管删除现有表的代码或多或少是不言自明的,但是方法createAllTables()定义了一个表来存储教程。 该属性tutorial用作哈希键。 对于我们的示例应用程序,1个容量单位的读写容量应该足够。 对于实际应用而言,这可能是不够的。 最后,代码等待表准备就绪。 请注意,在创建表时,我们不必指定所有属性。 现在,我们可以修改现有方法listAllCourses()以使用新的tutorials表:

@GET
@Produces("text/json")
@Path("/remove-all-tables")
@GET
@Produces("text/json")
@Path("/list-all-courses")
public Response listAllCourses(@QueryParam("author") String authorQuery) {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Listing all courses.");}List tutorials = new ArrayList<>();AmazonDynamoDB amazonDynamoDB = DbResource.createClient();Map expValues = new HashMap<>();expValues.put(":a", new AttributeValue(authorQuery));QueryRequest queryRequest = new QueryRequest("tutorials");queryRequest.setKeyConditionExpression("author = :a");queryRequest.setExpressionAttributeValues(expValues);QueryResult queryResult = amazonDynamoDB.query(queryRequest);List> items = queryResult.getItems();for (Map item : items) {AttributeValue author = item.get("author");AttributeValue title = item.get("title");Tutorial tutorial = new Tutorial();tutorial.setAuthor(author.getS());tutorial.setTitle(title.getS());tutorials.add(tutorial);}return Response.status(200).entity(tutorials).build();
}

首先,我们获得对AmazonDynamoDB客户端的引用。 它的方法query()采用QueryRequest的实例对请求的表执行查询。 在这个简单的示例中,我们要搜索给定作者提供的所有教程。 作者作为查询参数传递到URL。 在JAX-RS中,这意味着我们为方法listAllCourses()指定了一个参数,该参数以@QueryParam("author")注释。

传递给注释的值表示URL中查询参数的名称,该参数应作为String传递给方法。 查询的条件写为: author = :a 。 字符串:a是一个属性,我们必须为此提供一个值。 这是通过构造HashMap并将key :a的值放入其中来完成的。 然后在QueryRequest上设置此HashMap

调用queryResult.getItems()是一个包含地图实例的列表。 每个地图代表一个项目,其键/值对存储在地图内部。 因此,我们遍历此列表,并将每个项目转换为类Tutorial的实例,然后将Tutorial列表返回给REST方法的调用者。 Java对象到JSON字符串的转换是由框架完成的。 现在,我们可以调用以下URL来创建表并列出所有作者:

http://.eu-central-1.elasticbeanstalk.com/tomcat-web-service/db/remove-all-tables
http://.eu-central-1.elasticbeanstalk.com/tomcat-web-service/db/create-all-tables
http://.eu-central-1.elasticbeanstalk.com/tomcat-web-service/tutorial/list-all-courses?author=test

请在字符串<your-app>和该区域上方的URL中替换为<your-app>的CNAME和区域。 删除所有表当然不是必需的,但是在这种情况下它并没有害处,因为我们只删除存在的表。 现在由您决定实现一种将数据插入表中的方法。

3.5使用RDS

在DynamoDB旁边,您还可以使用关系数据库。 因此,我们在AWS控制台中导航到我们的环境,然后选择菜单项“配置”。 在配置页面的下部,我们找到一个名为“创建新的RDS数据库”的链接:

单击此按钮后,将显示以下页面

在此页面上,我们必须指定RDS实例的基本配置详细信息。 由于我们没有快照,因此在相应的字段中选择“无”。 作为数据库引擎,我们在5.6.37版中选择了“ MySQL”。 该数据库应在具有5 GB存储空间的“微型”实例上运行。 “删除策略”确定了我们删除实例后应如何处理。 由于这是一个教程,因此我们不关心数据,而是让Amazon删除实例及其所有数据。

或者,您也可以指定创建数据库快照。 对于我们的示例应用程序,“单个可用区”也足够。 提供主用户名和密码后,我们可以单击“应用”。 现在,最多可能需要10分钟才能使RDS数据库可用。 同时,我们可以调整pom.xml文件中的Maven dependencies列表,并添加mysql连接器,如下所示:

<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.44</version>
</dependency>

请注意,我们必须将此依赖项的范围设置为compile ,因为所引用的jar文件应放置在我们战争的lib文件夹中。 否则,MySQL驱动程序类将在运行时不可用。 接下来,我们创建一个名为RdsResource的新类:

@Path("/rds")
public class RdsResource {private static final Log LOGGER = LogFactory.getLog(RdsResource.class);@GET@Produces("text/json")@Path("/list-all-tables")public Response listAllTables() {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Listing all tables.");}List tableNames = null;try {tableNames = listAllTablesIntern();} catch (SQLException e) {return Response.status(500).entity("Listing all tables failed: " + e.getLocalizedMessage()).build();}return Response.status(200).entity(tableNames).build();}private List listAllTablesIntern() throws SQLException {List tableNames = new ArrayList<>();try (Connection connection = createConnection()) {if (connection != null) {try (Statement stmt = connection.createStatement()) {ResultSet resultSet = stmt.executeQuery("show tables");while (resultSet.next()) {String tableName = resultSet.getString(1);tableNames.add(tableName);}}}}return tableNames;}
public static Connection createConnection() throws SQLException {if (System.getProperty("RDS_HOSTNAME") != null) {try {Class.forName("com.mysql.jdbc.Driver");String dbName = System.getProperty("RDS_DB_NAME");String userName = System.getProperty("RDS_USERNAME");String password = System.getProperty("RDS_PASSWORD");String hostname = System.getProperty("RDS_HOSTNAME");String port = System.getProperty("RDS_PORT");String jdbcUrl = "jdbc:mysql://" + hostname + ":" + port + "/" + dbName;if (LOGGER.isDebugEnabled()) {LOGGER.debug("Connecting to JDBC-URL: " + jdbcUrl);}Connection con = DriverManager.getConnection(jdbcUrl, userName, password);if (LOGGER.isDebugEnabled()) {LOGGER.debug("Connection to JDBC-URL: " + jdbcUrl + " successful.");}return con;} catch (ClassNotFoundException e) {LOGGER.error("Could not load driver: " + e.getLocalizedMessage(), e);}}return null;}
}

它将提供一个以/rds/list-all-tables结尾的URL,并返回所有可用MySQL表的列表。 因此,方法listAllTables()调用私有方法listAllTablesIntern ,如果没有异常发生,则返回状态码为200的HTTP响应;如果列出表失败,则返回状态码为500的HTTP响应。

私有方法listAllTablesIntern()利用另一个私有方法来获取与数据库的连接: createConnection() 。 在基于Tomcat的环境中,所有必要的信息都通过系统属性传递给应用程序。 通过查询这些预定义的系统属性,我们可以构造以下形式的JDBC URL:

jdbc:mysql://" + hostname + ":" + port + "/" + dbName

在实际的应用程序中,这可能成为以下形式的URL:

jdbc:mysql://aa1im36v00yvfox.cwazv5kmikco.eu-central-1.rds.amazonaws.com:3306/ebdb

有了JDBC连接,我们可以发出一条列出所有现有数据库表的SQL语句:

try (Statement stmt = connection.createStatement()) {ResultSet resultSet = stmt.executeQuery("show tables");while (resultSet.next()) {String tableName = resultSet.getString(1);tableNames.add(tableName);}
}

我们将由JAX-RS框架呈现的列表中的表名列表返回到JSON数组中。 在云端更新示例应用程序的版本之后,我们可以在浏览器中打开以下URL:

http://.eu-central-1.elasticbeanstalk.com/tomcat-web-service/rds/list-all-tables

再次:请在上述URL中将字符串<your-app>和区域替换为您的应用程序的CNAME和区域。 这应该显示一个空的JSON数组。 设置完如上所述的所有内容之后,我们现在可以实现创建和删除应用程序表的方法:

@GET
@Produces("text/json")
@Path("/remove-all-tables")
public Response removeAllTables() {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Removing all tables.");}List tableNames = new ArrayList<>();List tables;try {tables = listAllTablesIntern();} catch (SQLException e) {return Response.status(500).entity("Listing all tables failed: " + e.getLocalizedMessage()).build();}for (String table : tables) {try (Connection connection = createConnection()) {if (connection != null) {try (Statement stmt = connection.createStatement()) {stmt.executeUpdate("drop table " + table);tableNames.add(table);}}} catch (SQLException e) {LOGGER.error("Removing all tables failed: " + e.getLocalizedMessage(), e);return Response.status(500).entity("Removing all tables failed: " + e.getLocalizedMessage()).build();}}return Response.status(200).entity(tableNames).build();
}
@GET
@Produces("text/json")
@Path("/create-all-tables")
public Response createAllTables() {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Creating all tables.");}List tableNames = new ArrayList<>();try (Connection connection = createConnection()) {if (connection != null) {try (Statement stmt = connection.createStatement()) {stmt.executeUpdate("create table tutorials (" +"id int not null auto_increment, " +"author varchar(30) not null," +"title varchar(30) not null," +"primary key (id)" +")");tableNames.add("tutorials");}}} catch (SQLException e) {LOGGER.error("Creating tables failed: " + e.getLocalizedMessage(), e);return Response.status(500).entity("Creating tables failed: " + e.getLocalizedMessage()).build();}return Response.status(200).entity(tableNames).build();
}

上面的代码使用已经说明的方法createConnection() ,因此可以轻松理解。 创建和删除表的两条SQL语句是使用JDBC API的executeUpdate()方法发出的。 使用完每个语句和连接后,我们将关闭它们以释放资源。 请注意,在现实世界中的应用程序中,您可能需要合并连接,以便将现有连接重新用于下一个请求。 修改插入和查询教程的代码由读者决定。

4.具有Spring Boot的Java Web应用程序

上一章介绍了如何使用Apache Tomcat环境运行简单的REST API服务器。 The same can be achieved through the popular Spring Boot framework. In this chapter we are therefore going to build a Spring Boot application that runs inside a “Java” environment on AWS and not within a “Tomcat” environment.

Note that we can also build the Spring Boot application to run within the Tomcat environment, but for demonstrating purposes we are running our application in standalone mode. First, we create a new maven project with the following command:

mvn archetype:generate -DgroupId=com.javacodegeeks.ultimate.aws.eb -DartifactId=spring-boot-web -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

Next, we let our project inherit from the Spring Boot parent project:

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.9.RELEASE</version>
</parent>

This provides us all necessary versions but does not add any dependencies. Hence, we must add them to our pom.xml :

<properties><jersey.version>2.26</jersey.version>
</properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>org.glassfish.jersey.core</groupId><artifactId>jersey-client</artifactId><version>${jersey.version}</version><scope>test</scope></dependency><dependency><groupId>org.glassfish.jersey.inject</groupId><artifactId>jersey-hk2</artifactId><version>${jersey.version}</version><scope>test</scope></dependency>
</dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins>
</build>

The main dependency is spring-boot-starter-web . The other ones are used for our integration tests. spring-boot-maven-plugin is a maven plugin that packages the application such that we can start it easily from the command line. Now we can implement the simplest application possible:

@RestController
@EnableAutoConfiguration
public class Example {@RequestMapping("/")String home() {return "Hello World!";}public static void main(String[] args) throws Exception {SpringApplication.run(Example.class, args);}}

The main() method starts the Example class as controller and passes the optional arguments from the command line to the run() method of the SpringApplication . Annotating the class with @RestController turns it into a REST endpoint. The method home() just returns a static string. Its annotation @RequestMapping("/") denotes that it should be called in case the user requests the root URL of our application.

In the Amazon AWS cloud, the proxy server in front of our application will route all incoming traffic to port 5000. This forces us to start our application on this specific port. Hence, we create a file application.properties with the following content and place it under src/main/resources :

server.port=5000

Now we are able to start the application on the command line:

mvn spring-boot:run

This will produce an output similar to the following (shortened) one:

[...]
[INFO] --- spring-boot-maven-plugin:1.5.9.RELEASE:run (default-cli) @ spring-boot-web ---.   ____          _            __ _ _/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/  ___)| |_)| | | | | || (_| |  ) ) ) )'  |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::        (v1.5.9.RELEASE)[...]

Pressing Ctrl+C will terminate the application. As we do not want to test all the code manually after having it uploaded to the cloud, we write a small integration test like the following one:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { Example.class }, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class ExampleTest {@Testpublic void testHelloWorld() {Client client = ClientBuilder.newClient();WebTarget target = client.target("http://localhost:5000").path("/");Response response = target.request().get();assertThat(response.getStatus(), is(200));}
}

It is a simple junit test that runs with a SpringRunner . The annotation SpringBootTest tells the framework which resource we want to test and the port to use. The actual code of our test is simple and uses a jersey client to invoke the URL http://localhost:5000/ .

After we have received the response, we verify its status code. Knowing that everything works as expected locally, we create a new environment on Amazon Beanstalk. The procedure is the same as before, but now we chose the “Java” instead of the “Tomcat” environment:

Once the new environment has been created, we can enter the following URL into our web browser and check that it works:

http://spring-boot-web-env.eu-central-1.elasticbeanstalk.com/

Please replace spring-boot-web-env and eu-central-1 with the CNAME and region of your environment. Now you should see the string “Hello World” in your browser. Note that we use the standard port 80 to invoke the REST API and not the port 5000, as the proxy in front of our application listens on the standard port and only dispatches this to port 5000 of our application.

5.下载源代码

This was an Amazon Elastic Beanstalk Tutorial.

下载
You can download the full source codes of this example here:

  • spring-boot-web
  • tomcat-web-service

翻译自: https://www.javacodegeeks.com/2017/12/amazon-elastic-beanstalk-tutorial.html

Amazon Elastic Beanstalk教程–最终指南(PDF下载)相关推荐

  1. 部署微服务– Spring Boot fatjar到Amazon Elastic Beanstalk

    最近,我正在研究概念验证的Web应用程序,我想将其部署到公共云以进行快速演示. 我决定使用Amazon,因为我已经有过使用它的经验. 亚马逊提供了几种不同的方式来部署Java Web应用程序. EC2 ...

  2. 谷歌搜索引擎优化初学者指南pdf下载

    "有哪些简单的方法可以提高我的网站在谷歌中的排名?" 对于这个问题,推荐SEO初学者阅读:谷歌搜索引擎优化初学者指南 谷歌搜索引擎优化初学者指南:涵 盖了网站管理员需要考虑优化的诸 ...

  3. python程序猿_python程序员指南 pdf下载

    python程序员指南 pdf是一本专为对python编程感兴趣的朋友准备的指导图书,作从最基本的基础知识到繁琐的运用,都进行的详细的解答,是你自学的最好教程了,感兴趣欢迎下载学习! python程序 ...

  4. 5W字高质量java并发系列详解教程(上)-附PDF下载

    文章目录 第一章 java.util.concurrent简介 主要的组件 Executor ExecutorService ScheduledExecutorService Future Count ...

  5. 在Amazon Elastic Beanstalk上部署Spring Boot应用程序

    在此博客中,我们将看到如何在Amazon ElasticBeanstalk上部署Spring Boot应用程序. Amazon ElasticBeanstalk具有一个预配置的Java环境,可用于部署 ...

  6. Hive编程指南.pdf下载

    好东西就是要免费共享的,尤其是知识,只有个人强大了,祖国才会强大,哈哈,有点扯了!为节省大家的时间,现将现成的资料奉上: Hive编程指南.pdf 链接:https://pan.baidu.com/s ...

  7. Amazon Elastic Beanstalk HTTPS self-signed certificate 创建与上传

    基于本机上以安装亚麻Amazon EB CLI的记录.EB CLI在Mac上比较推荐用 Homebrew brew install awsebcli 官网上也有用pip安装的选择,但是本机pip安装后 ...

  8. 用于单元测试的JUnit教程–最终指南(PDF下载)

    编者注: 我们在Java Code Geeks上提供了许多JUnit教程,例如JUnit入门示例 , 使用断言和注释的 JUnit 示例 , JUnit注释示例等. 但是,为了方便读者,我们希望将所有 ...

  9. java jsp学习指南_JSP教程–最终指南

    java jsp学习指南 编者注: JavaServer Pages(JSP)技术使您可以轻松创建同时包含静态和动态组件的Web内容. JSP技术提供了Java Servlet技术的所有动态功能,但提 ...

最新文章

  1. zabbix 搭建笔记
  2. day32—CSS多列布局学习
  3. IIS集成模式和经典模式 系统部署的不同
  4. Redis笔记(七):Redis应用场景
  5. UVA10700 Camel trading【贪心】
  6. 图片加载完后执行事件
  7. vi 打开文件,行末尾有^M
  8. 抖音直播电商带货项目商业运营计划书短视频创业规划方案
  9. 关于快速幂与快速积取模实现的尝试
  10. 河南计算机对口升学题,河南省计算机对口升学专基模拟试题(四)[1]
  11. 新闻客户端纷纷内容化,微博却在强化新闻属性
  12. 4r照片尺寸是多大_数码照片4D、4R、6RW是什么意思?4R照片尺寸是多大图片?
  13. 介绍5款非常棒的移动自动化测试工具
  14. java 代码实现加锁_java内置锁实现锁住代码块方案(同一个对象或锁住整个类.class)...
  15. mysql week weekofyear_MySQL WEEKOFYEAR()用法及代码示例
  16. 苹果4怎么越狱_什么是刷机?什么是越狱?刷机和越狱是一回事吗?
  17. 卷积神经网络权重,特征图计算
  18. Dev-c++怎么设置背景色
  19. unity IK初步应用 让游戏物体眼镜对准摄像头
  20. 杰理之 高低速(HSB/LSB)时钟硬件模块【篇】

热门文章

  1. 15.4 普通克里金插值
  2. Laravel实现简单的API认证
  3. 上海之行(十一)外滩
  4. “sample“, “batch“, “epoch“ 分别是什么?
  5. 手撸MIPS32——2、基本流水线设计与实现(理论部分)
  6. matlab遗传算法(例题及代码)
  7. 网络编程--出现bind error: Address already in use问题
  8. 数学建模——多元线性回归 /多元线性拟合 (工具:matlab)
  9. 嵌入式核心板应用于多参数监护仪解决方案
  10. Kotlin 之旅8 Kotlin与Java共存