网关(Gateway)
我们的业务不能对所有人都开放,而进行筛选就需要进行身份认证,这件事就是由网关来做到的,一切请求都先提交到网关来进行处理。
网关的功能
- 身份认证和权限校验
- 服务路由(选择微服务的模块)、负载均衡(选择一个微服务的具体实例)
- 请求限流
SpringCloud中的组件
- gateway
- zuul
Zuul基于Servlet进行实现,属于阻塞式编程。SpringCloudGateWay则是基于Spring5中的WebFlux,属于响应式编程,具有更好的性能。
配置
依赖配置:
<dependencies>
<!-- nacos服务注册发现依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 网关gateway依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
注意要自己把启动类配置好
搭建网关服务
配置application.yml,详细配置见代码
server:
port: 10010
spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:80 # nacos地址
gateway:
routes:
- id: user-service # 标识这个路由,必须唯一
uri: lb://userservice #路由的目标地址
predicates: # 路由断言,判断请求是否符合规则
- Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是,则将其代理到这个路由里来(userservice)
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
路由断言工厂
路由断言会被
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory
这个路由断言工厂获取并进行处理
predicates:
- Path=/order/**
- After=2031-01-20T17:42:47.789-07:00[Asia/Shanghai] # After标签,要求其必须在xxx时间之后进行访问
在这个工厂中,我们可以配置许多设置,在SpringCloud的gateway文档中可以进行查看:
例如:
-
要求在某个时间点之后进行请求(After)
-
要在两个时间点之间进行请求(Between)
-
必须包含某些Cookie(Cookie)
-
ip必须是指定范围(RemoteAddr)
-
等
网关过滤器
GatewayFilter是请求进入网关的路由之后,再经过各种各样的过滤器才能最终对微服务进行调用,同样的,微服务返回的相应也需要经过各种各样的过滤器才能返回给路由再进行响应。
SpringCloud提供了31种过滤器,(GatewayFilterFactory)
添加请求头、限制流量、添加响应头等等
示例(标签写在routes的某个id下,可以配置某个服务的过滤器):
filters:
- AddRequestHeader=Truth,Hello World
也可以在gateway标签下写default-filters来配置全局过滤器,这样全部的请求就都会进入这里:
default-filters:
- AddRequestHeader=Truth,Hello World
定制化全局过滤器
全局过滤器也是用于处理一切进入网关的请求和微服务相应,与GatewayFilter的作用一样,但GatewayFilter通过配置进行定义,逻辑固定,而GlobalFilter的逻辑需要自己写代码进行实现,较为灵活。
案例:定义全局过滤器,拦截并判断用户身份
创建一个AuthorizeFilter类用来实现功能
实现GlobalFilter接口并重写filter方法(必须有@Order和@Component):
@Order(-1) //该注解决定过滤器的优先级,value越小,执行越靠前,或者实现一个Ordered接口并重写方法,在方法的return中标记数字也可以
@Component //注入IOC
public class AuthorizeFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 获取请求参数
ServerHttpRequest request = exchange.getRequest(); //获取请求
MultiValueMap<String, String> params = request.getQueryParams(); //获取全部参数
// 2. 获取参数中的authorization参数
String auth = params.getFirst("authorization"); //获取第一个匹配的value
// 3. 判断参数是否等于admin
if("admin".equals(auth)) {
// 4. 若等于、放行
return chain.filter(exchange); //放行当前过滤器,在过滤器中找到下一个过滤器,若当前是最后一个就直接放行
}
// 5. 若不等于、拦截并设置状态码
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); //未认证的枚举,其状态码为401
return exchange.getResponse().setComplete(); //拦截
}
}
请求进入网关之后会进入过滤器链中,并按顺序执行过滤器。
过滤器的种类有:路由过滤器、DefaultFilter、GlobalFilter,他们是被适配器模式所转换成GatewayFilter实例对象并进行排序的。
Order越小、执行越靠前,而对于路由器过滤器和DefaultFilter来说,会令order按照配置的顺序由1开始递增(defaultFilter和路由过滤器分开进行排序)
当过滤器的order值相同时,会按照defaultFilter > 路由过滤器 > GlobalFilter的顺序执行
跨域问题
跨域问题是指浏览器禁止请求发起者与客户端之间发生跨域(url不同、端口号不同)的ajax请求,请求会被浏览器拦截。
再网关中,使用CORS解决跨域问题
在yml文件中添加配置信息:
spring:
cloud:
gateway:
globalcors: # 全局跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求的拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
- "http://www.leyou.com"
allowedMethods: # 允许跨域的ajax请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 跨域检测的有效期
Docker
Docker是一个部署工具,我们在实际开发过程中会运用到许多许多的组件(NodeJS、Redis、RabbitMQ、Mysql)这些组件依赖的包、技术、和环境都不同,我们要去手动的部署这些信息是很不容易的,所以Docker就是来帮助我们进行快速部署的部署工具。
Docker基础知识
Docker如何解决依赖兼容性问题
- 将函数库(libs)和Deps(依赖)、配置与应用一起打包。
- 将每个应用放到一个隔离容器中去运行,避免其相互干扰(沙箱机制)。
Docker如何解决开发、测试、生产环境有差异的问题
- Docker镜像中包含完整的运行环境,包括系统函数库,仅依赖系统的Linux内核(不与Ubuntu、CentOS有关,其是将这些不同的应用函数一起打包的),因此可以在任意Linux操作系统上运行(直接依赖于Linux系统)
Docker架构
Docker是CS架构
- 服务端(server):Docker守护进程,负责Docker指令的处理,管理镜像、容器等等。
- 客户端(client):通过命令或RestAPI向Docker服务端发送命令,可以在本地或者远程进行指令发送
使用Docker命令得到Docker镜像(DockerHub(DockerRegistry)),再通过Docker镜像完成部署
Docker的安装
Docker安装之前需要先安装yum工具:
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2 --skip-broken
更新本地镜像源:
# 设置docker镜像源
yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g'
/etc/yum.repos.d/docker-ce.repo
yum makecache fast
安装docker:
yum install -y docker-ce
注意关闭防火墙:
# 关闭防火墙
systemctl stop firewalld
# 禁止开机启动防火墙
systemctl disable firewalld
开启Docker:
systemctl start docker
也可以使用下面命令来查看Docker的状态:
systemctl status docker
配置国内镜像(按步骤分开执行)
# 第一步创建
sudo mkdir -p /etc/docker
# 第二步创建json文件并输入内容
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors":["https://n0dwemtq.mirror.aliyuncs.com"]
}
EOF
# 重新加载并重启docker,这两个命令可以一起执行
sudo systemctl daemon-reload
sudo systemctl restart docker
Docker基本操作
Docker的镜像名称一般由两部分组成:[repository]:[tag]
其中repository指的是镜像名称,tag指的是版本,例如:mysql:5.7,若没有定义tag标签,则其默认会设置为最新版本的镜像。
基本命令:
# 本地构建镜像
docker build
# 从远程docker服务器拉取镜像
docker pull
# 查看当前镜像
docker images
# 删除镜像
docker rmi
# 推送镜像到服务(可能私服)
docker push
# 保存镜像为一个压缩包
docker save
# 加载镜像压缩包为镜像
docker load
案例:从DockerHub中拉取一个nginx镜像并查看
hub.docker.com
拉取nginx:
docker pull nginx
查看:
docker images
案例:使用docker save将nginx镜像导出磁盘,然后再通过load加载回来
# 创建docker镜像的tar文件
docker save -o nginx.tar nginx:latest
# 删除镜像
docker rmi nginx:latest
# 加载tar压缩下的镜像
docker load -i nginx.tar
Docker容器相关命令
# 创建一个容器
docker run
# 运行一个容器
docker unpause
# 暂停一个容器,将线程设为阻塞态
docker pause
# 停止一个容器,删除进程、内存回收
docker stop
# 从停止启动一个容器
docker start
# 查看容器运行日志
docker logs
# 查看所有运行的器及其状态
docker ps
# 进入容器执行命令
docker exec
# 删除指定容器,删除进程、内存回收、文件系统
docker rm
案例:创建并运行一个Nginx容器
docker run --name containerName -p 80:80 -d nginx
- docker run:创建一个容器
- –name: 给容器起一个名字,比如叫做mn
- -p:将宿主机端口与容器端口映射,冒号左边是宿主机端口,右边是容器端口
- -d:指定该容器为后台运行
- nginx:镜像名称
这样创建好nginx的容器之后就可以再外面进行访问了
案例:进入Nginx容器并修改HTML文件内容,添加自定义的内容
进入Nginx容器的命令:
docker exec -it mn bash
命令解读:
-
docker exec:进入容器内部,执行一个命令
-
-it:给当前进入的容器创建一个标准输入、输出终端,允许我们与容器进行交互
-
mn:要进入的容器的名称
-
bash:进入容器后要执行的命令,这里的bash是一个linux终端交互命令
这个命令会让我们进入容器内部,而容器内部其实也是一个小的阉割版的Linux系统
注意,nginx的默认静态文件会放在这个文件夹下:
/usr/share/nginx/html
直接修改某句话的命令:
下面命令会将文件中内容进行替换,第一句替换内容,第二句添加其中文编码格式
sed -i 's#Welcome to nginx#Hello World#g' index.html
sed -i 's#<head>#<head><meta charset="utf-8">#g' index.html
注意:exec命令可以进入容器修改文件,但在容器中修改文件是不推荐的。
创建Redis容器并实现持久化
Redis的容器创建命令:
docker run --name mr -p 6379:6379 -d redis redis-server --appendonly yes
- -p:设置redis的端口号
- -d:设置来的镜像名称
- –appendonly:设置为AOF模式
进入redis容器:
docker exec -it mr bash
进入redis-cli、keys *查看数据、set key value来设置键值对
数据卷
解决Docker容器与数据的耦合问题:
- 我们修改容器内内容必须进入容器
- 不便于重复利用已完成的代码
- 难以对数据进行升级维护
数据卷是一个虚拟目录,其指向宿主机文件系统中的某个目录。再将需要操作的容器内的文件夹挂载到数据卷上,这样就可以通过操作宿主机文件夹来操作容器内的文件夹
数据卷的相关操作命令:
# 创建数据卷,create后是数据卷的名称
docker volume create html
# 显示数据卷
docker volume ls
# 查看某个数据卷
docker volume inspect html
# 删除未使用的数据卷
docker vloume prune
# 删除指定的数据卷
docker volume rm html
数据卷的挂载操作(将数据卷挂载到文件目录中)
案例:创建一个nginx容器,修改容器内的html目录内的index.html内容
docker run --name mn -p 80:80 -v html:/usr/share/nginx/html -d nginx
-v:冒号右边的目录挂载到冒号左边的数据卷中
在这之后,我们去修改宿主机中对应的文件夹就可以对应修改容器中的文件了
我们可以使用Final Shell来控制编辑
案例:创建一个MySQL容器并将宿主机挂载到容器(目录挂载)
下载Mysql的压缩文件(Linux-Generic版本),
将Mysql.tar上传到虚拟机并加载为镜像:
docker load -i mysql.tar
创建两个文件夹用来挂在Mysql的data和conf文件夹用来做数据操作和配置文件。
mkdir mysql/data
mkdir mysql/conf
创建mysql容器
docker run \
--name mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-p 3306:3306 \
-v /tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf \
-v /tmp/mysql/data:/var/lib/mysql \
-d \
mysql:5.7.25
–name:容器名称
-e:一系列参数,这里配置的是数据库密码
-p:虚拟机端口号和容器端口号
-v:数据卷挂载
-d:后台启动
mysql:5.7.25:数据库以及其版本
之后就可以在Windows下的数据库管理软件中查看到数据库信息了。
Dockerfile自定义镜像
自定义镜像可以允许你自己创建自己的Java项目镜像,这样做可以让你自己将自己的Java项目部署在Docker上
镜像是指将应用程序及其需要的系统函数库、环境、配置、依赖打包而成的
基于操作系统的函数库(Ubuntu、CentOS)(基础镜像层) ----- 安装包 ---- 依赖 ---- 配置 ---- 入口
DockerFile
DockFile就是一个文本文件,其中包含一个个的指令,用指令来说明要执行什么操作来构建镜像。每个指令都会形成一层Layer
指令 | 说明 | 示例 |
---|---|---|
FROM | 指定基础镜像 | FROM centos:6 |
ENV | 设置环境变量 | ENV key value |
COPY | 拷贝本地文件到镜像的目录 | COPY ./mysql-5.7.rpm /tmp |
RUN | 执行Linux的Shell命令,一般是安装过程 | RUN yum install gcc |
EXPOSE | 指定容器运行时监听的窗口,是给镜像的使用者看的 | EXPOSE 8080 |
ENTRYPOINT | 镜像中应用的启动命令,容器运行时调用 | ENTRYPOINT java -jar xx.jar |
案例:
这里要先将三个文件放到一个文件夹里:
jdk8、Dockerfile(执行的文件)、docker-demo.jar(java部署的jar包)
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
执行docker的镜像部署命令将自己的java文件构建成dockerFile镜像
docker build -t javaweb:1.0 .
-t:添加java工程的名称以及版本、
.:代表Dockerfile文件的位置
构建下面文件:
docker run --name web -p 8090:8090 -d javaweb:1.0
访问下面地址就可以看到信息:
http://192.168.202.130:8090/hello/count
案例:基于java:8-alpine镜像简化Dockerfile文件
将Dockerfile修改成下面这样,也可以进行正常构建(优化了重复代码(jdk的配置等等))
# 指定基础镜像
FROM java:8-alpine
COPY ./docker-demo.jar /tmp/app.jar
# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
微服务项目部署:
修改端口,之前端口都是在本地上的,将其全部修改为nacos
使用Maven先clean再package,将生成的target文件夹下的app.jar拷贝到对应的有dockerfile文件的文件夹下并将其上传到服务器
进入到有docker-compose的文件下:
docker-compose up -d
查看日志信息:
docker-compose logs -f
搭建私有仓库
配置Docker信任地址
# 打开要修改的文件
vi /etc/docker/daemon.json
# 添加内容:
"insecure-registries":["http://192.168.150.101:8080"]
# 重加载
systemctl daemon-reload
# 重启docker
systemctl restart docker
配置Docker的私有仓库
创建一个文件夹与一个docker-compose文件
version: '3.0'
services:
registry:
image: registry
volumes:
- ./registry-data:/var/lib/registry
ui:
image: joxit/docker-registry-ui:static
ports:
- 8080:80
environment:
- REGISTRY_TITLE=传智教育私有仓库
- REGISTRY_URL=http://registry:5000
depends_on:
- registry
使用下面的命令启动这个文件
docker-compose up -d
在浏览器进入:192.168.202.130:8080就可以访问图形化界面的该仓库
推送镜像到私有仓库
# 重新tag本地镜像
docker tag nginx:latest 192.168.202.130:8080/nginx:1.0
# 推送镜像
docker push 192.168.202.130:8080/nginx:1.0
# 拉取镜像
docker pull 192.168.202.130:8080/nginx:1.0