1. lnrp
环境构建
1.1.
构建镜像
nginx
的
dockerfile
FROM centos:centos7
RUN mkdir /data && mkdir /conf
RUN groupadd -r nginx && useradd -r -g nginx nginx
#修改时区
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
ARG PHP_VERSION=7.2
#添加centos源(先下载wget)
COPY ./epel-7.repo /etc/yum.repos.d/epel.repo
#COPY
#RUN yum install -y wget
#RUN wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/Centos-7.repo
RUN yum update -y \
&& yum clean all \
&& yum makecache \
&& yum -y install gcc gcc-c++ autoconf automake make zlib zlib-devel net-tools openssl* pcre* wget \
&& yum clean all && rm -rf /var/cache/yum/*
#声明匿名卷
VOLUME /data
COPY ./nginx-1.14.1.tar.gz /data/nginx-1.14.1.tar.gz
RUN cd /data \
&& tar -zxvf nginx-1.14.1.tar.gz \
&& cd nginx-1.14.1 \
&& ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx \
&& make && make install && rm -rf /data/nginx-1.14.1.tar.gz && rm -rf /data/nginx-1.14.
COPY ./conf/nginx.conf /conf
#全局使用nginx,软链接
RUN ln -s /usr/local/nginx/sbin/* /usr/local/sbin
#进入容器时默认打开的目录
WORKDIR /conf
#声明端口
EXPOSE 80
#容器启动的时候执行,在docker run过程当中是会被其他指令替代
#CMD ["/usr/local/nginx/sbin/nginx","-c","/conf/nginx.conf","-g","daemon off;"]
#执行一条指
# ENTRYPOINT ["/usr/local/nginx/sbin/nginx","-c","/conf/nginx.conf","-g","daemon off;"]
php
的
dockerfile
FROM php:7.3-fpm-alpine
# Version
ENV PHPREDIS_VERSION 4.0.0
# Libs
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
&& apk add \
curl \
vim \
wget \
git \
openssl-dev\
zip \
unzip \
g++ make autoconf
# docker方式安装PDO extension # 安装扩展
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
&& docker-php-ext-install pdo_mysql \
&& docker-php-ext-install pcntl \
&& docker-php-ext-install sysvmsg
# Redis extension
RUN wget http://pecl.php.net/get/redis-${PHPREDIS_VERSION}.tgz -O /tmp/redis.tar.tgz \
&& pecl install /tmp/redis.tar.tgz \
&& rm -rf /tmp/redis.tar.tgz \
&& docker-php-ext-enable redis
# 修改php.ini的文件 extension=redis.so
EXPOSE 9000
#设置工作目录
WORKDIR /www
redis的
dockerfile
FROM centos:centos7
RUN groupadd -r redis && useradd -r -g redis redis
RUN mkdir data ;\
yum update -y ; \
yum -y install gcc automake autoconf libtool make wget epel-release gcc-c++;
COPY ./redis-5.0.7.tar.gz redis-5.0.7.tar.gz
RUN mkdir -p /usr/src/redis; \
tar -zxvf redis-5.0.7.tar.gz -C /usr/src/redis; \
rm -rf redis-5.0.7.tar.gz; \
cd /usr/src/redis/redis-5.0.7 && make ; \
cd /usr/src/redis/redis-5.0.7 && make install
COPY ./conf/redis.conf /usr/src/redis/redis-5.0.7/redis.conf
EXPOSE 6379
ENTRYPOINT ["redis-server", "/usr/src/redis/redis-5.0.7/redis.conf"]
或者直接
docker pull redis:6.0-rc-alpine
构建镜像
docker build -t php7 .
docker build -t nginx1.4 .
docker build -t redis5 .
保存容器数据卷
mkdir docker
mkdir docker/images
mkdir docker/images/data/
mkdir docker/images/data/php
mkdir docker/images/data/nginx
mkdir docker/images/data/redis
mkdir docker/images/data/php/www
mkdir docker/images/data/nginx/conf
mkdir docker/images
mkdir docker/images/data/
mkdir docker/images/data/php
mkdir docker/images/data/nginx
mkdir docker/images/data/redis
mkdir docker/images/data/php/www
mkdir docker/images/data/nginx/conf
添加config.conf文件到docker/images/data/nginx/conf目录
user root;
worker_processes 1;
events {
worker_connections 1024;
}
http {
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile on;
keepalive_timeout 65;
server {
listen 80;
access_log /usr/local/nginx/logs/$host main;
location / {
root /www; #php容器的目录
default_type text/html;
}
location ~ \.php/?.* {
default_type text/html;
#做php-fpm 配置,注意地址
root /www; #php-fpm容器当中的路径,不是nginx容器路径
fastcgi_index index.php;
fastcgi_pass 192.168.200.140:9001; #php容器端口
#为php-fpm指定的根目录
fastcgi_param SCRIPT_FILENAME $DOCUMENT_ROOT$fastcgi_script_name;
#注意是容器当中的位置
#定义变量 $path_info ,用于存放pathinfo信息
set $path_info "";
if ($fastcgi_script_name ~ "^(.+?\.php)(/.+)$") {
#将文件地址赋值给变量 $real_script_name
set $real_script_name $1;
#将文件地址后的参数赋值给变量 $path_info
set $path_info $2;
}
#配置fastcgi的一些参数
fastcgi_param SCRIPT_NAME $real_script_name;
fastcgi_param PATH_INFO $path_info;
include /usr/local/nginx/conf/fastcgi_params;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
添加index.php文件到docker/images/data/php/www目录
<?php
$redis = new Redis();
$redis->connect('192.168.169.140', 6379);//serverip port
$redis ->set("test" , "Hello World");
echo $redis ->get("test");
构建容器
docker run -itd -v /docker/images/data/nginx/conf:/conf -p 81:80 --name nginx1.4 nginx1.4
docker run -itd -v /docker/images/data/php/www:/www -p 9001:9000 --name php-fpm-7 php7
docker run -itd -p 6379:6379 --name redis5 redis5
验证
关闭防火墙 systemctl stop firewalld
在浏览器中输入192.168.200.140:81/index.php 返回Hello World
3. redis
基础内容
3.1 redis
介绍
redis
是 一 个
key-value
存 储 系 统 。 它 支 持 存 储 的
value
类型相对更多,包括
string(
字符串
)
、
list(
链 表
)
、
set(
集合
)
、
zset(sorted set --
有序集合
)
和
hash
(哈希类型)。这些数据类型都支持
push/pop
、
add/remove
及
取交集并集和差集及更丰富的操作, 而且这些操作都是原子性的。在此基础上,
redis
支持 各种不同方式的排序。为了保证效率,数据都是缓存在 内存中
1.
为什么会出现
redis
呢?
对于网页来说数据来自于数据库,而数据库实际上是存在这性能的瓶颈(一般单表早期的瓶颈就在
2000w
了
--[
索引的优化效率瓶颈
]
)
即时是做了主从以及集群架构也依然还是会有速率的问题,因此这个时候以内存暂时存储数据的想法就横空出世了;产品也很多,不去探讨重点讲解
redis
2. redis
特性
2.1
速度快
正常情况下,
Redis
执行命令的速度非常快,官方给出的数字是读写性能可以达到
10
万
/
秒,当然这也取决于机器的性能,但这里先不讨论机器性能上的差异,只分析一下是什么造就了
Redis
除此之快的速度,可以大致归
纳为以下四点
:
1. Redis
的所有数据都是存放在内存中的,,所以把数据放在内存中是
Redis
速度快的最主要原因。
2. Redis
是用
C
语言实现的,一般来说
C
语言实现的程序
“
距离
”
操作系统更近,执行速度相对会更快。
3. Redis
使用了单线程架构,预防了多线程可能产生的竞争问题。
4.
作者对于
Redis
源代码可以说是精打细磨,曾经有人评价
Redis
是少有的集性能和优雅于一身的开源代码。
2.2
基于
key-value
几乎所有的编程语言都提供了类似字典的功能,例如
Java
里的
map
、
Python
里的
dict,
类似于这种组织数据的方式叫作基于键值的方式,与很多键值对数据库不同的是,
Redis
中的值不仅可以是字符串,而且还可以是具
体的数据结构,这样不仅能便于在许多应用场景的开发,同时也能够提高开发效率。
Redis
的全称是
REmote Dictionary Server,
它主要提供了
5
种数据结构
:
字符串、哈希、列表、集合、有序集合,同时在字符串的基础之上
演变出了位图
( Bitmaps)
和
HyperLogLog
两种神奇的
“
数据结构
”
,并且随着
LBS ( Location BasedService,
基于位置服务
)
的不断发展,
Redis 3.2
版本中加人有关
GEO (
地理信息定位
)
的功能,总之在这些数据结构的帮助下,
开发者可以开发出各种
“
有意思
”
的应用。
2.3.
丰富的功能
除了
5
种数据结构,
Redis
还提供了许多额外的功能
:
1.
提供了键过期功能,可以用来实现缓存。
2.
供了发布订阅功能,可以用来实现消息系统。
3.
支持
Lua
脚本功能,可以利用
Lua
创造出新的
Redis
命令。
4.
提供了简单的事务功能,能在一定程度 上保证事务特性。
5.
提供了流水线
( Pipeline)
功能,这样客户端能将
-
批命令
-
一次性传到
Redis,
减少网络的开销。
2.4.
简单稳定
2.5.
支持的语言多
2.6.
持久化
2.7.
主从复制
3. redis
应用场景
3.1.
缓存
缓存机制几乎在所有的大型网站都有使用,合理地使用缓存不仅可以加快数据的访问速 座而且能够有效地降低后端数据源的压力。
Redis
提供了键值过期时间设置,并且也提供
7
灵活控制最大内存和内存溢出后的淘汰
策略。可以这么说,一个合理的缓存设计能够为一 个网站的稳定保驾护航。第
11
章将对缓存的设计与使用进行详细说明。
3.2.
排行榜系统
排行榜系统几乎存在于所有的网站,例如按照热度排名的排行榜,按照发布时间的排行榜,按照各种复杂维度计算出的排行榜,
Redis
提供了列表和有序集合数据结构,合理地使用这些数据结构可以很方便地构建各种
排行榜系统。
3.3.
计数器
计数器在网站中的作用至关重要,例如视频网站有播放数、电商网站有浏览数,为了保证数据的实时性,每一次播放和浏览都要做加
1
的操作,如果并发量很大对于传统关系型数据的性能是一种挑战。
Redis
天然支持计
数功能而且计数的性能也非常好,可以说是计数器系统的重要选择。
3.4.
社交网站
赞
/
踩、粉丝、共同好友
/
喜好、推送、下拉刷新等是社交网站的必备功能,由于社交网站访问量通常 比较大,而且传统的关系型数据不太适 合保存这种类型的数据,
Redis
提供的数据结构可以相对比较容易地实现这些
功能。
3.5.
消息队列
消息队列系统可以说是一
-
个大型网站的必备基础组件,因为其具有业务解耦、非实时业务削峰等特性。
Redis
提供了发布订阅功能和阻塞队列的功能,虽然和专业的消息队列比还不够足够强大,但是对于般的消息队列
功能基本可以满足。
3.2 redis
基础数据类型及场景运用
3.2.1 字符
串
字符串是
redis
最基础的数据结构。首先键都是字符串类型,而且其他几种数据结构都是在字符串类型基础上构建的,所以字符串类型能为其他四种类型结构的学习奠定基础。
1.
命令
--
设置参数:
set key value
setnx key value
set key value xx
mset key value [key value ...]
--
获取参数
get key
get key [key ...]
--
计数
incr key
decr key
--
是否存在
exists key
2.
场景
1.
缓存功能
如下图是比较典型的缓存使用场景,其中
redis
作为缓存层,
mysql
作为存储层大部分请求的数据都是从
redis
中获取。由于
redis
具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用
2.
计数
许多应用都会使用
redis
作为计数的基础工具,它可以实现快速计数、查询缓存的功能,同时数据可以异步落地到其他数据源。比如视频播放一次就自动加
1
3.
共享
session
4.
限速
很多应用处于安全的考虑,会在每次进行登入的时候,设置短信验证登入;而对于短信接口不被频繁访问,会限制用户每分钟获取验证码的频率,比如一份中不超过
5
次
当然也可以设置
ip
地址不能在
1s
之间内访问超过
n
次
3.2.2
哈希
哈希类型是指键值本身又是一个键值对结构(就是多维数组),
value={{field, value},...{fieldN, valueN}}
1.
命令
--
设置
hset set field value
如:
hset user:1 name shineyork
--
获取
hget key field
如:
hget user:1 name
--
判断是否存在
hkeys key
2.
场景
典型场景就是记录用信息
3.2.3
列表
列表类型是用来存储多个有序的字符串,如图
a,b,c,d,e
从左到右组成一个有序的列表,列表中的每个字符串称为元素。在
redis
中,可以对列表两端插入
(push)
和弹出
(pop)
,还可以指定范围的元素列表,获取指定索引下
表的元素等。列表是种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发上有很多应用场景。
1.
命令
--
从右边插入元素
rpush key value [value ...]
--
实例
> rpush listkey c b a
> lrange listkey 0 -1
--
从左边插入元素
lpush key value [value ...]
--
获取列表指定索引下标元素
lindex key index
--
获取列表长度
llen key
--
删除
--
从左侧弹出元素
lpop key
--
从右侧弹出元素
rpop key
2.
使用场景
1.
消息队列
Redis
的
lpush+brpop
命令组合即可实现阻塞队列,生产者客户端使用
lrpush
从列表左侧插入元素,多个消费者客户端使用
brpop
命令阻塞式的
“
抢
”
列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性
2.
文章列表
每个用户有属于自己的文章列表,先需要分页展示文章列表。此时可以考虑使用列表因为列表不但是有序的,同时支持按照索引范围获取元素。
3.2.4
集合
集合
(set)
类型也是用来保存多个的字符串元素,但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取原生。集合
user:1:name
包含
着
“peter”
、
“shineyork”
、
“cara”
、
“will”
四个元素。
1.
命令
--
添加元素
sadd key element [element ...]
--
实例
> exists myset
> sadd myset a b c
(integer)
> sadd myset a b
0
--
删除元素
srem key element [element ...]
--
从集合随机弹出元素
spop key
--
获取所有元素
smembers key
2.
使用场景
集合类型比较典型的使用场景是标签
(tag)
。例如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣点就是标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共
同喜欢的标签,这些数据对于用户体验以及增强用户黏合度比较重要
下面使用集合类型实现标签功能的若干功能。
(1)
给用户添加标签
sadd user:1:tags tag1 tag2 tag5
sadd user:2:tags tag1 tag2 tag3
...
sadd user:k:tags tag1 tag2 tag3
(2)
给标签添加用户
sadd tag1:users user:1 user:3
sadd tag2:users user:1 user:2 user:3
3.2.5
有序集合
有序集合相对哈希、列表、集合来说会有点点陌生, 但既然叫有序集合,那么它和集合必然有着联系,它保留了集合不能有重复成员的特性,但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下标作为
排序依据不同的是,它给每个元素设置
-
个分数
(score)
作为排序的依据。
1.
命令
--
添加成员
zadd key score member [score member ...]
--
实例
> zadd user:name 300 php
(integer)1
> zadd user:name 1 .net 20 c++ 90 java 200 python
--
计算排名
zrank key member
zrevrank key member
--
删除成员
zrem key member [member ...]
2.
使用场景
有序集合比较典型的使用场景就是排行榜系统。例如视频网站需要对用户上传的视频做排行榜,榜单的维度可能是多个方面的:按照实际、按照播放数量、按照获得的赞数。比如以赞数这个维度,记录每天用户上传视
频的排行榜。
(1)
添加用户赞数
例如用户
mike
上传了一个视频,并获得了
3
个赞,可以使用有集合的
zadd
和
zincrby
功能:
zadd user:info:20200428 3 mike
如果之后再获得一个赞,可以使用
zincrby
(2)
取消用户赞数
由于各种原因
(
例如用户注销、用户作弊需要将用户删除,此时需要讲用户从榜单删除掉,可以使用
zrem
。
zrem user:info:20200428 mike
(3)
展示获取赞数最多的是个用户
此功能使用
zrevrange
命令实现:
zrevrangebyrank user:info:20200428 0 9
(4)
展示用户信息以及用户分数
此功能将用户名作为键后缀,讲用户信息保存在哈希类型中,至于用户的分数和排名可以使用
zscore
和
zrank
两个功能:
zscore user:info:20200428 mike
zrank user:info:20200428 mike
4. docker
网络模式
docker
安装后,默认会创建下面三种网络类型
1. bridge
:桥接网络
默认情况下启动的
Docker
容器,都是使用
bridge
,
Docker
安装时创建的桥接网络,每次
Docker
容器重启时,会按照顺序获取对应的
IP
地址,这个就导致重启下,
Docker
的
IP
地址就变
2. none
:
无指定网络 使用
--network=none
,
docker
容器就不会分配局域网的
IP
3. host
:
主机网络 使用
--network=host
,此时,
Docker
容器的网络会附属在主机上,两者是互通的。 例如,在容器中运行一个
Web
服务,监听
8080
端口,则主 机的
8080
端口就会自动映射到容器中
弊端:
1
)最明显的就是容器不再拥有隔离、独立的网络栈。容器会与宿主机竞争网络栈的使用,并且容器的崩溃就可能导致宿主机崩溃,在生产环境中,这种 问题可能是不被允许的。
2
)容器内部将不再拥有所有的端口资源,因为一些端口已经被宿主机服务、
bridge
模式的容器端口绑定等其他服务占用掉了
4.
指定自定义网络
因为默认的网络不能制定固定的地址,所以我们将创建自定义网络,并指定网段:
192.168.1.0/24
并命名为
mynetwork
,指令
如下:
~ docker network create --subnet=192.160.1.0/24 mynetwork
~ docker run -itd --network=redis-network --ip 192.168.1.10 --name dockerName imagesName
~ docker network ls
~ docker network rm mynetwork
!注意:这个网络段不要和宿主机的网络端冲突,不然会容易对宿主机产生影响
5. redis
主从主从复制
5.1.
什么是主从复制
主从复制,是指将一台
Redis
服务器的数据,复制到其他的
Redis
服务器。前者称为主节点
(master)
,后者称为从节点
(slave),
数据的复制是单向的,只能由主节点到 从节点。
默认情况下,每台
Redis
服务器都是主节点,且一个主节点可以有多个从节点
(
或没有从节点
)
,但一个从节点只能有一个主节点。
5.2.
实践主从复制
dockerfile
FROM
alpine:3.11
RUN
sed -i
's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g'
/etc/apk/repositories \
&& apk add gcc g++ libc-dev wget vim openssl-dev make linux-headers \
&& rm -rf /var/cache/apk/*
COPY
./redis-5.0.7.tar.gz redis-5.0.7.tar.gz
#
通过选择更小的镜像,删除不必要文件清理不必要的安装缓存,从而瘦身镜像
#
创建相关目录能够看到日志信息跟数据跟配置文件
RUN
mkdir -p /usr/src/redis \
&& mkdir -p /redis/data \
&& mkdir -p /redis/conf \
&& mkdir -p /redis/log \
&& mkdir -p /var/log/redis \
&& tar -zxvf redis-5.0.7.tar.gz -C /usr/src/redis \
&& rm -rf redis-5.0.7.tar.gz \
&& cd /usr/src/redis/redis-5.0.7 && make \
&& cd /usr/src/redis/redis-5.0.7 && make install;
EXPOSE
6379
# CMD ["redis-server"]
构建容器
docker network create --subnet=192.160.1.0/24 redis5sm
docker run -itd -v /redis_2004/masterandslave/master:/redis -p 6350:6379 --network=redis5sm --ip=192.160.1.150 --name redis5master redis5asm
docker run -itd -v /redis_2004/masterandslave/slave:/redis -p 6340:6379 --network=redis5sm --ip=192.160.1.140 --name redis5slave redis5asm
3.
配置主从
这里通过命令构建
1.
确保主服务器的
redis-master
的配置是
ok
的
# bind 127.0.0.1
protected-mode no
2.
进入从服务器
执行如下命令
SLAVEOF 192.160.1.150 6379
配置即可
常见问题
操作:
docker network create --subnet=192.160.1.0/24 redis5sm
docker run -itd -v /redis_2004/masterandslave/master:/redis -p 6350:6379 --network=redis5sm --ip=192.160.1.150 --name redis5master redis5asm
docker run -itd -v /redis_2004/masterandslave/slave:/redis -p 6340:6379 --network=redis5sm --ip=192.160.1.140 --name redis5slave redis5asm
从redis:
slaveof 192.160.1.150 6350
127.0.0.1:6379> info replication
master_link_status:down
问题:
1.master_link_status是down,且主redis添加一个值从redis没有
2.我用redis客户端在本地连接直接连接不上
3.自定网络的ip是可以随意的吗 比如我主机是192.168.200.130 docker可以直接定义成192.160.1.150
解决方法
1.master_link_status是down,且主redis添加一个值从redis没有 这个是因为你的从机找不到主机
原因:
a. 你的主机配置问题
b. 你的从机地址填错了
c. 网络不通,建议ping一下看看
2.我用redis客户端在本地连接直接连接不上
原因:
a.服务没有开启
b.端口开放的问题;宿主机和容器的端口
3.自定网络的ip是可以随意的吗 : 可以