随堂笔记第五阶段

随堂笔记

http://code.tarena.com.cn/CGBCode/cgb2108/

http://218.247.142.198/CGBCode/cgb2108/

tarenacode

code_2013

Gitee代码仓库

王海涛/CGB2108https://gitee.com/benwang6/cgb2108

一、Spring Cloud Netflix

  • 注册中心

    • Nacos
    • Eureka
  • 配置中心

    • Nacos
    • Spring Cloud Config / BUS
  • 远程调用、负载均衡

    • Feign、Ribbon
  • 系统容错、限流

    • Sentinel
    • Hystrix
  • API网关

    • Spring Cloud Gateway
    • Zuul
  • 数据监控

    • Sentinel
    • Hystrix Dashboard+Turbine
    • 链路跟踪监控:Sleuth+Zipkin

业务创建结构

  1. 父项目

  2. commons

  3. 业务模块

    1. 新建 spring 模块
    2. pom.xml
    3. application.yml
    4. service
    5. controller

1. Eureka 注册中心

1.1 搭建Eureka注册中心服务

  1. 新建 spring 模块: sp05-eureka

  2. 修改 pom.xml,添加 eureka server

  3. yml

    • 禁用自我保护模式
    • 主机名:eureka1
    • 针对单台服务器,不向自己注册,不从自己拉取注册表
  4. 启动类注解:@EnableEurekaServer 触发eureka server的自动配置

1.2 Eureka的四条运行机制

  • 注册

    客户端一次次的反复连接注册中心进行注册,直到注册成功为止

  • 拉取

    客户端每30秒拉取一次注册表,刷新注册表

  • 心跳

    客户端每30秒发送一次心跳数据,如果服务器端连续三次收不到一个服务的心跳,会删除它的注册信息

  • 自我保护模式

    • 由于网络故障,15分钟内,85%服务器出现心跳异常,会自动进入自我保护模式
    • 保护所有注册信息不删除
    • 等待网络恢复后,可以退出保护模式,恢复正常
    • 开发调试期间应该关闭保护模式,避免影响测试

1.3 Eureka客户端

  1. 修改 hosts 文件 win+r,输入 drivers,进入 etc 目录找到 hosts 文件

    127.0.0.1     eureka1
    127.0.0.1     eureka2
  2. 修改 2,3,4 三个模块,添加 eureka client 依赖

  3. 修改 2,3,4 的 yml 配置,添加 eureka 的连接地址 http://eureka1:port1/eureka

1.4 商品和eureka的高可用

客户端启动多个服务器

通过 springboot 的启动参数 --server.port设置商品的启动端口

  1. 运行的下拉菜单中,选择 edit configuration

  2. 找到商品的启动配置

  3. 修改两个位置

    • 启动配置名称: Sp02-item-8001、Sp02-item-8002

    • 配置启动参数 program arguments:

      • --server.port=8001
      • --server.port=8002

Eureka高可用

  1. 添加 eureka1 和 eureka2 的 profile 配置

  2. 配置两个启动配置

    • Sp05Eureka-2001,添加启动参数:--spring.profiles.active=eureka1
    • Sp05Eureka-2001,添加启动参数:--spring.profiles.active=eureka2
    • 修改 2,3,4,连接两台 eureka 服务器

1.5 订单远程调用

1.5.1 调用过程

  1. 调用方添加 feign 依赖

  2. 启动类添加注解:@EnableFeignClients

  3. 添加两个远程调用接口,接口上添加注解@FeignClients

    • ItemClient
    • UserClient
  4. OrderServiceImpl 中实现远程调用

1.5.2 Feign 集成 Ribbon

  • 负载均衡 -- 默认启用了负载均衡
  • 重试 -- 默认启用了重试

1.6 Ribbon 重试

调用后台服务失败(异常、服务器崩溃、超时),可以自动发起重试调用

重试参数:

  • ribbon.MaxAutoRetries - 单台服务器的重试次数,默认0
  • ribbon.MaxAutoRetriesNextServer - 更换服务器的次数,默认1
  • ribbon.ReadTimeout - 接收响应的超时时间,默认1000
  • ribbon.ConnectTimeout - 与后台服务器建立连接等待超时时间,默认1000
  • ribbon.OkToRetryOnAllOperations - 是否对所有类型请求都进行重试,默认只对 GET 请求重试

2. Zuul - API网关

  • 四大特征:
  • 统一的调用入口
  • 统一的权限校验(过滤器)
  • 集成 Ribbon
  • 集成 Hystrix

2.1 统一的调用入口

  1. 新建 spring 模块: sp06-zuul

  2. pom.xml 添加依赖: zuul(server)、eureka client(注册到注册中心)

  3. yml 配置路由规则

# 下面的路由规则是默认配置
# 根据注册表,可以完成自动配置
# 最好自己手动配置,防止注册表不全
zuul:
  routes:
    # ** 包含深层子路径
    # * 只包含单层路径
    item-service: /item-service/**
    user-service: /user-service/**
    order-service: /order-service/**  

    4.启动类添加注解:@EnableZuulProxy

2.2 Zuul 统一权限校验

继承 Zuul 的过滤器,在过滤器中判断用户权限

添加商品访问的权限判断(是否携带token):

  1. 新建过滤器类:AccessFilter,继承 ZuulFilter
  2. 按照规则实现过滤器
  3. 添加注解:@Component
  • zuul的自动配置,会从 spring 容器自动发现过滤器实例,完成自动配置
package com.jt.sp06zuul.filter;

import com.jt.utils.JsonResult;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 *通过继承自定义过滤器,类型需是前置
 * */
@Component//交给spring容器扫描并创建实例
@Slf4j
public class AccessFilter extends ZuulFilter {
    //设置过滤器的类型:是pre\routing\post等的哪一类
    //自动配置的回调方法
    @Override
    public String filterType() {
        log.info("自动配置的过滤器类型");
        //return "pre";
        return FilterConstants.PRE_TYPE;
    }

    //过滤器的顺序号
    @Override
    public int filterOrder() {
        /**
         * 在默认的第五个过滤器中,在上下文对象放入了serviceId
         * */
        return 6;
    }

    //针对当前请求进行判断,是否执行过滤代码
    @Override
    public boolean shouldFilter() {
        //获取当前请求的上下文对象
        RequestContext currentContext = //RequestContext以k-v结构存储
                RequestContext.getCurrentContext();
        //从上下文对象取出正在访问的serviceId
        String serviceId  =
                (String)currentContext.get(FilterConstants.SERVICE_ID_KEY);
        //判断
        return "item-service".equalsIgnoreCase(serviceId);
    }

    //过滤代码
    @Override
    public Object run() throws ZuulException {
        //1.获得上下文对象
        RequestContext currentContext =
                RequestContext.getCurrentContext();
        //2.从上下文对象取出request对象
        HttpServletRequest request = currentContext.getRequest();
        //3.用request接收token参数
        String token = request.getParameter("token");
        //4.判断token是否存在
        if (StringUtils.isBlank(token)){//此判断为空方法包括:null,空串"",空白字符"  "
            //4.1阻止继续访问
            currentContext.setSendZuulResponse(false);
            //4.2直接返回响应
            String json =
                    JsonResult.build().code(500).msg("没有登录").toString();
            //4.2.1设置响应头和响应体
            currentContext.addZuulResponseHeader("Content-Type",
                    "application/json;charset=UTF-8");
            currentContext.setResponseBody(json);
        }
        return null;//zuul当前版本中返回任何数据都是无效的
    }
}

2.3 Zuul 集成 Ribbon

  • 默认已经启动 ribbon 的负载均衡

  • 默认不启用 ribbon 的重试

    • 在网关重试,可能造成后台服务大面积出现压力翻倍
    • 重试功能应该尽量往后放
  • 启用重试

    1. 添加 spring-retry 依赖
    2. yml配置启用重试: zuul.retryable=true

2.4 zuul集成Hystrix

  • zuul默认已经启用了Hystrix

  • 添加降级

    1. 添加降级类,实现 FallbackProvider 接口

    2. 添加 @Component

      • zuul的自动配置会从spring容器自动发现降级类的实例,完成自动配置
package com.jt.sp06zuul.fb;

import com.jt.utils.JsonResult;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**当调用后台的商品服务失败
 * 执行网关中的这个降级类,向客户端返回降级结果
 */
@Component
public class OrderFB implements FallbackProvider {
    //设置当前降级类
    // */null:对所有服务都应用降级类
    @Override
    public String getRoute() {
        return "order-service";
    }

    //发回给客户端的降级响应数据
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            //@Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR;//
            }
            //状态码
            //@Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR.value();//500
            }
            //状态文本
            //@Override
            public String getStatusText() throws IOException {
                return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
            }

            public void close() {
                /**
                 * 用来关闭下面的输入流
                 * ByteArrayInputStream java虚拟机中 是不占用底层系统资源
                 * 所有不需要写*/
            }

            public InputStream getBody() throws IOException {
                String json =
                        JsonResult.build().code(500).msg("调用后台服务失败").toString();
                return new ByteArrayInputStream(json.getBytes("UTF-8"));
            }


            public HttpHeaders getHeaders() {
                HttpHeaders h = new HttpHeaders();
                h.add( "Content-Type", "application/json;charset=UTF-8");
                return h;
            }
        };
    }
}

3. Hystrix

3.1 容错和限流工具

  • 容错 -- 通过降级来容错

    • 调用后台服务失败,执行当前模块的一段降级代码,返回降级结果

      • 错误提示
      • 缓存数据
      • 根据具体业务逻辑,返回任何结果都可以
  • 限流 -- 通过熔断来限制后台服务的流量

    • 流量过大时,后台服务出现大量错误,会自动触发熔断

    • 10秒20次请求(必须首先满足)

    • 50%出错,执行了降级

    • 半开状态

      • 断路器打开后一段时间,会进入半开状态
      • 会尝试发送一次客户端调用 调用成功,关闭断路器,恢复正常链路 调用失败,继续保持打开状态

3.2 Hystrix数据监控

Hystrix利用 springboot 的 Actuator 工具来暴露自己的监控数据

     搭建 Hystrix Dashboard

  1. 新建 spring 模块:sp07-hystrix-dashboard
  2. 配置 pom.xml,添加 hystrix dashboard
  3. yml 配置允许抓取的服务器列表
  4. 启动类添加注解:@EnableHystrixDashboard

访问路径: http://localhost:4001/hystrix

         <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>

3.3 Actuator

springboot提供的项目监控指标工具,提供了多种监控数据

  • 健康状态
  • 环境变量、配置参数
  • spring mvc 的映射路径
  • JVM 虚拟机堆内存镜像
  • spring 容器中所有的对象
  • .....

     添加 actuator

  1. 添加 actuator 依赖

  2. yml配置暴露的监控数据

    m.e.w.e.i="*"       暴露所有监控数据
    m.e.w.e.i=health, env, beans, mappings
    m.e.w.e.i=hystris.stream
  3. 访问 http://localhost:3001/actuator

4. Turbine

从多台服务器抓取 Hystrix 日志,进行聚合,

Hystrix dashboard从Turbine抓取聚合后的日志数据

  1. 新建 spring 模块: sp08-turbine

  2. pom.xml,添加 turbine、eureka client

  3. yml

    聚合的服务列表:zuul,a,b,c
    对聚合的日志数据命名:new String("default")
  4. 启动类注解:@EnableTurbine

  5. 访问 http://localhost:5001/turbine.stream

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
        </dependency>

5. Spring cloud config 配置中心

5.1集中的管理和维护配置文件

5.1.1 创建远程仓库

  • 在 gitee 中,右上角点加号新建仓库
  • 仓库名: springcloud1
  • 设置成开源项目

5.1.2 创建本地仓库

  • vcs -- create git repository

    也可以 double shift 在 action 中搜索 create git repository

  • 选择 springcloud1 工程文件夹作为本地仓库目录

  • ctrl+k 向本地仓库提交文件 或者点右上角的对勾按钮 或者可以 double shift 在 action 中搜索 commit

  • 选中全部文件、填写提交信息,执行提交

5.1.3 把本地仓库推送到远程仓库

  • ctrl+shift+k 执行推送 或者点右上角的向上箭头按钮 或者可以 double shift 在 action 中搜索 push
  • 点左上角链接 define remote
  • 粘贴远程仓库 springcloud1 的地址

5.2 三个业务模块的配置文件,放到 git 仓库

  1. 在 springcloud1 工程中新建文件夹:config

  2. 复制 2,3,4 的 application.yml 到 config 目录

    • item-service-dev.yml
    • user-service-dev.yml
    • order-service-dev.yml
  3. 三个文件中添加 override-none: true 防止下载的配置,覆盖本地参数设置

  4. 提交到本地仓库

  5. 向远程仓库推送

5.3 搭建配置中心

  1. 新建 sp09-config 模块

  2. 添加 config server 、eureka client 依赖

  3. yml配置

    git 仓库地址
    仓库中存放配置文件的文件夹路径
  4. 启动类注解:@EnalbeConfigServer

  5. 启动 05、09,检查确认

    #注册表中有 config-server
    http://eureka1:2001/  
    http://localhost:6001/item-service/dev  # item-service dev.yml http://localhost:6001/user-service/dev
    http://localhost:6001/order-service/dev
    
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>

5.4 配置中心的客户端

  1. 修改 2,3,4 的 application.yml,代码全部注释

  2. 修改 03 用户,添加配置中心客户端设置

    • 添加 config client 依赖

    • 新建 bootstrap.yml (在引导配置阶段,从配置中心下载 application.yml)

    • 添加三条配置:

      • 连接 eureka
      • 指定配置中心的服务id
      • 从配置中心下载 user-service-dev.yml

二、VMware

16+

Nat 网络网段

使用 192.168.64.0 网段

编辑 -- 虚拟网络编辑器 -- 上面选择 vmnet8 -- 左下角修改网段 64

加载虚拟机镜像

  • 课前资料/虚拟机/centos-8-2105.zip

  • 课前资料/虚拟机/centos-7-1908.zip

  • 能用centos-8就用centos-8,用不了的话可以换成7

  • 两个虚拟机中已经做了几步基础配置:

    • 阿里的yum安装源和扩展安装源

    • 安装了三个工具: python、pip、ansible

    • 两个脚本文件,用来设置ip地址

      • ip-static:用来设置固定ip
      • ip-dhcp:用来设置自动获取ip
  1. 解压 centos-8-2105.zip
  2. 双击 centos-8-2105.vmx,加载虚拟机
  3. 启动虚拟机,按提示选择 "已复制虚拟机"
  4. 登录的用户名密码,都是 root

测试虚拟机和宿主机之间网络是否互通

./ip-dhcp

# ping 宿主机在 nat 网络中的地址
# 宿主机 ping 不通,可能需要关闭windows防火墙
ping 192.168.64.1    

# 在 windows 的cmd窗口中 ping 虚拟机
ping 192.168.64.128

解决网络设置问题

# centos 7 禁用 NetworkManager 系统服务
systemctl stop NetworkManager
systemctl disable NetworkManager

# centos 8 开启 VMware 托管
nmcli n on
systemctl restart NetworkManager

# 如果还有问题,可以重置 VMware 虚拟网络
编辑 -- 虚拟网络编辑器 -- 还原默认设置
然后再设置 nat 网络网段 64

安装 Docker 虚拟机

  1. 关闭 centos-8-2105

  2. 克隆 centos-8-2105: docker-base

  3. 用 mobaxterm 工具上传文件到 /root/

    • 课前资料\DevOps课前资料\docker\docker-install 文件夹
  4. 参考 Docker离线安装笔记,从第3步开始执行(前两步下载文件不用执行)

三、Rabbitmq 消息中间件

在分布式系统中,用来在模块之间传递数据的工具

消息服务在分布式系统中,是非常重要的组件

常用的消息服务器:

  • Rabbitmq
  • Activemq
  • Kafka
  • Rocketmq
  • Tubemq
  • ....

1. 搭建 Rabbitmq 服务器

  1. 准备好docker环境

  2. 从 docker base 克隆: rabbitmq

  3. 设置ip地址

./ip-static
ip: 192.168.64.140

ifconfig   #如果有问题,参考 VMware 下面的 解决网络设置问题
  1. 上传镜像压缩文件到/root/

    DevOps课前资料\docker\rabbit-image.gz
  2. 执行导入 docker load -i rabbit-image.gz

  3. 按照 csdn rabbitmq 笔记,docker 运行 rabbitmq

2 Rabbitmq 原生API测试

  1. 新建 Empty project: rabbitmq
  2. 新建 maven 模块: rabbitmq-api
  3. 添加 rabbitmq 依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.4.3</version>
        </dependency>

四、配置中心 + Bus 配置刷新

Bus - 消息总线

辅助完成配置刷新,向消息服务器发送刷新指令,从消息服务器接收指令,执行刷新操作

修改 2,3,4,9 添加 Bus 和 Rabbitmq

  1. 添加依赖

    • Bus
    • Rabbitmq
    • binder rabbit
  2. yml 配置rabbitmq 连接

    • config模块的 application.yml
    • 2,3,4 修改 config 文件夹的三个文件,提交推送到远程仓库
  3. 09项目,用actuator暴露 bus-refresh 路径

    • actuator依赖
    • yml配置: m.e.w.e.i=bus-refresh
  4. 启动项目

POST http://localhost:6001/actuator/bus-refresh
查看 2,3,4 控制台,有没有重新连接6001刷新配置
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-bus</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
#config-server 暴露 bus-refresh 刷新端点
management:
  endpoints:
    web:
      exposure:
        include: bus-refresh

五、消息服务案例

  1. config+bus配置刷新 把刷新指令发送到 rabbitmq,其他模块接收指令执行刷新操作 主题模式
  2. Sleuth+zipkin 链路跟踪 各个模块中产生的链路跟踪日志,通过 rabbitmq 中转发送到zipkin进行处理 简单模式
  3. 订单的流量削峰 商城的订单系统,生成订单发送到 rabbitmq,后台的消费者模块接收订单存储到数据库 简单模式、工作模式

1. Sleuth + zipkin 链路跟踪

Sleuth 用来产生链路跟踪日志

A -> B -> C -> D

# 服务名,链路id,当前模块id,是否发送到zipkin(10%抽样比例)
A, UYT43TRF45Y, UYT43TRF45Y, true
B, UYT43TRF45Y, IUYT34T4Y56, true
C, UYT43TRF45Y, I656Y45T344, true
D, UYT43TRF45Y, I6456Y32T44, true

添加Sleuth

  1. 修改 2,3,4,6 添加 sleuth 依赖
  2. sleuth 自动配置,0配置

2,3,4,6中的链路跟踪日志,发送到rabbitmq,再向zipkin中转发送

  1. 在 2,3,4,6 中添加依赖:zipkin client

  2. 在 06 添加 rabbitmq 的依赖和连接配置

  3. 修改 06 的 application.yml

    日志发送方式: rabbit
  4. 在 config 文件夹修改 2,3,4 ,添加发送方式,提交推送到远程仓库

2. Eureka客户端注册正确地址

选择正确的网卡

bootstrap.ym

#选择注册的网卡
#必须在应用启动之前,在引导配置阶段完成
spring:
  cloud:
    inetutils:
      preferred-networks:
        - 176\.20\.16\..+ #自己的物理网络ip网段 +表示一到多个

注册ip地址,而不是主机名

application.yml

eureka:
  instance:
    prefer-ip-address: true # 使用ip进行注册
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} # 界面列表中显示的格式也显示ip

3. 订单的流量削峰

导入项目

  1. 课前资料\elasticsearch\pd-商城项目案例.zip 里面的 pd-web 文件夹,解压到 rabbitmq 工程目录
  2. 打开 rabbitmq 工程
  3. 修改 pom.xml pom.xml 拖拽到 idea 编辑,修改springboot 版本:2.3.2.RELEASE
  4. 右键点 pom.xml 的编辑区域,add as maven project
  5. jdk版本1.8,编译版本8

导入数据库

  1. 用 sqlyog,右键点数据库连接,从sql转储文件导入,选择 pd-web 模块目录下的 pd.sql 文件

  2. 导入成功后,右键点数据库连接,刷新

  3. 如果导入失败,可以增大数据库缓存,在 sqlyog 中执行四条 sql:

set global max_allowed_packet=100000000;
set global net_buffer_length=100000;
SET GLOBAL  interactive_timeout=28800000;
SET GLOBAL  wait_timeout=28800000

     4. 清理测试数据

# 删除测试用户
delete from pd_user
# 删除订单数据
delete from pd_order
delete from pd_order_item

启动项目

  1. 右键点 RunPdAPP,运行
  2. 在启动配置中,配置 working directory,设置成 pd-web 文件夹的路径
  3. 重启 RunPdAPP
  4. 访问 http://localhost 如果看到浏览器在下载,可以清一下浏览器缓存

订单发送到Rabbitmq

  1. 添加依赖: rabbitmq
  2. yml 配置 rabbitmq 连接
  3. 在启动类(或者自定义自动配置类)中配置队列的参数: orderQueue, true, false, false
  4. 修改 OrderServiceImpl.saveOrder() 使用 AmqpTemplate 封装工具发送订单

订单的消费者

  1. rabbitmq的依赖、连接配置、使用的队列的配置
  2. 新建消费者类: OrderConsumer
  3. 通过注解配置,从 orderQueue 接收订单
  4. 调用 OrderServiceImpl.saveOrder() 存储订单
  5. 调整 OrderServiceImpl

分布式事务

搭建业务案例

数据库初始化工具

  1. 新建 Empty project 工程: seata-at
  2. 新建spring模块:db-init
  3. 添加 spring jdbc、mysql 驱动
  4. yml配置 数据库连接
  5. 在 resources/sql/ 文件夹下添加四个sql脚本文件
  6. 启动类添加初始化方法,执行这四个 sql 脚本
  • mysql 5.x 版本,需要调整sql脚本

    • 四个文件中, ctrl+f 查找 datetime(6),把 (6)删掉
    • order.sql 的最后一行,NOW() 函数改成数字 0

eureka注册中心

  1. 新建 spring 模块: eureka

  2. 添加 eureka server 依赖

  3. yml 配置:

    • 用默认端口 8761
    • 禁用自我保护模式
    • 单台服务器,不向自己注册,不从自己拉取
  4. 启动类注解: @EnableEurekaServer

父项目

  1. 新建 Maven 模块:order-parent
  2. 复制 pom.xml
  3. 删除它的 src 文件夹

账户,库存,订单三个业务模块

  1. 新建 spring 模块

  2. 不加依赖,从父项目继承

  3. application.yml

    • app.name
    • port
    • eureka
    • 数据库连接
    • mybatis
    • sql语句日志
  4. bootstrap.yml

    • 选择注册的网卡
  5. 实体类

  6. Mapper

  7. Service

  8. Controller

全局唯一id发号器

  1. mirrors / lookingatstarts / easyIdGenerator · GIT CODE 下载源码
  2. 解压到 seata-at 工程目录
  3. 文件夹名改成 easy-id
  4. pom.xml 拖拽到 idea,修改 springboot 版本
  5. 右键点击编辑区域,add as maven project
  6. 添加 eureka client 依赖
  7. 添加 eureka 连接配置
  8. 调整发号器的配置

订单远程调用 easy-id、库存、账户

  1. 启动类添加 @EnalbleFeignClients

  2. 定义远程调用接口

    • EasyIdClient
    • StorageClient
    • AccountClient
  3. 修改 OrderServiceImpl 完成远程调用

Seata AT 事务

启动 TC 事务协调器

  1. 从Seata官方网站下载事务协调器服务(课前资料有)

  2. 配置:

    • registry.conf -- 连接 eureka,向注册表注册
    • file.conf -- 协调器运行期间记录的日志数据的存储位置
    • seata-server.bat -- 设置 JVM 使用的内存大小
  3. 在 cmd 中运行 seata-server.bat

  • 必须用 JDK 1.8
  • 命令窗口不能关闭
  • 命令窗口不能选中,否则应用会被挂起,暂停执行

在业务模块中添加 Seata AT 事务

  1. 添加 seata 依赖

  2. 三个配置:

    • application.yml -- 事务组的组名
    • registry.conf -- 设置注册中心的地址
    • file.conf -- 事务组对用使用哪个协调器
  3. 自定义自动配置类,创建数据源代理对象

    • AT事务的自动事务控制代码,都是由数据源代理对象提供的
  4. 排除 springboot 默认的数据源自动配置

  5. 业务方法上添加事务注解

    • @Transactional - 控制本地事务
    • @GlobalTransactional - 创建 TM,启动全局事务,只在第一个模块添加

Seata TCC 事务

TCC 是一种有侵入的事务方案

  • 所有事务控制代码都需要自己实现
  • 底层的数据库、业务代码都需要修改

有些复杂情况下,AT事务无法实现自动事务控制,就需要手动控制事务的方案

TCC - 两个阶段的三种操作

  • 第一个阶段

    Try - 预留资源、冻结数据

  • 第二阶段

    • Confirm - 确认、提交,使用一阶段冻结的数据实现业务数据处理
    • Cancel - 取消、回滚,把一阶段冻结的数据恢复回去

导入工程的无事务版本

  1. 新建 Empty project: seata-tcc
  2. seata-at/无事务版本.zip,解压到 seata-tcc 工程目录
  3. 导入模块

业务模块中添加 Seata TCC 事务

  1. seata 依赖

  2. 三个配置文件

    • application.yml -- 组名
    • registry.conf -- eureka地址
    • file.conf -- 事务组对应使用的协调器
  3. 修改 Mapper,添加新的 TCC 三个数据库操作

  4. 添加 ResultHolder 控制幂等性

  5. 按照 Seata TCC 的规则,添加 TccAction 接口和实现,实现三个操作的方法

  6. 调整业务方法,不直接完成业务,而是调用第一阶段方法冻结数据

  7. 在第一个模块的业务方法上添加 @GlobalTransactional

Rocketmq

安装搭建 Rocketmq 服务器

  1. 克隆 centos-8-2105: rocketmq

  2. 设置 ip

./ip-static
ip: 192.168.64.141

ifconfig
  1. 上传文件到 /root/

    分布式事务\rocketmq\ 文件夹的三个文件
  2. 按照 csdn 笔记来安装,所有联网下载都不用执行

启动三个服务

cd /usr/local/rocketmq/

nohup sh bin/mqnamesrv &

nohup sh bin/mqbroker -n localhost:9876 &

如果要从配置文件启动,用这个命令:
nohup sh bin/mqbroker -n localhost:9876 -c conf/broker.conf &

cd ~/

新版本:
nohup java -jar rocketmq-dashboard-1.0.1-SNAPSHOT.jar --server.port=8080 --rocketmq.config.namesrvAddr=localhost:9876 &

旧版本:
nohup java -jar rocketmq-console-ng-1.0.1.jar --server.port=8080 --rocketmq.config.namesrvAddr=localhost:9876 &

查看是否有三个服务的进程:
jps

Rocketmq

  1. 新建 Empty project 工程: rocketmq-dtx
  2. 新建 Maven 模块: rocketmq-api
  3. pom.xml 复制添加依赖

可靠消息最终一致性事务

订单异步调用账户

  1. seata-at/无事务版本.zip,解压到 rocketmq-dtx 工程目录
  2. 导入模块

修改 Order 订单

  1. 添加 Rocketmq 依赖

  2. yml配置

    • name server
    • 生产者组名
  3. 添加新的数据表 tx_table,用来存储本地事务执行状态,来应对事务回查

  4. 实体类 TxInfo

  5. TxMapper,插入状态、查询状态

  6. 添加 AccountMessage 用来封装向账户发送的消息数据

  7. 添加 JsonUtil 工具类

  8. 修改 OrderServiceImpl,在业务方法中发送事务消息

  9. 实现监听器,执行本地事务和处理事务回查

账户接收消息,执行扣减账户金额

  1. yml 配置 rocketmq 连接
  2. 添加 AccountMessage 用来封装接收的消息数据
  3. 添加 JsonUtil 工具类
  4. 新建消费者类:AccountConsumer
  5. 通过注解配置接收消息
  6. 收到消息后调用业务方法,完成金额扣减

Docker

Elasticsearch

  1. 克隆 docker-base: es

  2. 设置ip

    ./ip-static
    ip: 192.168.64.181
    ifconfig
  3. 上传文件到 /root/

    • elasticsearch/pditems 文件夹
    • elasticsearch/elasticsearch-analysis-ik-7.9.3.zip
    • elasticsearch/es-img.gz
  4. 导入镜像 docker load -i es-img.gz

  5. 服务器内存设置到 2G 或以上

    右键点es虚拟机---设置---修改内存大小
  6. 设置系统的底层参数

    echo 'vm.max_map_count=262144' >>/etc/sysctl.conf
    查看
    cat /etc/sysctl.conf
  7. 重启服务器 shutdown -r now

案例:拼多商城搜索条

  1. pom.xml 添加 elasticsearch 依赖
  2. yml 配置 es 服务器地址列表
  3. 添加实体类: Item
  4. 添加 ItemRepository 接口继承ElasticsearchRepository< >,定义商品搜索方法
  5. SearchService
  6. SearchController
  7. search.jsp
        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
		</dependency>
spring:
  elasticsearch:
    rest:
      uris:
        - http://192.168.64.181:9200
        - http://192.168.64.181:9201
        - http://192.168.64.181:9202
logging:
  level:
    tracer: trace  #REST API调用的 http 协议数据日志
package cn.tedu.springdataes.es;
import cn.tedu.springdataes.entity.Student;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.annotations.Highlight;
import org.springframework.data.elasticsearch.annotations.HighlightField;
import org.springframework.data.elasticsearch.annotations.HighlightParameters;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import java.util.List;

/*
spring data API 的 Repository 数据访问规范,
不需要自己写代码,只需要定义抽象接口、抽象方法就可以访问数据库的数据

父接口中已经定义了基础增删改查方法

分页:
    - Pageable 封装向服务器提交的分页参数
    - Page (可选)封装服务器返回的这一页数据,以及所有分页属性
 */
public interface StudentRepository
        extends ElasticsearchRepository<Student, Long> {
    // 在 name 字段搜索关键词
    List<Student> findByName(String key);

    /*
    SearchHit
        需要使用 List<SearchHit<Student>> 返回类型来获得高亮结果,
        SearchHit中封装了高亮字段的结果,Student对象中包含的是原始学生数据
     */

    @Highlight(
            //高亮参数,设置前后标签
            parameters = @HighlightParameters(
                    preTags = "<em>",
                    postTags = "</em>"
            ),
            //高亮的字段
            fields = {@HighlightField(name = "name")}
    )
    // 在 name 字段,或者 birthDate 字段中搜索
    List<SearchHit<Student>> findByNameOrBirthDate(String name, String birthDate, Pageable pageable);

}
@Component
public class StudentSearcher {
    // 用来执行查询的工具对象
    @Autowired
    private ElasticsearchOperations op;

    public List<Student> findByName(String key) {
        Criteria c = new Criteria("name");
        c.is(key); //在 name 字段搜索指定的关键词
        return exec(c, null);
    }
    public List<Student> findByBirthDate(
            String from, String to, Pageable pageable) {
        Criteria c = new Criteria("birthDate");
        c.between(from, to);
        return exec(c, pageable);
    }

    private List<Student> exec(Criteria c, Pageable pageable) {
        //把搜索条件和分页参数封装成查询对象
        CriteriaQuery q = new CriteriaQuery(c);
        if (pageable != null) {
            q.setPageable(pageable);
        }
        //执行搜索,把搜索结果封装成Student对象
        SearchHits<Student> shs = op.search(q, Student.class);
        List<Student> list = new ArrayList<>();
        for (SearchHit<Student> sh : shs) {
            list.add(sh.getContent());
        }
        return list;
    }
}
@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private ItemRepository r;
    @Override
    public List<SearchHit<Item>> search(String key, Pageable pageable) {
        return r.findByTitleOrSellPoint(key, key, pageable);
    }
}
package com.pd.controller;
import com.pd.pojo.Item;
import com.pd.service.SearchService;
import com.sun.javafx.scene.text.HitInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.ArrayList;
import java.util.List;

@Controller
@RequestMapping("search")
public class SearchController {
    @Autowired
    private SearchService searchService;

    //?key=手机&page=0&size=20
    @GetMapping("/toSearch.html")
    public String search(Model model, String key, Pageable pageable) {
        // model对象用来向jsp传递数据的工具
        List<SearchHit<Item>> list = searchService.search(key, pageable);
        List<Item> items = zhuanHuan(list); //转换
        // items 集合传递到 jsp 进行显示
        // pageable 对象传递到 jsp 显示翻页
        model.addAttribute("items", items);
        model.addAttribute("p", pageable);

        return "/search.jsp";
    }

    private List<Item> zhuanHuan(List<SearchHit<Item>> list) {
        List<Item> items = new ArrayList<>();
        for (SearchHit<Item> sh : list) {
            Item item = sh.getContent(); //原始数据,没有高亮
            List<String> hlTitle = sh.getHighlightField("title"); //高亮title
            item.setTitle(pinJie(hlTitle));//高亮title替换原始的title数据
            items.add(item);
        }
        return items;
    }
    private String pinJie(List<String> hlTitle) {
        StringBuilder s = new StringBuilder();
        for (String s1 : hlTitle) {
            s.append(s1);
        }
        return s.toString();
    }
}

Kubernetes

自动容器部署管理工具

谷歌的开源工具

Kubernetes 集群安装非常复杂

有辅助的开源项目,辅助安装集群

K8s集群方案:

  1. 内存16G或以上: 三台服务器
  2. 内存8G: 两台服务器

小点:

Kubernetes/k8s集群方案
kubectl命令
3个核心概念:
Pod:k8s的容器,是对docker容器的封装对象
控制器RC(Replication Controller):自动控制容器部署、销毁的工具
Service: 提供一个不变的访问地址,转发请求地址到容器(即调用)
手动创建pod
Pod设置标签:根据标签来选中对应的服务器
kubectl create -f kubia-manual-with-labels.yml
kubectl get po --show-labels
新添加标签
kubectl label po kubia-manual-v2 env=debug
修改标签
kubectl label po kubia-manual-v2 env=debug --overwrite
env=prod/product
creation_method=manual
删除标签:kubectl label po kubia-22f7k env-
节点选择器:根据节点把pod部署到匹配的工作节点服务器
kubectl label node 192.168.64.191 gpu=true
namespace/ns:同一个服务器里不能重名
kubetcl delete po kubia-gpu
标签过滤删除:
kubectl delete po -l creation_method=manual
kubectl delete ns custom-namespace
全部容器删除:kubectl delete po --all
删除后控制器会自动重写创建新的容器
存活探针:
创建配置文件:
kubectl create -f kubia-liveness-probe.yml
查看日志:
kubectl logs kubia-liveness --
livenessProbe:
       httpget:
              path:
              port: 8080
设置延迟: initialDelaySeconds: 15
避免容器内应用没有完全启动的情况下就开始探测
kubectl get svc -ns kube-system
edit编辑
ReplicaSet(新版控制器rs)-->replicas:3-->selector
增强选择器:app in(xxx,xxx)
DS监控器ssd
job控制器
schedule:
cronjob:计划任务控制器
Endpoints:pod和service之间的一种资源
是地址列表,通过双方的name来和service绑定,自动创建
kubectl get ep//查看Endpoints
kubectl describe svc kubia//查看service详情,有关联的endpoints对象
用ip地址来关联容器
手动创建endpoints对象来调用外部系统服务external-service
kubectl get svc//获取IP集群
curl http://xxxxx.com//服务地址
在external-service里面直接指定要调用的一个域名
kubectl exec -it kubia-xxx bash//进入容器执行
service对外暴露:端口暴露到服务器
ports: nodeport节点端口  port  targetport
k8s支持数据挂载:
EmptyDir:临时数据文件
index-xml
共享文件夹
安装NFS文件系统:dnf install nfs-utils
mkdir /etc/nfs_data
async同步,no_root_squash:服务器端使用root权限
启动
systemctl enable nfs-server
systemctl enable rpcbind
systemctl start nfs-server
systemctl start rpcbind
挂载到另一服务器的文件夹
mount -t nfs 当前服务器:文件夹 挂载文件夹
mongodb:键值对存储
persistent volume持久数据卷(like实现类):
    存储位置
    存储条件:
        空间:1G
        访问模式:readwriteonce/readonlimany
persistent volume claim声明(like接口):存储条件
fortune: args间隔时间覆盖cmd命令参数
fortune: env通过设置环境变量interval覆盖间隔时间参数
config Maps:配置存储cm
       sleep-interval: 5
停机升级(新旧版本不兼容时)&逐步替换,滚动升级
Deployment对象:自动化实现滚动升级
创建过程:deploy-->rs-->po
修改镜像image: luksa/kubia: v1-->v2--v3...版本
创建出RS控制器,并控制控制器的容器数量
配置的容器数量spec: replicas: 3
添加属性:kubectl patch deploy kubia -p xxx
通过service来回调用容器pod
kubectl rollout status deploy kubia//查看滚动升级状态
kubectl get deploy
hash值标签hash=xxxx
新版本测试失败需要回滚到上一版本:kubectl rollout undo deploy kubia
控制滚动升级速率:
maxSurge 25% 滚动过程中可以超出的容器数量
maxUnavailable 25% 最多允许多少容器不可用
kubectl get deploy -o yaml
pause暂停升级
就绪探针:readinessProbe: periodSeconds: 1
探测成功,容器变为就绪状态
工作中可以使用Deployment对象来部署容器,方便之后的升级
K8S部署springcloud项目:
构建镜像:上传文件到root
镜像jdk-->各个模块的镜像-->压缩复制到其他服务器-scp->导入load
-->部署控制器、service
docker build -t centos7-jdk8:v1 jdk/
docker build -t sp-eureka:v1 eureka/
控制器名:metadata: name: eureka1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值