【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第三篇,本篇我们将会准备开始构建一个员工对象微服务。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包括在电子商务、B2B集成、空间分析、SOA架构、大数据以及云计算等领域的软件产品架构经验,他是AWS认证解决方案架构师,在3Pillar之前先后就职于Oracle、ChoicePoint和Booz Allen Hamilton。Dan毕业于乔治●华盛顿大学,他也是一个父亲、业余木工爱好者,还参加过包括国际障碍大赛这样的障碍赛跑。
本篇构建微服务的步骤如下:
● 建立一个新的Spring Boot工程
● 定义我们的员工对象
● 持久连接
● 公开Web服务
● 定义一个Docker容器来运行我们的微服务,包括连接到我们在第二篇中创建的Mongo容器
● 在稍早设置的Docker Machine中运行我们的容器
建立我们的Spring Boot工程
我们将在我们的工程根目录下创建一个文件夹(生产环境中是在一个单独的版本库中)来承载我们的服务,在这个文件夹中,我们将创建build.grade文件。Spring Boot的优势就是在于对于依赖定义非常纯粹,如魔法般地带来了大量的互操作性。我们的build.grade文件看起来如下:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.0.RELEASE'
}
}
apply plugin: 'spring-boot'
repositories { jcenter()
}
dependencies {
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.boot:spring-boot-starter-web"
}
现在,正如我们提到的,我们将使用IDE,具体地说就是Spring Tool Suite(STS),所以我们将增加对于Gradle的支持。首先,打开STS,接着在打开的Dashboard页面上点击“IDE Extensions”的链接:
在屏幕上选择“Grade Support”,点击“Install”,按照提示完成安装,其中包括重启STS:

重启之后,选择“Import project…”,接着选择Gradle project(现在可用了),指向你的目录,然后点击“Build Model”——这将会用“Employee”来产生项目列表——选择这个列表并且点击“Finish”,这将会导入一个简单的build.grade到我们启动的工程。一旦工程被导入之后,第一个将是Spring Boot配置类,在这个场景中,我们将按照他们的服务来命名,所以在这种情况下配置类将会命名为EmployeeBoot。Spring大量使用了注解和反射,所以最小的配置也是需要的(忽略导入):
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class EmployeeBoot {
public static void main(String[] args) {
SpringApplication.run(EmployeeBoot.class);
}
}
我们的Employee类
接下来,我们将使得我们的POJO类来持有员工信息,我们目前保持最少的字段,有必要的话我们将增加:
Employee.Class
@Document(collection=”employees”)
public class Employee {
@Id
private String id;
private String email;
private String fullName;
private String managerEmail;
// getters and setters omitted for brevity
}
注意@Document这个注解——这会将这个对象连结为一个Mongo文档,并为该Employee“文档”在哪里存储指定了集合名字。
接下来我们将定义一个Spring持久类来读取这些Employee:
EmployeeRepository.class:
public interface EmployeeRepository extends
MongoRepository<Employee, String> {
}
Spring框架的一个优美之处就是在于所有你需要写的——比如扩展自MongoRepository的接口甚至不需要手动编写实现代码。两个通用的参数中第一个表示需要持久化的对象类型(Employee),第二个表示唯一的标识符类型(String类型)。Spring框架将会自动提供所声明的功能95%的实现。接下来的问题就是:Spring如何知道数据库的位置?Spring默认情况下将会寻找localhost:27017,这显然是不会工作的,所以我们需要直接设置位置新型。我们可以实现自己的Mongo模板Bean,但是幸运的是Spring允许我们通过Java属性表传递连接信息。我们可以定义一个属性文件或者在命令行上传递进来。我们选择了后者因为当稍后构建我们的容器时命令行方式非常方便。最后我们需要创建的是一个或者两个Rest端点并确保它们可以工作。我们将构建一个快速的Spring Controller,然后我们就可以测试了。
EmployeeController.java
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
EmployeeRepository employeeRepository;
@RequestMapping(method = RequestMethod.POST)
public Employee create(@RequestBody Employee employee){
Employee result = employeeRepository.save(employee);
return result;
}
@RequestMapping(method = RequestMethod.GET, value="/{employeeId}")
public Employee get(@PathVariable String employeeId){
return employeeRepository.findOne(employeeId);
}
}
最开始的@RestController和@RequestMapping这两个类级别的注解告诉Spring框架需要公开这是一个接收JSON的Rest服务,并且公开了URI路径是/employee。@Autowired注解告诉Spring框架采用上面我们定义的Repository接口的自动生成实现代码并将其注入到这个Controller中。现在到了特定的操作——方法级别的@RequestMapping注解表明这个方法将会基于HTTP动词来使用(在本例中是POST和GET),另外对于GET操作,我们指明了一个URL路径{employee},比如使用/employee/abcd1234来寻找一个员工并且返回该值。
现在我们有了足够多的准备工作可以来测试了,第一点,我们需要编译和运行我们的Spring Boot应用,在Eclipse中有很多方法可以做到这一点,但是我们将从命令行开始,并以我们的方式工作。在你的Employee目录中,敲入: gradle build
,这个命令将会编译Spring Boot应用到build/lib/Employee.jar,这个jar包包含了运行这个应用锁需要的所有东西,包括一个嵌入式的Servlet容器(默认情况下是Tomcat)。
在我们运行和测试这个应用之前,我们需要稍作回顾——我们的Mongo服务又在哪里?观察“docker ps”命令的输出,我们记得虚拟机的32777端口映射到了Mongo容器的27017端口,虚拟机的IP地址是192.126.99.100。如前所述,我们可以通过传递一个环境变量属性的方式来把连接属性传递给Spring,所以运行这个应用的命令行如下:
java -Dspring.data.mongodb.uri=mongodb://192.168.99.100:32777/micros -jar build/libs/Employee.jar
一旦应用启动之后(应该在1~4秒),你可以使用选项中的Rest工具来打开Web服务。

不要忘了在HTTP头部包含值为“application/json”的“Content-Type”,你应该收到如下的响应(id的值取决于你自己的情况):
{
"id": "55fb2f1930e07c6c844b02ff",
"email": "dan.greene@3pillarglobal.com",
"fullName": "Daniel Greene",
"managerEmail": null
}
你可以通过如下的调用测试我们的GET方法:
http://localhost:8080/employee/55fb2f1930e07c6c844b02ff
你应该得到相同的文档,万岁!我们的服务起作用了!
将Boot应用导入容器
现在我们需要构建我们的第一个容器来运行我们的Employee微服务。我们的途径是定义一个Dockerfile,这个文件将会定义如何生成一个镜像并放入我们的精益求精、短小精悍的微服务中。让我们阅读这个文件并逐步解析(参考第三篇第三步请跳到这儿):
FROM java:8
VOLUME /tmp
ADD build/libs/Employee.jar app.jar
EXPOSE 8080
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-Dspring.data.mongodb.uri=mongodb://mongodb/micros", "-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
● 我们从一个包含了安装了Java 8的标准镜像(镜像名字为“Java”,标签为“8”)开始构建过程
● 我们接着定义了一个名为/tmp的卷
● 接着将本地文件系统的一个文件添加进来,并且重命名为“app.jar”,重命名不是必须的,只是一个可用的可选项
● 我们声明想要公开容器的8080端口
● 在容器内运行一个touch命令,这样可以确保app.jar文件的修改日期
● ENTRYPOINT命令定义了容器启动时需要运行的内容——我们运行Java,设置我们的Spring Mongo属性,还有快速的附加属性来加速Tomcat启动时间,然后指向我们的jar包。
现在我们通过运行如下命令构建镜像:
docker build -t microservicedemo/employee .
我们可以敲入 docker image
看到结果
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
microservicedemo/employee latest 364ffd8162b9 15 minutes ago 846.6 MB
接下来的问题就是“我们的服务容器如何与Mongo容器交互?”,为此,我们引入了容器的link机制。当你运行一个新的容器时,你可以传入一个可选的-link参数指定一个运行中的容器的名字,这样新的容器就可以与这个容器通讯了,所以我们的命令是:
docker run -P -d --name employee --link mongodb microservicedemo/employee
我们启动了一个新的容器,公开了端口(-P),以后台方式运行(-d),命名为employee(—name),接着将这个新的容器连接到了一个名为“mongodb”的容器(-link),连接过程如下:
● 在employee容器的host文件添加一个条目指向MongoDB容器的运行位置
● 在employee容器内添加一些环境变量来协助其他必要的编程访问,可以运行如下命令查看:
docker exec employee bash -c 'env | grep MONGODB'
● 允许容器通过公开的端口来直接通讯,这样就不需要担心主机的部分映射了。如果你还记得上述的内容,我们设置了Spring Mongo到MongoDB的URL作为主机名(mongodb://mongodb/micros),所以有了host文件条目和运行在默认端口的Mongo,Boot应用容器可以连上数据库了
在容器运行时我们可以来执行相同的Web服务,只是这次是作为容器来运行的(对于我来说,容器的8080端口会被映射到虚拟机的32772端口):

本篇我们已经取得了很大的进展,我们有了两个容器可以工作并且可以互相通讯,接下来的第四篇我们将添加一些额外的服务/容器,可以观察到构建更新的进度并与CI工具集成工作。
原文链接: BUILDING A MICROSERVICE ARCHITECTURE WITH SPRING BOOT AND DOCKER, PART III (翻译:胡震)