目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

AlibabaCloud

单机应用到分布式架构演进

单机架构

  • 优点: 易于测试 便于集成 小型项目友好
  • 缺点: 开发速度慢 启动时间长 依赖庞大

分布式架构

  • SOA :Service Oriented Architecture 面向服务的架构 其中包含多个服务, 服务之间通过相互依赖最终提供一系列的功能, 一个服务 通常以独立的形式存在与操作系统进程中, 各个服务之间 通过网络调用。
  • 微服务:将一个大的单体应用进行细粒度的服务化拆分,每个拆分出来的服务各自独立打包部署,各个服务之间 通过网络调用。
  • 优点
    • 易开发、理解和维护
    • 独立的部署和启动
  • 缺点
    • 分布式系统-》分布式事务问题
    • 需要管理多个服务-》服务治理

微服务架构常见的核心组件

  • 网关
    • 路由转发 + 过滤器
      • /api/v1/video/ 视频服务
      • /api/v1/order/ 订单服务
      • /api/v1/user/ 用户服务
  • 服务发现注册(注册中心)
    • 调用和被调用方的信息维护
  • 配置中心
    • 管理配置,动态更新 application.properties
  • 链路追踪
    • 分析调用链路耗时 例子:下单-》查询商品服务获取商品价格-》查询用户信息-》保存数据库
  • 负载均衡器
    • 分发流量到多个节点,降低压力
  • 熔断
    • 保护自己和被调用方

业界微服务架构常见解决方案

  • ServiceComb
    • 华为内部的 CSE(Cloud Service Engine)框架开源, 一个微服务的开源解决方案,社区相对于下面几个比较小
    • 文档不多,通信领域比较强
  • dubbo
  • SpringCloud
    • 全家桶 + 轻松嵌入第三方组件(Netflix 奈飞)
    • 官网:https://spring.io/projects/spring-cloud
    • 配套
      • 通信方式:http RESTFul
      • 注册中心:eruka
      • 配置中心:config
      • 断路器:hystrix
      • 网关:zuul/gateway
      • 分布式追踪系统:sleuth+zipkin
  • Spring Alibaba Cloud
    • 全家桶 + 阿里生态多个组件组合 +SpringCloud 支持
    • 官网 https://spring.io/projects/spring-cloud-alibaba
    • 配套
      • 通信方式:http RESTFul
      • 注册中心:nacos
      • 配置中心:nacos
      • 断路器:sentinel
      • 网关:gateway
      • 分布式追踪系统:sleuth+zipkin

AlibabaCloud 核心组件介绍

  • 官网介绍

  • 为什么要选择 AlibabaCloud , 和 SpringCloud 的区别

  • SpringCloud 和 AlibabaCloud 组件存在很大交集,互相配合

    • SpringCloud 很多组件是基于第三方整合,目前多个已经不更新了,比如 zuul、eureka、hystrix 等
    • AlibabaCloud 提供一站式微服务解决方法,已经和 SpringCloud 进行了整合,组件互相支持
  • AlibabaCloud 全家桶介绍

  • 版本说明

在线教育环微服务模块划分和环境准备

在线教育模块划分

  • 视频服务
  • 订单服务
  • 用户服务

微服务数据库介绍和数据导入

  • 视频服务 cloud_video 库 video 表
 1CREATE TABLE `video` (
 2  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 3  `title` varchar(524) DEFAULT NULL COMMENT '视频标题',
 4  `summary` varchar(1026) DEFAULT NULL COMMENT '概述',
 5  `cover_img` varchar(524) DEFAULT NULL COMMENT '封面图',
 6  `price` int(11) DEFAULT NULL COMMENT '价格,分',
 7  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
 8  `point` double(11,2) DEFAULT '8.70' COMMENT '默认8.7,最高10分',
 9  PRIMARY KEY (`id`)
10) ENGINE=InnoDB AUTO_INCREMENT=48 DEFAULT CHARSET=utf8;
1INSERT INTO `video` (`id`, `title`, `summary`, `cover_img`, `price`, `create_time`, `point`)
2VALUES
3  (30, '互联网架构之JAVA虚拟机JVM零基础到高级实战', 'https://xdvideo-file.oss-cn-shenzhen.aliyuncs.com/video/2020/maven/%E8%AF%A6%E6%83%85%E5%9B%BE.png', 'https://xdvideo-file.oss-cn-shenzhen.aliyuncs.com/video/2020/maven/%E5%AE%98%E7%BD%91%E4%B8%BB%E5%9B%BE-mawen.png', 3980, '2021-06-24 22:14:00', 9.10),
4  (40, '全新微信小程序零基础到项目实战', 'https://xdvideo-file.oss-cn-shenzhen.aliyuncs.com/video/2020/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E8%AF%A6%E6%83%85%E5%9B%BE.png', 'https://xdvideo-file.oss-cn-shenzhen.aliyuncs.com/video/2020/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%AE%98%E7%BD%91%E4%B8%BB%E5%9B%BE-%E5%B0%8F%E7%A8%8B%E5%BA%8F.png', 5980, '2021-01-18 22:14:00', 9.10),
5  (41, '玩转搜索框架ElasticSearch7.x实战', 'https://xd-video-pc-img.oss-cn-beijing.aliyuncs.com/xdclass_pro/video/2019_backend/elasticsearch7_detail.jpeg', 'https://xd-video-pc-img.oss-cn-beijing.aliyuncs.com/xdclass_pro/video/2019_backend/elasticsearch7.png', 4880, '2021-01-10 22:14:00', 8.70),
6  (45, 'Docker实战视频教程入门到高级dockerfile/compose-Harbor', 'https://xdvideo-file.oss-cn-shenzhen.aliyuncs.com/video/2020/Docker/%E8%AF%A6%E6%83%85%E5%9B%BE.jpeg', 'https://xdvideo-file.oss-cn-shenzhen.aliyuncs.com/video/2020/Docker/%E5%AE%98%E7%BD%91%E4%B8%BB%E5%9B%BE-docker.png', 5980, '2021-01-10 22:14:00', 9.30),
7  (46, '新版javase零基础到高级教程小白自学编程', 'https://xdvideo-file.oss-cn-shenzhen.aliyuncs.com/video/2020/%E6%96%B0%E7%89%88javase/%E8%AF%A6%E6%83%85%E5%9B%BE.png', 'https://file.xdclass.net/video/2020/%E6%96%B0%E7%89%88javase/%E5%AE%98%E7%BD%91%E4%B8%BB%E5%9B%BE-javase.png', 3980, '2021-01-24 22:14:00', 8.80),
8  (47, 'Nodejs教程零基础入门到项目实战前端视频教程', 'https://xdvideo-file.oss-cn-shenzhen.aliyuncs.com/video/2020/node/%E5%AE%98%E7%BD%91%E8%AF%A6%E6%83%85%E5%9B%BE-node.png', 'https://xdvideo-file.oss-cn-shenzhen.aliyuncs.com/video/2020/node/%E5%AE%98%E7%BD%91%E4%B8%BB%E5%9B%BE-node.png', 6980, '2021-01-24 22:14:00', 8.90);
  • 用户服务 cloud_user 库 user 表
 1CREATE TABLE `user` (
 2  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 3  `phone` varchar(32) DEFAULT NULL,
 4  `pwd` varchar(128) DEFAULT NULL,
 5  `sex` int(2) DEFAULT NULL,
 6  `img` varchar(128) DEFAULT NULL,
 7  `create_time` datetime DEFAULT NULL,
 8  `role` int(11) DEFAULT NULL COMMENT '1是普通用户,2是管理员',
 9  `username` varchar(128) DEFAULT NULL,
10  `wechat` varchar(128) DEFAULT NULL,
11  PRIMARY KEY (`id`)
12) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
1INSERT INTO `user` (`id`, `phone`, `pwd`, `sex`, `img`, `create_time`, `role`, `username`, `wechat`)
2VALUES
3  (1, '123', '666', 1, 'xdclass.net', '2021-09-09 00:00:00', 1, 'jack', 'xdclass6'),
4  (2, '2323432', '794666918', 1, 'wwwww', '2020-05-20 04:54:01', 1, '小滴Anna姐姐', 'xdclass-anna'),
5  (3, '2323432', 'xdclass-lw', 1, 'wwwww', '2020-05-20 04:54:42', 1, '二当家小D', 'xdclass1'),
6  (4, '2323432', '3232323', 1, 'wwwww', '2020-05-20 04:55:07', 1, '老王', 'xdclass-lw');
  • 订单服务 cloud_order 库 video_order 表
 1CREATE TABLE `video_order` (
 2  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 3  `out_trade_no` varchar(64) DEFAULT NULL COMMENT '订单唯一标识',
 4  `state` int(11) DEFAULT NULL COMMENT '0表示未支付,1表示已支付',
 5  `create_time` datetime DEFAULT NULL COMMENT '订单生成时间',
 6  `total_fee` int(11) DEFAULT NULL COMMENT '支付金额,单位分',
 7  `video_id` int(11) DEFAULT NULL COMMENT '视频主键',
 8  `video_title` varchar(256) DEFAULT NULL COMMENT '视频标题',
 9  `video_img` varchar(256) DEFAULT NULL COMMENT '视频图片',
10  `user_id` int(12) DEFAULT NULL COMMENT '用户id',
11  PRIMARY KEY (`id`)
12) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8;

使用 Maven 聚合工程创建微服务架构

  • maven 聚合工程
    • xdclass-common
    • xdclass-video-service
    • xdclass-user-service
    • xdclass-order-service
  • 创建聚合工程(记得删除聚合工程 src 目录)
 1<?xml version="1.0" encoding="UTF-8"?>
 2<project xmlns="http://maven.apache.org/POM/4.0.0"
 3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5    <modelVersion>4.0.0</modelVersion>
 6
 7    <groupId>net.xdclass</groupId>
 8    <artifactId>xdclass-cloud</artifactId>
 9    <version>1.0-SNAPSHOT</version>
10    <modules>
11        <module>xdclass-common</module>
12        <module>xdclass-video-service</module>
13        <module>xdclass-user-service</module>
14        <module>xdclass-order-service</module>
15    </modules>
16
17    <!-- 一般来说父级项目的packaging都为pom,packaging默认类型jar类型-->
18    <packaging>pom</packaging>
19
20
21    <properties>
22        <java.version>1.8</java.version>
23    </properties>
24
25
26    <!-- 锁定版本 -->
27    <dependencyManagement>
28        <dependencies>
29            <!--https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies/2.3.3.RELEASE-->
30            <dependency>
31                <groupId>org.springframework.boot</groupId>
32                <artifactId>spring-boot-dependencies</artifactId>
33                <version>2.3.3.RELEASE</version>
34                <type>pom</type>
35                <scope>import</scope>
36            </dependency>
37
38            <!--https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies/Hoxton.SR8-->
39            <dependency>
40                <groupId>org.springframework.cloud</groupId>
41                <artifactId>spring-cloud-dependencies</artifactId>
42                <version>Hoxton.SR8</version>
43                <type>pom</type>
44                <scope>import</scope>
45            </dependency>
46
47            <!--https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-alibaba-dependencies/2.2.1.RELEASE-->
48            <dependency>
49                <groupId>com.alibaba.cloud</groupId>
50                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
51                <version>2.2.1.RELEASE</version>
52                <type>pom</type>
53                <scope>import</scope>
54            </dependency>
55
56        </dependencies>
57    </dependencyManagement>
58
59    <build>
60        <plugins>
61            <plugin>
62                <groupId>org.springframework.boot</groupId>
63                <artifactId>spring-boot-maven-plugin</artifactId>
64                <configuration>
65                    <fork>true</fork>
66                    <addResources>true</addResources>
67                </configuration>
68            </plugin>
69        </plugins>
70    </build>
71</project>
  • 添加子项目依赖,在三个子项目中添加 SpringBoot 的依赖
 1    <dependencies>
 2        <dependency>
 3            <groupId>org.springframework.boot</groupId>
 4            <artifactId>spring-boot-starter-web</artifactId>
 5        </dependency>
 6        <dependency>
 7            <groupId>net.xdclass</groupId>
 8            <artifactId>xdclass-common</artifactId>
 9            <version>1.0-SNAPSHOT</version>
10        </dependency>
11    </dependencies>
  • 在 common 包中创建实体类,生成 setter、getter 方法
 1package net.xdclass.domain;
 2
 3import java.util.Date;
 4
 5public class User {
 6        private Integer id;
 7        private String name;
 8        private String pwd;
 9        private String headImg;
10        private String phone;
11        private Date createTime;
12        private String wechat;
13}
14
15public class Video {
16    private Integer id;
17    private String title;
18    private String summary;
19    private String coverImg;
20    private Integer  price;
21    private Date createTime;
22    private Double point;
23}
24
25public class VideoOrder {
26    private Integer id;
27    private String outTradeNo;
28    private Integer state;
29    private Date createTime;
30    private  Integer totalFee;
31    private Integer videoId;
32    private String videoTitle;
33    private String videoImg;
34    private Integer userId;
35}
  • 聚合工程 pom.xml 修改【注意】
 1    <properties>
 2        <java.version>1.8</java.version>
 3        <maven.compiler.source>1.8</maven.compiler.source>
 4        <maven.compiler.target>1.8</maven.compiler.target>
 5    </properties>
 6  
 7  
 8  <dependency>
 9    <groupId>org.mybatis.spring.boot</groupId>
10    <artifactId>mybatis-spring-boot-starter</artifactId>
11    <version>2.1.2</version>
12    <type>pom</type>
13    <scope>import</scope>
14  </dependency>
  • 分别在三个项目中引入 MyBatis 依赖和数据库驱动
1      <dependency>
2        <groupId>org.mybatis.spring.boot</groupId>
3        <artifactId>mybatis-spring-boot-starter</artifactId>
4        </dependency>
5
6        <dependency>
7        <groupId>mysql</groupId>
8        <artifactId>mysql-connector-java</artifactId>
9      </dependency>
  • 3 个模块配置数据库连接(记得修改 端口、应用名称、数据库名称)application.yml
 1server:
 2  port: 9000
 3spring:
 4  application:
 5    name: xdclass-video-service
 6  datasource:
 7    driver-class-name: com.mysql.cj.jdbc.Driver
 8    url: jdbc:mysql://192.168.31.101:50000/cloud_video?useUnicode=true&characterEncoding=utf-8&useSSL=false
 9    username: root
10    password: 123456
11# 控制台输出sql、下划线转驼峰
12mybatis:
13  configuration:
14    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
15    map-underscore-to-camel-case: true
  • video 启动类
 1package net.xdclass;
 2
 3import org.springframework.boot.SpringApplication;
 4import org.mybatis.spring.annotation.MapperScan;
 5import org.springframework.boot.autoconfigure.SpringBootApplication;
 6
 7@SpringBootApplication
 8@MapperScan("net.xdclass.dao")
 9public class VideoApplication {
10    public static void main(String[] args) {
11        SpringApplication.run(VideoApplication.class, args);
12    }
13}
  • controller
 1package net.xdclass.controller;
 2
 3import net.xdclass.service.VideoService;
 4import org.springframework.beans.factory.annotation.Autowired;
 5import org.springframework.web.bind.annotation.RequestMapping;
 6import org.springframework.web.bind.annotation.RestController;
 7
 8@RestController
 9@RequestMapping("api/v1/video")
10public class VideoController {
11    @Autowired
12    private VideoService videoService;
13
14    @RequestMapping("find_by_id")
15    public Object findById(int videoId) {
16        return videoService.findById(videoId);
17    }
18}
19
  • service
 1package net.xdclass.service;
 2
 3import net.xdclass.domain.Video;
 4
 5public interface VideoService {
 6    Video findById(int videoId);
 7}
 8
 9
10
11package net.xdclass.service.impl;
12
13import net.xdclass.dao.VideoMapper;
14import net.xdclass.domain.Video;
15import net.xdclass.service.VideoService;
16import org.springframework.beans.factory.annotation.Autowired;
17import org.springframework.stereotype.Service;
18
19@Service
20public class VideoServiceImpl implements VideoService {
21    @Autowired
22    private VideoMapper videoMapper;
23  
24    @Override
25    public Video findById(int videoId) {
26        return videoMapper.findById(videoId);
27    }
28}
29
  • mapper
 1package net.xdclass.dao;
 2
 3import net.xdclass.domain.Video;
 4import org.apache.ibatis.annotations.Select;
 5import org.springframework.stereotype.Repository;
 6
 7@Repository
 8public interface VideoMapper {
 9
10    @Select("select * from video where id=#{videoId}")
11    Video findById(int videoId);
12}
13
  • 测试
 1// http://localhost:9000/api/v1/video/find_by_id?videoId=40
 2
 3{
 4  "id": 40,
 5  "title": "全新微信小程序零基础到项目实战",
 6  "summary": "https://xdvideo-file.oss-cn-shenzhen.aliyuncs.com/video/2020/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E8%AF%A6%E6%83%85%E5%9B%BE.png",
 7  "coverImg": "https://xdvideo-file.oss-cn-shenzhen.aliyuncs.com/video/2020/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%AE%98%E7%BD%91%E4%B8%BB%E5%9B%BE-%E5%B0%8F%E7%A8%8B%E5%BA%8F.png",
 8  "price": 5980,
 9  "createTime": "2021-01-18T22:14:00.000+00:00",
10  "point": 9.1
11}

服务直接的调用-下单购买视频

服务直接怎么调用:

 1RPC:
 2  远程过程调用,像调用本地服务(方法)一样调用服务器的服务
 3  支持同步、异步调用
 4  客户端和服务器之间建立TCP连接,可以一次建立一个,也可以多个调用复用一次链接
 5  RPC数据包小
 6    protobuf
 7    thrift
 8  rpc:编解码,序列化,链接,丢包,协议
 9  
10Rest(Http):
11  http请求,支持多种协议和功能
12  开发方便成本低
13  http数据包大
14  java开发:resttemplate或者httpclient
  • 启动类
 1package net.xdclass;
 2
 3import org.springframework.boot.SpringApplication;
 4import org.springframework.boot.autoconfigure.SpringBootApplication;
 5import org.springframework.context.annotation.Bean;
 6import org.springframework.web.client.RestTemplate;
 7
 8
 9@SpringBootApplication
10public class OrderApplication {
11
12    public static void main(String [] args){
13
14        SpringApplication.run(OrderApplication.class,args);
15    }
16  
17    @Bean
18    public RestTemplate getRestTemplate(){
19        return new RestTemplate();
20    }
21
22
23}
  • controller
 1package net.xdclass.controller;
 2
 3import net.xdclass.domain.Video;
 4import net.xdclass.domain.VideoOrder;
 5import org.springframework.beans.factory.annotation.Autowired;
 6import org.springframework.web.bind.annotation.RequestMapping;
 7import org.springframework.web.bind.annotation.RestController;
 8import org.springframework.web.client.RestTemplate;
 9
10import java.util.Date;
11
12@RestController
13@RequestMapping("api/v1/video_order")
14public class OrderController {
15
16
17    @Autowired
18    private RestTemplate restTemplate;
19  
20    @RequestMapping("/save")
21    public Object save(int videoId){
22
23        Video video = restTemplate.getForObject("http://localhost:9000/api/v1/video/find_by_id?videoId="+videoId, Video.class);
24
25        VideoOrder videoOrder = new VideoOrder();
26        videoOrder.setVideoId(video.getId());
27        videoOrder.setVideoTitle(video.getTitle());
28        videoOrder.setCreateTime(new Date());
29        return videoOrder;
30
31    }
32}
33
  • 测试
 1// 20201214161212
 2// http://localhost:8000/api/v1/video_order/save?videoId=40
 3
 4{
 5  "id": null,
 6  "outTradeNo": null,
 7  "state": null,
 8  "createTime": "2020-12-14T08:12:12.640+00:00",
 9  "totalFee": null,
10  "videoId": 40,
11  "videoTitle": "全新微信小程序零基础到项目实战",
12  "videoImg": null,
13  "userId": null
14}
  • 存在的问题:
1* 服务之间的IP信息写死
2* 服务之间无法提供负载均衡
3* 多个服务直接关系调用维护复杂

Nacos(服务治理)

  • 微服务架构图讲解

  • 什么是注册中心(服务治理)

    • 服务注册:服务提供者 provider,启动的时候向注册中心上报自己的网络信息
    • 服务发现:服务消费者 consumer,启动的时候向注册中心上报自己的网络信息,拉取 provider 的相关网络信息
    • 核心:服务管理,是有个服务注册表,心跳机制动态维护,服务实例在启动时注册到服务注册表,并在关闭时注销。
  • 为什么要用

    • 微服务应用和机器越来越多,调用方需要知道接口的网络地址,如果靠配置文件的方式去控制网络地址,对于动态新增机器,维护带来很大问题
  • 主流的注册中心:zookeeper、Eureka、consul、etcd、Nacos

    • AlibabaCloud 搭配最好的是 Nacos,且服务的注册发现之外,还支持动态配置服务

Nacos 搭建

  • 官网:https://nacos.io/zh-cn/
  • Linux/Mac 安装 Nacos
    • 解压安装包
    • 进入 bin 目录
    • 启动 sh startup.sh -m standalone
    • 访问 localhost:8848/nacos
    • 默认账号密码 nacos/nacos

订单-视频服务之间的调用

  • 视频服务集成 Nacos
1添加依赖
1<!--添加nacos客户端-->
2<dependency>
3     <groupId>com.alibaba.cloud</groupId>
4     <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
5</dependency>
1配置Nacos地址
 1server:
 2  port: 9000
 3
 4spring:
 5  application:
 6    name: xdclass-video-service
 7  cloud:
 8    nacos:
 9      discovery:
10        server-addr: 192.168.31.101:8848
1启动类增加注解
1@EnableDiscoveryClient
  • 订单服务集成 Nacos
  • 用户服务集成 Nacos
  • 服务之间的调用
 1package net.xdclass.controller;
 2
 3import net.xdclass.domain.Video;
 4import net.xdclass.domain.VideoOrder;
 5import org.springframework.beans.factory.annotation.Autowired;
 6import org.springframework.cloud.client.ServiceInstance;
 7import org.springframework.cloud.client.discovery.DiscoveryClient;
 8import org.springframework.web.bind.annotation.RequestMapping;
 9import org.springframework.web.bind.annotation.RestController;
10import org.springframework.web.client.RestTemplate;
11
12import java.util.Date;
13import java.util.List;
14
15@RestController
16@RequestMapping("api/v1/video_order")
17public class OrderController {
18
19    @Autowired
20    private DiscoveryClient discoveryClient;
21
22    @Autowired
23    private RestTemplate restTemplate;
24
25    @RequestMapping("/save")
26    public Object save(int videoId){
27
28        //Video video = restTemplate.getForObject("http://localhost:9000/api/v1/video/find_by_id?videoId="+videoId, Video.class);
29
30        List<ServiceInstance> list = discoveryClient.getInstances("xdclass-video-service");
31        ServiceInstance serviceInstance = list.get(0);
32
33        Video video = restTemplate.getForObject("http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+
34                "/api/v1/video/find_by_id?videoId="+videoId,Video.class);
35
36        VideoOrder videoOrder = new VideoOrder();
37        videoOrder.setVideoId(video.getId());
38        videoOrder.setVideoTitle(video.getTitle());
39        videoOrder.setCreateTime(new Date());
40        return videoOrder;
41
42    }
43}
44
1测试
 1// 20201215120717
 2// http://localhost:8000/api/v1/video_order/save?videoId=40
 3
 4{
 5  "id": null,
 6  "outTradeNo": null,
 7  "state": null,
 8  "createTime": "2020-12-15T04:07:17.708+00:00",
 9  "totalFee": null,
10  "videoId": 40,
11  "videoTitle": "全新微信小程序零基础到项目实战",
12  "videoImg": null,
13  "userId": null
14}

负载均衡和常见的解决方案

1什么是负载均衡(Load Balance)
1分布式系统中一个非常重要的概念,当访问的服务具有多个实例时,需要根据某种“均衡”的策略决定请求发往哪个节点,这就是所谓的负载均衡,
2原理是将数据流量分摊到多个服务器执行,减轻每台服务器的压力,从而提高了数据的吞吐量
  • 软硬件角度负载均衡的种类
    • 通过硬件来进行解决,常见的硬件有 NetScaler、F5、Radware 和 Array 等商用的负载均衡器,但比较昂贵的
    • 通过软件来进行解决,常见的软件有 LVS、Nginx 等,它们是基于 Linux 系统并且开源的负载均衡策略
  • 从端的角度负载均衡有两种
    • 服务端负载均衡
    • 客户端负载均衡(订单服务调用视频服务,订单服务在本地决定最终调用哪个节点)

常见的负载均衡策略(看组件的支持情况)

  • 节点轮询
    • 简介:每个请求按顺序分配到不同的后端服务器
  • weight 权重配置
    • 简介:weight 和访问比率成正比,数字越大,分配得到的流量越高
  • 固定分发
    • 简介:根据请求按访问 ip 的 hash 结果分配,这样每个用户就可以固定访问一个后端服务器
  • 随机选择、最短响应时间等等

AlibabaCloud 集成 Ribbon 实现负载均衡

  • 什么是 Ribbon Ribbon 是一个客户端负载均衡工具,通过 Spring Cloud 封装,可以轻松和 AlibabaCloud 整合
  • 订单服务增加 @LoadBalanced 注解
1@Bean
2@LoadBalanced
3public RestTemplate restTemplate() {
4  return new RestTemplate();
5}
  • 调用实战
 1package net.xdclass.controller;
 2
 3import net.xdclass.domain.Video;
 4import net.xdclass.domain.VideoOrder;
 5import org.springframework.beans.factory.annotation.Autowired;
 6import org.springframework.cloud.client.ServiceInstance;
 7import org.springframework.cloud.client.discovery.DiscoveryClient;
 8import org.springframework.web.bind.annotation.RequestMapping;
 9import org.springframework.web.bind.annotation.RestController;
10import org.springframework.web.client.RestTemplate;
11
12import java.util.Date;
13import java.util.List;
14
15@RestController
16@RequestMapping("api/v1/video_order")
17public class OrderController {
18
19    @Autowired
20    private DiscoveryClient discoveryClient;
21
22    @Autowired
23    private RestTemplate restTemplate;
24
25    @RequestMapping("/save")
26    public Object save(int videoId){
27
28        //Video video = restTemplate.getForObject("http://localhost:9000/api/v1/video/find_by_id?videoId="+videoId, Video.class);
29
30        List<ServiceInstance> list = discoveryClient.getInstances("xdclass-video-service");
31        ServiceInstance serviceInstance = list.get(0);
32
33        //Video video = restTemplate.getForObject("http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/api/v1/video/find_by_id?videoId="+videoId,Video.class);
34
35        Video video = restTemplate.getForObject("http://xdclass-video-service/api/v1/video/find_by_id?videoId="+videoId, Video.class);
36
37        VideoOrder videoOrder = new VideoOrder();
38        videoOrder.setVideoId(video.getId());
39        videoOrder.setVideoTitle(video.getTitle());
40        videoOrder.setCreateTime(new Date());
41
42        videoOrder.setServerInfo(video.getServeInfo());
43        return videoOrder;
44
45    }
46}
47

测试两次的请求端口号不同

 1// 20201215124016
 2// http://localhost:8000/api/v1/video_order/save?videoId=40
 3
 4{
 5  "id": null,
 6  "outTradeNo": null,
 7  "state": null,
 8  "createTime": "2020-12-15T04:40:16.015+00:00",
 9  "totalFee": null,
10  "videoId": 40,
11  "videoTitle": "全新微信小程序零基础到项目实战",
12  "videoImg": null,
13  "userId": null,
14  "serverInfo": "192.168.31.100:9000"
15}
16
17// 20201215124041
18// http://localhost:8000/api/v1/video_order/save?videoId=40
19
20{
21  "id": null,
22  "outTradeNo": null,
23  "state": null,
24  "createTime": "2020-12-15T04:40:41.175+00:00",
25  "totalFee": null,
26  "videoId": 40,
27  "videoTitle": "全新微信小程序零基础到项目实战",
28  "videoImg": null,
29  "userId": null,
30  "serverInfo": "192.168.31.100:9001"
31}

负载均衡策略调整

1订单服务增加配置
2
3xdclass-video-service:
4  ribbon:
5    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

微服务新一代负载均衡组件 Open-Feign

  • 原先 ribbon 代码存在的问题:不规范,风格不统一,维护性比较差
  • 什么是 Feign:
1SpringCloud提供的伪http客户端(本质还是用http),封装了Http调用流程,更适合面向接口化
2让用Java接口注解的方式调用Http请求.
3
4不用像Ribbon中通过封装HTTP请求报文的方式调用 Feign默认集成了Ribbon

改造微服务 集成 Feign 实现远程方法调用

  • Feign 让方法调用更加解耦
  • 使用 feign 步骤讲解
    • 加入依赖(订单服务)
1<dependency>
2            <groupId>org.springframework.cloud</groupId>
3            <artifactId>spring-cloud-starter-openfeign</artifactId>
4</dependency>
1配置注解
1启动类增加@EnableFeignClients
  • 增加一个接口
1订单服务增加接口,服务名称记得和nacos保持一样
2@FeignClient(name="xdclass-video-service") 
  • 编写代码
 1package net.xdclass.service;
 2
 3import net.xdclass.domain.Video;
 4import org.springframework.cloud.openfeign.FeignClient;
 5import org.springframework.web.bind.annotation.GetMapping;
 6import org.springframework.web.bind.annotation.RequestParam;
 7
 8@FeignClient(name="xdclass-video-service")
 9public interface VideoService {
10    @GetMapping(value = "/api/v1/video/find_by_id")
11    Video findById(@RequestParam("videoId") int videoId);
12}
13

controller 调用视频服务

 1package net.xdclass.controller;
 2
 3import net.xdclass.domain.Video;
 4import net.xdclass.domain.VideoOrder;
 5import net.xdclass.service.VideoService;
 6import org.springframework.beans.factory.annotation.Autowired;
 7import org.springframework.cloud.client.ServiceInstance;
 8import org.springframework.cloud.client.discovery.DiscoveryClient;
 9import org.springframework.web.bind.annotation.RequestMapping;
10import org.springframework.web.bind.annotation.RestController;
11import org.springframework.web.client.RestTemplate;
12
13import java.util.Date;
14import java.util.List;
15
16@RestController
17@RequestMapping("api/v1/video_order")
18public class OrderController {
19
20    @Autowired
21    private DiscoveryClient discoveryClient;
22
23    @Autowired
24    private RestTemplate restTemplate;
25
26    @Autowired
27    private VideoService videoService;
28
29    @RequestMapping("/save")
30    public Object save(int videoId){
31
32        //Video video = restTemplate.getForObject("http://localhost:9000/api/v1/video/find_by_id?videoId="+videoId, Video.class);
33
34        List<ServiceInstance> list = discoveryClient.getInstances("xdclass-video-service");
35        ServiceInstance serviceInstance = list.get(0);
36
37        // Video video = restTemplate.getForObject("http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+ "/api/v1/video/find_by_id?videoId="+videoId,Video.class);
38
39        //Video video = restTemplate.getForObject("http://xdclass-video-service/api/v1/video/find_by_id?videoId="+videoId, Video.class);
40
41        Video video = videoService.findById(videoId);
42        VideoOrder videoOrder = new VideoOrder();
43        videoOrder.setVideoId(video.getId());
44        videoOrder.setVideoTitle(video.getTitle());
45        videoOrder.setCreateTime(new Date());
46
47        videoOrder.setServerInfo(video.getServeInfo());
48        return videoOrder;
49
50    }
51}
52

测试

1http://localhost:8000/api/v1/video_order/find_by_id?videoId=40

post 方式对象传输 Open-Feign 实现远程方法调用

ordercontroller

 1package net.xdclass.controller;
 2
 3import net.xdclass.domain.Video;
 4import net.xdclass.domain.VideoOrder;
 5import net.xdclass.service.VideoService;
 6import org.springframework.beans.factory.annotation.Autowired;
 7import org.springframework.cloud.client.ServiceInstance;
 8import org.springframework.cloud.client.discovery.DiscoveryClient;
 9import org.springframework.web.bind.annotation.PostMapping;
10import org.springframework.web.bind.annotation.RequestBody;
11import org.springframework.web.bind.annotation.RequestMapping;
12import org.springframework.web.bind.annotation.RestController;
13import org.springframework.web.client.RestTemplate;
14
15import java.util.Date;
16import java.util.HashMap;
17import java.util.List;
18import java.util.Map;
19
20@RestController
21@RequestMapping("api/v1/video_order")
22public class OrderController {
23
24    @Autowired
25    private DiscoveryClient discoveryClient;
26
27    @Autowired
28    private RestTemplate restTemplate;
29
30    @Autowired
31    private VideoService videoService;
32
33    @RequestMapping("/find_by_id")
34    public Object findById(int videoId){
35
36        //Video video = restTemplate.getForObject("http://localhost:9000/api/v1/video/find_by_id?videoId="+videoId, Video.class);
37
38        List<ServiceInstance> list = discoveryClient.getInstances("xdclass-video-service");
39        ServiceInstance serviceInstance = list.get(0);
40
41        // Video video = restTemplate.getForObject("http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+ "/api/v1/video/find_by_id?videoId="+videoId,Video.class);
42
43        //Video video = restTemplate.getForObject("http://xdclass-video-service/api/v1/video/find_by_id?videoId="+videoId, Video.class);
44
45        Video video = videoService.findById(videoId);
46        VideoOrder videoOrder = new VideoOrder();
47        videoOrder.setVideoId(video.getId());
48        videoOrder.setVideoTitle(video.getTitle());
49        videoOrder.setCreateTime(new Date());
50
51        videoOrder.setServerInfo(video.getServeInfo());
52        return videoOrder;
53
54    }
55
56    @RequestMapping("save")
57    public Object save(@RequestBody Video video) {
58        int rows = videoService.save(video);
59
60        Map<String, Object> map = new HashMap<>();
61        map.put("rows", rows);
62        return map;
63    }
64}
65

VideoService

 1package net.xdclass.service;
 2
 3import net.xdclass.domain.Video;
 4import org.springframework.cloud.openfeign.FeignClient;
 5import org.springframework.web.bind.annotation.GetMapping;
 6import org.springframework.web.bind.annotation.PostMapping;
 7import org.springframework.web.bind.annotation.RequestBody;
 8import org.springframework.web.bind.annotation.RequestParam;
 9
10@FeignClient(name="xdclass-video-service")
11public interface VideoService {
12    @GetMapping(value = "/api/v1/video/find_by_id")
13    Video findById(@RequestParam("videoId") int videoId);
14
15    @PostMapping(value = "/api/v1/video/save")
16    int save(@RequestBody Video video);
17}
18

VideoController

 1package net.xdclass.controller;
 2
 3import net.xdclass.domain.Video;
 4import net.xdclass.service.VideoService;
 5import org.springframework.beans.factory.annotation.Autowired;
 6import org.springframework.web.bind.annotation.PostMapping;
 7import org.springframework.web.bind.annotation.RequestBody;
 8import org.springframework.web.bind.annotation.RequestMapping;
 9import org.springframework.web.bind.annotation.RestController;
10
11import javax.servlet.http.HttpServletRequest;
12
13/**
14 * @Description 旭瑶&小滴课堂 xdclass.net
15 * @Author 二当家小D  代码、笔记和技术指导联系我即可
16 * @Version 1.0
17 **/
18
19@RestController
20@RequestMapping("api/v1/video")
21public class VideoController {
22
23
24
25    @Autowired
26    private VideoService videoService;
27
28
29    @RequestMapping("find_by_id")
30    public Object findById(int videoId, HttpServletRequest request){
31        Video video = videoService.findById(videoId);
32        video.setServeInfo(request.getServerName() + ":" + request.getServerPort());
33        return video;
34    }
35
36    @PostMapping("save")
37    public int save(@RequestBody Video video){
38        System.out.println(video.getTitle());
39        return  1;
40    }
41}
42

测试

 1localhost:8000/api/v1/video_order/save
 2
 3{
 4    "title":"post方式测试"
 5}
 6
 7
 8VideoController控制台打印
 9post方式测试
10
11postman返回数据
12{
13    "rows": 1
14}
15
16

Ribbon 和 feign 两个的区别和选择

1选择feign
2默认集成了ribbon
3写起来更加思路清晰和方便
4采用注解方式进行配置,配置熔断等方式方便

CAP

  • 可能会有疑惑,可以看多几遍
  • CAP 定理: 指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可同时获得
    • 一致性(C):所有节点都可以访问到最新的数据
    • 可用性(A):每个请求都是可以得到响应的,不管请求是成功还是失败
    • 分区容错性(P):除了全部整体网络故障,其他故障都不能导致整个系统不可用
  • CAP 理论就是说在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡
1CA: 如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但放弃P的同时也就意味着放弃了系统的扩展性,也就是分布式节点受限,没办法部署子节点,这是违背分布式系统设计的初衷的
2
3CP: 如果不要求A(可用),每个请求都需要在服务器之间保持强一致,而P(分区)会导致同步时间无限延长(也就是等待数据同步完才能正常访问服务),一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验,等待所有数据全部一致了之后再让用户访问系统
4
5AP:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。
  • 常见注册中心:zk、eureka、nacos
  • 那你应该怎么选择
Nacos Eureka Consul Zookeeper
一致性协议 CP+AP AP CP CP
健康检查 TCP/HTTP/MYSQL/Client Beat 心跳 TCP/HTTP/gRPC/Cmd Keep Alive
雪崩保护
访问协议 HTTP/DNS HTTP HTTP/DNS TCP
SpringCloud 集成 支持 支持 支持 支持
  • Zookeeper:CP 设计,保证了一致性,集群搭建的时候,某个节点失效,则会进行选举行的 leader,或者半数以上节点不可用,则无法提供服务,因此可用性没法满足

  • Eureka:AP 原则,无主从节点,一个节点挂了,自动切换其他节点可以使用,去中心化

  • 结论:

    • 分布式系统中 P,肯定要满足,所以只能在 CA 中二选一
    • 没有最好的选择,最好的选择是根据业务场景来进行架构设计
    • 如果要求一致性,则选择 zookeeper/Nacos,如金融行业 CP
    • 如果要求可用性,则 Eureka/Nacos,如电商系统 AP
    • CP : 适合支付、交易类,要求数据强一致性,宁可业务不可用,也不能出现脏数据
    • AP: 互联网业务,比如信息流架构,不要求数据强一致,更想要服务可用

BASE

什么是 Base 理论

1CAP 中的一致性和可用性进行一个权衡的结果,核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性, 来自 ebay 的架构师提出
  • Basically Available(基本可用)
    • 假设系统,出现了不可预知的故障,但还是能用, 可能会有性能或者功能上的影响
  • Soft state(软状态)
    • 允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时
  • Eventually consistent(最终一致性)
    • 系统能够保证在没有其他新的更新操作的情况下,数据最终一定能够达到一致的状态,因此所有客户端对系统的数据访问最终都能够获取到最新的值

海量请求下的微服务架构存在的问题

  • 高并发下存在的问题
    • 微服务拆分多个系统,服务之间互相依赖,可能会由于系统负载过高,突发流量或者网络等各种异常情况 导致服务不可用。
  • 核心思想-面向失败编程
    • 不要外界影响
    • 不被请求拖垮
      • 上游服务(限流保护好自己)
      • 下游服务(熔断,不会一直等待下游服务的应答)

面向失败编程-微服务架构容错方案介绍

  • 限流

  • 漏斗,不管流量多大,均匀的流入容器,令牌桶算法,漏桶算法
    image-20200908182543365

  • 熔断:

    • 保险丝,熔断服务,为了防止整个系统故障,包含当前和下游服务 下单服务 -》商品服务-》用户服务 -》(出现异常-》熔断风控服务
  • 降级:

    • 抛弃一些非核心的接口和数据,返回兜底数据 旅行箱的例子:只带核心的物品,抛弃非核心的,等有条件的时候再去携带这些物品
  • 隔离:

    • 服务和资源互相隔离,比如网络资源,机器资源,线程资源等,不会因为某个服务的资源不足而抢占其他服务的资源
  • 熔断和降级互相交集

    • 相同点:
      • 从可用性和可靠性触发,为了防止系统崩溃
      • 最终让用户体验到的是某些功能暂时不能用
    • 不同点
      • 服务熔断一般是下游服务故障导致的,而服务降级一般是从整体系统负荷考虑,由调用方控制
  • 想进行微服务的容错,业界目前有 Sentinel、Hystrix,相对于 AlibabaCloud 而言,Sentinel 是最好的搭配
    image-20200908195729987

新版分布式系统的流量防卫兵-Sentinel 介绍

  • 什么是 Sentinel
    • 阿里巴巴开源的分布式系统流控工具
    • 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性
    • 丰富的应用场景:消息削峰填谷、集群流量控制、实时熔断下游不可用应用等
    • 完备的实时监控:Sentinel 同时提供实时的监控功能
    • 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合
  • 官网:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
  • Sentinel 版本:2.2.1
  • 核心概念:
    • 资源:是 Sentinel 中的核心概念之一,可以是 Java 程序中任何内容,可以是服务或者方法甚至代码,总结起来就是我们要保护的东西
    • 规则:定义怎样的方式保护资源,主要包括流控规则、熔断降级规则等

Sentinel-features-overview

流量防卫兵-Sentinel 依赖引入和控制台搭建

  • Sentinel 分为两个部分
    • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo、Spring Cloud 等框架也有较好的支持。
    • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
  • 微服务引入 Sentinel 依赖
1 <dependency>
2            <groupId>com.alibaba.cloud</groupId>
3            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
4</dependency>
  • Sentinel 控制台搭建

  • 文档:https://github.com/alibaba/Sentinel/wiki/控制台

  • 控制台包含如下功能:

    • 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
    • 监控 (单机和集群聚合)通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
    • 规则管理和推送:统一管理推送规则。
    • 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。

    注意:Sentinel 控制台目前仅支持单机部署

1//启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本,
2//-Dserver.port=8090 用于指定 Sentinel 控制台端口为 8090 
3//默认用户名和密码都是 sentinel
4
5java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar
6
7
8http://192.168.31.101:8090/#/login
9

image-20200908195822102

AliababCloud 微服务整合 Sentinel 限流配置实操

多个微服务接入 Sentinel 配置

1spring:
2  cloud:
3    sentinel:
4      transport:
5        dashboard: 192.168.31.101:8090 
6        port: 9999 
7
8#dashboard: 8080 控制台端口
9#port: 9999 本地启的端口,随机选个不能被占用的,与dashboard进行数据交互,会在应用对应的机器上启动一个 Http Server,该 Server 会与 Sentinel 控制台做交互, 若被占用,则开始+1一次扫描
  • 微服务注册上去后,由于 Sentinel 是懒加载模式,所以需要访问微服务后才会在控制台出现
  • 限流配置实操
    • 控制台配置

image-20200908203338254

  • 浏览器刷新(http://localhost:8000/api/v1/video_order/find_by_id?videoId=40)
    1Blocked by Sentinel (flow limiting)
    

image-20200908203307475

Sentinel 流量控制功能

流量控制(flow control)

  • 原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

两种规则

  • 基于统计并发线程数的流量控制
1并发数控制用于保护业务线程池不被慢调用耗尽
2
3Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目)
4
5如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离
  • 基于统计 QPS 的流量控制
1当 QPS 超过某个阈值的时候,则采取措施进行流量控制
  • 控制面板介绍
    • 资源名:默认是请求路径,可自定义
    • 针对来源:对哪个微服务进行限流,默认是不区分来源,全部限流,这个是针对 区分上游服务进行限流, 比如 视频服务 被 订单服务、用户服务调用,就可以针对来源进行限流

image-20200908210235984

基于并发线程进行限流配置实操

  • 开发临时接口,方便测试
 1    @RequestMapping("list")
 2    public Object list(){
 3        try {
 4            TimeUnit.SECONDS.sleep(3);
 5        } catch (InterruptedException e) {
 6            e.printStackTrace();
 7        }
 8        Map<String,String> map  = new HashMap<>();
 9
10        map.put("title1","ALibabaCloud微服务专题");
11        map.put("title2","小滴课堂面试专题第一季");
12
13        return map;
14    }
  • 基于统计并发线程数的流量控制
1并发数控制用于保护业务线程池不被慢调用耗尽
2
3Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目)
4
5如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置
  • 流控规则会下发到微服务,微服务如果重启,则流控规则会消失可以持久化配置
  • 选择阈值类型 ”线程数“ ,配置是 1
  • 刷新浏览器

流控规则效果

  • 直接拒绝

  • 冷启动预热

  • 匀速排队

  • 流量控制的效果包括以下几种:

    • 直接拒绝:默认的流量控制方式,当 QPS 超过任意规则的阈值后,新的请求就会被立即拒绝
    • Warm Up:冷启动/预热,如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值

    image-20200908212417778

    • 匀速排队:严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法,主要用于处理间隔性突发的流量,如消息队列,想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求image
      • 注意:
        • 匀速排队等待策略是 Leaky Bucket 算法结合虚拟队列等待机制实现的。
        • 匀速排队模式暂时不支持 QPS > 1000 的场景
  • 流控文档

Sentinel-微服务高可用利器-熔断降级规则

  • 熔断降级(虽然是两个概念,基本都是互相配合)
    • 对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一
    • 对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩
    • 熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置
  • 什么是 Sentinel 降级规则
  • Sentinel 熔断策略
    • 慢调用比例(响应时间): 选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用

      • 比例阈值:修改后不生效-目前已经反馈给官方那边的 bug
      • 熔断时长:超过时间后会尝试恢复
      • 最小请求数:熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断

      image-20200909121342893

    • 异常比例:当单位统计时长内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断

      • 比例阈值
      • 熔断时长:超过时间后会尝试恢复
      • 最小请求数:熔断触发的最小请求数,请求数小于该值时,即使异常比率超出阈值也不会熔断

      image-20200909121357918

    • 异常数:当单位统计时长内的异常数目超过阈值之后会自动进行熔断

      • 异常数:
      • 熔断时长:超过时间后会尝试恢复
      • 最小请求数:熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断

      image-20200909121415806

Sentinel 的熔断状态和恢复

  • 服务熔断一般有三种状态(画图)
    • 熔断关闭(Closed)

      • 服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制
    • 熔断开启(Open)

      • 后续对该服务接口的调用不再经过网络,直接执行本地的 fallback 方法
    • 半熔断(Half-Open)

      • 所谓半熔断就是尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率

      image-20200909171947975

  • 熔断恢复:
    • 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态)尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率。
    • 如果成功率达到预期,则说明服务已恢复,进入熔断关闭状态;如果成功率仍旧很低,则重新进入熔断状态

服务调用熔断例子

  • 修改代码
  • 熔断测试(异常比例)
 1 int temp = 0;
 2    @RequestMapping("list")
 3    public Object list(){
 4
 5//        try {
 6//            TimeUnit.SECONDS.sleep(3);
 7//        } catch (InterruptedException e) {
 8//            e.printStackTrace();
 9//        }
10
11        temp++;
12        if(temp%3 == 0){
13            throw  new RuntimeException();
14        }
15        Map<String,String> map  = new HashMap<>();
16        map.put("title1","ALibabaCloud微服务专题");
17        map.put("title2","小滴课堂面试专题第一季");
18
19        return map;
20    }

Sentinel 自定义异常

  • 默认降级返回数据问题
    • 限流和熔断返回的数据有问题-
    • 微服务交互基本都是 JSON 格式,如果让自定义异常信息
  • AlibabCloud 版本升级,不兼容问题
    • v2.1.0 到 v2.2.0 后,Sentinel 里面依赖进行了改动,且不向下兼容
  • 自定义降级返回数据
    • 【旧版】实现 UrlBlockHandler 并且重写 blocked 方法
1@Component
2public class XdclassUrlBlockHandler implements UrlBlockHandler {
3    @Override
4    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
5       //降级业务处理
6    }
7}
8
  • 【新版】实现 BlockExceptionHandler 并且重写 handle 方法
1public class XdclassUrlBlockHandler implements BlockExceptionHandler {
2   
3    @Override
4    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
5    //降级业务处理
6    }
7}

Sentinel 自定义降级异常数据开发实战

异常种类

1FlowException  //限流异常
2DegradeException  //降级异常
3ParamFlowException //参数限流异常
4SystemBlockException //系统负载异常
5AuthorityException //授权异常

【新版】实现 BlockExceptionHandler 并且重写 handle 方法

 1package net.xdclass.config;
 2
 3import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
 4import com.alibaba.csp.sentinel.slots.block.BlockException;
 5import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
 6import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
 7import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
 8import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
 9import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
10import com.alibaba.fastjson.JSON;
11import org.springframework.stereotype.Component;
12
13import javax.servlet.http.HttpServletRequest;
14import javax.servlet.http.HttpServletResponse;
15import java.io.IOException;
16import java.util.HashMap;
17import java.util.Map;
18
19@Component
20public class XdclassUrlBlockHandler implements BlockExceptionHandler {
21    @Override
22    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
23        Map<String,Object> backMap=new HashMap<>();
24        if (e instanceof FlowException){
25            backMap.put("code",-1);
26            backMap.put("msg","限流-异常啦");
27        }else if (e instanceof DegradeException){
28            backMap.put("code",-2);
29            backMap.put("msg","降级-异常啦");
30        }else if (e instanceof ParamFlowException){
31            backMap.put("code",-3);
32            backMap.put("msg","热点-异常啦");
33        }else if (e instanceof SystemBlockException){
34            backMap.put("code",-4);
35            backMap.put("msg","系统规则-异常啦");
36        }else if (e instanceof AuthorityException){
37            backMap.put("code",-5);
38            backMap.put("msg","认证-异常啦");
39        }
40
41        // 设置返回json数据
42        httpServletResponse.setStatus(200);
43        httpServletResponse.setHeader("content-Type","application/json;charset=UTF-8");
44        httpServletResponse.getWriter().write(JSON.toJSONString(backMap));
45    }
46}

测试(添加限流、或者降级)

1// 20201216160043
2// http://localhost:8000/api/v1/video_order/list
3
4{
5  "msg": "限流-异常啦",
6  "code": -1
7}

Sentinel 整合 OpenFeign 配置实战

订单服务(假设调视频服务失败,视频服务不可用,兜底数据)

整合步骤

  • 加入依赖
1<dependency>
2    <groupId>com.alibaba.cloud</groupId>
3    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
4</dependency>
  • 开启 Feign 对 Sentinel 的支持
1feign:
2  sentinel:
3    enabled: true
  • 创建容错类, 实现对应的服务接口, 记得加注解 @Service
 1package net.xdclass.service.fallback;
 2
 3import net.xdclass.domain.Video;
 4import net.xdclass.service.VideoService;
 5import org.springframework.stereotype.Service;
 6
 7@Service
 8public class VideoServiceFallback implements VideoService {
 9
10    @Override
11    public Video findById(int videoId) {
12        Video video = new Video();
13        video.setTitle("熔断降级数据");
14        return video;
15    }
16
17    @Override
18    public int save(Video video) {
19        return 0;
20    }
21}
  • 配置 feign 容错类,@FeignClient(name="xdclass-video-service", fallback = VideoServiceFallback.class)
 1package net.xdclass.service;
 2
 3import net.xdclass.domain.Video;
 4import net.xdclass.service.fallback.VideoServiceFallback;
 5import org.springframework.cloud.openfeign.FeignClient;
 6import org.springframework.web.bind.annotation.GetMapping;
 7import org.springframework.web.bind.annotation.PostMapping;
 8import org.springframework.web.bind.annotation.RequestBody;
 9import org.springframework.web.bind.annotation.RequestParam;
10
11@FeignClient(name="xdclass-video-service", fallback = VideoServiceFallback.class)
12public interface VideoService {
13    @GetMapping(value = "/api/v1/video/find_by_id")
14    Video findById(@RequestParam("videoId") int videoId);
15
16    @PostMapping(value = "/api/v1/video/save")
17    int save(@RequestBody Video video);
18}
19

测试(兜底数据,对用户更加友好)

 1localhost:8000/api/v1/video_order/save?videoId=40
 2
 3{
 4    "id": null,
 5    "outTradeNo": null,
 6    "state": null,
 7    "createTime": "2020-12-16T08:24:54.173+00:00",
 8    "totalFee": null,
 9    "videoId": 40,
10    "videoTitle": "全新微信小程序零基础到项目实战",
11    "videoImg": null,
12    "userId": null,
13    "serverInfo": "192.168.31.100:9001"
14}
15
16//关闭视频服务之后再访问
17localhost:8000/api/v1/video_order/save?videoId=40
18
19{
20    "id": null,
21    "outTradeNo": null,
22    "state": null,
23    "createTime": "2020-12-16T08:23:05.331+00:00",
24    "totalFee": null,
25    "videoId": null,
26    "videoTitle": "熔断降级数据",
27    "videoImg": null,
28    "userId": null,
29    "serverInfo": null
30}

微服务的网关和应用场景

  • 什么是网关
    • API Gateway,是系统的唯一对外的入口,介于客户端和服务器端之间的中间层,处理非业务功能 提供路由请求、鉴权、监控、缓存、限流等功能
    • 统一接入
      • 智能路由
      • AB 测试、灰度测试
      • 负载均衡、容灾处理
      • 日志埋点(类似 Nignx 日志)
    • 流量监控
      • 限流处理
      • 服务降级
    • 安全防护
      • 鉴权处理
      • 监控
      • 机器网络隔离
  • 主流的网关
    • zuul:是 Netflix 开源的微服务网关,和 Eureka,Ribbon,Hystrix 等组件配合使用,依赖组件比较多,性能教差
    • kong: 由 Mashape 公司开源的,基于 Nginx 的 API gateway
  • nginx+lua:是一个高性能的 HTTP 和反向代理服务器,lua 是脚本语言,让 Nginx 执行 Lua 脚本,并且高并发、非阻塞的处理各种请求
  • SpringCloud gateway: Spring 公司专门开发的网关,替代 zuul
  • 注意:AlibabaCloud 全家桶还没对应的网关,我们就用 SpringCloud 官方推荐的 Gateway

image-20200910120344509

介绍网关 SpringCloud Gateway

  • 什么是 SpringCloud Gateway
    • Spring 官方出品,基于 Spring5+Reactor 技术开发的网关
    • 性能强劲基于 Reactor+WebFlux、功能多样
    • 基于 springboot2.x, 直接可以 jar 包方式运行
  • 官方文档

创建 SpringCloud 网关项目和依赖添加

创建 Gateway 项目

  • 添加依赖
1 <dependency>
2            <groupId>org.springframework.cloud</groupId>
3            <artifactId>spring-cloud-starter-gateway</artifactId>
4</dependency>
  • 主启动类
     1package net.xdclass;
     2
     3import org.springframework.boot.SpringApplication;
     4import org.springframework.boot.autoconfigure.SpringBootApplication;
     5
     6@SpringBootApplication
     7public class GatewayApplication {
     8    public static void main(String[] args) {
     9        SpringApplication.run(GatewayApplication.class, args);
    10    }
    11}
    12
    
  • 配置实战 application.yml
 1server:
 2  port: 8888
 3spring:
 4  application:
 5    name: api-gateway
 6  cloud:
 7    gateway:
 8      routes: #数组形式
 9        - id: order-service  #路由唯一标识
10          uri: http://127.0.0.1:8000  #想要转发到的地址
11          order: 1 #优先级,数字越小优先级越高
12          predicates: #断言 配置哪个路径才转发
13            - Path=/order-server/**
14          filters: #过滤器,请求在传递过程中通过过滤器修改
15            - StripPrefix=1  #去掉第一层前缀
16
17#访问路径 http://localhost:8888/order-server/api/v1/video_order/list
18#转发路径 http://localhost:8000/order-server/api/v1/video_order/list
19#转发路径 http://localhost:8000/api/v1/video_order/list
20#需要过滤器去掉前面第一层
21
22

测试(记得启动订单服务)

1http://localhost:8888/order-server/api/v1/video_order/list
2
3ALibabaCloud

配置项怎么看?

  • 点击 routes 进去

SpringCloud Gateway 网关整合 Nacos 开发实战

原先存在的问题

  • 微服务地址写死
  • 负载均衡没做到

添加 Nacos 服务治理配置

  • 网关添加 naocs 依赖
1        <!--添加nacos客户端-->
2        <dependency>
3            <groupId>com.alibaba.cloud</groupId>
4            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
5        </dependency>
  • 启动类开启支持
1@EnableDiscoveryClient
  • 修改配置文件
 1server:
 2  port: 8888
 3spring:
 4  application:
 5    name: api-gateway
 6
 7  cloud:
 8    nacos:
 9      discovery:
10        server-addr: 192.168.31.101:8848
11
12    gateway:
13      routes: #数组形式
14        - id: order-service  #路由唯一标识
15          #uri: http://127.0.0.1:8000  #想要转发到的地址
16          uri: lb://xdclass-order-service #从nacos进行转发,负载均衡
17          order: 1 #优先级,数字越小优先级越高
18          predicates: #断言 配置哪个路径才转发
19            - Path=/order-server/**
20          filters: #过滤器,请求在传递过程中通过过滤器修改
21            - StripPrefix=1  #去掉第一层前缀
22      discovery:
23        locator:
24          enabled: true  #开启网关拉起nacos的服务
25
26#访问路径 http://localhost:8888/order-server/api/v1/video_order/list
27#转发路径 http://localhost:8000/order-server/api/v1/video_order/list
28#转发路径 http://localhost:8000/api/v1/video_order/list
29#需要过滤器去掉前面第一层
30
31

测试(开启 nacos,2 个订单服务端口不同)

1http://localhost:8888/order-server/api/v1/video_order/list
2
38000
4
58001

SpringCloud Gateway 配置和交互流程

网关的配置项回顾

  • 路由:是网关的基本单元,由 ID、URI、一组 Predicate、一组 Filter 组成,根据 Predicate 进行匹配转发
1route组成部分
2id:路由的ID
3uri:匹配路由的转发地址
4predicates:配置该路由的断言,通过PredicateDefinition类进行接收配置。
5order:路由的优先级,数字越小,优先级越高。

交互流程

  • 客户端向 Spring Cloud Gateway 发出请求
  • 如果网关处理程序映射确定请求与路由匹配
  • 则将其发送到网关 Web 处理程序
  • 通过特定过滤器链运行,前置处理-后置处理

Spring Cloud 网关图

Gateway 内置的路由断言

  • 什么是 Gateway 路由断言

    • Predicate 来源于 Java8,接受输入参数,返回一个布尔值结果
    • Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则
    • 转发的判断条件,SpringCloud Gateway 支持多种方式,常见如:Path、Query、Method、Header 等
    • 支持多个 Predicate 请求的转发是必须满足所有的 Predicate 后才可以进行路由转发
  • 内置路由断言介绍 RoutePredicateFactory 接口实现类
    image-20200910144246781

  • 参数编写规则 XXXRoutePredicateFactory,使用 XXX 作为参数配置, 例如下面

1predicates:
2  - Host=
3  - Path=
4  - Method=
5  - Header=
6  - Query=
7  - Cookie=

Gateway 内置的路由接口定时下线实战

需求:接口需要在指定时间进行下线,过后不可以在被访问

  • 使用 Before ,只要当前时间小于设定时间,路由才会匹配请求
  • 东 8 区的 2020-09-11T01:01:01.000+08:00 后,请求不可访问
  • 为了方便测试,修改时间即可
1predicates:
2  - Before=2020-09-09T01:01:01.000+08:00
3  - Query=source # 请求一定要带source这个参数,否则会被拒绝404

SpringCloud Gateway 过滤器

  • 什么是网关的过滤器

Spring Cloud 网关图

  • 过滤器生命周期
    • PRE: 这种过滤器在请求被路由之前调用,一般用于鉴权、限流等
    • POST:这种过滤器在路由到微服务以后执行,一般用于修改响应结果,比如增加 header 信息、打点结果日志
  • 网关过滤器分类
    • 局部过滤器 GatewayFilter:应用在某个路由上,每个过滤器工厂都对应一个实现类,并且这些类的名称必须以 GatewayFilterFactory 结尾
    • 全局过滤器:作用全部路由上,
  • 内置很多局部过滤器,顶级接口 GatewayFilterFactory,

image-20200910151857760

  • 内置很多全局过滤器,顶级接口 GlobalFilter

image-20200910152346987

网关 Gateway 全局过滤器实现用户鉴权

自定义全局过滤器实现鉴权

 1package net.xdclass.filter;
 2
 3import org.apache.commons.lang.StringUtils;
 4import org.springframework.cloud.gateway.filter.GatewayFilterChain;
 5import org.springframework.cloud.gateway.filter.GlobalFilter;
 6import org.springframework.core.Ordered;
 7import org.springframework.http.HttpStatus;
 8import org.springframework.stereotype.Component;
 9import org.springframework.web.server.ServerWebExchange;
10import reactor.core.publisher.Mono;
11
12@Component
13public class UserGlobalFilter implements GlobalFilter, Ordered {
14    @Override
15    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
16
17        String token = exchange.getRequest().getHeaders().getFirst("token");
18
19        System.out.println(token);
20        //TODO 根据业务开发对应的鉴权规则
21        if(StringUtils.isBlank(token)){
22            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
23            return exchange.getResponse().setComplete();
24        }
25
26        //继续往下执行
27        return chain.filter(exchange);
28
29    }
30
31    //数字越小,优先级越高
32    @Override
33    public int getOrder() {
34        return 0;
35    }
36}
37

测试

1http://localhost:8888/order-server/api/v1/video_order/list
2
3Post   header  token
  • 注意:网关不要加太多业务逻辑,否则会影响性能,务必记住

链路追踪系统

  • 抛两个常见的问题

    • 微服务调用链路出现了问题怎么快速排查?
  • 微服务调用链路耗时长怎么定位是哪个服务?

  • 链路追踪系统

    • 分布式应用架构虽然满足了应用横向扩展的需求,但是运维和诊断的过程变得越来越复杂,例如会遇到接口诊断困难、应用性能诊断复杂、架构分析复杂等难题,传统的监控工具并无法满足,分布式链路系统由此诞生
  • 核心:将一次请求分布式调用,使用 GPS 定位串起来,记录每个调用的耗时、性能等日志,并通过可视化工具展示出来

  • 注意:AlibabaCloud 全家桶还没对应的链路追踪系统,我们使用 Sleuth 和 zipking(内部使用的鹰眼)

Sleuth 链路追踪系统

什么是 Sleuth

1    [order-service,96f95a0dd81fe3ab,852ef4cfcdecabf3,false]
2  
3    第一个值,spring.application.name的值
4  
5    第二个值,96f95a0dd81fe3ab ,sleuth生成的一个ID,叫Trace ID,用来标识一条请求链路,一条请求链路中包含一个Trace ID,多个Span ID
6  
7    第三个值,852ef4cfcdecabf3、spanid 基本的工作单元,获取元数据,如发送一个http
8  
9    第四个值:false,是否要将该信息输出到zipkin服务中来收集和展示。

各个微服务添加依赖

1<dependency>
2      <groupId>org.springframework.cloud</groupId>
3      <artifactId>spring-cloud-starter-sleuth</artifactId>
4</dependency>

zipkin 介绍和部署

  • 什么是 zipkin
  • 同类产品
    • 鹰眼(EagleEye)
    • CAT
    • Twitter 开源 zipkin,结合 sleuth
    • Pinpoint,运用 JavaAgent 字节码增强技术
  • StackDriver Trace (Google)
  • 开始使用
    • 安装包在资料里面,启动服务
1java -jar zipkin-server-2.12.9-exec.jar

architecture-1

链路追踪组件 Zipkin+Sleuth 整合

  • sleuth 收集跟踪信息通过 http 请求发送给 zipkin server
  • zipkin server 进行跟踪信息的存储以及提供 Rest API 即可
  • Zipkin UI 调用其 API 接口进行数据展示默认存储是内存,可也用 MySQL 或者 Elasticsearch 等存储
  • 微服务加入依赖
1<dependency>
2    <groupId>org.springframework.cloud</groupId>
3    <artifactId>spring-cloud-starter-zipkin</artifactId>
4</dependency>
  • 配置地址和采样百分比配置
 1spring:
 2  application:
 3    name: api-gateway
 4  zipkin:
 5    base-url: http://192.168.31.101:9411/ #zipkin地址
 6    discovery-client-enabled: false  #不用开启服务发现
 7
 8  sleuth:
 9    sampler:
10      probability: 1.0 #采样百分比
1默认为0.1,即10%,这里配置1,是记录全部的sleuth信息,是为了收集到更多的数据(仅供测试用)。
2在分布式系统中,过于频繁的采样会影响系统性能,所以这里配置需要采用一个合适的值。

image-20200910233308640

image-20200910233241449

微服务链路追踪系统 Zipkin 持久化配置

  • 现存在的问题
    • 服务重启会导致链路追踪系统数据丢失
  • 持久化配置:MySQL 或者 Elasticsearch
    • 创建数据库表 SQL 脚本,创建数据库名 zipkin_log
       1CREATE TABLE IF NOT EXISTS zipkin_spans (
       2  `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
       3  `trace_id` BIGINT NOT NULL,
       4  `id` BIGINT NOT NULL,
       5  `name` VARCHAR(255) NOT NULL,
       6  `remote_service_name` VARCHAR(255),
       7  `parent_id` BIGINT,
       8  `debug` BIT(1),
       9  `start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
      10  `duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query',
      11  PRIMARY KEY (`trace_id_high`, `trace_id`, `id`)
      12) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
      13
      14ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';
      15ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';
      16ALTER TABLE zipkin_spans ADD INDEX(`remote_service_name`) COMMENT 'for getTraces and getRemoteServiceNames';
      17ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';
      18
      19CREATE TABLE IF NOT EXISTS zipkin_annotations (
      20  `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
      21  `trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',
      22  `span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',
      23  `a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',
      24  `a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
      25  `a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',
      26  `a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',
      27  `endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',
      28  `endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
      29  `endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',
      30  `endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'
      31) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
      32
      33ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
      34ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';
      35ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds';
      36ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';
      37ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces and autocomplete values';
      38ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces and autocomplete values';
      39ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';
      40
      41CREATE TABLE IF NOT EXISTS zipkin_dependencies (
      42  `day` DATE NOT NULL,
      43  `parent` VARCHAR(255) NOT NULL,
      44  `child` VARCHAR(255) NOT NULL,
      45  `call_count` BIGINT,
      46  `error_count` BIGINT,
      47  PRIMARY KEY (`day`, `parent`, `child`)
      48) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
      
    • 启动命令
      1java -jar zipkin-server-2.12.9-exec.jar --STORAGE_TYPE=mysql --MYSQL_HOST=192.168.31.101 --MYSQL_TCP_PORT=3306 --MYSQL_DB=zipkin_log --MYSQL_USER=root --MYSQL_PASS=123456
      

作者:Soulboy