Docker与微服务实战(高级篇)- 【上】

Docker与微服务实战(高级篇)- 【上】

  • 一、Docker复杂安装详说
    • 1.1 Mysql主从复制--原理-【尚硅谷Mysql高级篇】
    • 1.2 Mysql主从复制--【一主一从】搭建步骤
      • 1.2.1新建--【主服务器】--容器实例--3307
      • 1.2.2.进入/app/mysql-master/conf目录下新建my.cnf
      • 1.2.3.修改完配置后重启master实例
      • 1.2.4.进入mysql-master容器
      • 1.2.5.master容器实例内创建数据同步用户
      • 1.2.6.新建---【从服务器】---容器实例--3308
      • 1.2.7.进入/app/mysql-slave/conf目录下新建my.cnf
      • 1.2.8.修改完配置后重启slave实例
      • 1.2.9.在主【数据库中】查看主从同步状态
      • 1.2.10.进入mysql-slave容器
      • 1.2.11.在【从数据库】中配置主从复制
      • 1.2.12.在mysql主从复制的时候,使用change to改变主从信息的时候,却发现change不了
      • 1.2.13.在【从数据库】中查看主从同步状态
      • 1.2.14.在【从数据库】中开启主从同步
      • 1.2.15.【查看从数据库】状态发现已经同步
      • 1.2.16.主从复制测试
        • 1.2.16.1.主机新建库--使用库--新建表--插入数据
        • 1.2.16.2.从机使用表--查看记录
      • 1.2.17. 遇见的异常
  • 二、安装Redis集群(大厂面试第4季--分布式存储案例真题)
    • 2.1 面试题---【1~2亿条数据需要缓存,请问如何设计这个存储案例】
    • 2.2 哈希取余分区
    • 2.3 一致性哈希算法分区
    • 2.4 哈希槽分区
    • 2.5 哈希槽计算
  • 三、3主3从redis集群扩缩容配置案例架构说明
    • 3.1 架构图
    • 3.2 关闭防火墙+启动docker后台服务
    • 3.3 redis集群配置【3主3从】
      • 3.3.1.docker运行redis--命令参数--分步解释
      • 3.3.2.新建6个docker容器实例
      • 3.3.3.进入容器redis-node-1并为6台机器构建集群关系
      • 3.3.4.链接进入6381作为切入点,查看`集群状态`
    • 3.4 主从容错切换迁移案例
      • 3.4.1.数据读写存储
        • 3.4.1.1.启动1机构成的集群并通过exec进入
        • 3.4.1.2.防止路由失效加参数-c,并新增两个key
        • 3.4.1.3.查看集群状态
      • 3.4.2.容错切换迁移
        • 3.4.2.1. 主6381和从机切换,先停止主机6381
        • 3.4.2.2. 再次查看集群信息
        • 3.4.2.3. 先还原之前的3主3从
        • 3.4.2.5. 查看集群状态【redis-cli --cluster check `自己ip`:6381】
        • 3.4.2.6.虚拟机自动关机,重启恢复原样
    • 3.5 主从扩容案例
      • 3.5.1.分析图
      • 3.5.2.实操步骤
      • 3.5.3.新建`6387、6388`两个节点+新建后启动+查看是否8节点
      • 3.5.4.进入`6387`容器实例内部--`docker exec -it redis-node-7 /bin/bash`
      • 3.5.5.将新增的`6387`节点【空槽号】作为master节点加入原集群
      • 3.5.6.检查集群情况第1次
      • 3.5.7.重新分派槽号--命令【redis-cli --cluster reshard IP地址:端口号】
      • 3.5.8.检查集群情况第2次--【槽号分派说明】
      • 3.5.9.为主节点6387分配从节点6388
      • 3.5.10.检查集群情况第3次
    • 3.6 主从缩容案例
      • 3.6.1 分析图
      • 3.6.2 实操步骤
      • 3.6.3 检查集群情况1获得6388的节点ID
      • 3.6.4 将6388删除--从集群中将4号从节点6388删除
      • 3.6.5 将6387的槽号清空,重新分配,本例将清出来的槽号都给6381
      • 3.6.6 检查集群情况第二次
      • 3.6.7 将6387删除
      • 3.6.8 检查集群情况第三次
    • 3.7 总结
  • 四、DockerFile解析
    • 4.1 DockerFile介绍
    • 4.2 DockerFile构建过程解析
      • 4.2.1.镜像结构
      • 4.2.2.DockerFile基础知识
      • 4.2.3.docker执行DockerFile的大致流程
      • 4.2.4.小总结
    • 4.3 DockerFile常用保留字指令
      • 4.3.1 FROM:定义基于哪个镜像构建新的镜像。
      • 4.3.2 MAINTAINER:为镜像添加作者信息:姓名和邮箱地址
      • 4.3.3 RUN:在容器构建时需要运行的命令。
      • 4.3.4 EXPOSE:当前容器对外暴露的端口
      • 4.3.5 WORKDIR:指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点
      • 4.3.6 USER: 指定该镜像以什么样的用户去执行,如果都不指定默认是root
      • 4.3.7 ENV:用来在构建镜像过程中设置环境变量
      • 4.3.8 ADD:将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包
      • 4.3.9 COPY:类似ADD,拷贝文件和目录到镜像中
      • 4.3.10 VOLUME:容器数据卷,用于数据保存和持久化工作
      • 4.3.11 CMD:设置容器默认要运行的命令及参数。
      • 4.3.12 ENTRYPOINT:
      • 4.3.13 CMD与ENTRYPOINT
      • 4.3.14 小总结
    • 4.4 案例
      • 4.4.1 自定义镜像mycentosjava8
        • 4.4.1.1 要求:Centos7镜像具备vim+ifconfig+jdk8
        • 4.4.1.2 下载JDK8
        • 4.4.1.3 编写Dockerfile文件
        • 4.4.1.4 构建
        • 4.4.1.5 运行
        • 4.4.1.6. 构建报错【centos8及8版本以上缺乏中文语言包和地址vault.centos.org需更改】
          • 4.4.1.6.1.Error: Failed to download metadata for repo ‘appstream‘: Cannot prepare internal mirrorlist
          • 4.4.1.6.2.`CentOS 8` 中执行命令,出现报错:Failed to set locale, defaulting to C.UTF-8报错原因:
          • 4.4.1.6.3.总结编写Dockerfile,centos8添加以下内容
          • 4.4.1.6.4.再次测试centos8以上版本镜像
      • 4.4.2 虚悬镜像:仓库名、标签都是<none>的镜像,俗称dangling image
      • 4.4.3 自定义镜像myubuntu
        • 4.4.3.1 编写Dockerfile
        • 4.4.3.2 构建新镜像
        • 4.4.3.3 运行新镜像
      • 4.4.4.案例-自定义镜像-Dockerfile
    • 4.5 小总结
  • 五、Docker微服务实战
    • 5.1.在IDEA新建一个普通微服务模块
      • 5.1.1.建Moudle--spring-boot-docker-demo
      • 5.1.2.改POM
      • 5.1.3.写yml
      • 5.1.4.主启动
      • 5.1.5.业务类
      • 5.1.6.测试
      • 5.1.7.打包成一个jar包并上传
    • 5.2.通过Dockerfile发布到微服务部署到docker容器
      • 5.2.1.IDEA工具将微服务打包成jar包并上传
      • 5.2.2.编写Dockerfile
        • 5.2.2.1.Dockerfile内容
        • 5.2.2.2.将微服务jar包和Dockerfile文件上传到同一个目录下/app/mydocker
      • 5.2.3.构建镜像
      • 5.2.4.运行容器
      • 5.2.5.访问测试
  • 六、Docker网络
    • 6.1.分析图
    • 6.2.Docker 不启动,默认的网络情况
    • 6.3.Docker 启动,网络情况
    • 6.4.查看Docker 的网络命令模式
      • 6.4.1.docker网络命令大全
      • 6.4.2.查看Docker网络
      • 6.4.3.查看网络数据源
      • 6.4.4.创建网络
      • 6.4.5.删除网络
    • 6.5.Docker网络作用
    • 6.6.Docker网络模式
      • 6.6.1.总体介绍
      • 6.6.2.网络实例内默认网络IP生产规则
        • 6.6.2.1.先启动两个ubuntu实例
        • 6.6.2.2.:docker inspect 容器ID or 容器名字
        • 6.6.2.3.关闭u2实例,新建u3,查看ip变化
        • 6.6.2.4.结论:docker容器内部的ip是有可能会发生改变的
      • 6.6.3.案例说明【bridge】
        • 6.6.3.1.bridge是什么
        • 6.6.3.2.说明
        • 6.6.3.3.代码
        • 6.6.3.4.两两匹配验证
      • 6.6.4.案例说明【host】
        • 6.6.4.1.host是什么
        • 6.6.4.2.说明
        • 6.6.4.3.代码
        • 6.6.4.4.没有设置-p端口映射,如何启动访问tomcat84
      • 6.6.5.案例说明【none】
      • 6.6.6.案例说明【container】
        • 6.6.6.1.Alpine操作系统是一个面向安全的轻型Linux发行版
        • 6.6.6.2.命令演示
        • 6.6.6.3.运行结果,验证共用搭桥
        • 6.6.6.4.加入关闭alpine1,查看alpine2
      • 6.6.7.案例说明【自定义网络】
        • 6.6.7.1.启动两个tomcat并进入容器内部
        • 6.6.7.2.按照ip地址ping【ok】
        • 6.6.7.3.按照服务名ping【error】
        • 6.6.7.4.自定义桥接网络,自定义网络默认使用的是桥接网络bridge
        • 6.6.7.5.新建自定义网络
        • 6.6.7.6.新建容器加入上一步新建的自定义网络
        • 6.6.7.7.互相ping测试
        • 6.6.7.8.问题结论:自定义网络本身就维护好了主机名和ip的对应关系( ip和域名都能通)
  • 七、Docker-compose容器编排
    • 7.1.Docker-compose由来
    • 7.2.下载、安装及卸载
      • 7.2.1.下载
      • 7.2.2.安装步骤
      • 7.2.3.卸载步骤
    • 7.3.Compose核心概念
    • 7.4.Compose使用的三个步骤
    • 7.5.docker-compose.yml文件及常用命令
      • 7.5.1.docker-compose.yml文件解读
      • 7.5.2.docker-compose.yml的编写规则介绍
      • 7.5.3.Compose命令格式
      • 7.5.4.Compose常用命令
      • 7.5.5.以nginx容器操作为例
    • 7.6.Compose编排微服务
      • 7.6.1.改造升级微服务工程spring-boot-docker-demo
        • 7.6.1.1.SQL建库建表
        • 7.6.1.2.改pom.xml
        • 7.6.1.3.写application.yml
        • 7.6.1.4.主启动类SpringBootDockerDemoApplication.java
        • 7.6.1.5.业务类
          • 7.6.1.5.1.config配置类RedisConfig.java
          • 7.6.1.5.2.config配置类SwaggerConfig.java
          • 7.6.1.5.3.实体类User.java
          • 7.6.1.5.4.实体类UserDTO.java
          • 7.6.1.5.5.编写UserMapper.java
          • 7.6.1.5.6.在src/main/resources目录下创建mapper文件夹,并编写UserMapper.xml
          • 7.6.1.5.7.新建UserService、UserServiceImpl
          • 7.6.1.5.8.新建UserController
        • 7.6.1.7.mvn package命令将微服务形成新的jar包,并上传到Linux服务器 /app/mydocker目录下
        • 7.6.1.8.编写Dockerfile
        • 7.6.1.9.构建镜像
      • 7.6.2.不使用Compose
        • 7.6.2.1.单独的mysql容器
          • 7.6.2.1.1.新建mysql容器实例
          • 7.6.2.1.2.进入mysql容器实例内并新建库db2024 + 新建表t_user
        • 7.6.2.2.单独的redis容器
        • 7.6.2.3.微服务工程
        • 7.6.2.4.上面三个容器实例依次顺序启动成功
        • 7.6.2.5.swagger测试
      • 7.6.3.上面成功了,有哪些问题【痛点】
      • 7.6.4.使用Compose
        • 7.6.4.1.编写docker-compose.yml文件
        • 7.6.4.2.第二次修改微服务工程
        • 7.6.4.3.执行 docker-compose up 或者 docker-compose up -d
        • 7.6.4.4.进入mysql容器实例并新建库db2024+新建表t_user
        • 7.6.4.5.测试通过
        • 7.6.4.6.Compose常用命令
        • 7.6.4.7.关停
      • 7.6.5.Redis总是连接失败总结
        • 7.6.5.1.can't connect to redis-server
        • 7.6.5.2.将redis.conf文件下列参数更改
        • 7.6.5.3.测试redis连接
        • 7.6.5.4.docker redis容器连接不上--可以自定义网络模式
        • 7.6.5.5.Docker运行Redis容器总结
      • 7.6.6.docker: Error response from daemon: driver failed programming external connectivity on endpoint redis

一、Docker复杂安装详说

1.1 Mysql主从复制–原理-【尚硅谷Mysql高级篇】

1.2 Mysql主从复制–【一主一从】搭建步骤

1.2.1新建–【主服务器】–容器实例–3307

# 安装mysql,启动数据卷,使得将docker内的数据保留进宿主机的磁盘中,设置mysql密码为123456
docker run \
--name mysql-master \
--privileged=true \
-e MYSQL_ROOT_PASSWORD=123456 \
-p 3307:3306 \
-v /app/mysql-master/log:/var/log/mysql \
-v /app/mysql-master/data:/var/lib/mysql \
-v /app/mysql-master/conf:/etc/mysql \
-d \
mysql:5.7

在这里插入图片描述

1.2.2.进入/app/mysql-master/conf目录下新建my.cnf

cd /app/mysql-master/conf
pwdvim my.cnf

粘贴以下内容

[client]
default_character_set=utf8
[mysql]
default-character-set=utf8[mysqld]
## 设置server-id,同一局域网中需要唯一
server-id=101
## binlog-do-db=数据库名  //同步的数据库名称,如果不配置,表示同步所有的库
## 指定不需要同步的数据库名称,忽略mysql库
binlog-ignore-db=mysql
## 开启二进制日志功能
log-bin=mall-mysql-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(binlog格式:mixed,statement,row)
## ROW(行模式,记录所有变动的行,存入binlog,缺点:当遇到批量修改的sql时,容易导致日志sql过多)
## STATEMENT(记录每条修改的SQL,存入binlog,缺点:当遇到now()这些函数时,会导致主从出现数据误差)
## MIXED(实现ROW和STATMENT切换,缺点:无法识别@@的系统变量,比如@@hostname)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1026错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062collation_server=utf8_general_ci
character_set_server=utf8

在这里插入图片描述

1.2.3.修改完配置后重启master实例

docker restart mysql-master

在这里插入图片描述

1.2.4.进入mysql-master容器

# 进入master容器 & 进入数据库
docker exec -it mysql-master /bin/bashmysql -uroot -p123456

在这里插入图片描述

1.2.5.master容器实例内创建数据同步用户

# 在主库中添加账号slave,密码为123456并赋予权限
create user 'slave'@'%' identified by '123456';
# 授予权限
grant replication slave,replication client on *.* to 'slave'@'%';

在这里插入图片描述

1.2.6.新建—【从服务器】—容器实例–3308

docker run \
--name mysql-slave \
--privileged=true \
-e MYSQL_ROOT_PASSWORD=123456 \
-p 3308:3306 \
-v /app/mysql-slave/log:/var/log/mysql \
-v /app/mysql-slave/data:/var/lib/mysql \
-v /app/mysql-slave/conf:/etc/mysql \
-d \
mysql:5.7

在这里插入图片描述

1.2.7.进入/app/mysql-slave/conf目录下新建my.cnf

cd /app/mysql-slave/conf
pwdvim my.cnf

粘贴以下内容

[client]
default_character_set=utf8
[mysql]
default-character-set=utf8[mysqld]
## 设置server-id,同一局域网中需要唯一
server-id=102## binlog-do-db=数据库名  //同步的数据库名称,如果不配置,表示同步所有的库
## 指定不需要同步的数据库名称,忽略mysql库
binlog-ignore-db=mysql
## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用
log-bin=mall-mysql-slave1-bin## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(binlog格式:mixed,statement,row)
## ROW(行模式,记录所有变动的行,存入binlog,缺点:当遇到批量修改的sql时,容易导致日志sql过多)
## STATEMENT(记录每条修改的SQL,存入binlog,缺点:当遇到now()这些函数时,会导致主从出现数据误差)
## MIXED(实现ROW和STATMENT切换,缺点:无法识别@@的系统变量,比如@@hostname)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1026错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062## relay_log配置中继日志
relay_log=mall-mysql-relay-bin
## log_slave_updates表示slave将复制事件写进自己的二进制文件
log_slave_updates=1
## 这个配置项可以设置从库的只读模式,将其值设置为 1 表示只读,设置为 0 表示读写。
## slave设置为只读(具有super权限的用户除外)
read_only=1collation_server=utf8_general_ci
character_set_server=utf8

在这里插入图片描述

1.2.8.修改完配置后重启slave实例

docker restart mysql-slave

在这里插入图片描述

1.2.9.在主【数据库中】查看主从同步状态

show master status;

在这里插入图片描述

1.2.10.进入mysql-slave容器

docker exec -it mysql-slave /bin/bashmysql -uroot -p123456

在这里插入图片描述

1.2.11.在【从数据库】中配置主从复制

记住File和Position,后面需要用到。
此时一定不要操作Master库,否则将会引起Master状态的变化,File和Position字段也将会进行变化。

在这里插入图片描述
查看主机ip(mysql-master)

docker inspect mysql-master

在这里插入图片描述

# 设置从库读取主库的服务器配置,分别为:主机IP、账号、密码、MASTER_LOG_FILE是上面查询的File,MASTER_LOG_POS是上面查询的Position
change master to master_host='宿主机ip',
master_user='slave',
master_password='123456',
master_port=3307,
master_log_file='mall-mysql-bin.00001',
master_log_pos=617,
master_connect_retry=30;
主从复制命令参数说明:
master_host : 主数据库的IP地址;
master_port : 主数据库的运行端口;
master_user : 在主数据库创建的用于同步数据的用户账号;
master_password : 在主数据库创建的用于同步数据的用户密码;
master_log_file : 指定从数据库要复制数据的日志文件,通过查看主数据的状态,获取File参数;
master_log_pos : 指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取Position参数;
master_connect_retry : 连接失败重试的时间间隔,单位为秒。
change master to 
master_host='192.168.229.133',
master_user='slave',
master_password='123456',
master_port=3307,
master_log_file='mall-mysql-bin.000001',
master_log_pos=617,
master_connect_retry=30;

在这里插入图片描述
使用start slave 开启主从复制过程后,如果 SlavelORunning一直是Connecting,则说明主从复制一直处于连接状态,这种情况一般是下面几种原因造成的,我们可以根据Last_IO_Error提示予以排除。
在这里插入图片描述
在这里插入图片描述

1、网络不通: 检查ip、端口
2、密码不对 : 检查是否创建用于同步的用户和用户密码是否正确
3、pos不对 : 检查Master的Position正常启动后如下:

在这里插入图片描述

1.2.12.在mysql主从复制的时候,使用change to改变主从信息的时候,却发现change不了

mysql创建从节点出现如下报错提示

ERROR 3021 (HY000): This operation cannot be performed with a running slave io thread; 
run STOP SLAVE IO_THREAD FOR CHANNEL '' first.ERROR 3021 (HY000):此操作不能在运行的从io线程中执行;运行停止slave原因很简单,我们需要,先stop slave;先把slave  stop掉 然后再使用change就可以了。
change完之后,记得start slave; 开启slave
因为之前已经创建过主节点,需停掉之前的配置 再重新配置# 停止主从复制
stop slave;
# 重置主机配置
reset master;# 再次配置主从即可,查看从节点状态
show slave status\G;

在这里插入图片描述

1.2.13.在【从数据库】中查看主从同步状态

#查看主从同步状态[Slave_IO_Running列 和 Slave_SQL_Running列 是否开启主从同步]
show slave status \G;

在这里插入图片描述

Slave_lO_Running和Slave_SQL_Running是查看主从是否运行的关键字段,默认为NO,表示没有进行主从复制。

1.2.14.在【从数据库】中开启主从同步

# 开启主从复制
start slave;

在这里插入图片描述

1.2.15.【查看从数据库】状态发现已经同步

show slave status \G;

在这里插入图片描述

1.2.16.主从复制测试

1.2.16.1.主机新建库–使用库–新建表–插入数据

在这里插入图片描述

1.2.16.2.从机使用表–查看记录

在这里插入图片描述

1.2.17. 遇见的异常

  • 配置好my.conf启动成功后登录时异常:[ERROR] unknown variable ‘server-id=1’
    将新增的所有主从配置往上移就解决了

  • 执行重启命令systemctl restart mysqld;时异常:Failed to restart mysqld.service: Unit not found 
    MySQL在安装时没有创建名为mysqld的服务,cd /etc/init.d查看mysql映射的服务名。
    在这里插入图片描述
     我这里是mysql,所以修改重启命令为:systemctl restart mysql

  • 在从库设置主库配置时异常:

  • ERROR 3021 (HY000): This operation cannot be performed with a running slave io thread; run STOP SLAVE IO_THREAD FOR CHANNEL '' first.
    这是因为从库已经启动了主从复制,先执行STOP SLAVE;停止主从复制,再执行配置即可

  • MySQL8.0在从库设置主库配置时异常:

  • Last_IO_Error: error connecting to master 'slave@111.11.11.111:3306' - retry-time: 60 retries: 18 message: Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.
    修改主库slave账号的密码加密方式:alter user ‘slave’@‘%’ identified with mysql_native_password by ‘123456’;

二、安装Redis集群(大厂面试第4季–分布式存储案例真题)

cluster(集群)模式-docker版哈希槽分区进行亿级数据存储

2.1 面试题—【1~2亿条数据需要缓存,请问如何设计这个存储案例】

1~2亿条数据需要缓存,请问如何设计这个存储案例

单机单台100%不可能,肯定是分布式存储,用redis如何落地?

2.2 哈希取余分区

哈希取余分区: 节点映射、数据变动、容易出错

在这里插入图片描述
2亿条记录就是2亿个k,v,我们单机不行必须要分布式多机,
假设有3台机器构成一个集群,用户每次读写操作都是根据公式 : hash(key)%N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。

优点:
简单粗暴,直接有效,只需要预估好数据规划好节点,例如3台、8台、10台,就能保证一段时间的数据支撑。
使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的作用。
缺点:
原来规划好的节点,进行扩容或者缩容就比较麻烦了额,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:Hash(key)/3会变成Hash(key)/? 。此时地址经过取余运算的结果将发生很大变化,根据公式获取的服务器也会变得不可控。
某个redis机器宕机了,由于台数数量变化,会导致hash取余全部数据重新洗牌。

2.3 一致性哈希算法分区

一致性哈希算法分区
一致性Hash算法背景
一致性哈希算法在1997年由麻省理工学院中提出的,设计目标是为了解决分布式缓存数据变动和映射问题,某个机器宕机了,分母数量改变了,自然取余数不OK了。
提出一致性Hash解决方案。目的是当服务器个数发生变动时,尽量减少影响客户端到服务器的映射关系。

  • 算法构建一致性哈希环
  一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间[0.,2^32-1],这个是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(O=2^32),这样让它逻辑上形成了一个环形空间。它也是按照使用取模的方法,前面笔记介绍的节点取模法是对节点(服务器)的数量进行取模。而一致性Hash算法是对2^32取模,简单来说,`一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环`,如假设某哈希函数H的值空间为0-2^32-1(即哈希值是一个32位无符号整形),整个哈希环如下图:整个空间`按顺时针方向组织`,圆环的正上方的点代表0,O点右侧的第一个点代表1,以此类推,2、3、4、……直到2^32-1,也就是说0点左侧的第一个点代表2^32-1,0和2^32-1在零点中方向重合,我们把这个由2^32个点组成的圆环称为Hash环。
  • 服务器IP节点映射
将集群中各个IP节点映射到环上的某一个位置。
将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。
假如4个节点NodeA、B、C、D,经过IP地址的哈希函数计算(hash(ip)),使用IP地址哈希后在环空间的位置如下:

在这里插入图片描述

  • key落到服务器的落键规则
当我们需要存储一个kv键值对时,首先计算key的hash值, hash(key)将这个key使用相同的函数Hash计算出哈希值并确定此数据在
环上的位置,`从此位置沿环顺时针“行走”`,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。
如我们有Qbject A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:根据一致性Hash算法
,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上。

在这里插入图片描述

  • 一致性哈希算法的容错性
假设Node C宕机,可以看到此时对象A、B、D不会受到影响,只有C对象被重定位到Node D。
一般的,在一致性Hash算法中,如果一台服务器不可用,则`受影响的数据仅仅是此服务器到其环空间中前一台服务器
(即沿着逆时针方向行走遇到的第一台服务器)之间数据`,其它不会受到影响。
简单说,就是C挂了,受到影响的只是B、C之间的数据,并且这些数据会转移到D进行存储。

在这里插入图片描述

  • 一致性哈希算法的扩展性
数据量增加了,需要增加一台节点Node X,X的位置在A和B之间,那收到影响的也就是A到X之间的数据,重新把A到X的数据录入到X上即可,
不会导致hash取余全部数据重新洗牌。

在这里插入图片描述

  • 一致性哈希算法的数据倾斜问题
`Hash环的数据倾斜问题`
一致性Hash算法在服务`节点太少`时,容易因为节点分布不均匀而造成`数据倾斜`(被缓存的对象大部分集中缓存在某一台服务器上)问题,
例如系统中只有两台服务器:

在这里插入图片描述

为了在节点数目发生改变时尽可能少的迁移数据将所有的存储节点排列在收尾相接的Hash环上,每个key在计算Hash后会`顺时针`找到临近的存储节点存放。
而当有节点加入或退出时仅影响该节点在Hash环上`顺时针相邻的后续节点。``优点`
加入和删除节点只影响哈希环中顺时针方向的相邻的节点,对其他节点无影响。`缺点`
数据的分布和节点的位置有关,因为这些节点不是均匀的分布在哈希环上的,所以数据在进行存储时达不到均匀分布的效果。

2.4 哈希槽分区

为了解决一致性哈希算法的`数据倾斜问题`,哈希槽实质就是一个数组,数组[0,2^14-1]形成hash slot空间。
`能干什么`
解决均匀分配的问题,`在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系,`
现在就相当于节点上放的是槽,槽里放的是数据。

在这里插入图片描述

槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。
哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。
`多少个hash槽`
一个集群只能有16384个槽,编号0-16383(0-2^14-1)`这些槽会分配给集群中的所有主节点`,分配策略没有要求。可以指定哪些编号的槽分配给
哪个主节点。集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是几key就落入对
应的槽里。slot =CRC16(key)% 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.5 哈希槽计算

在这里插入图片描述

三、3主3从redis集群扩缩容配置案例架构说明

3.1 架构图

在这里插入图片描述

3.2 关闭防火墙+启动docker后台服务

systemctl start dockersystemctl start docker

3.3 redis集群配置【3主3从】

3.3.1.docker运行redis–命令参数–分步解释

命令说明
docker run \ # 创建并运行docker 容器实例
--name mr \  # 给容器起个名称
--privileged=true \  # Docker会赋予容器几乎与主机相同的权限,即获取宿主机root用户权限
--appendonly yes \ # 开启AOF持久化功能
-v /app/redis/redis.conf:/etc/redis/redis.conf \ #容器卷,宿主机地址:docker容器内部地址  redis配置文件挂载
-v /app/redis/data:/data \  #redis数据文件挂载
--net host \			# 使用宿主机的ip和端口,指定网络类型为host,即与宿主机使用同一网络,默认
-d redis:6.0.8 \  # 后台运行 redis镜像和版本号
redis-server /etc/redis/redis.conf \  #运行容器 并使用配置文件启动容器内的 redis-server
--cluster-enabled yes \ # 是否开启redis集群
--port 6386 \ # redis端口号
# -p 6380:6379 \  #宿主机端口号:容器端口	端口映射
# --requirepass 123456 设置密码 (如果你是通过docker 容器内部连接的话,就随意,可设可不设。但是如果想向外开放的话,一定要设置,不然容易中病毒)

在这里插入图片描述

3.3.2.新建6个docker容器实例

docker run -d --name redis-node-1 --net host --privileged=true -v /app/share/redis-node-1:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6381docker run -d --name redis-node-2 --net host --privileged=true -v /app/share/redis-node-2:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6382docker run -d --name redis-node-3 --net host --privileged=true -v /app/share/redis-node-3:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6383docker run -d --name redis-node-4 --net host --privileged=true -v /app/share/redis-node-4:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6384docker run -d --name redis-node-5 --net host --privileged=true -v /app/share/redis-node-5:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6385docker run -d --name redis-node-6 --net host --privileged=true -v /app/share/redis-node-6:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6386

在这里插入图片描述

3.3.3.进入容器redis-node-1并为6台机器构建集群关系

docker exec -it redis-node-1 /bin/bashifconfig

注意,进入docker容器后才能执行一下命令,且注意自己的真实IP地址

redis-cli \
--cluster create \
192.168.229.133:6381 \
192.168.229.133:6382 \
192.168.229.133:6383 \
192.168.229.133:6384 \
192.168.229.133:6385 \
192.168.229.133:6386 \
--cluster-replicas 1  
# -a 000415 验证密码
  • --cluster create: 构建集群
  • --cluster-replicas 1 :表示为每个master创建一个slave节点
  • -a 000415:表示设置的密码

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3.3.4.链接进入6381作为切入点,查看集群状态

# 查看集群信息
cluster info# 本集群有哪些节点
cluster nodes

在这里插入图片描述
在这里插入图片描述

本次实际主从节点对应情况

主服务器Master从服务器Slave
63816384
63826385
63836386

在这里插入图片描述

3.4 主从容错切换迁移案例

3.4.1.数据读写存储

# 启动1机构成的集群并通过exec进入
docker exec -it redis-node-1 /bin/bash# 对6381新增两个key
redis-cli -p 6381# 防止路由失效加参数-c并新增两个key
redis-cli -p 6381 -c# 查看集群信息
redis-cli --cluster check 192.168.229.133:6381
3.4.1.1.启动1机构成的集群并通过exec进入

在这里插入图片描述

3.4.1.2.防止路由失效加参数-c,并新增两个key

在这里插入图片描述

3.4.1.3.查看集群状态

在这里插入图片描述

3.4.2.容错切换迁移

3.4.2.1. 主6381和从机切换,先停止主机6381
docker stop redis-node-1
3.4.2.2. 再次查看集群信息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.4.2.3. 先还原之前的3主3从
# 先开启6381
docker restart redis-node-1# 在停止6384
docker stop redis-node-4# 再开启6384
docker restart redis-node-4# 主从机器分配情况以实际情况为准

在这里插入图片描述
在这里插入图片描述

3.4.2.5. 查看集群状态【redis-cli --cluster check 自己ip:6381】

redis-cli --cluster check 自己ip:6381

redis-cli --cluster check 192.168.229.133:6381

在这里插入图片描述

3.4.2.6.虚拟机自动关机,重启恢复原样

在这里插入图片描述

3.5 主从扩容案例

3.5.1.分析图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.5.2.实操步骤

新建6387、6388两个节点+新建后启动+查看是否8节点
进入6387容器实例内部--docker exec -it redis-node-7 /bin/bash
将新增的6387节点(空槽号)作为master节点加入原集群
检查集群情况第1次
重新分派槽号
检查集群情况第2次
为主节点6387分配从节点6388
检查集群情况第3次

3.5.3.新建6387、6388两个节点+新建后启动+查看是否8节点

docker run -d --name redis-node-7 --net host --privileged=true -v /app/share/redis-node-7:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6387docker run -d --name redis-node-8 --net host --privileged=true -v /app/share/redis-node-8:/data redis:6.0.8 redis-server --cluster-enabled yes --appendonly yes --port 6388

在这里插入图片描述

3.5.4.进入6387容器实例内部–docker exec -it redis-node-7 /bin/bash

docker exec -it redis-node-7 /bin/bash

3.5.5.将新增的6387节点【空槽号】作为master节点加入原集群

将新增的6387作为master节点加入集群redis-cli --cluster add-node 自己实际IP地址:6387 自己实际IP地址:6381
redis-cli --cluster add-node 192.168.229.133:6387 192.168.229.133:6381 
6387就是将要作为master新增节点
6381就是原来集群节点里面的领路人,相当于6387拜拜6381的码头从而找到组织加入集群

在这里插入图片描述

3.5.6.检查集群情况第1次

在这里插入图片描述

3.5.7.重新分派槽号–命令【redis-cli --cluster reshard IP地址:端口号】

# 重新分派槽号
redis-cli --cluster reshard 192.168.229.133:6381

在这里插入图片描述

在这里插入图片描述

3.5.8.检查集群情况第2次–【槽号分派说明】

redis-cli --cluster check 192.168.229.133:6381

槽号分派说明:为什么6387是3个新的区间,以前的还是连续?

重新分配成本太高,所以前3家各自匀出来一部分,从6381/6382/6383三个旧节点分别匀出1364个坑位给新节点6387

在这里插入图片描述

3.5.9.为主节点6387分配从节点6388

命令:
redis-cli --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主机节点ID

265b4d87bf36015ff8e491a2994753c590ee3d85 -------这个是6387的编号,按照自己实际情况redis-cli --cluster add-node 192.168.229.133:6388 192.168.229.133:6387 \
--cluster-slave --cluster-master-id 265b4d87bf36015ff8e491a2994753c590ee3d85

在这里插入图片描述

3.5.10.检查集群情况第3次

redis-cli --cluster check 192.168.229.133:6387

在这里插入图片描述

3.6 主从缩容案例

3.6.1 分析图

在这里插入图片描述

3.6.2 实操步骤

# 目的:6387和6388下线# 检查集群情况1获得6388的节点ID
redis-cli --cluster check 192.168.229.133:6382
ad001acdd52a4484c63c8c6c8d3c9f0de173cd4b# 将6388删除--从集群中将4号从节点6388删除
redis-cli --cluster del-node 192.168.229.133:6388 ad001acdd52a4484c63c8c6c8d3c9f0de173cd4b
redis-cli --cluster check 192.168.229.133:6382# 将6387的槽号清空,重新分配,本例将清出来的槽号都给6381
redis-cli --cluster reshard 192.168.229.133:6381
4096
1ce24a697196e2f225204fa664d65d7800b07d06 ---6381槽号ID
265b4d87bf36015ff8e491a2994753c590ee3d85 ---6387槽号ID
done# 检查集群情况第二次
redis-cli --cluster check 192.168.229.133:6381
4096个槽位都给了6381,它变成了8192个槽位,相当于全部都给6381;不然要输入3次,一锅端# 将6387删除
redis-cli --cluster del-node ip:从机端口 6387节点ID
redis-cli --cluster del-node 192.168.229.133:6387 265b4d87bf36015ff8e491a2994753c590ee3d85# 检查集群情况第三次
redis-cli --cluster check 192.168.229.133:6381

3.6.3 检查集群情况1获得6388的节点ID

redis-cli --cluster check 192.168.229.133:6382ad001acdd52a4484c63c8c6c8d3c9f0de173cd4b

在这里插入图片描述

3.6.4 将6388删除–从集群中将4号从节点6388删除

命令:redis-cli --cluster del-node ip:从机端口 从机6388节点ID

redis-cli --cluster del-node 192.168.229.133:6388 ad001acdd52a4484c63c8c6c8d3c9f0de173cd4bredis-cli --cluster check 192.168.229.133:6382

在这里插入图片描述
在这里插入图片描述

3.6.5 将6387的槽号清空,重新分配,本例将清出来的槽号都给6381

redis-cli --cluster reshard 192.168.229.133:63814096
1ce24a697196e2f225204fa664d65d7800b07d06 ---6381槽号ID
265b4d87bf36015ff8e491a2994753c590ee3d85 ---6387槽号ID
done

在这里插入图片描述
在这里插入图片描述

3.6.6 检查集群情况第二次

redis-cli --cluster check 192.168.229.133:63814096个槽位都给了6381,它变成了8192个槽位,相当于全部都给6381;不然要输入3次,一锅端

在这里插入图片描述

3.6.7 将6387删除

命令:redis-cli --cluster del-node ip:从机端口 6387节点ID

redis-cli --cluster del-node ip:从机端口 6387节点IDredis-cli --cluster del-node 192.168.229.133:6387 265b4d87bf36015ff8e491a2994753c590ee3d85

在这里插入图片描述

3.6.8 检查集群情况第三次

在这里插入图片描述

3.7 总结

四、DockerFile解析

4.1 DockerFile介绍

dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。

官网:https://docs.docker.com/engine/reference/builder/

构建三步骤:

编写Dockerfile文件
docker build 命令构建镜像
docker run 依镜像运行容器实例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

FROM scratch
ADD centos-7-x86_64-docker.tar.xz /LABEL \org.label-schema.schema-version="1.0" \org.label-schema.name="CentOS Base Image" \org.label-schema.vendor="CentOS" \org.label-schema.license="GPLv2" \org.label-schema.build-date="20201113" \org.opencontainers.image.title="CentOS Base Image" \org.opencontainers.image.vendor="CentOS" \org.opencontainers.image.licenses="GPL-2.0-only" \org.opencontainers.image.created="2020-11-13 00:00:00+00:00"CMD ["/bin/bash"]

4.2 DockerFile构建过程解析

4.2.1.镜像结构

镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。

我们以MySQL为例,来看看镜像的组成结构:
请添加图片描述
简单来说,镜像就是在系统函数库、运行环境基础上,添加应用程序文件、配置文件、依赖文件等组合,然后编写好启动脚本打包在一起形成的文件。

4.2.2.DockerFile基础知识

1:每条保留字指令都必须为大写字母且后面要跟随至少一个参数
2:指令按照从上到下,顺序执行
3: #表示注释
4:每条指令都会创建一个新的镜像层并对镜像进行提交

4.2.3.docker执行DockerFile的大致流程

(1) docker从基础镜像运行一个容器
(2)执行一条指令并对容器作出修改
(3)执行类似docker commit的操作提交一个新的镜像层
(4) docker再基于刚提交的镜像运行一个新容器
(5)执行dockerfile中的下一条指令直到所有指令都执行完成

4.2.4.小总结

从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段:

  • Dockerfile是软件的原材料
  • Docker镜像是软件的交付品
  • Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例
    Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系
    的基石。
    在这里插入图片描述
  • 1 Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。
    Dockerile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;

  • 2 Docker镜像,在用Dockerfle定义一个文件之后,docker buid时会产生一个Docker镜像,当运行Docker镜像时会真正开始提供服务;

  • 3 Docker容器,容器是直接提供服务的。

4.3 DockerFile常用保留字指令

参考tomcat8的dockerfile入门:https://github.com/docker-library/tomcat

Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。

在这里插入图片描述

4.3.1 FROM:定义基于哪个镜像构建新的镜像。

Dockerfile的第一条必须是FROM。指定一个已经存在的基础镜像作为模板,表示新镜像是基于哪个镜像构建的

例如:FROM centos

在这里插入图片描述

4.3.2 MAINTAINER:为镜像添加作者信息:姓名和邮箱地址

指定新镜像的创建者名字和邮箱

例如:MAINTAINER bulut<bulut123@163.com>

4.3.3 RUN:在容器构建时需要运行的命令。

新镜像构建时在容器上运行的指令。RUN在docker build时运行。

shell格式:RUN <命令行命令>
RUN <命令行命令> 等同于,在终端操作的shell命令。
例如:

RUN yum -y install vim

exec格式:RUN [“可执行文件”, “参数1”, “参数2”]
例如:

RUN ["./test.php", "param1", "param2"],等同于RUN ./test.php param1 param2RUN ["./test.php", "dev", "offline"],等同于RUN ./test.php dev offline

4.3.4 EXPOSE:当前容器对外暴露的端口

EXPOSE 8080

4.3.5 WORKDIR:指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点

当容器运行时,通过docker exec -it 容器ID bash进入容器的工作目录
在这里插入图片描述

4.3.6 USER: 指定该镜像以什么样的用户去执行,如果都不指定默认是root

4.3.7 ENV:用来在构建镜像过程中设置环境变量

ENV MY_PATH /app/test
这个环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样;
也可以在其它指令中直接使用这些环境变量,比如 : WORKDIR $MY_PATH

例如定义了ENV MY_WORKDIR /app,则可以这样定义WORKDIR:WORKDIR $MY_WORKDIR

例如定义了ENV JAVA_HOME /app/java,则通过新镜像启动的容器中,可以执行命令echo $JAVA_HOME

ENV CATALINA_HOME /usr/local/tomcat
ENV PATH $CATALINA_HOME/bin:$PATH
RUN mkdir -p "$CATALINA_HOME"
WORKDIR $CATALINA_HOME

在这里插入图片描述

4.3.8 ADD:将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包

ADD centos-6-docker.tar.xz / 

4.3.9 COPY:类似ADD,拷贝文件和目录到镜像中

类似ADD,拷贝文件和目录到镜像中。

将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置

COPY src dest
COPY ["src" "dest"]<src源路径>:源文件或者源目录
<dest目标路径>:容器内的指定路径,该路径不用事先建好,会自动创建

4.3.10 VOLUME:容器数据卷,用于数据保存和持久化工作

定义容器卷,和参数-v一样

# 将mycat的配置文件的地址暴露出映射地址,启动时直接映射宿主机的文件夹VOLUME /usr/local/mycat

4.3.11 CMD:设置容器默认要运行的命令及参数。

指定通过新镜像启动容器时,需要执行的指令。CMD在docker run时运行。

CMD指令的格式和RUN相似,语法有两种:

  • shell格式 : CMD <命令>
  • exec格式 : CMD [“可执行文件”, “参数1”, “参数2”, …]
  • 参数列表格式:CMD [ “参数1”, “参数2”, …]。在指定了ENTRYPOINT指令后,用CMD指定具体的参数。

  • Dockerfile可以有多个CMD指令,但是只有最后一个有效。CMD会被docker run之后的参数替换。
  • 如果docker run后面指定了容器运行的命令,则CMD指令无效,CMD会被docker run之后的参数替换。
EXPOSE 8080
CMD ["catalina.sh", "run"]

在这里插入图片描述

  • CMD 在docker run时运行。
  • RUN 在docker build时运行。

4.3.12 ENTRYPOINT:

指定通过新镜像启动容器时,需要执行的指令。ENTRYPOINT在docker run时运行

ENTRYPOINT也是用来指定一个容器启动时要运行的命令
类似于CMD指令,但是ENTRYPOINT不会被docker run后面的命令覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序

命令格式:
ENTRYPOINT ["<excuteable>","<param1>","<param2>",...]

ENTRYPOINT可以和CMD一起用,一般是变参才会使用CMD,这里的CMD等于是在给ENTRYPOINT传参。

当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行其命令而是将CMD的内容作为参数传递给ENTRYPOINT指
令,他两个组合会变成 <ENTRYPOINT> "<CMD>"

例如:通过如下Dockerfile构建nginx:test镜像

FROM nginxENTRYPOINT ["nginx", "-c"]  # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参

效果如下

是否传参按照dockerfile编写执行docker run传参运行
Docker run命令docker run -nginx:testdocker run nginx:test - c /etc/nginx/new.conf
衍生出来的实际命令nginx -c /etc/nginx/nginx.confnginx -c /etc/nginx/new.conf

4.3.13 CMD与ENTRYPOINT

  • CMD
# dockerfile01
FROM centos
CMD ["ls", "-a"]docker bulid -f dockerfile01 -t cmd:1.0 .
docker run 镜像id
# 会执行ls -a命令
# 如果执行docker run cmd:1.0 -l, 本来是想执行ls -al,但是 CMD命令会被-l所替代,由于没有-l命令,所以会报错
  • ENTRYPOINT
# dockerfile02
FROM centos
ENTRYPOINT ["ls", "-a"]docker bulid -f dockerfile01 -t cmd:2.0 .
docker run 镜像id
# 会执行ls -a命令
# 如果执行docker run cmd:2.0 -l, 执行正常,命令为ls -al

4.3.14 小总结

BUILDBothRUN
FROMWORKDIRCMD
MAINTAINERUSERENV
COPYEXPOSE
ADDVOLUME
RUNENTRYPOINT
ONBUILD
.dockerignore
FROM: 表示基础镜像,就是我们的镜像从这里开始构建m
MAINTAINER: 表示维护者信息,就是这个镜像是谁创建的,一般需要写姓名和邮箱
RUN:表示镜像构建时需要运行的命令
ADD: 表示为了构建镜像需要添加的压缩等文件,例如要构建tomcat镜像,那么肯定要将tomcat压缩包放进去
WORKDIR: 表示镜像的工作目录
VOLUME: 表示挂载数据卷的目录位置,如果没有设置,就需要在运行run命令时通过-v进行设置
EXPOSE: 表示要暴露的端口,如果没有设置,就需要在运行run命令时通过-p进行设置
CMD:指定容器启动时需要运行的命令,只有最后一个会生效,可以被替代
ENTRYPOINT: 指定容器启动时需要运行的命令,可以追加命令
ONBUILD:当创建一个被继承(--volumes-from)的dockerfile时,就会运行这个指令,属于触发指令
COPY:类似ADD,将文件拷贝到镜像中
ENV:构建的时候设置环境变量

4.4 案例

4.4.1 自定义镜像mycentosjava8

4.4.1.1 要求:Centos7镜像具备vim+ifconfig+jdk8
docker search centos# 建议下载8版本以下的
# 从官方仓库拉取最新的 CentOS 7 镜像作为基础镜像
docker pull centos:7docker images centos:7docker run -it eeb6ee3f44bd /bin/bash

在这里插入图片描述

4.4.1.2 下载JDK8

下载地址:https://www.oracle.com/java/technologies/downloads/#java8

在这里插入图片描述

4.4.1.3 编写Dockerfile文件

在这里插入图片描述

Dockerfile

FROM centos:7
MAINTAINER dy<dy@163.com>ENV MYPATH /usr/local
WORKDIR $MYPATHRUN yum install -y yum-utils
# 安装vim编辑器
RUN yum -y install vim
# 安装ifconfig命令查看网络IP
RUN yum -y install net-tools
# 安装java8 及 lib 库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
# ADD 是相对路径jar,把jdk-8u391-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u391-linux-x64.tar.gz /usr/local/java# 配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_391
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.tar:$JAVA_HOME/lib/tools.jar$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin/:$PATHEXPOSE 80CMD echo $PATH
CMD echo "success--------------------ok"
CMD /bin/bash

参数说明:

  • FROM centos:7:指定基础镜像为 CentOS,版本号为7
  • MAINTAINER:指定镜像的维护者和联系方式。
  • ENV MYPATH /usr/local:定义一个环境变量 MYPATH,并设置其值为 /usr/local。
  • WORKDIR $MYPATH:将工作目录切换到 $MYPATH,即 /usr/local。
  • RUN yum -y install vim:在容器内运行命令,安装 Vim 编辑器。
  • RUN yum -y install net-tools:在容器内运行命令,安装 net-tools,包括常用的网络工具。
  • EXPOSE 80:声明容器将监听的端口号为 80,但这并不会自动映射到主机端口。
  • CMD echo $MYPATH:在容器启动时运行的命令,输出环境变量 $MYPATH 的值。
  • CMD echo “success--------------------ok”:在容器启动时运行的命令,输出固定的字符串 “success--------------------ok”。
  • CMD /bin/bash:在容器启动时运行的命令,启动一个交互式的 Bash 终端。
4.4.1.4 构建
# -t:是target的缩写
docker build -t 新镜像名字:TAG .docker build -t mycentosjava8:1.1 .
# 注意:TAG后面有个空格,有个点
# 最后的小数点:就表明是当前目录。docker build -f /app/myfile/Dockerfile  -t mycentosjava8:1.1 .
-f:后面跟随的是Dockerfile 文件
-t :后面跟随的镜像名和版本号。

在这里插入图片描述
在这里插入图片描述

4.4.1.5 运行
run -it 39ad01e4fa5e /bin/bashrun -it 39ad01e4fa5e /bin/bash

在这里插入图片描述

4.4.1.6. 构建报错【centos8及8版本以上缺乏中文语言包和地址vault.centos.org需更改】
4.4.1.6.1.Error: Failed to download metadata for repo ‘appstream‘: Cannot prepare internal mirrorlist

在学习使用 docker 技术过程中,基于 centos 镜像自定义新的镜像,其中基础镜像 centos 需要提前安装好 vim net-tools,然而在刚开始通过yum -y install vim安装 vim 时,便出现了错误提示信息:

Error: Failed to download metadata for repo ‘appstream‘: Cannot prepare internal mirrorlist
[root@192 myfile]# docker build -t centosjava8:1.1 .
[+] Building 60.9s (7/11)                                                                                                                            docker:default=> [internal] load build definition from Dockerfile                                                                                                           0.0s=> => transferring dockerfile: 836B                                                                                                                           0.0s=> [internal] load .dockerignore                                                                                                                              0.0s=> => transferring context: 2B                                                                                                                                0.0s=> [internal] load metadata for docker.io/library/centos:latest                                                                                               0.0s=> [1/7] FROM docker.io/library/centos                                                                                                                        0.0s=> [internal] load build context                                                                                                                              0.0s=> => transferring context: 107B                                                                                                                              0.0s=> CACHED [2/7] WORKDIR /usr/local                                                                                                                            0.0s=> ERROR [3/7] RUN yum -y install vim                                                                                                                        60.9s
------                                                                                                                                                              > [3/7] RUN yum -y install vim:
60.78 CentOS Linux 8 - AppStream                      0.0  B/s |   0  B     01:00    
60.78 Errors during downloading metadata for repository 'appstream':
60.78   - Curl error (6): Couldn't resolve host name for http://mirrorlist.centos.org/?release=8&arch=x86_64&repo=AppStream&infra=container [Could not resolve host: mirrorlist.centos.org]
60.79 Error: Failed to download metadata for repo 'appstream': Cannot prepare internal mirrorlist: Curl error (6): Couldn't resolve host name for http://mirrorlist.centos.org/?release=8&arch=x86_64&repo=AppStream&infra=container [Could not resolve host: mirrorlist.centos.org]
------
Dockerfile:8
--------------------6 |     7 |     # 安装vim编辑器8 | >>> RUN yum -y install vim9 |     # 安装ifconfig命令查看网络IP10 |     RUN yum -y install net-tools
--------------------
ERROR: failed to solve: process "/bin/sh -c yum -y install vim" did not complete successfully: exit code: 1

1️⃣上面的报错信息意思是,从仓库 ‘appstream’ 下载元数据失败:由于镜像列表中没有 URL,不能准备内部镜像列表。

2️⃣ 问题分析

3️⃣ 第一种可能的情况便是网络连接问题。检查是否可以连接外部网络,可以使用 ping baidu.com 查看是否有丢包情况。如果丢包,则进一步检查网络连接是否正常;如果没有丢包,继续阅读下文
4️⃣那么第二种情况,便是 CentOS 已经停止维护的问题。2020 年 12 月 8 号,CentOS 官方宣布了停止维护 CentOS Linux 的计划,并推出了 CentOS Stream 项目,CentOS Linux 8 作为 RHEL 8 的复刻版本,生命周期缩短,于 2021 年 12 月 31 日停止更新并停止维护(EOL),更多的信息可以查看 CentOS 官方公告。如果需要更新 CentOS,需要将镜像从 mirror.centos.org 更改为 vault.centos.org

在这里插入图片描述

5️⃣报错信息
在这里插入图片描述
那么针对上面提到的第二种情况,给出的解决方法如下:

首先,进入到 yum 的 repos 目录

cd /etc/yum.repos.d/

其次,修改 centos 镜像内容,需要将镜像从 mirror.centos.org 更改为 vault.centos.org

sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*

https://vault.centos.org/centos/8/AppStream/x86_64/os/repodata/
然后,生成缓存更新(第一次更新,速度稍微有点慢,耐心等待 2min 左右)

yum makecache

在这里插入图片描述

4.4.1.6.2.CentOS 8 中执行命令,出现报错:Failed to set locale, defaulting to C.UTF-8报错原因:
  • 1、没有安装相应的语言包。
  • 2、没有设置正确的语言环境。
RUN yum install glibc-langpack-zh

解决方法1:安装语言包

  • 设置语言环境需使用命令 locale
  • locale -a 命令,查看目前系统已安装的语言包
  • 安装中文语言包,命令yum install glibc-langpack-zh
  • 安装英文语言包,命令 dnf install glibc-langpack-endnf install langpacks-en glibc-all-langpacks -y

解决方法2:设置正确的语言环境

echo "export LC_ALL=en_US.UTF-8" >> /etc/profile
source /etc/profile

或使用命令locale -gen en_US.UTF-8
在这里插入图片描述

yum -y install vim
4.4.1.6.3.总结编写Dockerfile,centos8添加以下内容
FROM centos:latest# 更改镜像,需要将镜像从 mirror.centos.org 更改为 vault.centos.org
RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
# 安装中文语言包
RUN yum -y install glibc-langpack-zh

在这里插入图片描述

centos8--Dockerfile完整版

FROM centos:latest
MAINTAINER dy<dy@163.com>ENV MYPATH /usr/local
WORKDIR $MYPATH# 更改镜像,需要将镜像从 mirror.centos.org 更改为 vault.centos.org
RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
# 安装中文语言包
RUN yum -y install glibc-langpack-zh
RUN yum install -y yum-utils# 安装vim编辑器
RUN yum -y install vim
# 安装ifconfig命令查看网络IP
RUN yum -y install net-tools
# 安装java8 及 lib 库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
# ADD 是相对路径jar,把jdk-8u391-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u391-linux-x64.tar.gz /usr/local/java# 配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_391
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.tar:$JAVA_HOME/lib/tools.jar$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin/:$PATHEXPOSE 80CMD echo $PATH
CMD echo "success--------------------ok"
CMD /bin/bash
4.4.1.6.4.再次测试centos8以上版本镜像

Dockerfile

FROM centos:latest
MAINTAINER dy<dy@163.com>ENV MYPATH /usr/local
WORKDIR $MYPATH# 更改镜像,需要将镜像从 mirror.centos.org 更改为 vault.centos.org
RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
# 安装中文语言包
RUN yum -y install glibc-langpack-zhRUN yum install -y yum-utils
# 安装vim编辑器
RUN yum -y install vim
# 安装ifconfig命令查看网络IP
RUN yum -y install net-tools
# 安装java8 及 lib 库
RUN yum -y install glibc.i686EXPOSE 80CMD echo "success--------------------ok"
CMD /bin/bash
docker pull centos:latestmkdir -p /app/myfilecd /app/myfilevi Dockerfiledocker build -t centos8:1.1 .

在这里插入图片描述
在这里插入图片描述

4.4.2 虚悬镜像:仓库名、标签都是的镜像,俗称dangling image

Dockerfile

# 测试用的
from ubuntu
CMD echo 'action is success'
docker build
# 查看虚悬镜像
docker image ls -f dangling=true
# 删除虚悬镜像
docker image prune

在这里插入图片描述
在这里插入图片描述

4.4.3 自定义镜像myubuntu

4.4.3.1 编写Dockerfile

Dockerfile

FROM ubuntu
MAINTAINER dy<dy@163.com>ENV MYPATH /usr/local
WORKDIR $MYPATHRUN apt-get update
RUN apt-get install net-tools
RUN apt-get install -y iproute2
RUN apt-get install -y inetutils-pingEXPOSE 80CMD echo $MYPATH
CMD echo "install inconfig cmd into ubuntu success--------ok"
CMD /bin/bash
4.4.3.2 构建新镜像
docker build -t 新镜像名字:TAG .

在这里插入图片描述

4.4.3.3 运行新镜像
docker run -it 新镜像名字:TAG

在这里插入图片描述

4.4.4.案例-自定义镜像-Dockerfile

在这里插入图片描述
Dockerfile

# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录
ENV JAVA_DIR=/usr/local# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar# 安装JDK
RUN cd $JAVA_DIR \&& tar -xf ./jdk8.tar.gz \&& mv ./jdk1.8.0_144 ./java8# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.5 小总结

在这里插入图片描述

五、Docker微服务实战

5.1.在IDEA新建一个普通微服务模块

5.1.1.建Moudle–spring-boot-docker-demo

5.1.2.改POM

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.6</version><relativePath/></parent><groupId>com.itcast</groupId><artifactId>spring-boot-docker-demo</artifactId><version>0.0.1-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.complier.source>1.8</maven.complier.source><maven.complier.target>1.8</maven.complier.target><juint.version>4.12</juint.version><log4j.version>1.2.17</log4j.version><lombok.version>1.18.16</lombok.version><mysql.version>5.1.47</mysql.version><druid.version>1.1.16</druid.version><mapper.version>4.1.5</mapper.version><mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version><java.version>1.8</java.version></properties><dependencies><!--SpringBoot 通用依赖模版--><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><!--test--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>3.1.0</version></plugin></plugins></build></project>

5.1.3.写yml

server.port=6001

5.1.4.主启动

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

5.1.5.业务类

import java.util.UUID;@RestController
@RequestMapping("/order")
public class OrderController {@Value("${server.port}")private String port;@RequestMapping("/docker")public String helloDocker() {return "hell docker" + "\t" + port + "\t" + UUID.randomUUID().toString().replace("-", "");}@RequestMapping(value = "/index", method = RequestMethod.GET)public String index() {return "服务端口号:" + "\t" + port + "\t" + UUID.randomUUID().toString().replace("-", "");}
}

5.1.6.测试

在这里插入图片描述

5.1.7.打包成一个jar包并上传

在这里插入图片描述
在这里插入图片描述

5.2.通过Dockerfile发布到微服务部署到docker容器

5.2.1.IDEA工具将微服务打包成jar包并上传

5.2.2.编写Dockerfile

5.2.2.1.Dockerfile内容
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER DY
# volume 指定临时文件为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为 dy_docker.jar
ADD spring-boot-docker-demo-0.0.1-SNAPSHOT.jar dy_docker.jar
# 运行jar包
RUN bash -c 'touch /dy_docker.jar'
ENTRYPOINT ["java","-jar","/dy_docker.jar"]
# 暴露6001端口作为微服务
EXPOSE 6001
5.2.2.2.将微服务jar包和Dockerfile文件上传到同一个目录下/app/mydocker

5.2.3.构建镜像

docker build -t dy_docker.jar:1.1 .

在这里插入图片描述
在这里插入图片描述

5.2.4.运行容器

docker run -d -p 6001:6001 dy_docker.jar:1.1

在这里插入图片描述

在这里插入图片描述

5.2.5.访问测试

curl 192.168.229.134:6001/order/dockercurl 192.168.229.134:6001/order/indexcurl http://192.168.229.134:6001/order/dockercurl http://192.168.229.134:6001/order/index

在这里插入图片描述

在这里插入图片描述

六、Docker网络

Docker网络作用:
容器间的互联和通信以及端口映射
容器lP变动时候可以通过服务名直接网络通信而不受到影响

6.1.分析图

在这里插入图片描述

6.2.Docker 不启动,默认的网络情况

在这里插入图片描述

  • virbr0
    在CentDS7的安装过程中如果有选择相关虚拟化的的服务安装系统后,启动网卡时会发现有一个以网桥连接的私网地址的virbr0网卡
    (virbr0网卡:它还有一个固定的默认IP地址192.168.122.1),是做虚拟机网桥的使用的,其作用是为连接其上的虚机网卡提供NAT访问外网的功能。
    我们之前学习Linux安装,勾选安装系统的时候附带了libvirt服务才会生成的一个东西,如果不需要可以直接将libvirtd服务卸载。
yum remove libvirt-libs.x86_64

在这里插入图片描述

6.3.Docker 启动,网络情况

Docker 启动,会产生一个名为docker0的虚拟网桥
在这里插入图片描述

6.4.查看Docker 的网络命令模式

6.4.1.docker网络命令大全

# 查看Docker网络
docker network ls# 查看网络数据源
docker network inspect XXXX网络名字# 删除网络
docker network rm XXXX网络名字docker network --helpdocker network COMMAND
Commands:connect     Connect a container to a networkcreate      Create a networkdisconnect  Disconnect a container from a networkinspect     Display detailed information on one or more networksls          List networksprune       Remove all unused networksrm          Remove one or more networks

6.4.2.查看Docker网络

# 查看Docker网络
docker network ls

在这里插入图片描述

6.4.3.查看网络数据源

# 查看网络数据源
docker network inspect XXXX网络名字

6.4.4.创建网络

# 创建网络
docker network create XXXX网络名字

在这里插入图片描述

6.4.5.删除网络

# 删除网络
docker network rm XXXX网络名字

在这里插入图片描述

6.5.Docker网络作用

Docker网络作用:
容器间的互联和通信以及端口映射
容器lP变动时候可以通过服务名直接网络通信而不受到影响

6.6.Docker网络模式

6.6.1.总体介绍

网络模式简介
bridge为每一个容器分配、设置IP等,并将容器连接到一个docker0
虚拟网桥,默认为该模式。
host容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
none容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配 veth pair 和网桥连接,IP等。
container新创建的容器不会创建自己的网卡和配置自己的lP而是和—个指定的容器共享IP、端口范围等。

命令:

bridge模式:使用--network bridge指定,默认使用dockerOhost模式:使用--network host指定none模式:使用--network none指定container模式:使用--network container:NAME或者容器ID指定

6.6.2.网络实例内默认网络IP生产规则

docker inspect 容器ID or 容器名字

# 查看数据源倒数20行
docker inspect u1 | tail -n 20
6.6.2.1.先启动两个ubuntu实例

在这里插入图片描述

6.6.2.2.:docker inspect 容器ID or 容器名字

在这里插入图片描述

6.6.2.3.关闭u2实例,新建u3,查看ip变化

在这里插入图片描述

6.6.2.4.结论:docker容器内部的ip是有可能会发生改变的

6.6.3.案例说明【bridge】

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.6.3.1.bridge是什么

Docker服务默认会创建一个docker0网桥(其上有一个docker0内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker默认指定了docker0接口的IP地址和子网掩码,让主机和容器之间可以通过网桥相互通信。

# 查看bridge 网络的详细信息,并通过grep获取名额
docker network inspect bridge | grep name ifconfig | grep docker

在这里插入图片描述

6.6.3.2.说明
  • 1 Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容
    器一个IP地址,称为Container-lP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-lP直接通信。

  • 2 docker run的时候,没有指定network的话默认使用的网桥模式就是bridge,使的就是docker0。在宿主机ifconfig,就可以看到doc
    ker0和自己create的network(后面讲)eth0,eth1,eth……代表网卡一,网卡二,网卡三…… lo代表127.0.0.1,即localhost, inet addr用来表示网卡的IP地址

  • 3 网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。
    3.1 整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);
    3.2 每个容器实例内部也有一块网卡,每个接口叫eth0;
    3.3 docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一 一匹配。
    3.4 通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。
    在这里插入图片描述

6.6.3.3.代码
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8

在这里插入图片描述

6.6.3.4.两两匹配验证

在这里插入图片描述

6.6.4.案例说明【host】

6.6.4.1.host是什么

直接使用宿主机的IP地址与外界进行通信,不再需要额外进行NAT转换。

6.6.4.2.说明

容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。
在这里插入图片描述

6.6.4.3.代码

警告:

docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8

在这里插入图片描述

  • 问题: docke启动时总是遇见标题中的警告
  • 原因: docker启动时指定–network=host或-net=host,如果还指定了-p映射端口,那这个时候就会有此警告,并且通过-p设置的参数将不会起到任何作用,端口号会以主机端口号为主 ,重复时则递增。
  • 解决: 解决的办法就是使用docker的其他网络模式,例如–network=bridge,这样就可以解决问题,或者直接无视。

正确代码:

docker run -d --network host --name tomcat84 billygoo/tomcat8-jdk8

在这里插入图片描述

在这里插入图片描述

6.6.4.4.没有设置-p端口映射,如何启动访问tomcat84

http://宿主机IP:8080/

在CentOS里面用默认的火狐浏览器访问容器内的tomcat84看到访问成功,因为此时容器的IP借用主机的,所以容器共享宿主机网络IP,这样的好处是外部主机与容器可以直接通信。

在这里插入图片描述

6.6.5.案例说明【none】

none是禁用网络功能,只有lo标识(就是127.0.0.1表示本地回环)
在none模式下,并不为Docker容器进行任何网络配置。
也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo。
需要我们自己为Docker容器添加网卡、配置IP等。

docker run -d -p 8085:8080 --network none --name tomcat85 billygoo/tomcat8-jdk8

在这里插入图片描述

在这里插入图片描述

6.6.6.案例说明【container】

container网络模式
新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。
新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。
在这里插入图片描述

6.6.6.1.Alpine操作系统是一个面向安全的轻型Linux发行版

Apine Linux是一款独立的、非商业的通用Linux发行版,专为追求安全性、简单性和资源效率的用户而设计。
可能很多人没听说过这个Linux发行版本,但是经常用Docker的朋友可能都用过,
因为他小,简单,安全而著称,所以作为基础镜像是非常好的一个选择,可谓是麻雀虽小但五脏俱全,镜像非常小巧,不到6M的大小,所以特别适合容器打包。

6.6.6.2.命令演示
docker pull alpinedocker run -it  --name alpine1 alpine /bin/shdocker run -it --network container:alpine1 --name alpine2 alpine /bin/sh

在这里插入图片描述

6.6.6.3.运行结果,验证共用搭桥

在这里插入图片描述
在这里插入图片描述

6.6.6.4.加入关闭alpine1,查看alpine2

在这里插入图片描述
在这里插入图片描述

6.6.7.案例说明【自定义网络】

6.6.7.1.启动两个tomcat并进入容器内部
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8#上述成功启动并运行docker exec 进入各自容器实例内部
docker exec -it tomcat81 bashdocker exec -it tomcat82 bash

在这里插入图片描述

6.6.7.2.按照ip地址ping【ok】

在这里插入图片描述

6.6.7.3.按照服务名ping【error】

在这里插入图片描述

6.6.7.4.自定义桥接网络,自定义网络默认使用的是桥接网络bridge
6.6.7.5.新建自定义网络
docker network lsdocker network create p_network

在这里插入图片描述

6.6.7.6.新建容器加入上一步新建的自定义网络
docker run -d -p 8081:8080 --network p_network --name tomcat81 billygoo/tomcat8-jdk8docker run -d -p 8082:8080 --network p_network --name tomcat82 billygoo/tomcat8-jdk8#上述成功启动并运行docker exec 进入各自容器实例内部
docker exec -it tomcat81 bashdocker exec -it tomcat82 bash

在这里插入图片描述

6.6.7.7.互相ping测试

在这里插入图片描述

在这里插入图片描述

6.6.7.8.问题结论:自定义网络本身就维护好了主机名和ip的对应关系( ip和域名都能通)

在这里插入图片描述

七、Docker-compose容器编排

7.1.Docker-compose由来

Compose是Docker公司推出的一个工具软件,可以管理多个Docker容器组成一个应用。
你需要定义一个YAML格式的配置文件docker-compose.yml写好多个容器之间的调用关系
然后,只要一个命令,就能同时启动/关闭这些容器。

Docker-Compose是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。
在这里插入图片描述

docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来但是这样我们又面临了一个问题?

如果我需要同时部署好多个服务,难道要每个服务单独写Dockerfle然后在构建镜像,构建容器,这样累都累死了,所以docker官方给我们提供了docker-compose多服务部署的工具

例如:要实现一个Web微服务项目,除了Web服务容器本身,往往还需要再加上后端的数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包括负载均衡容器等等…

Compose允许用户通过一个单独的docker-compose.yml模板文件(YAML格式)来定义一组相关联的应用容器为一个项目(project可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。
Docker-Compose解决了容器与容器之间如何管理编排的问题。

7.2.下载、安装及卸载

7.2.1.下载

Docker Compose和Docker版本对应关系官网:https://docs.docker.com/compose/compose-file/compose-file-v3/

官网下载:https://docs.docker.com/compose/install/

Install Compose standalone: https://docs.docker.com/compose/install/standalone/

Docker Compose下载地址:https://github.com/docker/compose/releases

下载地址:https://docs.docker.com/compose/install/linux/#install-the-plugin-manually

在这里插入图片描述

7.2.2.安装步骤

命令下载或者手动下载:

curl -SL https://github.com/docker/compose/releases/download/v2.23.3/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose# 赋予执行权限,读写权限
chmod +x /usr/local/bin/docker-compose# 版本信息
docker-compose --version# 建立软连接:
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.2.3.卸载步骤

# 如果您使用curl以下方式安装,则卸载 Docker Compose:
sudo rm /usr/local/bin/docker-compose

7.3.Compose核心概念

文件

docker-compose.yml

服务 (service)

一个个应用容器实例,比如订单微服务、库存微服务、mysql容器、nginx容器或者redis容器

工程 (project)

由一组关联的应用容器组成的一个完整业务单元,在docker-compose.yml文件中定义。

7.4.Compose使用的三个步骤

  • 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件使用docker-compose.yml

  • 定义一个完整业务单元,安排好整体应用中的各个容器服务。

  • 最后,执行docker-compose up命令来启动并运行整个应用程序,完成一键部署上线

7.5.docker-compose.yml文件及常用命令

7.5.1.docker-compose.yml文件解读

内容如下:

version: "3.2"services:nacos:image: nacos/nacos-serverenvironment:MODE: standaloneports:- "8848:8848"mysql:image: mysql:5.7.25 environment:MYSQL_ROOT_PASSWORD: 123456volumes:- "$PWD/mysql/data:/var/lib/mysql"- "$PWD/mysql/conf:/etc/mysql/conf.d/"userservice:build: ./user-serviceorderservice:build: ./order-servicegateway:build: ./gatewayports:- "10010:10010"

可以看到,其中包含5个service服务:

  • nacos:作为注册中心和配置中心
    • image: nacos/nacos-server: 基于nacos/nacos-server镜像构建
    • environment:环境变量
      • MODE: standalone:单点模式启动
    • ports:端口映射,这里暴露了8848端口
  • mysql:数据库
    • image: mysql:5.7.25:镜像版本是mysql:5.7.25
    • environment:环境变量
      • MYSQL_ROOT_PASSWORD: 123:设置数据库root账户的密码为123
    • volumes:数据卷挂载,这里挂载了mysql的data、conf目录,其中有我提前准备好的数据
  • userserviceorderservicegateway:都是基于Dockerfile临时构建的

7.5.2.docker-compose.yml的编写规则介绍

以mysql服务为例,说明下常用字段的含义:

version: '3'#第一层:compose的版本号
services:   #第二层:服务配置信息mysql1:   #服务名image: mysql  #该服务所基于的镜像名environment:  #该服务的环境变量MYSQL_ROOT_PASSWORD: "ut.123456"ports:        #该服务的暴露端口- "3306:3306"container_name: "mysql1" #容器名networks: #该服务所加入的网络段- devvolumes:  #挂载数据卷- /platform/mysql/conf:/etc/my.cnf.d/my.cnf- /platform/mysql/data:/var/lib/mysql:rw"
networks:     #第三层:网络环境dev:driver: bridge

7.5.3.Compose命令格式

docker compose 命令的基本格式为:
docker-compose [-f …] [options] [COMMAND] [ARGS…]

7.5.4.Compose常用命令

命令作用
docker-compose -h#查看帮助
docker-compose up#启动所有docker-compose服务
docker-compose up -d#启动所有docker-compose服务并后台运行
docker-compose down#停止并删除容器、网络、卷、镜像。
docker-compose exec yml里面的服务id#进入容器实例内部 docker-compose exec docker-compose.yml文件中写的服务id /bin/bash
docker-compose ps#展示当前docker-compose编排过的运行的所有容器
docker-compose top#展示当前docker-compose编排过的容器进程
docker-compose logs yml里面的服务id#查看容器输出日志
dokcer-compose config#检查配置
dokcer-compose config -q#检查配置,有问题才有输出
docker-compose restart#重启服务
docker-compose start#启动服务
docker-compose stop#停止服务

7.5.5.以nginx容器操作为例

docker-compose ps	显示所有容器docker-compose build nginx	构建nginx镜像docker-compose up -d nginx	构建启动nignx容器docker-compose exec nginx bash	登录到nginx容器中docker-compose pause nginx	暂停nignx容器docker-compose unpause nginx	恢复ningx容器docker-compose start nginx	启动nignx容器docker-compose stop nginx	停止nignx容器docker-compose restart nginx	重新启动nginx容器docker-compose rm nginx	删除nginx容器docker-compose down	删除nginx容器和镜像docker-compose logs -f nginx	查看nginx的实时日志

DockerCompose的详细语法参考官网:https://docs.docker.com/compose/compose-file/

7.6.Compose编排微服务

7.6.1.改造升级微服务工程spring-boot-docker-demo

在这里插入图片描述

7.6.1.1.SQL建库建表
CREATE TABLE t_user (id int unsigned NOT NULL AUTO_INCREMENT,username varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',password varchar(50) NOT NULL DEFAULT '' COMMENT '密码',sex tinyint NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',deleted tinyint unsigned NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1114 DEFAULT CHARSET=utf8mb3 COMMENT='用户表';
insert into t_user (id,username,password)values
(1095,'xyz1',221462),
(1096,'xyz2',221463),
(1097,'xyz3',221464),
(1098,'xyz4',221465),
(1099,'xyz5',221466),
(1100,'xyz6',221467),
(1101,'xyz7',221468),
(1102,'xyz8',221469),
(1103,'xyz9',221470);
7.6.1.2.改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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.6</version><relativePath/></parent><groupId>com.itcast</groupId><artifactId>spring-boot-docker-demo</artifactId><version>1.2</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.complier.source>1.8</maven.complier.source><maven.complier.target>1.8</maven.complier.target><juint.version>4.12</juint.version><log4j.version>1.2.17</log4j.version><lombok.version>1.18.16</lombok.version><mysql.version>5.1.47</mysql.version><druid.version>1.1.16</druid.version><mapper.version>4.1.5</mapper.version><mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version></properties><dependencies><!--guava Google 开源的 Guava 中自带的布隆过滤器--><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>23.0</version></dependency><!-- redisson --><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.4</version></dependency><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><!--swagger2--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.9.2</version></dependency><!--SpringBoot与Redis整合依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--springCache--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!--springCache连接池依赖包--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!-- jedis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.1.0</version></dependency><!--Mysql数据库驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><!--SpringBoot集成druid连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>${druid.version}</version></dependency><!--mybatis和springboot整合--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis.spring.boot.version}</version></dependency><!-- 添加springboot对amqp的支持 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.10</version></dependency><!--通用基础配置junit/devtools/test/log4j/lombok/hutool--><!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.2.3</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version><optional>true</optional></dependency><!--persistence--><dependency><groupId>javax.persistence</groupId><artifactId>persistence-api</artifactId><version>1.0.2</version></dependency><!--通用Mapper--><dependency><groupId>tk.mybatis</groupId><artifactId>mapper</artifactId><version>${mapper.version}</version></dependency><!--test--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>3.1.0</version></plugin></plugins></build></project>
7.6.1.3.写application.yml
server.port=6001
# ========================alibaba.druid相关配置=====================
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.229.134:3306/db2024?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.druid.test-while-idle=false
# ========================redis相关配置=====================
spring.redis.database=0
spring.redis.host=192.168.229.134
spring.redis.port=6379
spring.redis.password=
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
# ========================mybatis相关配置===================
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.itcast.entry
# ========================swagger=====================
spring.swagger2.enabled=true
7.6.1.4.主启动类SpringBootDockerDemoApplication.java
package com.itcast;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;@SpringBootApplication
@MapperScan("com.itcast.mapper") //import tk.mybatis.spring.annotation.MapperScan;
public class SpringBootDockerDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDockerDemoApplication.class, args);}}
7.6.1.5.业务类
7.6.1.5.1.config配置类RedisConfig.java
package com.itcast.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.io.Serializable;@Configuration
@Slf4j
public class RedisConfig {/*** @param lettuceConnectionFactory* @return redis序列化的工具配置类,下面这个请一定开启配置* 127.0.0.1:6379> keys ** 1) "ord:102"  序列化过* 2) "\xac\xed\x00\x05t\x00\aord:102"   野生,没有序列化过*/@Beanpublic RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(lettuceConnectionFactory);//设置key序列化方式stringredisTemplate.setKeySerializer(new StringRedisSerializer());//设置value的序列化方式jsonredisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.afterPropertiesSet();return redisTemplate;}}
7.6.1.5.2.config配置类SwaggerConfig.java
package com.itcast.config;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;import java.text.SimpleDateFormat;
import java.util.Date;@Configuration
@EnableSwagger2
public class SwaggerConfig {@Value("${spring.swagger2.enabled}")private Boolean enabled;@Beanpublic Docket createRestApi() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).enable(enabled).select().apis(RequestHandlerSelectors.basePackage("com.itcast")) //你自己的package.paths(PathSelectors.any()).build();}public ApiInfo apiInfo() {return new ApiInfoBuilder().title("Api接口测试" + "\t" + new SimpleDateFormat("yyyy-MM-dd").format(new Date())).description("docker-compose").version("1.0").termsOfServiceUrl("https://www.atguigu.com/").build();}
}
7.6.1.5.3.实体类User.java
package com.itcast.entry;import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;@Table(name = "t_user")
public class User {@Id@GeneratedValue(generator = "JDBC")private Integer id;private String username; //用户名private String password; //密码private Byte sex; //性别 0=女 1=男private Byte deleted; //删除标志,默认0不删除,1删除@Column(name = "update_time")private Date updateTime; //更新时间@Column(name = "create_time")private Date createTime; //创建时间public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Byte getSex() {return sex;}public void setSex(Byte sex) {this.sex = sex;}public Byte getDeleted() {return deleted;}public void setDeleted(Byte deleted) {this.deleted = deleted;}public Date getUpdateTime() {return updateTime;}public void setUpdateTime(Date updateTime) {this.updateTime = updateTime;}public Date getCreateTime() {return createTime;}public void setCreateTime(Date createTime) {this.createTime = createTime;}
}
7.6.1.5.4.实体类UserDTO.java
package com.itcast.entry;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.util.Date;@NoArgsConstructor
@AllArgsConstructor
@Data
@ApiModel(value = "用户信息")
public class UserDTO implements Serializable {@ApiModelProperty(value = "用户ID")private Integer id;@ApiModelProperty(value = "用户名")private String username;@ApiModelProperty(value = "密码")private String password;@ApiModelProperty(value = "性别 0=女 1=男 ")private Byte sex;@ApiModelProperty(value = "删除标志,默认0不删除,1删除")private Byte deleted;@ApiModelProperty(value = "更新时间")private Date updateTime;@ApiModelProperty(value = "创建时间")private Date createTime;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Byte getSex() {return sex;}public void setSex(Byte sex) {this.sex = sex;}public Byte getDeleted() {return deleted;}public void setDeleted(Byte deleted) {this.deleted = deleted;}public Date getUpdateTime() {return updateTime;}public void setUpdateTime(Date updateTime) {this.updateTime = updateTime;}public Date getCreateTime() {return createTime;}public void setCreateTime(Date createTime) {this.createTime = createTime;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", sex=" + sex +'}';}
}
7.6.1.5.5.编写UserMapper.java
package com.itcast.mapper;import com.itcast.entry.User;
import tk.mybatis.mapper.common.Mapper;public interface UserMapper extends Mapper<User> {//tk.mybatis.mapper.common.Mapper
}
7.6.1.5.6.在src/main/resources目录下创建mapper文件夹,并编写UserMapper.xml

UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itcast.mapper.UserMapper"><resultMap id="BaseResultMap" type="com.itcast.entry.User"><id column="id" jdbcType="INTEGER" property="id"/><result column="username" jdbcType="VARCHAR" property="username"/><result column="password" jdbcType="VARCHAR" property="password"/><result column="sex" jdbcType="TINYINT" property="sex"/><result column="deleted" jdbcType="TINYINT" property="deleted"/><result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/><result column="create_time" jdbcType="TIMESTAMP" property="createTime"/></resultMap>
</mapper>
7.6.1.5.7.新建UserService、UserServiceImpl
public interface UserService {void addUser(User user);User findUserById(Integer id);void deleteUser(Integer id);void updateUser(User user);
}
package com.itcast.service.impl;import com.itcast.entry.User;
import com.itcast.mapper.UserMapper;
import com.itcast.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
@Slf4j
public class UserServiceImpl implements UserService {public static final String CACHE_KEY_USER = "user:";@Resourceprivate UserMapper userMapper;@Resourceprivate RedisTemplate redisTemplate;@Overridepublic void addUser(User user) {//1 先插入mysql并成功int i = userMapper.insertSelective(user);if (i > 0) {//2 需要再次查询一下mysql将数据捞回来并okuser = userMapper.selectByPrimaryKey(user.getId());//3 将捞出来的user存进redis,完成新增功能的数据一致性。String key = CACHE_KEY_USER + user.getId();redisTemplate.opsForValue().set(key, user);}}@Overridepublic User findUserById(Integer id) {User user = null;String key = CACHE_KEY_USER + id;//1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysqluser = (User) redisTemplate.opsForValue().get(key);if (user == null) {//2 redis里面无,继续查询mysqluser = userMapper.selectByPrimaryKey(id);if (user == null) {//3.1 redis+mysql 都无数据//你具体细化,防止多次穿透,我们规定,记录下导致穿透的这个key回写redisreturn user;} else {//3.2 mysql有,需要将数据写回redis,保证下一次的缓存命中率redisTemplate.opsForValue().set(key, user);}}return user;}@Overridepublic void deleteUser(Integer id) {String key = CACHE_KEY_USER + id;//1.从mysql里面删除userMapper.deleteByPrimaryKey(id);//2.从redis里面删除redisTemplate.delete(key);}@Overridepublic void updateUser(User user) {String key = CACHE_KEY_USER + user.getId();//1.从mysql更新数据userMapper.updateByPrimaryKey(user);User user1 = findUserById(user.getId());//2.从redis更新数据redisTemplate.opsForValue().set(key, user1);}
}
7.6.1.5.8.新建UserController
package com.itcast.controller;import cn.hutool.core.util.IdUtil;
import com.itcast.entry.User;
import com.itcast.entry.UserDTO;
import com.itcast.service.impl.UserServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.Random;@Api(description = "用户User接口")
@RestController
@Slf4j
@RequestMapping("/users")
public class UserController {@Resourceprivate UserServiceImpl userService;@ApiOperation("数据库新增3条记录")@PostMappingpublic void addUser() {for (int i = 1; i <= 3; i++) {User user = new User();user.setUsername("lb" + i);user.setPassword(IdUtil.simpleUUID().substring(0, 6));user.setSex((byte) new Random().nextInt(2));userService.addUser(user);}}@ApiOperation("删除1条记录")@DeleteMapping("/{id}")public void deleteUser(@PathVariable Integer id) {userService.deleteUser(id);}@ApiOperation("修改1条记录")@PutMappingpublic void updateUser(@RequestBody UserDTO userDTO) {User user = new User();BeanUtils.copyProperties(userDTO, user);userService.updateUser(user);}@ApiOperation("查询1条记录")@GetMapping("/{id}")public User findUserById(@PathVariable Integer id) {return userService.findUserById(id);}
}
7.6.1.7.mvn package命令将微服务形成新的jar包,并上传到Linux服务器 /app/mydocker目录下

在这里插入图片描述
在这里插入图片描述

7.6.1.8.编写Dockerfile
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER DY
# volume 指定临时文件为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为 docker-compose-test.jar
ADD spring-boot-docker-demo-1.2.jar docker-compose-test.jar
# 运行jar包
RUN bash -c 'touch /docker-compose-test.jar'
ENTRYPOINT ["java","-jar","/docker-compose-test.jar"]
# 暴露6001端口作为微服务
EXPOSE 6001
7.6.1.9.构建镜像
docker build -t docker-compose-test:1.2 .

在这里插入图片描述
在这里插入图片描述

7.6.2.不使用Compose

7.6.2.1.单独的mysql容器
7.6.2.1.1.新建mysql容器实例

/app/mysql/conf/my.cnf

[client]
default_character_set=utf8
[mysqld]
collation_server = utf8_general_ci
character_set_server = utf8
docker run \
-p 3306:3306 \
--name mysql57 \
--privileged=true \
-v /app/mysql/conf:/etc/mysql/conf.d \
-v /app/mysql/logs:/logs \
-v /app/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7

在这里插入图片描述

7.6.2.1.2.进入mysql容器实例内并新建库db2024 + 新建表t_user
docker exec -it mysql57 bashcreate database db2024;use db2024;
CREATE TABLE t_user (id int unsigned NOT NULL AUTO_INCREMENT,username varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',password varchar(50) NOT NULL DEFAULT '' COMMENT '密码',sex tinyint NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',deleted tinyint unsigned NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1114 DEFAULT CHARSET=utf8mb3 COMMENT='用户表';

在这里插入图片描述
在这里插入图片描述

7.6.2.2.单独的redis容器
docker run  \
-p 6379:6379 \
--name redis \
--privileged=true \
-v /app/redis/redis.conf:/etc/redis/redis.conf \
-v /app/redis/data:/data \
-d redis:6.0.8 redis-server /etc/redis/redis.conf 
docker exec -it redis608 bashredis-cli -p 6379

在这里插入图片描述
在这里插入图片描述

7.6.2.3.微服务工程
docker run -d -p 6001:6001 docker-compose-test:1.2docker run -d -p 6001:6001 82901d53b6fd
7.6.2.4.上面三个容器实例依次顺序启动成功

在这里插入图片描述

7.6.2.5.swagger测试

测试链接:http://192.168.229.134:6001/swagger-ui.html#/
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.6.3.上面成功了,有哪些问题【痛点】

  • 先后顺序要求固定,先mysql+redis才能微服务访问成功
  • 多个run命令…
  • 容器间的启停或宕机,有可能导致IP地址对应的容器实例变化,映射出错,要么生产IP写死(可以但是不推荐),要么通过服务调用

7.6.4.使用Compose

7.6.4.1.编写docker-compose.yml文件

docker-compose.yml

version: "3"services:microService:image: docker-compose-test:1.3container_name: ms01ports:- "6001:6001"volumes:- /app/microService:/datanetworks: - p_net depends_on: - redis- mysqlredis:image: redis:6.0.8ports:- "6379:6379"volumes:- /app/redis/redis.conf:/etc/redis/redis.conf- /app/redis/data:/datanetworks: - p_net command: redis-server /etc/redis/redis.confmysql:image: mysql:5.7environment:MYSQL_ROOT_PASSWORD: '123456'MYSQL_ALLOW_EMPTY_PASSWORD: 'no'MYSQL_DATABASE: 'db2024'MYSQL_USER: 'lb'MYSQL_PASSWORD: '123456'ports:- "3306:3306"volumes:- /app/mysql/db:/var/lib/mysql- /app/mysql/conf/my.cnf:/etc/my.cnf- /app/mysql/init:/docker-entrypoint-initdb.dnetworks:- p_netcommand: --default-authentication-plugin=mysql_native_password
networks: p_net: 

详解:

version: "3" # 版本号services:microService: # 定义服务名字,微服务microServiceimage: docker-compose-test:1.3 # 镜像名字:版本号container_name: ms01ports:- "6001:6001"volumes:- /app/microService:/datanetworks: - p_net depends_on: - redis- mysql
# docker run -d -p 6001:6001 -v /app/microService:/data --network p_net --name ms01 docker-compose-test:1.3redis:image: redis:6.0.8ports:- "6379:6379"volumes:- /app/redis/redis.conf:/etc/redis/redis.conf- /app/redis/data:/datanetworks: - p_net command: redis-server /etc/redis/redis.confmysql:image: mysql:5.7environment:MYSQL_ROOT_PASSWORD: '123456'MYSQL_ALLOW_EMPTY_PASSWORD: 'no'MYSQL_DATABASE: 'db2024'MYSQL_USER: 'lb'MYSQL_PASSWORD: '123456'ports:- "3306:3306"volumes:- /app/mysql/db:/var/lib/mysql- /app/mysql/conf/my.cnf:/etc/my.cnf- /app/mysql/init:/docker-entrypoint-initdb.dnetworks:- p_netcommand: --default-authentication-plugin=mysql_native_password #解决外部无法访问
# docker network create p_net
networks: p_net: 
7.6.4.2.第二次修改微服务工程

两处修改将192.168.229.134换成对应的服务名称mysql,redis

#spring.datasource.url=jdbc:mysql://192.168.229.134:3306/db2024?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.url=jdbc:mysql://mysql:3306/db2024?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.druid.test-while-idle=false
# ========================redis相关配置=====================
spring.redis.database=0
#spring.redis.host=192.168.229.134
spring.redis.host=redis
spring.redis.port=6379
spring.redis.password=
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
# ========================mybatis相关配置===================
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.itcast.entry
# ========================swagger=====================
spring.swagger2.enabled=true

在这里插入图片描述

# 构建新镜像docker-compose-test:1.3
docker build -t docker-compose-test:1.3 .

在这里插入图片描述

7.6.4.3.执行 docker-compose up 或者 docker-compose up -d
# 检查配置,有问题才输出
docker-compose config -q

在这里插入图片描述
在这里插入图片描述

7.6.4.4.进入mysql容器实例并新建库db2024+新建表t_user
docker exec -it d128363b77bc bashmysql -ulb -p123456
create database db2024;use db2024;CREATE TABLE t_user (id int unsigned NOT NULL AUTO_INCREMENT,username varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',password varchar(50) NOT NULL DEFAULT '' COMMENT '密码',sex tinyint NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',deleted tinyint unsigned NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1114 DEFAULT CHARSET=utf8mb3 COMMENT='用户表';
7.6.4.5.测试通过

测试链接:http://192.168.229.134:6001/swagger-ui.html#/
在这里插入图片描述

7.6.4.6.Compose常用命令
命令作用
docker-compose -h#查看帮助
docker-compose up#启动所有docker-compose服务
docker-compose up -d#启动所有docker-compose服务并后台运行
docker-compose down#停止并删除容器、网络、卷、镜像。
docker-compose exec yml里面的服务id#进入容器实例内部 docker-compose exec docker-compose.yml文件中写的服务id /bin/bash
docker-compose ps#展示当前docker-compose编排过的运行的所有容器
docker-compose top#展示当前docker-compose编排过的容器进程
docker-compose logs yml里面的服务id#查看容器输出日志
dokcer-compose config#检查配置
dokcer-compose config -q#检查配置,有问题才有输出
docker-compose restart#重启服务
docker-compose start#启动服务
docker-compose stop#停止服务

以nginx容器操作为例,常见操作如下:

docker-compose ps	显示所有容器docker-compose build nginx	构建nginx镜像docker-compose up -d nginx	构建启动nignx容器docker-compose exec nginx bash	登录到nginx容器中docker-compose pause nginx	暂停nignx容器docker-compose unpause nginx	恢复ningx容器docker-compose start nginx	启动nignx容器docker-compose stop nginx	停止nignx容器docker-compose restart nginx	重新启动nginx容器docker-compose rm nginx	删除nginx容器docker-compose down	删除nginx容器和镜像docker-compose logs -f nginx	查看nginx的实时日志

DockerCompose的详细语法参考官网:https://docs.docker.com/compose/compose-file/

7.6.4.7.关停

在这里插入图片描述

7.6.5.Redis总是连接失败总结

7.6.5.1.can’t connect to redis-server

在这里插入图片描述
Redis-Desktop-Manager连接需要四个参数介绍:

Name:自定义连接名Host:redis服务器地址,在CentOS终端中使用命令;ifconfig:该命令显示的,ens33中的inet后的地址即为redis-server host端口:默认6379Auth:数据库密码,通常是设置数据库config时自定义的密码
7.6.5.2.将redis.conf文件下列参数更改
#bind 127.0.0.1 # 注释掉,允许远程连接(注释或者改为bind 0.0.0.0) 
protected-mode no    # 保护模式由yes改为no
appendonly yes # 开启AOP持久化
daemonize no   # 后台启动设置为no
#requirepass 123456 # 开启redis验证
# 查看redis服务进程是否正常开启
ps -ef|grep redis|grep -v grep
ps -ef |grep redis

在这里插入图片描述

systemctl stop firewalld # 关闭linux防火墙systemctl status firewalld # 查看firewall的状态

在这里插入图片描述

7.6.5.3.测试redis连接

在这里插入图片描述
在这里插入图片描述

7.6.5.4.docker redis容器连接不上–可以自定义网络模式
# 创建自定义网络
docker network create test_network# 启动Redis容器,并加入自定义网络
docker run  \
-p 6380:6379 \
--network test_network \
--name redis6 \
--privileged=true \
-d redis:latest redis-server# 启动其他容器,并加入自定义网络

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.6.5.5.Docker运行Redis容器总结

在使用Docker运行Redis容器时,如果遇到连接不上的问题,可以先检查网络配置是否正确,包括IP地址冲突防火墙设置
如果仍然无法解决,可以尝试修改Redis配置文件,并挂载到容器中。
另外,如果需要容器间通信,可以选择合适的网络模式来解决问题。
在这里插入图片描述

7.6.6.docker: Error response from daemon: driver failed programming external connectivity on endpoint redis

docker: Error response from daemon: driver failed programming external connectivity on endpoint redis (88aa3ee8adb07d3da901e2a53d4f086ea9eca3a686c3a0374745d272aff71495):  (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 6379 -j DNAT --to-destination 172.17.0.3:6379 ! -i docker0: iptables: No chain/target/match by that name.

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/618026.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【算法】简单的二分查找算法

一个简单的二分查找算法&#xff1a; import java.util.Arrays; public class BinarySearch {public static int rank(int key,int[] a){int lo0;int hia.length-1;while (lo<hi){int midlo(hi-lo)/2;if (key<a[mid])himid-1;else if (key>a[mid])lomid1;else return …

嵌入式linux 编译qt5(以v851s为例)

本文参考Blev大神的博客&#xff1a;Yuzuki Lizard V851S开发板 --移植 QT5.12.9教程&#xff08;群友Blev提供&#xff09; - Allwinner / 柚木PI-V851S - 嵌入式开发问答社区 (100ask.net) 一. 环境准备 1.下载qt5源码&#xff1a;Open Source Development | Open Source …

【Maven】001-Maven 概述

【Maven】001-Maven 概述 文章目录 【Maven】001-Maven 概述一、Maven 概述1、为什么学习 MavenMaven 作为依赖管理工具Maven 作为构建工具其它 2、Maven 介绍3、Maven 软件工作模型图 一、Maven 概述 1、为什么学习 Maven Maven 作为依赖管理工具 依赖管理&#xff1a; Mave…

IDEA集成Gitee(码云)

文章目录 创建新仓库&#xff0c;存放项目拉取Gitee上的项目 1、安装插件 Idea默认不带码云插件&#xff0c;我们第一步要安装Gitee插件。 如图所示&#xff0c;在Idea插件商店搜索Gitee&#xff0c;然后点击右侧的Install按钮。 2、Settings>Version Conttol>Gitee 这里…

数据分析基础

数据运营的概念及意义 数据分析的三个维度 数据总览的作用及提升方法 数据总览的作用及提升方法 数据总览的作用及提升方法小结 影响作品数据的关键因素 影响作品数据的关键因素小结 用户数据的意义与作用 用户数据分析的方法与操作 用户数据分析的方法与操作小结 数据运营小结…

数据仓库 Apache Hive

一、数据分析 1、数据仓库 数据仓库&#xff08;英语&#xff1a;Data Warehouse&#xff0c;简称数仓、DW&#xff09;&#xff0c;是一个用于存储、分析、报告的数据系统。 数据仓库的目的是构建面向分析的集成化数据环境&#xff0c;分析结果为企业提供决策支持&#xff08…

Unity 编辑器篇|(四)编辑器拓展GUI类 (全面总结 | 建议收藏)

目录 1. 前言2. 参数2.1 静态变量2.2 静态函数2.3 委托 3. 功能3.1 按钮&#xff1a;Button、RepeatButton3.2 文本&#xff1a;Label 、TextField 、TextArea 、PasswordField3.3 滑动条&#xff1a;HorizontalScrollbar 、VerticalScrollbar3.4 滑条&#xff1a;VerticalSlid…

表单生成器基于(form-create-designer+ant design vue)

效果展示 1.源码地址&#xff1a; 前端&#xff1a;https://gitee.com/houshixin/form-design-ui 后端&#xff1a;https://gitee.com/houshixin/form-design-web 2.单独使用前端的时候就把请前后台的接口注释就可以 3.都启动的话&#xff1a; 1&#xff09;.先导入数据库 2.表…

C++代码重用:继承与组合的比较

目录 一、简介 继承 组合 二、继承 三、组合 四、案例说明 4.1一个电子商务系统 4.1.1继承方式 在上述代码中&#xff0c;Order类继承自User类。通过继承&#xff0c;Order类获得了User类的成员函数和成员变量&#xff0c;并且可以添加自己的特性。我们重写了displayI…

【java八股文】之Redis基础篇

1、Redis有哪几种基本的数据类型 字符串类型&#xff1a;用于存储文章的访问量Hash&#xff1a;用来存储key-value的数据结构&#xff0c;当单个元素比较小和元素数量比较少的时候 &#xff0c;底层是用ziplist存储。通常可以用来存储一些对象之类的List: 底层采用的quicklist …

2024儿童台灯哪个品牌更护眼推荐?五款知名品牌台灯推荐

只要有了娃&#xff0c;家长的吃穿用度可能不会特别讲究&#xff0c;但总想给孩子好的东西&#xff0c;尤其是关系到他们身心健康的&#xff0c;可以说是一掷千金。特别是眼睛视力方面&#xff0c;特别担心会遗传给孩子&#xff0c;自从他上幼儿园&#xff0c;我就一直在物色一…

WSL不同版本的Ubuntu更换清华镜像,加速Ubuntu软件下载速度

文章目录 不同版本的Ubuntu使用清华镜像&#xff0c;加速Ubuntu软件下载速度1. 备份源软件配置文件2. 复制镜像源3. 修改软件源配置文件4. 更新软件包列表&#xff0c;升级软件包等内容5. 从仓库中下载其它软件可能存在的问题 不同版本的Ubuntu使用清华镜像&#xff0c;加速Ubu…

鸿蒙Harmony--LocalStorage--页面级UI状态存储详解

走的太急疼的是脚&#xff0c;逼的太紧累的是心&#xff0c;很多时候&#xff0c;慢一点也没关系&#xff0c;多给自己一些耐心和等待&#xff0c;保持热爱&#xff0c;当下即是未来&#xff0c;生活自有安排! 目录 一&#xff0c;定义 二&#xff0c;LocalStorageProp定义 三…

Java Web 开发 从入门到实战(课后习题)

第1章 Web 前端基础 1.在以下标记中&#xff0c;用于改置页面标题的是&#xff08;&#xff09;。 A. <title> B. <caption> C. <head> D. <html> 注&#xff1a;caption是表格名称&#xff08;标题&#xff09; 2. 若设计网页的背景图形为bg.png&…

使用Mixtral-offloading在消费级硬件上运行Mixtral-8x7B

Mixtral-8x7B是最好的开放大型语言模型(LLM)之一&#xff0c;但它是一个具有46.7B参数的庞大模型。即使量化为4位&#xff0c;该模型也无法在消费级GPU上完全加载(例如&#xff0c;24 GB VRAM是不够的)。 Mixtral-8x7B是混合专家(MoE)。它由8个专家子网组成&#xff0c;每个子…

Linux--LNMP架构及应用部署

4.2 LNMP架构及应用部署 4.2.1构建LNMP网站平台 为了与Nginx、PHP环境保持一致&#xff0c;仍选择采用源代码编译的方式安装MySQL组件。以5.5.22 版本为例&#xff0c;安装过程如下所述。 &#xff08;1&#xff09;编译安装MySQL。 [rootnode01 ~]# yum -y install ncurses-…

Java中锁的解决方案

前言 在上一篇文章中&#xff0c;介绍了什么是锁&#xff0c;以及锁的使用场景&#xff0c;本文继续给大家继续做深入的介绍&#xff0c;介绍JAVA为我们提供的不同种类的锁。 JAVA为我们提供了种类丰富的锁&#xff0c;每种锁都有不同的特性&#xff0c;锁的使用场景也各不相…

Java 面试题 - 多线程并发篇

线程基础 创建线程有几种方式 继承Thread类 可以创建一个继承自Thread类的子类&#xff0c;并重写其run()方法来定义线程的行为。然后可以通过创建该子类的实例来启动线程。 示例代码&#xff1a; class MyThread extends Thread {public void run() {// 定义线程的行为} …

JUC02同步和锁

同步&锁 相关笔记&#xff1a;www.zgtsky.top 临界区 临界资源&#xff1a;一次仅允许一个进程使用的资源成为临界资源 临界区&#xff1a;访问临界资源的代码块 竞态条件&#xff1a;多个线程在临界区内执行&#xff0c;由于代码的执行序列不同而导致结果无法预测&am…

近视的孩子用什么灯?学生考研护眼台灯推荐

随着时代快速发展&#xff0c;2022年我国近视人数达到了7亿&#xff0c;呈现低龄化趋势&#xff0c;儿童及青少年人数占了53.8%。现在学业负担都很重&#xff0c;每个家长都不希望自己的孩子近视或加深近视了&#xff0c;都会想尽一切办法保护视力。而护眼台灯就成了家长购买台…