Spring Boot在微服务中的最佳实践

标签: spring boot 微服务 | 发表时间:2020-05-17 23:18 | 作者:cainzhong
出处:http://weekly.dockone.io

在本文中,我将列出构建Spring Boot应用程序的“金科玉律”,这些应用程序是微服务系统一部分。这些“金科玉律”都来自我过往的经验,我曾经将运行在JEE服务器上的单体SOAP应用程序迁往基于REST的小型Spring Boot应用程序。这些最佳实践假设你的产品上已经拥有许多微服务,且每天要应对海量的请求。让我们开始吧。

1. 收集度量指标

度量指标可视化可以改变组织中系统监控的方法,这非常令人惊讶。在Grafana中设置监控之后,我们能够识别系统中90%以上的大问题,避免这些问题在客户环境中发生并由客户提交给我们的支持团队。多亏了这两个带有大量图表和警报的监视器,我们的反应可能比以前快得多。如果您产品的体系架构是微服务而不是单体应用,那么度量指标会变得更加重要。

对我们来说,好消息是Spring Boot提供了收集最重要度量指标的内置机制。我们只需要设置一些配置文件来开启Spring Boot Actuator提供的预定义指标集。要使用Spring Boot Actuator,我们需要添加Spring Boot的启动依赖:
  
    org.springframework.boot
    spring-boot-starter-actuator



要启用Spring Boot Actuator的功能,我们必须将 management.endpoint.metrics设置为 true。现在,您可以通过调用 GET /actuator/metrics来检查度量指标的完整列表。对我们来说,最重要的指标之一是 http.server.requests,它展示了请求数量和响应时间的统计信息,并会自动记录下请求类型(POST、GET等)、HTTP状态码和uri。

度量指标必须被存储在某个地方。最流行的工具是 InfluxDBPrometheus。它们代表了两种不同的数据收集模型。Prometheus定期从应用程序的公开端点拉取数据,而InfluxDB提供了REST API,由应用程序来调用。 Micrometer库提供了与InfluxDB、Prometheus和其他一些工具的集成方案。要启用对InfluxDB的支持,我们需要添加以下依赖项。
  
    io.micrometer
    micrometer-registry-influx



我们还需要在Spring Boot的application.yml中配置InfluxDB的URL和数据库名。
management:  
metrics:
    export:
      influx:
            db: springboot
            uri: http://192.168.99.100:8086


要启用对Prometheus的支持,我们首先需要添加对应的Micrometer库,然后设置属性 management.endpoint.metricstrue
  
    io.micrometer
    micrometer-registry-prometheus



默认情况下,Prometheus每分钟尝试从定义的目标端点收集一次数据。可以在Prometheus的配置文件中设置这些参数,scrape_config部分负责指定一组目标和参数,以及如何与它们连接。
scrape_configs:  
- job_name: 'springboot'
    metrics_path: '/actuator/prometheus'
    static_configs:
    - targets: ['person-service:2222']


有时候,为指标数据打上标签是非常有用的,特别是当我们在单个微服务系统中存在多个应用程序,并将指标数据发送给单个Influx数据库时。下面是代码示例。
@Configuration  
class ConfigurationMetrics {

    @Value("\${spring.application.name}")
    lateinit var appName: String
    @Value("\${NAMESPACE:default}")
    lateinit var namespace: String
    @Value("\${HOSTNAME:default}")
    lateinit var hostname: String

    @Bean
    fun tags(): MeterRegistryCustomizer {
            return MeterRegistryCustomizer { registry ->
                    registry.config().commonTags("appName", appName).commonTags("namespace", namespace).commonTags("pod", hostname)
            }
    }

}


这是一张从Grafana中截取的监控视图,表示单个应用的 http.server.requests指标。



2. 不要忘记日志

日志记录在开发过程中不是很重要,但在维护过程中却非常重要。值得记住的是,在组织中,通过日志的质量可以间接判断应用程序的质量。通常,应用程序由支持团队维护,因此日志应该是非常重要的。不要试图把所有的事情都放在日志里,只有最重要的事件才应该被记录下来。

为微服务中的所有应用程序使用相同的日志格式也很重要。例如,如果要以JSON格式记录日志,那么对每个应用程序都应该用JSON格式。如果您使用标记appName来指示应用程序的名称或用instanceId来区分同一应用程序的不同实例,那么在任何地方都要这样做。为什么?您通常希望将微服务中收集的所有日志存储在一个单独的中央数据库中。最流行的工具(或者说是收集日志的工具)是 Elastic Stack (ELK)。将中央数据库的优势发挥到最大,您应该确保查询条件和响应结构对于所有应用程序都是相同的,尤其是将不同微服务之间的日志关联起来。怎么做呢?当然是通过使用第三方库。我推荐你使用Spring Boot logging,要使用它,你需要添加如下依赖。
  
com.github.piomin
logstash-logging-spring-boot-starter
1.2.2.RELEASE



这个库将迫使您使用一些良好的日志记录实践,并自动与Logstash集成(ELK中负责收集日志的工具)。它的主要特点是:

  • 能够记录完整的HTTP请求和HTTP响应,并使用适当的标签将这些日志发送到Logstash,这些标签表面了HTTP的请求类型和HTTP响应码

  • 计算和存储每个请求的执行时间
  • 当使用Spring RestTemplate调用的下游服务时,生成和传递correlationId


为了能够向Logstash发送日志,我们至少需要如下配置信息。
logging.logstash:  
enabled: true
url: 192.168.99.100:5000


在添加了 logstash-logging-spring-boot-starter之后,你就可以使用Logstash中的日志标记功能。下图是来自Kibana中的单条日志记录的截图。



我们还可以将库Spring Cloud Sleuth添加到我们的依赖项中。
   
    org.springframework.cloud
    spring-cloud-starter-sleuth



Spring Cloud Sleuth传递与Zipkin(一种流行的分布式跟踪工具)兼容的标头。它的主要特点是:
  • 它将跟踪(相关请求)和span IDs添加到Slf4J MDC
  • 它记录时间信息以帮助进行延迟分析
  • 它修改了日志的格式,以添加一些信息,比如附加的MDC字段
  • 它提供与其他Spring组件的集成,如OpenFeign、RestTemplate或Spring Cloud Netflix Zuul


3.让你的API易用

在大多数情况下,其他应用程序将通过REST API调用您的应用程序。因此我们需要小心的维护一份API文档。文档应该由代码生成。当然有一些工具可以做到这一点。其中最受欢迎的是Swagger。您可以使用SpringFox项目轻松地将Swagger 2集成到您的Spring Boot应用程序中。为了使用Swagger,我们需要添加如下依赖。第一个库负责从Spring MVC控制器代码中生成Swagger descriptor,而第二个库负责解析Swagger descriptor并在您的浏览器中展示页面。
  
    io.springfox
    springfox-swagger2
    2.9.2


    io.springfox
    springfox-swagger-ui
    2.9.2



我们还必须提供一些Bean来修改Swagger的默认行为。它应该只展示我们自己提供的REST API,而不展示Spring Boot Actuator的API。我们也可以通过定义UiConfiguration bean来定制UI外观。
@Configuration  
@EnableSwagger2
public class ConfigurationSwagger {

    @Autowired
    Optional build;

    @Bean
    public Docket api() {
            String version = "1.0.0";
            if (build.isPresent())
                    version = build.get().getVersion();
            return new Docket(DocumentationType.SWAGGER_2)
                            .apiInfo(apiInfo(version))
                            .select()
                            .apis(RequestHandlerSelectors.any())
                            .paths(PathSelectors.regex("(/components.*)"))
                            .build()
                            .useDefaultResponseMessages(false)
                            .forCodeGeneration(true);
    }

    @Bean
    public UiConfiguration uiConfig() {
            return UiConfigurationBuilder.builder().docExpansion(DocExpansion.LIST).build();
    }

    private ApiInfo apiInfo(String version) {
            return new ApiInfoBuilder()
                            .title("API - Components Service")
                            .description("Managing Components.")
                            .version(version)
                            .build();
    }
}


下图是Swagger 2 UI的例子。



接下来是为所有微服务定义相同的REST API准则。如果您始终如一地为您的微服务构建API,那么对于外部和内部客户端,集成微服务要简单得多。指南应该包含如何构建API的说明,需要在请求和响应上设置哪些HTTP header,如何生成错误码等。这些准则应该与组织中的所有开发人员和供应商共享。有关为Spring Boot微服务生成Swagger文档(包括为API网关上的所有应用程序公开它)的更详细说明,您可以参考我的文章 [Microservices API Documentation with Swagger2]

4. 不要害怕使用断路器

如果您使用Spring cloud在微服务之间进行通信,您可以利用Spring cloud Netflix Hystrix或Spring cloud断路器来实现断路。然而,由于Netflix不再开发Hystrix,Pivotal团队已经将第一个解决方案转移到了维护模式。推荐的解决方案是构建在 resilience4j项目之上的新的Spring Cloud断路器。
  
    org.springframework.cloud
    spring-cloud-starter-circuitbreaker-resilience4j



然后,我们需要为断路器设置所需的配置,方法是定义一个Customizer bean,该bean被传递给 Resilience4JCircuitBreakerFactory。以下示例使用默认值。
@Bean  
public Customizer defaultCustomizer() {
    return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
                    .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(5)).build())
                    .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())
                    .build());
}


有关将Hystrix断路器与Spring Boot应用程序集成的详细信息,请参阅我的另一篇文章:[ Part 3: Creating Microservices: Circuit Breaker, Fallback and Load Balancing with Spring Cloud

5. 使应用程序透明

我们不应该忘记,迁移到微服务的最重要原因之一是持续交付的需求。今天,快速交付变更的能力在市场上具有优势。您甚至应该能够在一天内多次交付更改。因此,重要的是当前是什么版本,它在哪里发布,以及它包括哪些更改。

在使用Spring Boot和Maven时,我们可以很容易地发布诸如最后更改日期、Git的commit id或应用程序的多个版本等信息。要实现这一点,我们只需要在我们的pom.xml中包含以下Maven插件。
  
    
            org.springframework.boot
            spring-boot-maven-plugin
            
                    
                            
                                    build-info
                            
                    
            
    
    
            pl.project13.maven
            git-commit-id-plugin
            
                    false
            
    



假设您已经启用了Spring Boot Actuator (参见第1节),您必须启用 /info端点来显示所有有趣的数据。
management.endpoint.info.enabled: true  


当然,我们有许多微服务组成的一个大系统,并且每个微服务都有一些正在运行的实例。最好是在一个应用程序中监视我们所有的实例——就像收集度量指标和收集日志一样。幸运的是,有一个专门用于Spring Boot应用程序的工具,它能够从所有Actuator端点收集数据并在UI中显示它们。它是由Codecentric开发的 Spring Boot Admin。使用Spring Boot Admin最方便的方式是创建一个专门的Spring Boot应用程序,该程序需要添加Spring Boot Admin的依赖项和服务发现的依赖项,例如Spring Cloud Netflix Eureka。
  
    de.codecentric
    spring-boot-admin-starter-server
    2.1.6


    org.springframework.cloud
    spring-cloud-starter-netflix-eureka-client



然后,我们应该通过使用 @EnableAdminServer注解来启用Spring Boot Admin。
@SpringBootApplication  
@EnableDiscoveryClient
@EnableAdminServer
@EnableAutoConfiguration
public class Application {

    public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
    }

}


通过Spring Boot Admin,我们可以轻松地浏览在服务发现中注册的应用程序列表,并检查每个应用程序的版本或提交信息。



我们可以点击“Details”按钮查看更多详细信息,这些信息都来自于 /info端点和从其他Actuator端点收集到的数据。



6. 编写合同测试

消费者驱动契约(Consumer Driven Contract, CDC)测试是一种方法,它允许您验证系统内应用程序之间的集成。系统内部的集成数量可能非常大,尤其是在维护基于微服务的系统时。由于 Spring Cloud Contract项目的存在,在Spring Boot中进行契约测试会相对容易一些。还有一些专门为CDC设计的框架,如Pact,但Spring Cloud Contract可能是首选,因为我们使用的是Spring Boot。

要在生产者端使用Spring Cloud Contract,我们需要添加Spring Cloud Contract Verifier。
  
    org.springframework.cloud
    spring-cloud-starter-contract-verifier
    test



在消费者端,我们需要添加Spring Cloud Contract Stub Runner。
  
    org.springframework.cloud
    spring-cloud-starter-contract-stub-runner
    test



第一步是定义契约。编写它的一种方法是使用Groovy语言。合同应在生产者和消费者双方进行核实。例如:
import org.springframework.cloud.contract.spec.Contract  
Contract.make {
    request {
            method 'GET'
            urlPath('/persons/1')
    }
    response {
            status OK()
            body([
                    id: 1,
                    firstName: 'John',
                    lastName: 'Smith',
                    address: ([
                            city: $(regex(alphaNumeric())),
                            country: $(regex(alphaNumeric())),
                            postalCode: $(regex('[0-9]{2}-[0-9]{3}')),
                            houseNo: $(regex(positiveInt())),
                            street: $(regex(nonEmpty()))
                    ])
            ])
            headers {
                    contentType(applicationJson())
            }
    }
}


契约与存根一起打包在JAR中。它可以发布到像Artifactory或Nexus这样的存储库管理器,然后消费者可以在JUnit测试期间从存储库管理器下载契约。生成的JAR文件以存根作为后缀。
@RunWith(SpringRunner.class)  
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
@AutoConfigureStubRunner(ids = {"pl.piomin.services:person-service: :stubs:8090"}, consumerName = "letter-consumer",  stubsPerConsumer = true, stubsMode = StubsMode.REMOTE, repositoryRoot = "http://192.168.99.100:8081/artifactory/libs-snapshot-local")
@DirtiesContext
public class PersonConsumerContractTest {

    @Autowired
    private PersonClient personClient;

    @Test
    public void verifyPerson() {
            Person p = personClient.findPersonById(1);
            Assert.assertNotNull(p);
            Assert.assertEquals(1, p.getId().intValue());
            Assert.assertNotNull(p.getFirstName());
            Assert.assertNotNull(p.getLastName());
            Assert.assertNotNull(p.getAddress());
            Assert.assertNotNull(p.getAddress().getCity());
            Assert.assertNotNull(p.getAddress().getCountry());
            Assert.assertNotNull(p.getAddress().getPostalCode());
            Assert.assertNotNull(p.getAddress().getStreet());
            Assert.assertNotEquals(0, p.getAddress().getHouseNo());
    }

}


契约测试不会验证微服务系统中的复杂用例。然而,这是测试微服务之间集成的第一阶段。一旦确保了应用程序之间的API契约是有效的,就可以进行更高级的集成或端到端测试。关于持续集成与Spring Cloud Contract的更详细解释,可以参考我的另一篇文章 Continuous Integration with Jenkins, Artifactory and Spring Cloud Contract

7. 更新到最新版本

Spring Boot和Spring Cloud相对频繁地发布新版本的框架。假设您的微服务的代码数量不是很多,那么很容易的就可以升级到最新版本。Spring Cloud使用release train模式发布项目的新版本,以简化依赖关系管理并避免库的不兼容版本之间的冲突问题。

此外,Spring Boot系统地改善了应用程序的启动时间和内存占用,因此值得对其进行更新。这是Spring Boot和Spring Cloud当前的稳定版本。
  
    org.springframework.boot
    spring-boot-starter-parent
    2.2.1.RELEASE


    
            
                    org.springframework.cloud
                    spring-cloud-dependencies
                    Hoxton.RELEASE
                    pom
                    import
            
    



结论

我向您展示了如何使用Spring Boot的特性和Spring Cloud的一些附加库,作为最佳实践这并不困难。这些最佳实践将使您更容易地迁移到基于微服务的体系结构中,并在容器中运行应用程序。

原文链接: https://piotrminkowski.com/201 ... ices/

相关 [spring boot 微服务] 推荐:

在微服务领域Spring Boot自动伸缩如何实现

- - IT瘾-tuicool
自动伸缩是每个人都想要的,尤其是在微服务领域. 让我们看看如何在基于Spring Boot的应用程序中实现. 我们决定使用 Kubernetes、 Pivotal Cloud Foundry或 HashiCorp's Nomad等工具的一个更重要的原因是为了让系统可以自动伸缩. 当然,这些工具也提供了许多其他有用的功能,在这里,我们只是用它们来实现系统的自动伸缩.

Spring Boot在微服务中的最佳实践

- - DockOne.io
在本文中,我将列出构建Spring Boot应用程序的“金科玉律”,这些应用程序是微服务系统一部分. 这些“金科玉律”都来自我过往的经验,我曾经将运行在JEE服务器上的单体SOAP应用程序迁往基于REST的小型Spring Boot应用程序. 这些最佳实践假设你的产品上已经拥有许多微服务,且每天要应对海量的请求.

Spring boot传统部署

- - 企业架构 - ITeye博客
使用spring boot很方便,一个jar包就可以启动了,因为它里面内嵌了tomcat等服务器. 但是spring boot也提供了部署到独立服务器的方法. 如果你看文档的话,从jar转换为war包很简单,pom.xml的配置修改略去不讲. 只看source的修改,很简单,只要一个配置类,继承自SpringBootServletInitializer, 并覆盖configure方法.

值得使用的Spring Boot

- - ImportNew
2013年12月12日,Spring发布了4.0版本. 这个本来只是作为Java平台上的控制反转容器的库,经过将近10年的发展已经成为了一个巨无霸产品. 不过其依靠良好的分层设计,每个功能模块都能保持较好的独立性,是Java平台不可多得的好用的开源应用程序框架. Spring的4.0版本可以说是一个重大的更新,其全面支持Java8,并且对Groovy语言也有良好的支持.

Spring Boot配置多个DataSource

- - 廖雪峰的官方网站
使用Spring Boot时,默认情况下,配置 DataSource非常容易. Spring Boot会自动为我们配置好一个 DataSource. 如果在 application.yml中指定了 spring.datasource的相关配置,Spring Boot就会使用该配置创建一个 DataSource.

Spring boot executable jar/war 原理

- - ImportNew
spring boot里其实不仅可以直接以 Java -jar demo.jar的方式启动,还可以把jar/war变为一个可以执行的脚本来启动,比如./demo.jar. 把这个executable jar/war 链接到/etc/init.d下面,还可以变为Linux下的一个service. 只要在spring boot maven plugin里配置:.

spring boot与spring batch、postgres及elasticsearch整合

- - 互联网 - ITeye博客
当系统有大量数据需要从数据库导入elasticsearch时,使用sping batch可以提高导入的效率. 这篇文章使用spring batch将数据从postgres导入elasticsearch. 本文使用spring data jest连接ES(也可以使用spring data elasticsearch连接ES),ES版本为5.5.3.

Spring Boot使用redis做数据缓存

- - ITeye博客
SysUser.class)); //请注意这里. 3 redis服务器配置. /** *此处的dao操作使用的是spring data jpa,使用@Cacheable可以在任意方法上,*比如@Service或者@Controller的方法上 */ public interface SysUserRepo1 extends CustomRepository {.

spring boot应用启动原理分析

- - ImportNew
在spring boot里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启动的,不需要另外配置一个Web Server. 如果之前没有使用过spring boot可以通过下面的demo来感受下. 下面以这个工程为例,演示如何启动Spring boot项目:.

Apache Shiro和Spring boot的结合使用

- - 企业架构 - ITeye博客
实际上在Spring boot里用Spring Security最合适,毕竟是自家东西,最重要的一点是Spring Security里自带有csrf filter,防止csrf攻击,shiro里就没有. 但是Spring Security有点太复杂,custmize起来比较费力,不如shiro来的简单.