Java微服务开发指南 -- 使用Spring Boot构建微服务

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 使用Spring Boot构建微服务     Spring Boot是一个广泛用来构建Java微服务的框架,它基于Spring依赖注入框架来进行工作。Spring Boot允许开发人员使用更少的配置来构建微服务,同时框架本身能够尽可能的减少开发人员的冲突,它和我们后面要介绍的两个框架类似,它通过以.

使用Spring Boot构建微服务

    Spring Boot是一个广泛用来构建Java微服务的框架,它基于Spring依赖注入框架来进行工作。Spring Boot允许开发人员使用更少的配置来构建微服务,同时框架本身能够尽可能的减少开发人员的冲突,它和我们后面要介绍的两个框架类似,它通过以下几个方面帮助开发人员:

  • 自动化配置,一般情况下都有默认配置
  • 提供一组流行的starter依赖,方便开发人员使用
  • 简化应用打包
  • 提升应用运行时的内省性(例如:Metrics与环境信息)

简化的配置

    Spring以噩梦般的配置而闻名,虽然框架本身相比其他组件模型(EJB 1.x 和 2.x)简单了不少,但是它也带来了自己的配置模式。也就是说,如果想要正确的使用Spring,你需要深入了解如何进行XML配置、了解JdbcTemplateJmsTemplate以及BeanFactory生命周期、了解Servlet监听器,你以为掌握了这些就可以开始开发了吗?实际上问题远没有结束,如果你要用Spring MVC编写一个简单的hello world,你还需要了解DispatcherServlet和一堆Model-View-Controller相关的类型。

    Spring Boot目标就是消除掉这些与业务无关的配置和概念,通过简单的注解,你就能够完成这些工作,当然如果你想继续想以前一样使用Spring,它也不会拦着你。

Starter依赖

    Spring广泛使用着,包括了大型企业应用,在应用中,用户将会使用到不同的技术组件,包括:JDBC数据源、消息队列、文件系统以及应用缓存等。开发人员需要在需要这些功能时,停下来,仔细分析一下自己究竟需要什么?需要的内容属于哪个依赖(“哦,我需要JPA依赖”),然后花费大量的时间在依赖组织和排除上。

    Spring Boot提供了功能域(一批jar包依赖)的依赖,它让开发人员声明需要的功能,而不用去关系究竟如何处理依赖关系。这些starter可以允许开发人员直接使用这些功能:

  • JPA持久化
  • NoSQL数据库支持,例如:MongoDB、Cassandra或者CouchBase
  • Redis缓存
  • Tomcat、Jetty或者Undertow的Servlet引擎
  • JTA事务

    通过直接添加一个starter,能够让开发人员获得这个特性相关的一组依赖,而这些依赖的组合已经被验证,省却了开发人员的不少时间。

应用打包

    Spring Boot是一组jar包和符合其约定的配置的构建块,因此它不会运行在现有的应用服务器中,而使用Spring Boot的大多数开发人员更喜欢的是直接运行的这种自包含的jar包。这意味着Spring Boot将所有的依赖和应用程序代码都包装到一个自包含的jar中,而这些jar包运行在一个平面的类加载器中。简单的类加载体系使得开发人员更容易理解应用程序的启动、依赖关系和日志输出,但更重要的是,它有助于减少应用从构建到生产环境的步骤数量。这意味着开发人员不必将打包好的应用放置到应用服务器中,而是直接运行这个standalone的应用,如果你需要servlet,那么完全可以将其打包在应用内,使其为你服务。

    没错,一个简单的java -jar <name.jar>就可以启动你的应用了!Spring Boot、Dropwizard和WildFly Swarm都遵循将所有内容打包成可执行的jar模式。但是传统的应用服务器包含的管理能力,怎么在这种模式下实现呢?

为生产环境而准备

    Spring Boot推出了一个叫做actuator的模块,它可以实现应用的指标统计。例如:我们可以收集日志、查看指标、生成执行线程dump、显示环境变量、了解gc以及显示BeanFactory中配置的bean。可以通过HTTP或者JMX暴露这些信息或者进行日志输出。借助Spring Boot,我们可以利用Spring框架的功能、减少配置并快速开发应用并上线。

    说了这么多,让我们看看怎么使用它。

开始使用

我们接下来使用Spring Boot的命令行工具(CLI)来创建第一个Spring Boot程序(CLI底层使用了Spring Initializer)。你也可以使用自己喜欢的方式,比如使用集成了Spring Initializer的IDE,或者直接访问web来创建一个工程。

Spring Boot CLI 的安装方式,可以参考 这里

Homebrew下:
brew tap pivotal/tap
brew install springboot

    一旦你安装了Spring Boot CLI,你可以这样检查一下。

$ spring --version
Spring CLI v1.5.4.RELEASE

    如果你能看到版本的输出,恭喜你,安装成功了。接下来,在你希望创建工程的目录下运行命令:spring init --build maven --groupId com.murdock.examples --version 1.0 --java-version 1.8 --dependencies web --name hola-springboot hola-springboot

在microservices-camp下运行。

    运行该命令后,将会在当前目录下创建一个hola-springboot目录,同时该目录下包含了一个完整的Spring Boot程序,简单的介绍一下这个命令中包含的内容。

  • --build
    使用的构建工具,示例中是:maven
  • --groupId
    maven坐标中的组Id,也就是代码的包名,如果你想改包名,只有在IDE中修改
  • --version
    maven坐标中的version
  • --java-version
    Java版本
  • --dependencies
    这是一个有趣的参数,我们可以指定某种开发类型的依赖。比如:web就是指当前项目使用Spring MVC框架,默认基于内嵌的Tomcat(Jetty和Undertow作为可选)。其他的依赖或者starter,比如:jpasecuritycassandra

    进入到hola-springboot目录中, 执行命令:mvn spring-boot:run,如果程序启动,没有报错,你就能看到如下的日志:

2017-06-18 10:46:51.070  INFO 3397 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-06-18 10:46:51.081  INFO 3397 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 0
2017-06-18 10:46:51.253  INFO 3397 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2017-06-18 10:46:51.262  INFO 3397 --- [           main] c.m.e.h.HolaSpringbootApplication        : Started HolaSpringbootApplication in 13.988 seconds (JVM running for 17.985)

    恭喜你!你快速的创建了一个Spring Boot应用,并且启动了它,你甚至可以访问http://localhost:8080,你会看到如下内容


chapter2-1.png

    可以看到返回了默认的出错页面,到目前为止,它除了这个什么也做不了。接下来,我们就添加一些特性,比如:REST访问,做一个helloworld式的应用。

后续实践内容与原文有不同,在操作性上要比原文具备更好的实践性。

你好,世界

    现在我们拥有了一个可以运行的Spring Boot应用,让我们为它添加一些简单的功能。首先,我们想做的是,让应用暴露一个位置是api/holaV1HTTP/REST端点,访问它将返回 Hola Spring Boot @ X,而其中的 X 是运行应用的本机IP。

    在编写代码前,先将hola-springboot导入到IDE中,在com.murdock.examples.holaspringboot包下面建立一个类,名称为HolaRestControllerV1

public class HolaRestControllerV1 {

    public String hola() throws UnknownHostException {
        String hostname = null;
        try {
            hostname = InetAddress.getLocalHost()
                    .getHostAddress();
        } catch (UnknownHostException e) {
            hostname = "unknown";
        }
        return "Hola Spring Boot @ " + hostname;
    }
}

    可以看到方法hola()返回了我们需要的内容,一个简单的字符串。

添加HTTP端点

    到现在为止,我们只是创建了一个名为HolaRestControllerV1POJO,你可以写一些单元测试去做验证,而让它暴露HTTP端点,则需要增加一些内容。

@RestController
@RequestMapping("/api")
public class HolaRestControllerV1 {

    @RequestMapping(method = RequestMethod.GET, value = "/holaV1", produces = "text/plain")
    public String hola() throws UnknownHostException {
        String hostname = null;
        try {
            hostname = InetAddress.getLocalHost()
                    .getHostAddress();
        } catch (UnknownHostException e) {
            hostname = "unknown";
        }
        return "Hola Spring Boot @ " + hostname;
    }
}
  • @RestController
    这个注解告知Spring,该类是一个用于暴露HTTP端点的控制器(可以暴露GET、PUT和POST等基于HTTP协议的功能)
  • @RequestMapping
    用于映射HTTP URI到对应的类或者方法

    通过添加这两个注解,我们就可以使得原有的类具备了暴露HTTP端点的能力。针对上面的代码,比如@RequestMapping("/api")代表着HolaRestControllerV1接受来自/api路径的请求,当添加@RequestMapping(method = RequestMethod.GET, value = "/holaV1", produces = "text/plain")时,表示告知Spring在/holaV1(其实是/api/holaV1)暴露HTTP GET端点,该端点接受的类型是text/plain。Spring Boot将会使用内置的Tomcat运行,当然你也可以切换到Jetty或者Undertow

    我们在hola-springboot目录下,执行mvn clean package spring-boot:run,然后使用浏览器访问http://localhost:8080/api/holaV1,如果一切正常,我们可以看到如下内容。


chapter2-2.png

    现在这些返回内容是写死的,如果我们想个应用增加一些环境相关的配置,如何做呢?比如:可以替代 Hola 这个词,比如使用 Guten Tag 德语,我们把这个应用部署给德国人用,我们需要一个将外部属性注入给应用的途径。

外部配置

    Spring Boot可以很容易使用诸如:properties文件、命令行参数和系统环境变量等作为外部的配置来源。我们甚至可以将这些配置属性通过Spring Context绑定到类型实例上,例如:如果想将helloapp.*属性绑定到HolaRestController,可以在类型上声明@ConfigurationProperties(prefix="helloapp"),Spring Boot会自动尝试将比如helloapp.foo或者helloapp.bar等这些属性值绑定到类型实例的foobar等字段上。

    在Spring Initializer CLI创建的工程中,已经有了一个application.properties,我们就可以在这个文件中定义新属性,比如:helloapp.saying

$ more src/main/resources/application.properties
helloapp.saying=Guten Tag aus

    创建一个新的控制器HolaRestControllerV2

@RestController
@RequestMapping("/api")
@ConfigurationProperties(prefix = "helloapp")
public class HolaRestControllerV2 {

    private String saying;

    @RequestMapping(method = RequestMethod.GET, value = "/holaV2", produces = "text/plain")
    public String hola() throws UnknownHostException {
        String hostname = null;
        try {
            hostname = InetAddress.getLocalHost()
                    .getHostAddress();
        } catch (UnknownHostException e) {
            hostname = "unknown";
        }
        return saying + " @ " + hostname;
    }

    public String getSaying() {
        return saying;
    }

    public void setSaying(String saying) {
        this.saying = saying;
    }
}

    停止之前运行的应用,然后在hola-springboot目录下,继续使用mvn clean package spring-boot:run来编译工程,运行这个应用,然后使用浏览器访问http://localhost:8080/api/holaV2,你会看到如下内容。


chapter2-3.png

    我们现在通过更改外部配置文件来使应用适应部署的环境,比如:调用服务的url、数据库url和密码以及消息队列配置,这些都适合作为配置。但是也要把握度,不是所有的内容都适合放置在配置中,比如:应用在任何环境下都应该具备相同的超时、线程池、重试等配置。

暴露应用的Metrics和信息

    一个Spring Boot应用搭建起来了,紧接着就是将其部署到生产环境,我们怎样监控它呢?当我们想知道它运行的怎么样,我们该怎么办呢?除非我们让应用向外暴露出Metrics,否则应用就会像黑盒子一样。Spring Boot专门提供了一个starter -- actuator来完成这个工作。

    让我们看看如何启用actuator,启用的过程非常简单。在hola-springboot/pom.xml中依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

    然后在hola-springboot/src/main/resources/application.properties中增加一个配置(安全原因):

$ more src/main/resources/application.properties
management.security.enabled=false

    随后,结束当前应用,在hola-springboot下运行:mvn clean package spring-boot:run重新编译工程,启动项目。

    我们可以通过浏览器访问几次http://localhost:8080/api/holaV1以及http://localhost:8080/api/holaV2,然后访问一下:http://localhost:8080/metrics,可以看到如下内容。


chapter2-4.png

    类似这样的URL还有许多:

    暴露出这些运行时信息,使得开发人员在忙于业务开发的同时,更轻松获取到系统信息。

怎样在maven之外运行

    到现在为止,我们还是以开发者视角使用maven来构建这个简单的工程。如果我们需要将它部署到其他环境,比如:开发、测试或者生产环境,需要怎么做呢?

    幸运的是,使用Spring Boot,我们可以轻松的发布和构建,Spring Boot推荐单一、可执行的jar,而在这个jar中包括了所有的依赖,这些依赖的jar都会放置在应用的类路径下。在hola-springboot下,运行mvn clean package,然后可以通过java -jar来运行。

$ mvn clean package
$ java -jar target/hola-springboot-1.0.jar

    就是这样,我们可以启动这个应用,后续接下来介绍的DropwizardWildFly Swarm都使用类似的方式进行。

调用其他服务

    在微服务环境下,每个服务都有提供功能的义务并服务好它的调用者。就像我们在第一章中谈的,因为网络的不确定性,构建分布式系统十分的困难,本章主要讨论一个服务怎样调用到后台的服务。

在第五章中,将会讨论服务的柔性、适应性交互和调用

    接下来将扩展hola-springboot项目,完成服务的调用,但在此之前,我们先要搭建一个后台服务,完成类似下图的交互。


chapter2-5.png

后台服务的构建,将采用forge + WildFly的方式进行,比原文中写一个Servlet部署到Jetty显得更好
关于forge的安装,在mac下:brew install jboss-forge

    通过以下方式,可以在microservices-camp下创建一个具备持久化能力的REST服务,它可以自由的部署到WildFly中。

    使用forge构建完成之后,可以将其导入到IDE中,如果观察BookEndpoint这个类型,你会发现涉及到CRUD以及分页查询等逻辑已经完全具备了。


chapter2-11.png

    通过上述命令,我们可以构建出一个hola-backend.war的应用,下面我们将其部署到WildFly中。WildFly的使用可以通过下载到本地运行,但是由于涉及到两个进程的交互,本文采用Docker的方式进行部署,读者可以自行准备环境。

笔者准备了WildFly镜像,可以简单的运行起来
执行:sudo docker run --name wildfly -it -p 9990:9990 -p 8080:8080 weipeng2k/wildfly-admin,可以启动一个WildFly,HTTP端口在8080,应用管理端口在9990
管理员账号笔者已经构建在镜像中:admin/Admin#hello1234

    登录到WildFly后台,通过管理界面,部署hola-backend.war


chapter2-6.png

    可以看到后台的更新日志,从中可以了解到应用部署正常。


chapter2-7.png

    使用这种方式的好处在于开发阶段如果有新的包生成直接进行上传就好,如果想整体销毁,直接停止删除容器即可,不会弄坏WildFly。下面使用chrome插件Postman构建Book数据,然后测试是否可用。

    新增数据测试。


chapter2-8.png

    查询数据测试。


chapter2-9.png

    看来后台服务应用hola-backend工作正常,当然可以通过WildFly的管理界面查询运行时信息,这点和Spring Boot的actuator很像,但是产品化的体验做的更好些。

    接下来在hola-springboot项目中新建BookRestController,使用RestTemplate来完成后端服务的交互。

@RestController
@RequestMapping("/api")
@ConfigurationProperties(prefix = "books")
public class BookRestController {

    private RestTemplate template = new RestTemplate();

    private String backendHost;

    private int backendPort;

    @RequestMapping(value = "/books/{bookId}",
            method = RequestMethod.GET, produces = "text/plain")
    public String greeting(@PathVariable("bookId") Long bookId) {
        String backendServiceUrl = String.format("http://%s:%d/hola-backend/rest/books/{bookId}", backendHost, backendPort);
        Map object = template.getForObject(backendServiceUrl, Map.class, bookId);
        return object.toString();
    }

    public String getBackendHost() {
        return backendHost;
    }

    public void setBackendHost(String backendHost) {
        this.backendHost = backendHost;
    }

    public int getBackendPort() {
        return backendPort;
    }

    public void setBackendPort(int backendPort) {
        this.backendPort = backendPort;
    }
}

    可以看到BookRestController将后端的host与port放在了配置中,而前缀是books,那么也就需要在application.properties中增加这些配置。

$ more src/main/resources/application.properties
books.backendHost=192.168.0.125
books.backendPort=8080

    接下来,打开浏览器访问:http://localhost:8080/api/books/1,它将访问hola-springboot,而hola-springboot将会调用hola-backend,最终由hola-springboot输出结果。


chapter2-10.png

小结

    通过本章的内容,我们学习了Spring Boot的基本知识,了解它与传统的WAREAR不同的部署方式,以及如何使用外部资源来完成配置,并通过actuator暴露了Metrics,使用RestTemplate调用了另一个服务。如果你想了解跟多内容,可以参考下面的链接。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
2天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
13 2
|
15天前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
23天前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
42 1
Spring 框架:Java 开发者的春天
|
16天前
|
Java 数据库连接 数据库
如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面
本文介绍了如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面。通过合理配置初始连接数、最大连接数和空闲连接超时时间,确保系统性能和稳定性。文章还探讨了同步阻塞、异步回调和信号量等并发控制策略,并提供了异常处理的最佳实践。最后,给出了一个简单的连接池示例代码,并推荐使用成熟的连接池框架(如HikariCP、C3P0)以简化开发。
34 2
|
22天前
|
Java 数据库连接 开发者
Spring 框架:Java 开发者的春天
【10月更文挑战第27天】Spring 框架由 Rod Johnson 在 2002 年创建,旨在解决 Java 企业级开发中的复杂性问题。它通过控制反转(IOC)和面向切面的编程(AOP)等核心机制,提供了轻量级的容器和丰富的功能,支持 Web 开发、数据访问等领域,显著提高了开发效率和应用的可维护性。Spring 拥有强大的社区支持和丰富的生态系统,是 Java 开发不可或缺的工具。
|
23天前
|
JSON Java Maven
实现Java Spring Boot FCM推送教程
本指南介绍了如何在Spring Boot项目中集成Firebase云消息服务(FCM),包括创建项目、添加依赖、配置服务账户密钥、编写推送服务类以及发送消息等步骤,帮助开发者快速实现推送通知功能。
56 2
|
9天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
19 0
|
8天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
4天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
21 9
|
7天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####