文章目录
- 瑞吉外卖项目优化-Day02
- 课程内容
- 前言
- 1. MySQL主从复制
- 1.1 介绍
- 1.2 搭建
- 1.2.1 准备工作
- 1.2.2 主库配置
- 1.2.3 从库配置
- 1.3 测试
- 2. 读写分离案例 (shardingJdbc)
- 2.1 背景介绍
- 2.2 ShardingJDBC介绍
- 2.3 数据库环境
- 2.4 初始工程导入
- 2.5 读写分离配置
- 2.6 测试
- 3. 项目实现读写分离
- 3.1 数据库环境准备(导入导出sql文件)
- 3.2 创建Git分支
- 3.3 读写分离配置
- 启动服务器,SQLFeatureNotSupportedException报错解决
- 服务器mysql主从配置的设置是永久的
- 3.4 功能测试
- 3.5 Git合并代码
- 4. Nginx-概述
- 4.1 介绍
- 4.2 下载和安装
- 4.2.1 下载
- 4.2.2 安装
- 4.3 目录结构
- 5. Nginx-命令
- 5.1 常用命令
- 5.2 环境变量配置
- 6. Nginx-应用
- 6.1 配置文件结构
- 6.2 部署静态资源
- 6.2.1 介绍
- 6.2.2 测试
- 6.3 反向代理
- 6.3.1 概念介绍
- 6.3.2 测试
- 6.4 负载均衡
- 6.4.1 概念介绍
- 6.4.2 测试
- 6.4.3 负载均衡策略
瑞吉外卖项目优化-Day02
课程内容
- MySQL主从复制
- 读写分离案例
- 项目实现读写分离
- Nginx-概述
- Nginx-命令
- Nginx-应用
前言
1). 存在的问题
在前面基础功能实现的过程中,我们后台管理系统及移动端的用户,在进行数据访问时,都是直接操作数据库MySQL的。结构如下图:
而在当前,MySQL服务器只有一台,那么就可能会存在如下问题:
1). 读和写所有压力都由一台数据库承担,压力大
2). 数据库服务器磁盘损坏则数据丢失,单点故障
2). 解决方案
为了解决上述提到的两个问题,我们可以准备两台MySQL,一台主(Master)服务器,一台从(Slave)服务器,主库的数据变更,需要同步到从库中(主从复制)。而用户在访问我们项目时,如果是写操作(insert、update、delete),则直接操作主库;如果是读(select)操作,则直接操作从库(在这种读写分离的结构中,从库是可以有多个的),这种结构我们称为 读写分离 。
今天我们就需要实现上述的架构,来解决业务开发中所存在的问题。
1. MySQL主从复制
MySQL数据库默认是支持主从复制的,不需要借助于其他的技术,我们只需要在数据库中简单的配置即可。接下来,我们就从以下的几个方面,来介绍一下主从复制:
1.1 介绍
MySQL主从复制是一个异步的复制过程,底层是基于Mysql数据库自带的 二进制日志 功能。就是一台或多台MySQL数据库(slave,即从库)从另一台MySQL数据库(master,即主库)进行日志的复制,然后再解析日志并应用到自身,最终实现 从库 的数据和 主库 的数据保持一致。MySQL主从复制是MySQL数据库自带功能,无需借助第三方工具。
二进制日志:
二进制日志(BINLOG)记录了所有的 DDL(数据定义语言)语句和 DML(数据操纵语言)语句,但是不包括数据查询语句。此日志对于灾难时的数据恢复起着极其重要的作用,MySQL的主从复制, 就是通过该binlog实现的。默认MySQL是未开启该日志的。
MySQL的主从复制原理如下:
MySQL复制过程分成三步:
1). MySQL master 将数据变更写入二进制日志( binary log)
2). slave将master的binary log拷贝到它的中继日志(relay log)
3). slave重做中继日志中的事件,将数据变更应用到自己的数据库中
1.2 搭建
虚拟机可以右键-》管理-》克隆 很方便 自己百度一下,克隆完,修改下ip即可,很容易的
# 修改静态ip (文件里写了注释,很容易找到)
vim /etc/sysconfig/network-scripts/ifcfg-ens33
# 重启网络服务,立即生效
systemctl restart network
1.2.1 准备工作
提前准备两台服务器,并且在服务器中安装MySQL,服务器的信息如下:
数据库 | IP | 数据库版本 |
---|---|---|
Master | 192.168.141.100 | 5.7.25 |
Slave | 192.168.141.101 | 5.7.25 |
并在两台服务器上做如下准备工作:
1). 防火墙开放3306端口号
firewall-cmd --zone=public --add-port=3306/tcp --permanent
firewall-cmd --zone=public --list-ports
2). 并将两台数据库服务器启动起来:
systemctl start mysqld
登录MySQL,验证是否正常启动
1.2.2 主库配置
服务器: 192.168.141.100
注意下面都是在100主服务器下的配置
1). 修改Mysql数据库的配置文件/etc/my.cnf
在最下面增加配置:
log-bin=mysql-bin #[必须]启用二进制日志
server-id=100 #[必须]服务器唯一ID(唯一即可)
2). 重启Mysql服务
执行指令:
systemctl restart mysqld
3). 创建数据同步的用户并授权
登录mysql,并执行如下指令,创建用户并授权:
GRANT REPLICATION SLAVE ON *.* to 'xiaoming'@'%' identified by 'Root@123456';
注:上面SQL的作用是创建一个用户 xiaoming ,密码为 Root@123456 ,并且给xiaoming用户授予REPLICATION SLAVE权限。常用于建立复制时所需要用到的用户权限,也就是slave必须被master授权具有该权限的用户,才能通过该用户复制。
当然新用户名和密码可以自己指定的
MySQL密码复杂程度说明:
目前mysql5.7默认密码校验策略等级为 MEDIUM , 该等级要求密码组成为: 数字、小写字母、大写字母 、特殊字符、长度至少8位
4). 登录Mysql数据库,查看master同步状态
执行下面SQL,记录下结果中File和Position的值
show master status;
记录下File: mysql-bin.000002
Position: 441
注:上面SQL的作用是查看Master的状态,执行完此SQL后不要再执行任何操作(否则文件和位置都可能会变)
1.2.3 从库配置
服务器: 192.168.141.101
注意以下配置都在101服务器下执行
1). 修改Mysql数据库的配置文件/etc/my.cnf
server-id=101 #[必须]服务器唯一ID
从库配置个id就行了,主库才需要开启二进制日志,开始图示上也能看出来
主库: 二进制日志( binary log)
从库:中继日志(relay log)
2). 重启Mysql服务
systemctl restart mysqld
3). 登录Mysql数据库,设置主库地址及同步位置
change master
to master_host='192.168.141.100',
master_user='xiaoming',
master_password='Root@123456',
master_log_file='mysql-bin.000003',
master_log_pos=154;
启动从库复制日志的IO线程
start slave;
关闭也是: stop slave
可能需要改的
master_host='192.168.141.100' # ip改成自己主数据库服务器的
master_user='xiaoming' # 改成自己创建用户的 用户名
master_password='Root@123456' # 改成自己创建用户的 密码
master_log_file='mysql-bin.000002' # 改成刚刚主库查到的 File值
master_log_pos=154; # 改成刚刚主库查到的 Position
参数说明:
A. master_host : 主库的IP地址
B. master_user : 访问主库进行主从复制的用户名(上面在主库创建的)
C. master_password : 访问主库进行主从复制的用户名对应的密码
D. master_log_file : 从哪个日志文件开始同步(上述查询master状态中展示的有)
E. master_log_pos : 从指定日志文件的哪个位置开始同步(上述查询master状态中展示的有)
4). 查看从数据库的状态
show slave status;
将内容复制到文本编辑nodepad–里看
+----------------------------------+-----------------+-------------+-------------+---------------+------------------+---------------------+----------------------------+---------------+-----------------------+------------------+-------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+----------------------------+-----------+---------------------+--------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+
| Slave_IO_State | Master_Host | Master_User | Master_Port | Connect_Retry | Master_Log_File | Read_Master_Log_Pos | Relay_Log_File | Relay_Log_Pos | Relay_Master_Log_File | Slave_IO_Running | Slave_SQL_Running | Replicate_Do_DB | Replicate_Ignore_DB | Replicate_Do_Table | Replicate_Ignore_Table | Replicate_Wild_Do_Table | Replicate_Wild_Ignore_Table | Last_Errno | Last_Error | Skip_Counter | Exec_Master_Log_Pos | Relay_Log_Space | Until_Condition | Until_Log_File | Until_Log_Pos | Master_SSL_Allowed | Master_SSL_CA_File | Master_SSL_CA_Path | Master_SSL_Cert | Master_SSL_Cipher | Master_SSL_Key | Seconds_Behind_Master | Master_SSL_Verify_Server_Cert | Last_IO_Errno | Last_IO_Error | Last_SQL_Errno | Last_SQL_Error | Replicate_Ignore_Server_Ids | Master_Server_Id | Master_UUID | Master_Info_File | SQL_Delay | SQL_Remaining_Delay | Slave_SQL_Running_State | Master_Retry_Count | Master_Bind | Last_IO_Error_Timestamp | Last_SQL_Error_Timestamp | Master_SSL_Crl | Master_SSL_Crlpath | Retrieved_Gtid_Set | Executed_Gtid_Set | Auto_Position | Replicate_Rewrite_DB | Channel_Name | Master_TLS_Version |
+----------------------------------+-----------------+-------------+-------------+---------------+------------------+---------------------+----------------------------+---------------+-----------------------+------------------+-------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+----------------------------+-----------+---------------------+--------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+
| Waiting for master to send event | 192.168.141.100 | xiaoming | 3306 | 60 | mysql-bin.000001 | 441 | localhost-relay-bin.000004 | 320 | mysql-bin.000001 | Yes | Yes | | | | | | | 0 | | 0 | 441 | 697 | None | | 0 | No | | | | | | 0 | No | 0 | | 0 | | | 100 | a2fae4c0-e35a-11ed-8483-000c29591af8 | /var/lib/mysql/master.info | 0 | NULL | Slave has read all relay log; waiting for more updates | 86400 | | | | | | | | 0 | | | |
+----------------------------------+-----------------+-------------+-------------+---------------+------------------+---------------------+----------------------------+---------------+-----------------------+------------------+-------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+----------------------------+-----------+---------------------+--------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+
1 row in set (0.00 sec)
或者\G地看
然后通过状态信息中的 Slave_IO_running 和 Slave_SQL_running 可以看出主从同步是否就绪,如果这两个参数全为Yes,表示主从同步已经配置完成。
MySQL命令行技巧:
\G : 在MySQL的sql语句后加上\G,表示将查询结果进行按列打印,可以使每个字段打印到单独的行。即将查到的结构旋转90度变成纵向;
如果Slave_IO_Running显示为No
是因为虚拟机直接复制的,uuid重复了,解决参考这篇博客 , 或者自行百度
https://blog.csdn.net/qq_54217349/article/details/126501053
1.3 测试
主从复制的环境,已经搭建好了,接下来,我们可以通过Navicat连接上两台MySQL服务器,进行测试。测试时,我们只需要在主库Master执行操作,查看从库Slave中是否将数据同步过去即可。(上面的工作没问题,应该就会自动同步了,mysql牛哇~)
- 先建立两个连接(非常简单)
1). 在master(100)中创建数据库test01, 刷新slave(101)查看是否可以同步过去
写sql出问题,就用GUI来做吧
主库创建的数据库,从库自动复制过去了
2). 在master(100)的test01数据下创建user表, 刷新slave(101)查看是否可以同步过去
3). 在master的user表中插入一条数据, 刷新slave查看是否可以同步过去
2. 读写分离案例 (shardingJdbc)
2.1 背景介绍
面对日益增加的系统访问量,数据库的吞吐量面临着巨大瓶颈。 对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。
通过读写分离,就可以降低单台数据库的访问压力, 提高访问效率,也可以避免单机故障。
主从复制的结构,我们在第一节已经完成了,那么我们在项目中,如何通过java代码来完成读写分离呢,如何在执行select的时候查询从库,而在执行insert、update、delete的时候,操作主库呢?这个时候,我们就需要介绍一个新的技术 ShardingJDBC。
2.2 ShardingJDBC介绍
Sharding-JDBC定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
使用Sharding-JDBC可以在程序中轻松的实现数据库读写分离。
Sharding-JDBC具有以下几个特点:
1). 适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
2). 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
3). 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。
依赖:
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
2.3 数据库环境
在主库中创建一个数据库rw, 并且创建一张表, 该数据库及表结构创建完毕后会自动同步至从数据库,SQL语句如下:
create database rw default charset utf8mb4;
use rw;
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
报错:[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column ‘information_schema.PROFILING.SEQ’ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
其实可以不用管的,sql语句其实正常执行了
非要解决也行:
vim /etc/my.cnf
sql_mode= STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
在mysql配置文件末尾加上一行:
然后重启mysql服务
systemctl restart mysqld
注意这个时候show master status;
就变了,需要在从数据库重新设置下
2.4 初始工程导入
链接:https://pan.baidu.com/s/1lN8H5GbjhALZfZYnVRtU8w
提取码:eo13
我们本案例主要是演示一下读写分离操作,对于基本的增删改查的业务操作,我们就不再去编写了,我们可以直接导入资料中提供的demo工程(rw_demo),在demo工程中,我们已经完成了user的增删改查操作,具体的工程结构如下:
2.5 读写分离配置
1). 在pom.xml中增加shardingJdbc的maven坐标
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
2). 在application.yml中增加数据源的配置
spring:
sharding-sphere:
datasource:
names:
master,slave
# 主数据源
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.141.100:3306/rw?characterEncoding=utf-8&useSSL=false
username: root
password: 1234
# 从数据源
slave:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.141.101:3306/rw?characterEncoding=utf-8&useSSL=false
username: root
password: 1234
masters-lave:
# 读写分离配置
# 从库查的策略: 此处轮询查每个从数据库
load-balance-algorithm-type: round_robin #轮询
# 最终的数据源名称
name: dataSource
# 主库数据源名称
master-data-source-name: master
# 从库数据源名称列表,多个逗号分隔
slave-data-source-names: slave
props:
sql:
show: true #开启SQL显示,默认false
直接复制到application.yml最下面即可
配置解析:
注意有些配置得修改成自己的
3). 在application.yml中增加配置 (允许bean定义覆盖)
spring:
main:
allow-bean-definition-overriding: true
该配置项的目的,就是如果当前项目中存在同名的bean,后定义的bean会覆盖先定义的。
如果不配置该项,项目启动之后将会报错:
报错信息表明,在声明 org.apache.shardingsphere.shardingjdbc.spring.boot 包下的SpringBootConfiguration中的dataSource这个bean时出错, 原因是有一个同名的 dataSource 的bean在com.alibaba.druid.spring.boot.autoconfigure包下的DruidDataSourceAutoConfigure类加载时已经声明了。
而我们需要用到的是 shardingjdbc包下的dataSource,所以我们需要配置上述属性,让后加载的覆盖先加载的。
最后再启动服务器,发现,果然创建了2个数据源:
2.6 测试
我们使用shardingjdbc来实现读写分离,直接通过上述简单的配置就可以了。配置完毕之后,我们就可以重启服务,通过postman来访问controller的方法,来完成用户信息的增删改查,我们可以通过debug及日志的方式来查看每一次执行增删改查操作,使用的是哪个数据源,连接的是哪个数据库。
1). 保存数据
控制台输出日志,可以看到insert操作的确实是master主库:
查看2个数据库表,都有数据了,而且一模一样。两边的操作完全一致真好啊(注意只修改主库)
crud的java代码和之前完全一样,对于java工程来说,唯一不一样的就只有application.yml里的数据库配置了(然后mysql数据库也手动配置了一下)
java代码完全一样,只是yml配置不一样,mysql数据库配置了一下 直接就实现了mysql主从复制,框架好牛啊
2). 修改数据
控制台输出日志,可以看到操作master主库:
3). 查询数据
控制台输出日志,可以看到操作slave主库:
4). 删除数据
控制台输出日志,可以看到操作master主库:
3. 项目实现读写分离
3.1 数据库环境准备(导入导出sql文件)
直接使用我们前面在虚拟机中搭建的主从复制的数据库环境即可。在主库中创建瑞吉外卖项目的业务数据库reggie, 并导入相关表结构和数据(我们可以将自己之前在本地开发时使用的数据库数据导出, 然后导入到服务器中的主库即可)。
1). 将自己本地的reggie数据库的数据导出SQL文件
这样做的话,我们之前自己开发时,添加的测试数据都还在的,便于测试。
2). 在主数据库master中,创建数据库reggie,并导入该SQL文件
master中创建数据库,会自动同步至slave从库
在master的reggie中导入sql文件(选择刚刚导出的sql文件)
3.2 创建Git分支
目前默认git中有两个分支master 和 v1.0 ,我们接下来进行读写分离的优化,就不在master和v1.0分支来操作了,我们需要在git上创建一个单独的分支v1.1,读写分离的优化,我们就在该分支上进行操作。具体创建分支的操作,和前面演示的一致。
当前创建的v1.1分支,是基于master分支创建出来的,所以目前master分支的代码, 和v1.1分支的代码是完全一样的,接下来把v1.1的代码也推送至远程仓库。
3.3 读写分离配置
1). 在项目的pom.xml增加依赖
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
2). 在项目的application.yml中配置数据源相关信息
原来的dataSource删了,直接用下面的配置覆盖
注意第一行spring: 不需要复制
spring:
sharding-sphere:
datasource:
names:
master,slave
# 主数据源
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.141.100:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 1234
# 从数据源
slave:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.141.101:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 1234
masters-lave:
# 读写分离配置
# 从库查的策略: 此处轮询查每个从数据库
load-balance-algorithm-type: round_robin #轮询
# 最终的数据源名称
name: dataSource
# 主库数据源名称
master-data-source-name: master
# 从库数据源名称列表,多个逗号分隔
slave-data-source-names: slave
props:
sql:
show: true #开启SQL显示,默认false
main:
allow-bean-definition-overriding: true
启动服务器,SQLFeatureNotSupportedException报错解决
启动服务器,报错如下:
Caused by: java.sql.SQLFeatureNotSupportedException: isValid at org.apache.shardingsphere.shardingjdbc.jdbc.unsupported.AbstractUnsupportedOperationConnection.isValid
解决如下:https://huaweicloud.csdn.net/638768a8dacf622b8df8b62c.html
package cn.whu.reggie.config;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
public class DataSourceHealthConfig extends DataSourceHealthContributorAutoConfiguration {
@Value("${spring.datasource.dbcp2.validation-query:select 1}")
private String defaultQuery;
public DataSourceHealthConfig(Map<String, DataSource> dataSources, ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
super(dataSources, metadataProviders);
}
@Override
protected AbstractHealthIndicator createIndicator(DataSource source) {
DataSourceHealthIndicator indicator = (DataSourceHealthIndicator) super.createIndicator(source);
if (!StringUtils.hasText(indicator.getQuery())) {
indicator.setQuery(defaultQuery);
}
return indicator;
}
}
服务器mysql主从配置的设置是永久的
虚拟机崩了 重启之后发现,从库似乎可以自动检测到主库当前的File, 不用再在从库重新设置了
不信就主库退出mysql重新进入查看一下:
ctrl+z
mysql -uroot -p1234
show master status;
从库直接重新执行
show slave status\G;
真的就自动同步了耶
那么主库不需要设置,从库也不需要设置,不就是开机自启动mysql,就自动全部都配置好了吗,真方便,不用管服务器端mysql的配置了
3.4 功能测试
配置完毕之后,我们启动项目进行测试,直接浏览器访问系统管理后台的页面,然后执行相关业务操作,看控制台输出的日志信息即可。
查询操作:
更新操作:
插入操作:
删除操作:
3.5 Git合并代码
读写分离的功能我们已经实现完毕了,那么接下来,我们就可以将当前分支v1.1代码提交并推送到远程仓库。
然后,再将v1.1的代码,合并到master分支,然后推送至远程仓库。
先切换到master, 再在master下合并v1.1
4. Nginx-概述
4.1 介绍
Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx的网站有:百度、京东、新浪、网易、腾讯、淘宝等。
Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。
官网:https://nginx.org/
4.2 下载和安装
4.2.1 下载
在Nginx的官网的下载页面中(http://nginx.org/en/download.html),就展示了当前Nginx版本,并提供了下载的链接。 如下:
在本项目中,我们所学习的Nginx选择的是稳定版本的1.16这个版本,我们可以直接从官网下载,当然在我们的课程资料中也已经提供了该版本的安装包。
开梯子,下载更快
下面的安装命令是直接在linux端在线现在
4.2.2 安装
接下来的操作都在100主服务器上进行配置
1). 安装依赖包
由于nginx是基于c语言开发的,所以需要安装c语言的编译环境,及正则表达式库等第三方依赖库。
yum -y install gcc pcre-devel zlib-devel openssl openssl-devel
2). 下载Nginx安装包
yum install wget
cd ~/software/
wget https://nginx.org/download/nginx-1.16.1.tar.gz
wget : (没有就安装一下)
wget命令用来从指定的URL下载文件。wget非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性,如果是由于网络的原因下载失败,wget会不断的尝试,直到整个文件下载完毕。如果是服务器打断下载过程,它会再次联到服务器上从停止的地方继续下载。
执行完wget指令后,就会在当前所在目录看到下载下来的文件。
3). 解压nginx压缩包
tar -zxvf nginx-1.16.1.tar.gz
4). 配置Nginx编译环境
cd nginx-1.16.1
mkdir -p /usr/local/nginx
./configure --prefix=/usr/local/nginx
说明:
--prefix 指定的目录,就是我们安装Nginx的目录。
5). 编译&安装
nginx-1.16.1 解压文件所在目录下
make && make install
make: 编译
make install: 安装
会自动安装到我们上面配置的目录/usr/local/nginx
里面去
cd /usr/local/nginx/
ll
4.3 目录结构
安装完Nginx后,我们可以切换到Nginx的安装目录(/usr/local/nginx),先来熟悉一下Nginx的目录结构,如下图:
cd /usr/local/
tree nginx/
备注:
上述我们用到的一个指令 tree,该指令可以将我们指定的目录以树状结构展示出来。如果没有这个指令,可以通过以下指令进行安装。
yum install tree
重点目录和文件如下:
目录/文件 | 说明 | 备注 |
---|---|---|
conf | 配置文件的存放目录 | |
conf/nginx.conf | Nginx的核心配置文件 | conf下有很多nginx的配置文件,我们主要操作这个核心配置文件 |
html | 存放静态资源(html, css, ) | 部署到Nginx的静态资源都可以放在html目录中 |
logs | 存放nginx日志(访问日志、错误日志等) | |
sbin/nginx | 二进制文件,用于启动、停止Nginx服务 |
5. Nginx-命令
5.1 常用命令
Nginx中,我们的二进制可执行文件(nginx)存放在sbin目录下,虽然只有一个可执行文件,但是我们可以通过该指令配合不同的参数达到更加强大的功能。接下来,我们就演示一下Nginx常见指令, 在执行下面的指令时,都需要在/usr/local/nginx/sbin/目录下执行。
1). 查看版本
./nginx -v
2). 检查配置文件
修改了nginx.conf核心配置文件之后,在启动Nginx服务之前,可以先检查一下conf/nginx.conf文件配置的是否有错误,命令如下:
./nginx -t
3). 启动
./nginx
启动之后,我们可以通过ps -ef指令来查看nginx的进程是否存在。
注意: nginx服务启动后,默认就会有两个进程。
启动之后,我们可以直接访问Nginx的80端口, http://192.168.141.100/
所以tomcat就用8080端口,80端口给nginx
注意:
要想正常访问Nginx,需要关闭防火墙或开放指定端口号,执行的指令如下:
A. 关闭防火墙
systemctl stop firewalld
B. 开放80端口
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --reload
firewall-cmd --zone=public --list-ports
4). 停止
./nginx -s stop
停止之后,我们可以查看nginx的进程:
ps -ef|grep nginx
4.x). 补充
启动再停止nginx后,nginx目录下会出现一些*_temp临时目录,正常现象,我们不会操作它,也就不用管它
logs目录本来是空的,现在启动,关闭等操作执行后,应该就有一些文件了
nginx.pid里记录的是nginx的进程号,因此得先启动nginx,才能查看到
5). 重新加载
当修改了Nginx配置文件后,需要重新加载才能生效,可以使用下面命令重新加载配置文件:
./nginx -s reload
- lx:
cd /usr/local/nginx/conf/
vim nginx.conf
# 将默认worker进程数由1修改为2
重新加载配置文件,使之生效(当然不reload 直接重启nginx服务 也行 就是麻烦一点(毕竟reload一条命令就行了))
/usr/local/nginx/sbin/nginx -s reload
再查看
ps -ef | grep nginx
现在果然有两个worker process 了
5.2 环境变量配置
在上述我们在使用nginx命令在进行服务的启动、停止、重新加载时,都需要用到一个指令nginx,而这个指令是在nginx/sbin目录下的,我们每一次使用这个指令都需要切换到sbin目录才可以,使用相对繁琐。那么我们能不能在任意目录下都可以执行该指令来操作nginx呢?答案是可以的,配置nginx的环境变量即可。
通过vim编辑器,打开/etc/profile文件, 在PATH环境变量中增加nginx的sbin目录,如下:
vim /etc/profile
# PATH=后面或者 export PATH=后面加上nginx的sbin目录,注意中间以冒号隔开
/usr/local/nginx/sbin:
修改完配置文件之后,需要执行 source /etc/profile
使文件生效。 接下来,我们就可以在任意目录下执行nginx的指令了,如:
6. Nginx-应用
介绍了并安装了Nginx之后,本章节将要讲解的是Nginx的使用,我们主要从以下四个方面进行讲解。
6.1 配置文件结构
nginx的配置文件(conf/nginx.conf)整体上分为三部分: 全局块、events块、http块。这三块的分别配置什么样的信息呢,看下表:
区域 | 职责 |
---|---|
全局块 | 配置和nginx运行相关的全局配置 |
events块 | 配置和网络连接相关的配置 |
http块 | 配置代理、缓存、日志记录、虚拟主机等配置 |
(文件开始到events之前的所有行配置都是全局块,也就是没有被{}包裹的都是全局块)
具体结构图如下:
在全局块、events块以及http块中,我们经常配置的是http块。
在http块中可以包含多个server块,每个server块可以配置多个location块。
6.2 部署静态资源
6.2.1 介绍
Nginx可以作为静态web服务器来部署静态资源。这里所说的静态资源是指在服务端真实存在,并且能够直接展示的一些文件,比如常见的html页面、css文件、js文件、图片、视频等资源。
相对于Tomcat,Nginx处理静态资源的能力更加高效,所以在生产环境下,一般都会将静态资源部署到Nginx中。
将静态资源部署到Nginx非常简单,只需要将文件复制到Nginx安装目录下的html目录中即可。
server {
listen 80; #监听端口
server_name localhost; #服务器名称
location / { #匹配客户端请求url
root html; #指定静态资源根目录
index index.html; #指定默认首页
}
}
6.2.2 测试
在资料中,我们提供了一个静态的html文件,我们需要将这个文件部署到nginx中,然后通过nginx访问html静态资源。
1). 将静态资源上传到 /usr/local/nginx/html 目录
先在本地自己写一个简单的html页面: 命名为 hello.html 免得覆盖了系统自带的index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>我的Nginx</title>
</head>
<body>
<h1>我的Nginx</h1>
</body>
</html>
然后上传到: /usr/local/nginx/html
cd /usr/local/nginx/html
rz
可怕,直接不小心就覆盖了原来的,就这样吧
2). 启动nginx
3). 访问
http://192.168.141.100/hello.html
http://192.168.141.100 , 访问该地址,访问的是nginx的默认首页
4). 配置首页
cd /usr/local/nginx/conf
vim nginx.conf
1、默认首页是 index.html 或者 index.htm
2、在http块中可以包含多个server块, 所以可以复制多个server,从而监听多个端口
如果我们需要将hello.html作为nginx的首页,可以修改location的index指令,配置为hello.html,如下:
配置完毕后,我们可以通过指令,来检查配置文件是否配置正确: nginx -t
配置文件修改了,我们需要重新加载一下,才可以生效:
nginx -s reload
5). 访问
http://192.168.141.100
6.3 反向代理
6.3.1 概念介绍
1). 正向代理
正向代理服务器是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。
正向代理的典型用途是为在防火墙内的局域网客户端提供访问Internet的途径。
正向代理一般是在客户端设置代理服务器(用户知道其存在),通过代理服务器转发请求,最终访问到目标服务器。
eg: 公司是局域网,通过代理服务器才可以访问公网
2). 反向代理
反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源,反向代理服务器负责将请求转发给目标服务器。用户不需要知道目标服务器的地址,也无须在用户端作任何设定,对于用户来说,访问反向代理服务器是完全无感知的。
小结:
正向代理是客户端设置代理服务器,用户能感知其的存在
反向代理是服务端设置的代理服务器,用户不知道服务端是否做了代理,为用户隐藏了真实后台服务器的存在,不仅方便了多个后台服务器的管理,还使得真实的后台服务器可以部署在局域网上,不必直接暴露在公网上。
那么在本小节,我们就是要使用nginx来作为反向代理服务器使用。 在nginx中,我们可以在nginx.conf中配置反向代理:
server {
listen 82;
server_name localhost;
location / {
proxy_pass http://192.168.141.101:8080; #反向代理配置,将请求转发到指定服务
}
}
上述配置的含义为: 当我们访问nginx的82端口时,根据反向代理配置,会将请求转发到 http://192.168.141.101:8080 对应的服务上。
6.3.2 测试
需求: 在192.168.141.101这台从服务器中部署了java应用,运行端口为8080,并提供了一个可访问的链接 /hello。现在我们需要在访问nginx的82端口时,通过nginx将请求转发到 192.168.141.101:8080 从服务器的服务。
1). 在192.168.141.101部署服务并启动
将资料中提供的 helloworld-1.0-SNAPSHOT.jar 上传到服务器端,并通过指令 java -jar helloworld-1.0-SNAPSHOT.jar 运行服务。
cd /usr/local/app/helloProfix
# 没有这个目录就手动创建一下
rz
# 选中上传
java -jar helloworld-1.0-SNAPSHOT.jar
现在其实直接访问tomcat也是可以访问到的
http://192.168.141.101:8080/hello
2). 在192.168.141.100中的nginx.conf中配置反向代理
主服务器下,进入nginx的安装目录,并编辑配置文件nginx.conf:
cd /usr/local/nginx/conf/
vim nginx.conf
在http块中,再添加一个server块虚拟主机的配置,监听82端口,并配置反向代理proxy_pass:
server {
listen 82;
server_name localhost;
location / {
proxy_pass http://192.168.141.101:8080; #反向代理配置,将请求转发到指定服务
}
}
注意和上个server同级
3). 检查配置文件,并重新加载
nginx -t
nginx -s reload
4). 访问
http://192.168.141.100:82/hello
★ nginx配置好了 那么访问 http://192.168.141.100:82/hello , 由于访问的是 nginx的82端口,而nginx反向代理一检测到82端口的请求,就立刻转发到配置好的 http://192.168.141.101:8080 也就是 会转发请求为 http://192.168.141.101:8080/hello 但是用户却感知不到
注意: 在访问82端口时,有可能访问不通,原因是以为防火墙中没有开放端口号。我们可以通过两种方式来解决该问题:
A. 关闭防火墙
systemctl stop firewalld
B. 开发指定端口
firewall-cmd --zone=public --add-port=82/tcp --permanent
firewall-cmd --reload
6.4 负载均衡
6.4.1 概念介绍
早期的网站流量和业务功能都比较简单,单台服务器就可以满足基本需求,但是随着互联网的发展,业务流量越来越大并且业务逻辑也越来越复杂,单台服务器的性能及单点故障问题就凸显出来了,因此需要多台服务器组成应用集群,进行性能的水平扩展以及避免单点故障出现。
应用集群: 将同一应用部署到多台机器上,组成应用集群,接收负载均衡器分发的请求,进行业务处理并返回响应数据
负载均衡器: 将用户请求根据对应的负载均衡算法分发到应用集群中的一台服务器进行处理
此处的负载均衡器,我们将会使用Nginx来实现,而Nginx的负载均衡是基于反向代理的,只不过此时所代理的服务器不是一台,而是多台。
6.4.2 测试
1). 将资料中提供的两个jar包,上传到192.168.141.101服务器上
我们在测试时,并没有那么多服务器,我们可以在一台服务器中启动多个服务,运行在不同的端口号上进行测试。
2). 运行上传上来的两个jar包,运行端口分别是 8080 , 8081
由于我们执行 java -jar 指令会占用前台窗口,所以我们可以开启两个窗口进行测试。
注意开一下8081端口
3). 在nginx中配置负载均衡
打开nginx的配置文件nginx.conf并增加如下配置:
upstream targetserver{ #upstream指令可以定义一组服务器
server 192.168.141.101:8080;
server 192.168.141.101:8081;
}
server {
listen 8080;
server_name localhost;
location / {
proxy_pass http://targetserver;
}
}
upstream是关键字,targetserver是可以自己取的名字
配置了2个转发的tomcat服务器地址,会自动以负载均衡算法(默认轮询算法)轮询转发到2个服务器
具体的配置位置如下:
4). 重新加载nginx配置文件,访问
nginx -s reload
测试时,我们直接访问nginx的8080端口(http://192.168.200.200:8080), 此时nginx会根据负载均衡策略,将请求转发到后面的两台服务器。
在上述的测试过程中,我们看到请求均衡的转发到了8080和8081,因为模式的负载均衡策略是轮询。
如果干掉一个服务器,则会智能地只转发给另一个正常的服务器
注意: 上述所有涉及到的端口号,都需要在对应的服务器的防火墙中开放,或者彻底关闭防火墙
6.4.3 负载均衡策略
负载均衡策略nginx服务器都已经帮你做好了,简单配置一下就行,多简单啊~
处理上述默认的轮询策略以外,在Nginx中还提供了其他的负载均衡策略,如下:
名称 | 说明 | 特点 |
---|---|---|
轮询 | 默认方式 | |
weight | 权重方式 | 根据权重分发请求,权重大的分配到请求的概率大 |
ip_hash | 依据ip分配方式 | 根据客户端请求的IP地址计算hash值, 根据hash值来分发请求, 会导致同一个IP发起的请求, 一定会转发到同一个服务器上 |
least_conn | 依据最少连接方式 | 哪个服务器当前处理的连接少, 请求优先转发到这台服务器 |
url_hash | 依据url分配方式 | 根据客户端请求url的hash值,来分发请求, 同一个url请求, 会发转发到同一个服务器上 |
fair | 依据响应时间方式 | 优先把请求分发给处理请求时间短的服务器 |
权重的配置:
#upstream指令可以定义一组服务器
upstream targetserver{
server 192.168.200.201:8080 weight=10;
server 192.168.200.201:8081 weight=5;
}
上述配置的weight权重是相对的,在上述的配置中,效果就是,在大数据量的请求下,最终8080接收的请求数是8081的两倍。(请求数量足够多时就是2:1)