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/

 

 

 

 

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