Spring Cloud 学习构建分布式应用(第二天)

一、最简单的分布式架构,服务消费者与服务提供者架构

1.1 定义

名词 定义
服务提供者 服务的被调用方(即:为其他服务提供服务的服务)
服务消费者 服务的调用方(即:依赖其他服务的服务)

1.2 架构概述

       以电影售票系统为例。如图,用户向电影微服务发起了一个购票的请求。在进行购票的业务操作前,电影微服务需要调用用户微服务的接口,查询当前用户的余额是多少、是不是符合购票标准等。在这种场景下,用户微服务就是一个服务提供者,电影微服务则是一个服务消费者。

      围绕该场景,先来编写一个用户微服务,然后编写一个电影微服务。

注意:

服务消费者和服务提供者描述的只是微服务之间的调用关系,一般成对出现。例如本文,用户微服务是是电影微服务的服务提供者,电影微服务是用户微服务的服务消费者。很多初学者和笔者交流时,会描述提供者如何如何……仿佛消费者和提供者是微服务的固有属性,这是不对的——例如A调用B,B调用C,那么B相对A就是提供者,B相对C就消费者。

二、开发

创建新的项目,并在目录下新建子模块,分别是microservice-simple-provider-user(服务提供者)和microservice-simple-consumer-movie(服务消费者)

2.1 创建microservice-simple-provider-user(服务提供者)

2.1.1 在父目录pom.xml文件添加配置

<!-- 引入spring boot的依赖 -->
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.7.RELEASE</version>
</parent>

2.1.2 在microservice-simple-provider-user(服务提供者)子模块pom.xml文件添加配置

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud-study</artifactId>
        <groupId>com.qhr.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microservice-simple-provider-user</artifactId>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.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-starter-data-jpa</artifactId>
        </dependency>
        <!-- 引入H2数据库,一种内嵌的数据库,语法类似MySQL -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
        <!-- 引入Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <!-- 引入spring cloud的依赖,不能少,主要用来管理Spring Cloud生态各组件的版本 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- 添加spring-boot的maven插件,不能少,打jar包时得用 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2.1.3 创建实体类User

package com.qhr.cloud.study.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.math.BigDecimal;

/**
 * @Author : qhr
 * @Description :
 * @Date : Created in 16:44 2020/6/24
 * @Modified By :
 */
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column
    private String username;
    @Column
    private String name;
    @Column
    private Integer age;
    @Column
    private BigDecimal balance;
}

2.1.4 创建持久层UserRepository文件

package com.qhr.cloud.study.repository;

import com.qhr.cloud.study.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
 * @Author : qhr
 * @Description :
 * @Date : Created in 16:46 2020/6/24
 * @Modified By :
 */
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

2.1.5 创建UserController文件

package com.qhr.cloud.study.controller;

import com.qhr.cloud.study.entity.User;
import com.qhr.cloud.study.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Optional;
/**
 * @Author : qhr
 * @Description :
 * @Date : Created in 16:49 2020/6/24
 * @Modified By :
 */
@RequestMapping("/users")
@RestController
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping("/{id}")
    public Optional<User> findById(@PathVariable Long id) {
        return this.userRepository.findById(id);
    }


    @GetMapping("/list")
    public List<User> getList() {
        return this.userRepository.findAll();
    }

}

2.1.6 创建启动类ProviderUserApplication

package com.qhr.cloud.study;

import com.qhr.cloud.study.entity.User;
import com.qhr.cloud.study.repository.UserRepository;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.math.BigDecimal;
import java.util.stream.Stream;

/**
 * @Author : qhr
 * @Description :
 * @Date : Created in 16:56 2020/6/24
 * @Modified By :
 */
@SpringBootApplication
public class ProviderUserApplication {
    public static void main(String args[]) {
        SpringApplication.run(ProviderUserApplication.class, args);
    }

    /**
     * 初始化用户信息
     * 注:Spring Boot2不能像1.x一样,用spring.datasource.schema/data指定初始化SQL脚本,否则与actuator不能共存
     * 原因详见:
     * https://github.com/spring-projects/spring-boot/issues/13042
     * https://github.com/spring-projects/spring-boot/issues/13539
     *
     * @param repository repo
     * @return runner
     */
    @Bean
    ApplicationRunner init(UserRepository repository) {
        return args -> {
            User user1 = new User(1L, "account1", "张三", 20, new BigDecimal(100.00));
            User user2 = new User(2L, "account2", "李四", 28, new BigDecimal(180.00));
            User user3 = new User(3L, "account3", "王五", 32, new BigDecimal(280.00));
            Stream.of(user1, user2, user3)
                    .forEach(repository::save);
        };
    }
}

2.1.7 创建application.yml文件

server:
  # 指定Tomcat端口
  port: 8000
spring:
  jpa:
    # 让hibernate打印执行的SQL
    show-sql: true
logging:
  level:
    root: INFO
    # 配置日志级别,让hibernate打印出执行的SQL参数
    org.hibernate: INFO
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE
    org.hibernate.type.descriptor.sql.BasicExtractor: TRACE

3.1 创建 microservice-simple-consumer-movie(服务消费者)

3.1.2 在microservice-simple-consumer-movie(服务消费者)的pom.xml中添加

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud-study</artifactId>
        <groupId>com.qhr.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microservice-simple-consumer-movie</artifactId>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.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-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <!-- 引入spring cloud的依赖 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- 添加spring-boot的maven插件 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3.1.3 创建实体User

package com.qhr.cloud.study.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;

/**
 * @Author : qhr
 * @Description :
 * @Date : Created in 17:15 2020/6/24
 * @Modified By :
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String username;
    private String name;
    private Integer age;
    private BigDecimal balance;
}

3.1.4 创建MovieController

package com.qhr.cloud.study.controller;

import com.qhr.cloud.study.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * @Author : qhr
 * @Description :
 * @Date : Created in 17:16 2020/6/24
 * @Modified By :
 */
@RequestMapping("/movies")
@RestController
public class MovieController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/users/{id}")
    public User findById(@PathVariable Long id) {
        // 这里用到了RestTemplate的占位符能力
        User user = this.restTemplate.getForObject("http://localhost:8000/users/{id}", User.class, id);
        // ...电影微服务的业务...
        return user;
    }

    @GetMapping("/users/list")
    public List<User> getList() {
        // 这里用到了RestTemplate的占位符能力
        List list = this.restTemplate.getForObject("http://localhost:8000/users/list", List.class);
        // ...电影微服务的业务...
        return list;
    }
}

3.1.5 创建启动类ConsumerMovieApplication

package com.qhr.cloud.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

/**
 * @Author : qhr
 * @Description :
 * @Date : Created in 17:17 2020/6/24
 * @Modified By :
 */
@SpringBootApplication
public class ConsumerMovieApplication {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

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

3.1.6 创建application.yml文件

server:
  port: 8010

 

 

4 测试

       分别启动microservice-simple-provider-user(服务提供者)和microservice-simple-consumer-movie(服务消费者),并通过http://localhost:8010/movies/users/list访问:

访问结果如下,证明环境搭建成功:

文章参考:http://www.itmuch.com/spring-cloud/finchley-2/

 

5 问题

至此,我们已经实现了这个最简单的分布式应用,应用之间通过HTTP通信。代码非常简单,但这些简单的代码里,存在着若干问题:

  • 应用没有监控,没有画板,一切指标都没有。
  • 地址硬编码问题。
  • 负载均衡。
  • 服务之间没有容错机制。
  • 如果应用发生故障,你怎么迅速找到问题所在?
  • 用户认证和授权。
  • .......

6 监控

6.1 监控的实现

       我们通过使用Spring Boot Actuator来实现监控的,Spring Boot Actuator是Spring Boot官方提供的监控组件。只需在需要用到的微服务的pom.xml文件添加以下配置,即可:

 <!--Spring Boot Actuator监测组件-->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>

6.2 Spring Boot Actuator使用的监控端点

端点(Spring Boot 2.x) 描述 HTTP方法 是否敏感 端点(Spring Boot 1.x)
auditevents 显示应用暴露的审计事件(比如认证进入、订单失败) GET  
beans 显示应用程序上下文所有的Spring bean GET beans
health

显示应用程序的健康指标,值由HealthIndicator的实现类提供;结果有UP、 DOWN、OUT_OF_SERVICE、UNKNOWN;如需查看详情,需配置:

management.endpoint.health.show-details

GET health
conditions 显示自动配置的信息 GET autoconfig
configprops 显示所有@ConfigurationProperties的配置属性列表 GET configprops
env 显示环境变量,包括系统环境变量以及应用环境变量 GET env
info 显示应用的信息,可使用info.* 属性自定义info端点公开的数据 GET info
loggers 显示和修改配置的loggers GET  
heapdump 返回一个GZip压缩的JVM堆dump GET dump
threaddump 执行一个线程dump GET dump
metrics 显示应用的度量标准信息 GET metrics
scheduledtasks 显示应用中的调度任务 GET  
httptrace 显示HTTP足迹,最近100个HTTP request/reponse GET  
mappings 显示所有的URL路径 GET mappings

参考文章:https://www.jianshu.com/p/8bfac9289c7e

访问http://{ip}:{port}/actuator/{endpoint} 端点,即可监控应用的运行状况。

6.3 测试

6.3.1 /health端点

为前文编写的microservice-simple-provider-user 服务整合Actuator后,访问:http://localhost:8000/actuator/health

{"status":"UP"}

6.3.2 /health端点展示详情

microservice-simple-provider-user 的application.yml,添加:

management:
  endpoint:
    health:
      # 是否展示健康检查详情
      show-details: always

访问测试:http://localhost:8000/actuator/health

{
    "status": "UP",
    "details": {
        "db": {
            "status": "UP",
            "details": {
                "database": "H2",
                "hello": 1
            }
        },
        "diskSpace": {
            "status": "UP",
            "details": {
                "total": 691212906496,
                "free": 397430468608,
                "threshold": 10485760
            }
        }
    }
}

6.3.3 暴露敏感路径

暴露指定敏感路径:

management:
  endpoints:
    web:
      exposure:
        # 暴露metrics端点,如需暴露多个,用,分隔;如需暴露所有端点,用'*'
        include: metrics

暴露所有敏感路径:

management:
  endpoints:
    web:
      exposure:
        # 开放所有监控端点
        include: '*'

访问:http://localhost:8000/actuator/metrics 

{
    "names": [
        "jvm.memory.max",
        "jdbc.connections.active",
        "jvm.gc.memory.promoted",
        "tomcat.cache.hit",
        "tomcat.cache.access",
        "jvm.memory.used",
        "jvm.gc.max.data.size",
        "jdbc.connections.max",
        "jdbc.connections.min",
        "jvm.gc.pause",
        "jvm.memory.committed",
        "system.cpu.count",
        "logback.events",
        "tomcat.global.sent",
        "jvm.buffer.memory.used",
        "tomcat.sessions.created",
        "jvm.threads.daemon",
        "system.cpu.usage",
        "jvm.gc.memory.allocated",
        "tomcat.global.request.max",
        "hikaricp.connections.idle",
        "hikaricp.connections.pending",
        "tomcat.global.request",
        "tomcat.sessions.expired",
        "hikaricp.connections",
        "jvm.threads.live",
        "jvm.threads.peak",
        "tomcat.global.received",
        "hikaricp.connections.active",
        "hikaricp.connections.creation",
        "process.uptime",
        "http.server.requests",
        "tomcat.sessions.rejected",
        "process.cpu.usage",
        "tomcat.threads.config.max",
        "jvm.classes.loaded",
        "hikaricp.connections.max",
        "hikaricp.connections.min",
        "jvm.classes.unloaded",
        "tomcat.global.error",
        "tomcat.sessions.active.current",
        "tomcat.sessions.alive.max",
        "jvm.gc.live.data.size",
        "tomcat.servlet.request.max",
        "hikaricp.connections.usage",
        "tomcat.threads.current",
        "tomcat.servlet.request",
        "hikaricp.connections.timeout",
        "jvm.buffer.count",
        "jvm.buffer.total.capacity",
        "tomcat.sessions.active.max",
        "hikaricp.connections.acquire",
        "tomcat.threads.busy",
        "process.start.time",
        "tomcat.servlet.error"
    ]
}

访问:http://localhost:8000/actuator/metrics/jvm.memory.max

{
    "name": "jvm.memory.max",
    "description": "The maximum amount of memory in bytes that can be used for memory management",
    "baseUnit": "bytes",
    "measurements": [
        {
            "statistic": "VALUE",
            "value": 4.484759551E9
        }
    ],
    "availableTags": [
        {
            "tag": "area",
            "values": [
                "heap",
                "nonheap"
            ]
        },
        {
            "tag": "id",
            "values": [
                "Compressed Class Space",
                "PS Survivor Space",
                "PS Old Gen",
                "Metaspace",
                "PS Eden Space",
                "Code Cache"
            ]
        }
    ]
}

参考文章:http://www.itmuch.com/spring-cloud/finchley-3/

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章