记录一次分布式微服务架构搭建过程

一、目标架构图

 
其中,nacos,nginx,mysql,mq,redis,gateway,服务均为集群模式。使用docker模拟集群分布。

 

  • Spring Boot 2.3.9.RELEASE
  • Spring Cloud H版
  • Cloud Alibaba 2.2.5.RELEASE
  • RabbitMq 3.7.14
  • Redis 5.0
  • mysql 5.7.*
  • Docker 18.09.0
  • JWT 0.9.0
  • Lombok 1.18.6
  • Maven 5.1



二、环境部署

所有的环境都是在服务器上用docker模拟分布式集群的方式搭建的



1.docker安装

 curl -sSL https://get.daocloud.io/docker | sh
在服务器终端中运行该指令



2.数据库



2.1docker安装mysql数据库5.7



2.1.1搭建单体数据库

1. 拉取 MySQL 镜像
docker pull mysql:5.7

2. 设置 MySQL 配置文件
将全部的配置文件和关联的文件夹统一放到 /opt/docker/mysql 中
创建 MySQL 配置文件文件夹
mkdir -p /opt/docker/mysql/conf.d
增加并修改配置文件 config-file.cnf
vim /opt/docker/mysql/conf.d/config-file.cnf
输入如下内容:

 
  1. [mysqld]
  2. # 设置表名不区分大小写 linux下默认是区分的,windows下默认不区分
  3. lower_case_table_names=1
  4. #server-id=1
  5. datadir=/var/lib/mysql
  6. #socket=/var/lib/mysql/mysqlx.sock
  7. #symbolic-links=0
  8. # sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
  9. [mysqld_safe]
  10. log-error=/var/log/mysqld.log
  11. pid-file=/var/run/mysqld/mysqld.pid

3. 启动
创建 MySQL 数据文件夹
mkdir -p /opt/docker/mysql/var/lib/mysql
启动,设置默认密码 root,TZ 设置容器的默认时区
docker run —name mysql \
—restart=always \
-p 3306:3306 \
-v /opt/docker/mysql/conf.d:/etc/mysql/conf.d \
-v /opt/docker/mysql/var/lib/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-e TZ=Asia/Shanghai \
-d mysql:5.7

4. 修改密码

 
  1. docker exec -it mysql bash
  2. 进入 MySQL
  3. mysql -uroot -p
  4. 输入刚才我们设置的密码 root
  5. 授权
  6. mysql> GRANT ALL ON *.* TO 'root'@'%';
  7. 刷新权限
  8. mysql> flush privileges;
  9. 更新加密规则
  10. mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'root' PASSWORD EXPIRE NEVER;
  11. 更新 root 密码
  12. mysql> ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
  13. 刷新权限
  14. mysql> flush privileges;
  15. 查看用户
  16. mysql> use mysql;mysql> select user,host from user;
  17. 退出容器
  18. exit

有此条记录即成功

 
navicat链接数据库测试,成功。



2.1.2搭建主从mysql数据库,且读写分离

直接docker启动两个mysql容器,分别叫mysql-master和mysql-slave;从数据库映射到3302端口。
 
进入主数据库,安装vim,编辑my.cnf文件如下

 
  1. [mysqld]
  2. log-bin=/var/log/mysql/mysql-bin
  3. server-id=1
  4. gtid_mode=ON
  5. enforce_gtid_consistency=1 # 强制执行GTID一致性。

如果省略server-id(或将其显式设置为默认值0),则主服务器拒绝来自从服务器的任何连接。如果省略server-id(或将其显式设置为默认值0),则主服务器拒绝来自从服务器的任何连接。

创建日志目录并赋予权限

 
  1. mkdir /var/log/mysql
  2. chown mysql.mysql /var/log/mysql

重启服务:systemctl restart mysqld
创建一个专门用于复制数据的用户

每个从服务器需要使用MySQL 主服务器上的用户名和密码连接到主站。计划使用用户 repl 可以从任何主机上连接到 master 上进行复制操作, 并且用户 repl 仅可以使用复制的权限。

创建repl用户
CREATE USER 'repl'@'%' IDENTIFIED BY '123456789';
用户名:repl
主机:使用通配符%,允许任意远程主机登陆
密码:123456789

对repl用户进行授权
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
在从库测试用户有效性
mysql -urepl -p'123456789' -h192.168.0.101

之后再配置从库的my.cnf
vim /etc/my.cnf

 
  1. [mysqld]
  2. server-id=2
  3. gtid_mode=ON
  4. enforce_gtid_consistency=1
  5. # 可选项, 把连接到 master 的信息存到数据库中的表中
  6. master-info-repository=TABLE
  7. relay-log-info-repository=TABLE
  8. read_only=1#可以设置只读

重启从数据库服务
systemctl restart mysqld
开启Master-Slave主从复制
进入Master库mysql客户端:输入show master status查看Master状态:

  1. change master to master_host='172.18.0.3',
  2. master_user='slave',
  3. master_password='123456',
  4. master_port=3306,
  5. master_log_file='master-bin.000001',
  6. master_log_pos=617,
  7. master_connect_retry=30;

在从数据库中执行start slave;开启主从
show slave status \G查看主从状态。



2.2执行nacos数据库的脚本

新建数据库nacos
之后运行sql脚本
脚本地址:nacos/nacos-mysql.sql at develop · alibaba/nacos · GitHub



3.nacos集群搭建

  • docker拉取nacos镜像:docker pull nacos/nacos-server:2.0.3
  • 依次启动三个nacos服务,分别以8846,8847,8848端口启动
    下面是docker compose脚本
 
  1. version: "3"
  2. services:
  3. nacos1:
  4. container_name: nacos-server01
  5. hostname: nacos-server01
  6. image: nacos/nacos-server:2.0.3
  7. environment:
  8. - MODE=cluster
  9. - PREFER_HOST_MODE=hostname
  10. - NACOS_SERVERS=nacos-server01:8848 nacos-server02:8848 nacos-server03:8848
  11. - SPRING_DATASOURCE_PLATFORM=mysql
  12. - MYSQL_SERVICE_HOST=你的mysql 地址
  13. - MYSQL_SERVICE_PORT=3306
  14. - MYSQL_SERVICE_USER=root
  15. - MYSQL_SERVICE_PASSWORD=123456
  16. - MYSQL_SERVICE_DB_NAME=nacos
  17. - JVM_XMS=256m
  18. - JVM_XMX=256m
  19. - JVM_XMN=256m
  20. volumes:
  21. - /home/nacos/cluster-logs/nacos-server01:/home/nacos/logs
  22. - /home/nacos/init.d:/home/nacos/init.d
  23. ports:
  24. - 8846:8848
  25. - 9555:9555
  26. - 9847:9849
  27. restart: on-failure
  28. nacos2:
  29. container_name: nacos-server02
  30. hostname: nacos-server02
  31. image: nacos/nacos-server:2.0.3
  32. environment:
  33. - MODE=cluster
  34. - PREFER_HOST_MODE=hostname
  35. - NACOS_SERVERS=nacos-server01:8848 nacos-server02:8848 nacos-server03:8848
  36. - SPRING_DATASOURCE_PLATFORM=mysql
  37. - MYSQL_SERVICE_HOST=你的mysql地址
  38. - MYSQL_SERVICE_PORT=3306
  39. - MYSQL_SERVICE_USER=root
  40. - MYSQL_SERVICE_PASSWORD=123456
  41. - MYSQL_SERVICE_DB_NAME=nacos
  42. - JVM_XMS=256m
  43. - JVM_XMX=256m
  44. - JVM_XMN=256m
  45. volumes:
  46. - /home/nacos/cluster-logs/nacos-server02:/home/nacos/logs
  47. - /home/nacos/init.d:/home/nacos/init.d
  48. ports:
  49. - 8847:8848
  50. - 9848:9849
  51. restart: on-failure
  52. nacos3:
  53. container_name: nacos-server03
  54. hostname: nacos-server03
  55. image: nacos/nacos-server:2.0.3
  56. environment:
  57. - MODE=cluster
  58. - PREFER_HOST_MODE=hostname
  59. - NACOS_SERVERS=nacos-server01:8848 nacos-server02:8848 nacos-server03:8848
  60. - SPRING_DATASOURCE_PLATFORM=mysql
  61. - MYSQL_SERVICE_HOST=你的mysql地址
  62. - MYSQL_SERVICE_PORT=3306
  63. - MYSQL_SERVICE_USER=root
  64. - MYSQL_SERVICE_PASSWORD=123456
  65. - MYSQL_SERVICE_DB_NAME=nacos
  66. - JVM_XMS=128m
  67. - JVM_XMX=128m
  68. - JVM_XMN=128m
  69. volumes:
  70. - /home/nacos/cluster-logs/nacos-server03:/home/nacos/logs
  71. - /home/nacos/init.d:/home/nacos/init.d
  72. ports:
  73. - 8848:8848
  74. - 9849:9849
  75. restart: on-failure

8848端口是nacos后台管理界面的端口,9849端口是nacos集群之间互相通信需要的端口。 为了三个nacos的通信,必须映射到宿主机。

运行上面的docker文件,创建docker容器
docker-compose -f cluster-hostname.yaml up -d
访问三个nacos服务,查看是否配置成功。

还要使用nignx来做nacos集群的负载均衡
Nginx 的部署与其他服务部署略有不同,我们需要先启动一个 Nginx 容器实例,然后从容器事例中拷贝出 Nginx 的配置文件到指定目录,之后我们将复制出的配置文件与 Nginx 容器的数据卷进行挂载,从而达到可以在容器外部修改配置文件的目的。这么做是因为,如果直接挂载,那么容器实例中的的目录将会被外部的挂载所覆盖。这是官方 Nginx 镜像的一个小缺陷,注意一下就行了。

 
  1. 运行 Nginx 容器
  2. $ docker run --name temp-nginx -p 8080:8080 -d nginx:latest
  3. 在宿主机创建 Nginx 的挂载目录
  4. # 实际创建时以自己的机器环境为准
  5. $ mkdir -p <宿主机挂载目录>
  6. 拷贝容器中的配置到宿主机的挂载目录
  7. $ docker cp <Nginx容器ID>:/etc/nginx/ <宿主机挂载目录>
  8. 停止并删除容器实例
  9. $ docker stop <Nginx容器ID>
  10. $ docker rm <Nginx容器ID>

修改 Nginx 配置文件

位置:<宿主机挂载目录>/conf.d/default.conf

配置文件主要修改两个部分,一个是新增 upstream,通过负载均衡来配置 Nacos 服务的节点;第二个是修改 server 下的 location,在其中添加反向代理配置。另外,如果你的 Nginx 没有配置过 server_name,那么还需要修改 server 下的 server_name 配置。

完整配置文件如下:

 
  1. # 添加负载均衡配置
  2. upstream nacos {
  3. server 服务器地址:8846 weight=2 max_fails=2 fail_timeout=10s;
  4. server 服务器地址:8847 weight=2 max_fails=2 fail_timeout=10s;
  5. server 服务器地址:8848 weight=1 max_fails=2 fail_timeout=10s;
  6. }
  7. server {
  8. listen 80;
  9. listen [::]:80;
  10. # 修改为宿主机的 IP地址
  11. server_name 服务器地址;
  12. #access_log /var/log/nginx/host.access.log main;
  13. location / {
  14. # 添加代理配置
  15. proxy_pass http://nacos;
  16. proxy_set_header Host $host;
  17. proxy_set_header X-Real-IP $remote_addr;
  18. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  19. proxy_set_header REMOTE-HOST $remote_addr;
  20. add_header X-Cache $upstream_cache_status;
  21. add_header Cache-Control no-cache;
  22. #root /usr/share/nginx/html;
  23. #index index.html index.htm;
  24. }
  25. #error_page 404 /404.html;
  26. # redirect server error pages to the static page /50x.html
  27. #
  28. error_page 500 502 503 504 /50x.html;
  29. location = /50x.html {
  30. root /usr/share/nginx/html;
  31. }
  32. # proxy the PHP scripts to Apache listening on 127.0.0.1:80
  33. #
  34. #location ~ \.php$ {
  35. # proxy_pass http://127.0.0.1;
  36. #}
  37. # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
  38. #
  39. #location ~ \.php$ {
  40. # root html;
  41. # fastcgi_pass 127.0.0.1:9000;
  42. # fastcgi_index index.php;
  43. # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
  44. # include fastcgi_params;
  45. #}
  46. # deny access to .htaccess files, if Apache's document root
  47. # concurs with nginx's one
  48. #
  49. #location ~ /\.ht {
  50. # deny all;
  51. #}
  52. }

编排 Nginx启动文件

 
  1. version: "3"
  2. services:
  3. nacos-nginx:
  4. container_name: nacos-nginx
  5. image: nginx:latest
  6. volumes:
  7. - /home/naocs-nginx:/etc/nginx
  8. ##注意这里容器挂在的位置
  9. ports:
  10. - 18848:80
  11. restart: on-failure

访问http://服务器ip:18848/nacos/ 查看效果



4.构造生产者消费者服务测试



4.1父工程创建

在本机上下载安装idea2021(具体方法baidu)
新建父工程cloud-demo
打开pom文件,在properties中控制整个项目的版本

 
  1. <properties>
  2. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  3. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  4. <java.version>1.8</java.version>
  5. <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
  6. <mysql.version>5.1.47</mysql.version>
  7. <mybatis.version>2.1.1</mybatis.version>
  8. </properties>

依赖管理

 
  1. <dependencyManagement>
  2. <dependencies>
  3. <!-- springCloud -->
  4. <dependency>
  5. <groupId>org.springframework.cloud</groupId>
  6. <artifactId>spring-cloud-dependencies</artifactId>
  7. <version>${spring-cloud.version}</version>
  8. <type>pom</type>
  9. <scope>import</scope>
  10. </dependency>
  11. <!--nacos的管理依赖-->
  12. <dependency>
  13. <groupId>com.alibaba.cloud</groupId>
  14. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  15. <version>2.2.5.RELEASE</version>
  16. <type>pom</type>
  17. <scope>import</scope>
  18. </dependency>
  19. <!-- mysql驱动 -->
  20. <dependency>
  21. <groupId>mysql</groupId>
  22. <artifactId>mysql-connector-java</artifactId>
  23. <version>${mysql.version}</version>
  24. </dependency>
  25. <!--mybatis-->
  26. <dependency>
  27. <groupId>org.mybatis.spring.boot</groupId>
  28. <artifactId>mybatis-spring-boot-starter</artifactId>
  29. <version>${mybatis.version}</version>
  30. </dependency>
  31. </dependencies>
  32. </dependencyManagement>
  33. <dependencies>
  34. <dependency>
  35. <groupId>org.projectlombok</groupId>
  36. <artifactId>lombok</artifactId>
  37. </dependency>
  38. </dependencies>



4.2生产者服务集群配置

新建module:service-provider1
修改pom文件,在resources中新建application.yml文件。新建启动类。写一个简单的接口,如下。

 
  1. server:
  2. port: 9001
  3. spring:
  4. datasource:
  5. url: jdbc:mysql://10.245.153.168:3306/cloud_order?useSSL=false
  6. username: root
  7. password: 123456
  8. driver-class-name: com.mysql.jdbc.Driver
  9. application:
  10. name: service-provider
  11. cloud:
  12. nacos:
  13. server-addr: 10.245.153.168:18848 # nacos服务地址
  14. @SpringBootApplication
  15. @EnableDiscoveryClient
  16. public class TestServiceMain {
  17. public static void main(String[] args) {
  18. SpringApplication.run(TestServiceMain.class,args);
  19. }
  20. }
  21. @RestController
  22. public class TestController {
  23. @GetMapping(value = "/test/gethello")
  24. public String getHello(){
  25. return "hello,service provider 1";
  26. }
  27. }

再仿照service-provider1创造项目service-provider2。记得修改端口为9002



4.3消费者服务配置

很简单就是用openfeign同步调用生产者的接口。不要忘记也将消费者注册进nacos。



4.4上传到服务器

使用的docker来运行springboot程序
maven clear;
maven install;
将jar包命名为service-provider1,service-provider2,上传到服务器,用指令sudo rz。
之后拉取一个有java运行环境的镜像。

 
  1. docker run -d --name service-provider1 -p 9001:9001 -p 19001:19001 \
  2. -v /mydata/service-provider1.jar:/usr/app.jar java:8 \
  3. java -jar -Xms256m -Xmx256m /usr/app.jar
  4. docker run -d --name service-provider2 -p 9002:9002 -p 19002:19002 \
  5. -v /mydata/service-provider2.jar:/usr/app.jar java:8 \
  6. java -jar -Xms256m -Xmx256m /usr/app.jar



5.GateWay网关集群

由于是测试,就先搭建一个集群,里面运行两个网关服务



5.1新建项目

module名:gateway
选择本机的jdk1.8,创建一个空白的maven项目。设置项目坐标gav,其中groupid为父工程groupid
 
在pom文件中导入依赖

 
  1. <dependencies>
  2. <!--nacos服务注册发现依赖-->
  3. <dependency>
  4. <groupId>com.alibaba.cloud</groupId>
  5. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  6. </dependency>
  7. <!--网关gateway依赖-->
  8. <dependency>
  9. <groupId>org.springframework.cloud</groupId>
  10. <artifactId>spring-cloud-starter-gateway</artifactId>
  11. </dependency>
  12. </dependencies>

编写application.yml文件,启动的端口为10010

 
  1. server:
  2. port: 10010
  3. logging:
  4. level:
  5. cn.itcast: debug
  6. pattern:
  7. dateformat: MM-dd HH:mm:ss:SSS
  8. spring:
  9. application:
  10. name: gateway
  11. cloud:
  12. nacos:
  13. server-addr: 10.245.153.168:18848 # nacos地址
  14. gateway:
  15. routes:
  16. default-filters:
  17. - AddRequestHeader=Truth,Itcast is freaking awesome!
  18. - AddRequestHeader=origin,gateway

创建主启动类
 
写一个权限过滤器组件

 
  1. @Component
  2. public class AuthorizeFilter implements GlobalFilter, Ordered {
  3. @Override
  4. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  5. // 1.获取请求参数
  6. ServerHttpRequest request = exchange.getRequest();
  7. MultiValueMap<String, String> params = request.getQueryParams();
  8. // 2.获取参数中的 authorization 参数
  9. String auth = params.getFirst("authorization");
  10. // 3.判断参数值是否等于 admin
  11. if ("admin".equals(auth)) {
  12. // 4.是,放行
  13. return chain.filter(exchange);
  14. }
  15. // 5.否,拦截
  16. // 5.1.设置状态码
  17. exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
  18. // 5.2.拦截请求
  19. return exchange.getResponse().setComplete();
  20. }
  21. @Override
  22. public int getOrder() {
  23. return -1;
  24. }
  25. }

在创建一个几乎完全相同的子项目gateway2,但是启动端口为10011,服务名不要改动

在两个网关服务中的yml文件里新增路由配置:



5.2启动测试

启动编写的网关集群,查看nacos服务列表
 
通过网关访问下我们的服务提供者


可见服务调用默认为轮询策略



5.3上传jar包到服务器测试

将gateway与gateway2进行一些maven install来生成jar包,然后用rz指令上传的服务器
 
我们都知道jar是运行在java环境中,所以只要容器中有java环境就可以运行jar包,镜像方式运行的原理也是如此。因此我们基于java镜像就可以实现jar包运行。
拉取java镜像
docker pull java:8
之后使用docker运行jar包

 
  1. docker run -d --name gateway1 -p 10010:10010 \
  2. -v /mydata/gateway1.jar:/usr/app.jar java:8 \
  3. java -jar -Xms256m -Xmx256m /usr/app.jar
  4. docker run -d --name gateway2 -p 10011:10011 \
  5. -v /mydata/gateway2.jar:/usr/app.jar java:8 \
  6. java -jar -Xms256m -Xmx256m /usr/app.jar

运行完后查看下效果
 

可见,gateway网关运行在的docker上时,显示的ip为docker中的虚拟ip,可以在application.yml文件中添加属性:
spring.cloud.nacos.discovery.ip:外网ip来修改。



5.4用nginx做负载均衡

类比做nacos集群的负载均衡,方法是一样的。
需要注意的是nginx配置文件要挂在到gateway-nginx文件夹下,防止产生冲突。



6.sentinel服务降级,流量控制



6.1安装

拉取镜像
docker pull bladex/sentinel-dashboard
运行docker file

 
  1. docker run -dit \
  2. --name sentinel-nacos \
  3. -p 18858:8858 \
  4. --restart=always \
  5. -e NACOS_SERVER_ADDR=10.245.153.168:18848 \
  6. -e NACOS_USERNAME=nacos \
  7. -e NACOS_PASSWORD=nacos \
  8. -e NACOS_NAMESPACE=public \
  9. -e NACOS_GROUP_ID=SENTINEL_GROUP \
  10. bladex/sentinel-dashboard

访问http://你的ip:18858/ , 输入用户名密码都是sentinel
看看后台页面
 
将之前的service-provider1项目进行sentinel流控,修改yml文件



注意,由于我们的sentinel运行在服务器里的docker中,故要添加spring.cloud.sentinel.transport.client-ip属性,该属性为你的电脑的公网ip,也就是service-provider1项目运行在的主机的ip
运行起项目,访问几次gethello接口

添加一个流控规则,在看看访问接口的反应



6.2持久化规则

当我们一旦重启sentinel应用,sentinel写的规则消失,生产环境需要将配置规则进行持久化。
在我们之前建立的service-provider1项目中的pom文件里 ,添加依赖
 
yml文件中,添加



在nacos配置中心新增配置

注意属性的一一对应
运行下sentinel,访问刚才添加配置的接口,规则成功

再重启sentinel,发现规则还在。

只能通过Nacos修改规则配置,通过Dashboard修改规则配置不自动刷新Nacos配置,重启Dashboard后只保留Nacos中数据!!!



6.4到目前为止的项目架构



7.redis



7.1redis集群方式

1.主从复制
2.哨兵集群
3.redis-cluster集群
我们本次使用redis主从复制的形式搭建redis集群
什么是主从复制?
答:就是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。默认情况下,每台Redis服务器都是主节点,且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。给大家个图来理解下:
 
主从复制有什么作用?

答:①:数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

②:故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。换句话说就是主服务器挂掉了,从服务器顶上去。

③:负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。



7.2开始搭建

目标:一主,一从,测试用
先拉取镜像
docker run —name redis-master -d -p 16379:6379 redis:latest
docker run —name redis-master -d -p 16380:6379 redis:latest
使用指令docker exec 和redis-cli
分别进入两个redis数据库,查看ip信息输入info

 
  1. 172.17.0.4
  2. 172.17.0.5

我们把172.17.0.5作为随从数据库,在其reids终端中输入
SLAVEOF 主ip 主端口(均是前面的ip与端口)
之后再用info指令查看是否修改成功。
 
 



7.3测试

我们在主节点设置一个属性 set name : why
我们在从随从节点获取 get name : why
成功
 



7.4进一步扩展

可以在部署哨兵模式或者分片集群模式。



8.rabbitMQ消息队列



8.1RabbitMQ安装(单机版)

在线拉取镜像

 
  1. docker pull rabbitmq:3-management

执行下面的命令来运行MQ容器

 
  1. docker run \
  2. -e RABBITMQ_DEFAULT_USER=admin \
  3. -e RABBITMQ_DEFAULT_PASS=123456 \
  4. --name mq \
  5. --hostname mq1 \
  6. -p 15672:15672 \
  7. -p 5672:5672 \
  8. -d \
  9. rabbitmq:3-management

启动成功后访问地址:http://服务器ip:15672
按照上面的用户名密码登录




8.2RabbitMQ安装(集群版)

在RabbitMQ的官方文档中,讲述了两种集群的配置方式:

  • 普通模式:普通模式集群不进行数据同步,每个MQ都有自己的队列、数据信息(其它元数据信息如交换机等会同步)。例如我们有2个MQ:mq1,和mq2,如果你的消息在mq1,而你连接到了mq2,那么mq2会去mq1拉取消息,然后返回给你。如果mq1宕机,消息就会丢失。
  • 镜像模式:与普通模式不同,队列会在各个mq的镜像节点之间同步,因此你连接到任何一个镜像节点,均可获取到消息。而且如果一个节点宕机,并不会导致数据丢失。不过,这种方式增加了数据同步的带宽消耗。

我们先来看普通模式集群,我们的计划部署两节点的mq集群:
| mq1 | 15672 —-> 15672 | 5672 —-> 5672 |
| mq2 | 15673 —-> 15672 | 5673 —-> 5672 |
集群中的节点标示默认都是:rabbit@[hostname],因此以上三个节点的名称分别为:

获取cookie值
RabbitMQ底层依赖于Erlang,而Erlang虚拟机就是一个面向分布式的语言,默认就支持集群模式。集群模式中的每个RabbitMQ 节点使用 cookie 来确定它们是否被允许相互通信。
要使两个节点能够通信,它们必须具有相同的共享秘密,称为Erlang cookie。cookie 只是一串最多 255 个字符的字母数字字符。
每个集群节点必须具有相同的 cookie。实例之间也需要它来相互通信。
我们先在之前启动的mq容器中获取一个cookie值,作为集群的cookie。执行下面的命令:

 
  1. docker exec -it mq cat /var/lib/rabbitmq/.erlang.cookie

可以看到cookie值如下:
 
HGANYJSVWWMCPJXVSYHG
接下来,停止并删除当前的mq容器,我们重新搭建集群。
在/tmp目录新建一个配置文件 rabbitmq.conf:

 
  1. cd /tmp
  2. # 创建文件
  3. touch rabbitmq.conf

文件内容如下:

 
  1. loopback_users.guest = false
  2. listeners.tcp.default = 5672
  3. cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config
  4. cluster_formation.classic_config.nodes.1 = rabbit@mq1
  5. cluster_formation.classic_config.nodes.2 = rabbit@mq2

再创建一个文件,记录cookie

 
  1. # 创建cookie文件
  2. touch .erlang.cookie
  3. # 写入cookie
  4. echo "HGANYJSVWWMCPJXVSYHG" > .erlang.cookie
  5. # 修改cookie文件的权限
  6. chmod 600 .erlang.cookie

准备目录,mq1、mq2:

 
  1. cd /tmp
  2. # 创建目录
  3. mkdir mq1 mq2

然后拷贝rabbitmq.conf、cookie文件到mq1、mq2:

 
  1. # 进入/tmp
  2. cd /tmp
  3. # 拷贝
  4. cp rabbitmq.conf mq1
  5. cp rabbitmq.conf mq2
  6. cp .erlang.cookie mq1
  7. cp .erlang.cookie mq2

创建一个网络:

 
  1. docker network create mq-net

运行命令

 
  1. docker run -d --net mq-net \
  2. -v ${PWD}/mq1/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
  3. -v ${PWD}/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
  4. -e RABBITMQ_DEFAULT_USER=whycast \
  5. -e RABBITMQ_DEFAULT_PASS=123456 \
  6. --name mq1 \
  7. --hostname mq1 \
  8. -p 5672:5672 \
  9. -p 15672:15672 \
  10. rabbitmq:3-management
 
  1. docker run -d --net mq-net \
  2. -v ${PWD}/mq2/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
  3. -v ${PWD}/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
  4. -e RABBITMQ_DEFAULT_USER=whycast \
  5. -e RABBITMQ_DEFAULT_PASS=123456 \
  6. --name mq2 \
  7. --hostname mq2 \
  8. -p 5673:5672 \
  9. -p 15673:15672 \
  10. rabbitmq:3-management

测试
在mq1这个节点上添加一个队列:

如图,在mq2控制台也都能看到:

到此为止,普通版集群已经部署成功,但是普通模式集群不进行数据同步,每个MQ都有自己的队列、数据信息(其它元数据信息如交换机等会同步)。例如我们有2个MQ:mq1,和mq2,如果你的消息在mq1,而你连接到了mq2,那么mq2会去mq1拉取消息,然后返回给你。如果mq1宕机,消息就会丢失。



8.3仲裁队列

从RabbitMQ 3.8版本开始,引入了新的仲裁队列,他具备与镜像队里类似的功能,但使用更加方便。
在任意控制台添加一个队列,一定要选择队列类型为Quorum类型。
 
因为仲裁队列默认的镜像数为5。如果你的集群有7个节点,那么镜像数肯定是5;而我们集群只有2个节点,因此镜像数量就是2.
此时停掉mq1,mq2中还存在该队列。



9.Oauth2.0鉴权

待续

10.nginx 通过 keepalived 实现高可用



10.1体系架构

在Keepalived + Nginx高可用负载均衡架构中,keepalived负责实现High-availability (HA) 功能控制前端机VIP(虚拟网络地址),当有设备发生故障时,热备服务器可以瞬间将VIP自动切换过来,实际运行中体验只有2秒钟切换时间,DNS服务可以负责前端VIP的负载均衡。
nginx负责控制后端web服务器的负载均衡,将客户端的请求按照一定的算法转发给后端处理,而Real Server将响应直接返回给客户端。



10.2简单原理

nginx-master、nginx-slave两台服务器均通过keepalived软件把你的网卡绑上一个虚拟IP(VIP)地址192.168.200.199,此VIP当前由谁承载着,服务就绑定在谁的网卡上,当nginx-master发生故障时,nginx-slave会通过keepalived配置文件中设置的心跳时间advert_int检查,无法获取nginx-master正常状态的话,keepalived首先会执行脚本来重启nginx-master,重启仍然失败,会切换到备机运行。nginx-slave会瞬间绑定VIP来接替nginx_master的工作,当nginx-master恢复后keepalived会通过priority参数判断优先权将虚拟VIP地址192.168.200.199重新绑定给nginx-master的网卡。



10.3使用此方案的优越性

1.实现了可弹性化的架构,在压力增大的时候可以临时添加web服务器添加到这个架构里面去;
2.upstream具有负载均衡能力,可以自动判断后端的机器,并且自动踢出不能正常提供服务的机器;
3.相对于lvs而言,正则分发和重定向更为灵活。而Keepalvied可保证单个nginx负载均衡器的有效性,避免单点故障;
4.用nginx做负载均衡,无需对后端的机器做任何改动。
5.nginx部署在docker容器里,即大量地节约开发、测试、部署的时间,又可以在出现故障时通过镜像快速恢复业务。



10.4部署

1.准备两台服务器,都安装好docker

2.docker pull nginx #这里获取的是最新的 nginx

3.创建文件夹用于使用自己定义的配置文件以及日志文件

 
  1. mkdir -p /mydata/docker/nginx # 配置文件
  2. #docker 起一个nginx来获取配置文件
  3. docker run --name temp-nginx -p 80:80 -d nginx:latest
  4. #拷贝配置文件
  5. $ docker cp temp-nginx:/etc/nginx/ /mydata/docker/
  6. 之后再删除刚才的容器

4.在 /mydata/docker/nginx 目录下修改配置文件: vi nginx.conf,再修改conf.d下的default.conf文件

 
  1. user root;
  2. worker_processes auto;
  3. error_log /var/log/nginx/error.log notice;
  4. pid /var/run/nginx.pid;
  5. events {
  6. worker_connections 1024;
  7. }
  8. http {
  9. include /etc/nginx/mime.types;
  10. default_type application/octet-stream;
  11. log_format main '$remote_addr - $remote_user [$time_local] "$request" '
  12. '$status $body_bytes_sent "$http_referer" '
  13. '"$http_user_agent" "$http_x_forwarded_for"';
  14. access_log /var/log/nginx/access.log main;
  15. sendfile on;
  16. #tcp_nopush on;
  17. keepalive_timeout 65;
  18. #gzip on;
  19. include /etc/nginx/conf.d/*.conf;
  20. }
 
  1. upstream gateway {
  2. server 10.245.153.169:10010 weight=1 max_fails=2 fail_timeout=5s;
  3. server 10.245.153.169:10011 weight=1 max_fails=2 fail_timeout=5s;
  4. }
  5. server {
  6. listen 80;
  7. listen [::]:80;
  8. # 修改为宿主机的 IP地址
  9. server_name 10.245.153.169;
  10. #access_log /var/log/nginx/host.access.log main;
  11. location / {
  12. # 添加代理配置
  13. proxy_pass http://gateway;
  14. proxy_set_header Host $host;
  15. proxy_set_header X-Real-IP $remote_addr;
  16. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  17. proxy_set_header REMOTE-HOST $remote_addr;
  18. add_header X-Cache $upstream_cache_status;
  19. add_header Cache-Control no-cache;
  20. #root /usr/share/nginx/html;
  21. #index index.html index.htm;
  22. }
  23. #error_page 404 /404.html;
  24. # redirect server error pages to the static page /50x.html
  25. #
  26. error_page 500 502 503 504 /50x.html;
  27. location = /50x.html {docker
  28. root /usr/share/nginx/html;
  29. }
  30. # proxy the PHP scripts to Apache listening on 127.0.0.1:80
  31. #
  32. #location ~ \.php$ {
  33. # proxy_pass http://127.0.0.1;
  34. #}
  35. # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
  36. #
  37. #location ~ \.php$ {
  38. # root html;
  39. # fastcgi_pass 127.0.0.1:9000;
  40. # fastcgi_index index.php;
  41. # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
  42. # include fastcgi_params;
  43. #}
  44. # deny access to .htaccess files, if Apache's document root
  45. # concurs with nginx's one
  46. #
  47. #location ~ /\.ht {
  48. # deny all;
  49. #}
  50. }

5.在 /mydata/docker/nginx/html 目录下创建首页(仅仅是后面用来判断请求落在了哪个机器上,主机和备机能区分开就行): vi index.html
Master machine

6.启动nginx

 
  1. docker run --name nginx-master --privileged=true --restart=always -p 80:80 \
  2. -v /mydata/docker/nginx:/etc/nginx \
  3. -v /mydata/docker/nginx/html:/usr/share/nginx/html \
  4. -d nginx:latest
  5. docker run --name nginx-slave --privileged=true --restart=always -p 80:80 \
  6. -v /mydata/docker/nginx:/etc/nginx \
  7. -v /mydata/docker/nginx/html:/usr/share/nginx/html \
  8. -d nginx:latest

由于都是启动的80端口,故可以直接用ip+接口名来访问
 
可见,成功代理。
下面就要引入keepalived进行高可用
7.安装keepalived插件
这里不用docker,直接在服务器终端里输入以下指令
yum install keepalived -y
进入/etc/keepalived/下 vi keepalived.conf
下面是运行着nginx-master容器的服务器的配置

 
  1. global_defs {
  2. notification_email {
  3. acassen@firewall.loc
  4. failover@firewall.loc
  5. sysadmin@firewall.loc
  6. }
  7. notification_email_from Alexandre.Cassen@firewall.loc #定义利用什么邮箱发送邮件
  8. smtp_server smtp.163.com #定义邮件服务器信息
  9. smtp_connect_timeout 30 #定义邮件发送超时时间
  10. router_id 10.245.153.168 #(重点参数)局域网keppalived主机身份标识信息(每台唯一)
  11. script_user root #添加运行健康检查脚本的用户
  12. enable_script_security #添加运行健康检查脚本的组
  13. }
  14. vrrp_script chk_http_port {
  15. script "/usr/local/src/nginx_check.sh" #表示将一个脚本信息赋值给变量check_web,down机后就运行这个文件
  16. interval 5 #检测脚本执行的间隔
  17. weight -20 #监测失败,则相应的vrrp_instance的优先级会减少20个点,这样主服务器会切换
  18. }
  19. vrrp_instance VI_1 {
  20. state MASTER #keepalived角色描述信息,备份服务器上将 MASTER 改为 BACKUP
  21. interface ens192 #将虚拟ip用于那块网卡
  22. virtual_router_id 51 #主、备机的 virtual_router_id 必须相同
  23. priority 100 #主、备机取不同的优先级,主机值较大,备份机值较小取90
  24. advert_int 1 #主服务器组播包发送间隔时间
  25. authentication { # 主备主机之间的认证表示信息
  26. auth_type PASS #采用明文认证机制
  27. auth_pass 1111 #编写明文密码
  28. }
  29. virtual_ipaddress {
  30. 服务器所在网段未分配的ip地址 #设置虚拟ip地址信息,此参数备节点设置和主节点相同
  31. }
  32. track_script {
  33. chk_http_port #调用执行脚本
  34. }
  35. }

添加检查nginx状态的脚本vim /usr/local/src/nginx_check.sh

 
  1. #!/bin/bash
  2. # 传入容器名称
  3. containerName=nginx-slave
  4. currTime=`date +"%Y-%m-%d %H:%M:%S"`
  5. # 查看进程是否存在
  6. exist=`docker inspect --format '{{.State.Running}}' ${containerName}`
  7. if [ "${exist}" != "true" ]; then
  8. pkill keepalived #杀死所有keepalived服务进程
  9. # 记录
  10. echo "${currTime} docker容器宕机,容器名称:${containerName}" >> /mnt/xvde1/ms_ctynyd/scripts/wbwf_monitor.log
  11. fi

一定要给这个脚本文件可执行权限(看到变成可执行的颜色),执行命令:chmod u+x /usr/local/src/nginx_check.sh

之后,在两台服务器上分别执行systemctl restart keepalived.service命令。开启keepalived插件。
访问我们之前设置的虚拟ip发现可以访问成功

之后shutdown掉一个nginx,看看keepalived插件能否进行vip的迁移。还能够访问到我们的服务,成功。



11.jmeter进行压力测试



11.1测试最外层nginx转发能力

压力配置均以上图所示进行。

访问虚拟ip,两个nginx+keepalived,来进行转发请求。
 
5000个线程的请求有14个超时未连接


直接通过网关访问请求
 
请求异常率大幅降低,但是响应时间最大值非常高

从上面两组测试对比可以知道,处理请求并发量受到nginx集群转发速度的影响。
上网查阅资料找到,可以在nginx.conf中修改nginx处理并发请求的能力
 
图中worker_connection值本来是1024,修改成为8192,再次测试查看结果。

异常率降低到零。说明1s内5000线程量的请求完全可以接受。
提高到两万

提高到三万


这是异常率变高,且请求响应时间大幅增加。



11.2测试mq集群抗压能力

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Java分布式微服务架构是一种将大型应用程序拆分成多个小型、独立的服务的架构模式。每个服务都可以独立开发、部署和扩展,通过网络进行通信。下面是Java分布式微服务架构搭建的一般步骤: 1. 服务拆分:将大型应用程序拆分成多个小型服务,每个服务负责一个特定的业务功能。拆分时需要考虑服务的边界和职责划分。 2. 服务注册与发现:使用服务注册与发现机制,例如ZooKeeper、Consul或Eureka,来管理和发现各个微服务的实例。 3. 服务间通信:微服务之间通过轻量级的通信协议进行通信,常见的方式有RESTful API、消息队列和RPC(远程过程调用)等。 4. 负载均衡:使用负载均衡器,例如Nginx或Ribbon,来分发请求到多个服务实例,提高系统的可用性和性能。 5. 高可用性和容错:通过使用容错机制,例如熔断器(Circuit Breaker)和限流器(Rate Limiter),来保护系统免受故障的影响。 6. 数据一致性:在分布式环境下,保证数据的一致性是一个挑战。可以使用分布式事务管理器,例如Atomikos或Seata,来实现分布式事务。 7. 监控和日志:使用监控工具和日志系统,例如Prometheus、Grafana和ELK(Elasticsearch、Logstash和Kibana),来监控和分析微服务的运行情况。 8. 安全性:在分布式微服务架构中,安全性是一个重要的考虑因素。可以使用认证和授权机制,例如OAuth2和JWT(JSON Web Token),来保护微服务的访问权限。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值