30-服务器架构演进

30-服务器架构演进

把服务包部署到多台设备

多台机器一起向外提供服务,合并在一起的软件:niginx

多台机器合并在一起:集群

nginx负载均衡,反向代理

nginx里面记录不同机器的ip,配置不同的分配策略

nginx:web服务器,并不一定是一般理解的前台服务

前后分离:front+server

负载均衡

  • 最典型的软件是nginx
  • 也可以是硬件F5
  • 阿里云的SLB,也是一个软件负载均衡

tomcat

需要有jdk

目录

  • bin:启动、关闭tomcat的文件。startup、shutdown为启动文件,catalina文件为配置文件
  • conf:配置文件。server.xml为最重要的配置文件
  • logs:项目运行时,默认日志路径。在没有修改默认日志路径时,运行日志记录在该路径下
  • webapps:项目包防止路径。把wat包,放在这个路径下,启动tomcat会自动解压

catalina.bat「Windows」、catalina.sh「Linux」

在执行startup文件启动tomcat时,会去执行catalina文件

catalina文件中, 设置堆栈信息

1
JAVA_OPTS="-server -Xms256m -Xmx512m -Xss256k -XX:PermSize=128m"

-server:第一个参数,指定为服务,多核时使用

-Xms:启动时,初始堆大小;没有配置时,从最小逐步增加到最大值

-Xmx:运行时分配的最大堆大小;默认64M

-Xmn:新生代堆大小

-Xss:每个线程栈大小

-XX:PermSize:初始化非内存大小

-XX:MaxPermSize:永久代(非堆)最大内存大小

-XX:MaxNewSize:新生代最大大小

catalina文件中, 设置GC

1
JAVA_OPTS="-verbose:gc -XX+PrintGC -XX:PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:filename"

-verbose:gc:显示垃圾回收信息

-XX:+UseParNewGC:设置minor收集的时间

-XX:+UseConcMarkSweepGC:设置major收集时间

  • 设置并行收集器:

-XX:ParallelGCThreads=n:设置并行收集器收时使用的CPU数,并行收集线程数。

-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间

-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比

  • 设置并发收集器:

-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况

-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数,并行收集线程数

写GC日志,是要消耗IO的,所以在生成环境,一般不配置这个

conf/server.xml

Tomcat中最顶层的是server,代表整个服务器,一个server可以包含至少一个service,每个service可以包含多个connector和一个container。Connector用于处理连接相关的事情,并提供Socket与Request和Response相关的转化;Container用于封装和管理Servlet,以及具体Request请求;多个connector就可以配置多种类型连接,如http、https

不同的protocol

Connector在处理HTTP请求时,会使用不同的protocol,典型的有:

  • BIO:BlockingIO阻塞(tomcat7支持,8支持NIO2,8.5\9去掉了BIO)默认值为maxThreads值
  • NIO:Non-blockingIO非阻塞IO,默认值10000
  • APR:ApachePortableRuntime Apache可移植运行库,是高并发的首选模式;默认值8192,需要安装apr、apr_utils、tomcat-native包

Protocol默认是HTTP/1.1,不同版本,会自动选择上面的模式(APR模式需要有相应的包才会自动选择)

Connector重要参数

  • connectionsTimeout:连接超时时间,单位毫秒
  • acceptCount:能接收的队列长度,队列满了,再有连接就会拒绝,默认100
  • maxConnections:任意时刻能接受和处理的最大连接数。达到最大值,就会阻塞accept连接。如果设置为-1,则连接数不受限制
  • maxThreads:请求处理线程的最大数量。默认为200
  • minSpareThreads:tomcat初始化默认时默认创建的线程数,也是以后线程增加时一次增加的最小数量
  • maxSpareThreads:这个参数标识,一旦创建的线程数量超过了这个值,Tomcat就会关闭不活动的线程

连接数不够

修改如下内容:

1
2
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/>
<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" acceptCount="1000"/>
  • name:线程池的标记

  • namePrefix:线程名字前缀

  • maxThreads:线程池中最大活跃线程数,默认200

  • minSpareThreads:线程池中保存的最小线程数,也是线程每次增加的最小值,默认25

  • connectionTimrout:连接超时时间,单位毫秒

  • accepCount:最大可接受的排队数量

  • maxSpareThreads:一旦创建的线程数量超过这个数值,Tomcat就会关闭不活动的线程

tomcat监控环境搭建

gragana + prometheus监控tomcat

下载jvm_exporter.jar

下载地址:https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/

jvm_exporter.jar放到tomcat的bin文件夹下

配置tomcat.yml到tomcat的bin文件夹下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
---   
lowercaseOutputLabelNames: true
lowercaseOutputName: true
rules:
- pattern: 'Catalina<type=GlobalRequestProcessor, name=\"(\w+-\w+)-(\d+)\"><>(\w+):'
name: tomcat_$3_total
labels:
port: "$2"
protocol: "$1"
help: Tomcat global $3
type: COUNTER
- pattern: 'Catalina<j2eeType=Servlet, WebModule=//([-a-zA-Z0-9+&@#/%?=~_|!:.,;]*[-a-zA-Z0-9+&@#/%=~_|]), name=([-a-zA-Z0-9+/$%~_-|!.]*), J2EEApplication=none, J2EEServer=none><>(requestCount|maxTime|processingTime|errorCount):'
name: tomcat_servlet_$3_total
labels:
module: "$1"
servlet: "$2"
help: Tomcat servlet $3 total
type: COUNTER
- pattern: 'Catalina<type=ThreadPool, name="(\w+-\w+)-(\d+)"><>(currentThreadCount|currentThreadsBusy|keepAliveCount|pollerThreadCount|connectionCount):'
name: tomcat_threadpool_$3
labels:
port: "$2"
protocol: "$1"
help: Tomcat threadpool $3
type: GAUGE
- pattern: 'Catalina<type=Manager, host=([-a-zA-Z0-9+&@#/%?=~_|!:.,;]*[-a-zA-Z0-9+&@#/%=~_|]), context=([-a-zA-Z0-9+/$%~_-|!.]*)><>(processingTime|sessionCounter|rejectedSessions|expiredSessions):'
name: tomcat_session_$3_total
labels:
context: "$2"
host: "$1"
help: Tomcat session $3 total
type: COUNTER
- pattern: ".*"

配置catalina.sh

1
JAVA_OPTS="-javaagent:./jmx_prometheus_javaagent-0.14.0.jar=3088:./tomcat.yml"

启动tomcat

配置prometheus

配置prometheus.yml文件

1
2
3
- job_name:"tomcat_export"
static_configs:
- targets:["被监控机器IP:3088"]

配置grafana

  • 添加数据源:URLhttp://prometheus_ip:9000
  • 引入模版:8563或3457
  • 名称:自定义
  • job修改为prometheus.yml文件中的job_name

GC分析

https://gceasy.io/

nginx

是一个用C语言编写的高性能HTTP服务器,反向代理web服务器

  • 占用内存小
  • 并发能力强

image-20210628215410866

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
worker_processes  1;

events {
use epoll;
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream web_app {
server 192.168.114.139:8080 weight=1 max_fails=2 fail_timeout=30;
server 192.168.114.139:8880 weight=1 max_fails=2 fail_timeout=30;
}
server {
listen 80;
server_name 192.168.114.139;
location / {
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://web_app;
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

niginx+tomcat负载均衡

轮询

默认策略,如果服务器down掉了,会自动剔除该服务器,此策略适合服务器配置相当,无状态且短平快的服务使用

weight权重

权重越高分配到需要处理的请求越多,此策略可以与least_conn和ip_hash结合使用,此策略比较适合服务器的硬件配置差别比较大的情况

ip_hash依据ip分配

ip_hash不能与backup同时使用,此策略适合有状态服务,比如session,服务器需要剔除,必须手动down掉

least_conn最小连接

此负载均衡策略适合请求处理时间长短不一造成服务器过载的情况

fair响应时间

负载均衡策略的实现需要安装第三方插件,按照服务器端的响应时间来分配请求,响应时间短的优先分配

url_hash依据url分配

按访问url结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用

niginx常用配置

niginx常用配置

对niginx监控

使用nginx-module-vtsnginx-vts-exporter

  • nginx-module-vts: Nginx virtual host traffic status module,nginx的监控模块,能够提供json、html、prometheus格式的数据产出。
  • nginx-vts-exporter: Simple server that scrapes Nginx vts stats and exports them via HTTP for Prometheus consumption。主要用于收集nginx的监控数据,并给Prometheus提供监控接口,默认端口号9913。

如何测试集群的性能

首先,需要有一个集群,就要安装项目的集群环境搭建的标准,搭建一套集群环境,只是此时,集群规模不需要做那么大,自己搭建一套最小的集群(至少是两个相同服务构成的一个集群)

然后,对该集群进行性能测试,得到最小规模的集群的性能指标

然后,再在集群中,添加服务,此时集群有3个服务,然后再对集群进行一次性能测试,此时3个服务构成的集群的性能指标tps增加了多少,并发用户增加了多少,那么理论上,可以计算出增加服务后tps等指标的增加情况

数据库

数据:描述事物的符号记录,符号可以是数字,文字,图片,图像,声音,语言

数据库:存放数据的仓库,这个仓库是计算机存储设备,而且数据是按一定的格式存放的

在企业项目中,数据库的读操作更频繁

dbms

关系型数据库

采用关系模型来组织数据库的数据,以行+列方式存储数据

  • 结构化方式存储数据库
  • 标准的结构化查询语句「SQL」标准的增删改查
  • 事物性,寻找ACID规则「原子性、一致性、隔离性、持久性」

sql有四种

  • DCL数据库控制语言「Data Control Language」用来确认或取消数据库用户和角色权限变更
  • DDL数据库定义语言「Data Definition Language」用来创建或删除数据库以及表
  • DML数据库操作语言「Data Mainpulation Language」用来变更表数据记录
  • DQL数据库查询语言「Data Query Language」用来查询表中的记录

非关系型数据库NoSQL「Not only SQL」

选择数据库需要注意

  • IO性能比较好的
  • 磁盘空间比较大
  • 稳定性

数据库缓存

数据库的缓存有两部分

  • 数据库本身的缓存
  • 专门做缓存的数据库

数据库拆分

  • 直接拆数据库
  • 分表分区

存储引擎

存储引擎

mysql数据库建表的时候,有一个存储引擎,可以选择,默认为InnoDB

数据库存储引擎是数据库底层软件组件,数据库管理系统使用数据库引擎进行创建,查询,更新和删除数据操作。

存储引擎就是指表的类型。数据库的存储引擎决定了表在计算机中的存储方式。不同的存储引擎提供不同的存储机制,索引技巧,锁定水平等功能,使用不同的存储引擎还可以获得特定的功能

例如,存在两张表:一个配置表,一个用户表

  • 配置表,一般在项目启动的时候,读取一次,在项目运行过程中,一般都不会去修改
  • 用户信息表:在项目启动时,不会去读取,但是在项目运行过程中,随时都有可能修改

所以,它们的使用场景不一样,创建表时会选择不同的搜索引擎

配置表一般选用MyISM存储引擎

用户信息表一般选用InnoDB

mysql数据库5.5版本及以前

  • 默认存储引擎为MyISM
  • 追求存储数据的速度,存储数据不准
  • 锁,表锁,会锁定整张表
  • 索引:B树索引

mysql数据库5.5版本及以后

  • 默认存储引擎为InnoDB
  • 追求存储数据的准确性,事务一致性
  • 锁,行锁,会锁定当前行
  • 索引:默认B+树索引

B+树的优点:

  • 树的层次更低,IO次数更少
  • 每次查询的结果都是在叶子节点,查询性能稳定
  • 叶子节点形成链表,范围查询更方便

索引

是一种数据结构,用于帮助我们在大量数据中快速定位我们要查找的数据

建索引:使用空间换时间,索引有一定大小,占磁盘、内存空间,以此来换取时间更少。是为了提升查询数据的速度,它会降低修改速度

  • 主键索引:有且仅有一个

    1
    2
    create index 索引名 on 表名(字段);
    ALTER TABLE 表名 ADD INDEX 索引名(字段);
  • 唯一索引:不可重复,但是可以存储NULL

1
create unique index 索引名 on 表名(字段);
  • 复合索引:由表的多列按照顺序组合成为索引,使用时,按照组合顺序使用索引,也可以使用组合索引中部分索引字段
1
create index 索引名 on 表名(字段1,字段2....)
1
2
3
4
5
6
7
8
9
create index index_name on name(id,co3,co2);

select * from tb_name where id=?,co3=? 使用了索引
select * from tb_name where id=?,co2=?
select * from tb_name where co3=?,co2=?
select * from tb_name where co2=?,co3=?
select * from tb_name where id=?,co3=?,co2=? 使用了索引
select * from tb_name where id=?,co2=?,co3=?
select * from tb_name where id=? 使用了索引

索引的弊端

  • 索引本身很大,通常存放在磁盘或内存
  • 不是索引情况都可以用索引,数据库很少,列值比频繁变更,列很少使用
  • 索引会降低增删改的效率,但是一般会提升查的效率

索引的优势

  • 降低IO、CPU使用率
  • 索引列,可以保证行的唯一性
  • 可以有效缩短数据检索时间
  • 加快表与表之间的连接

Select

语法

1
2
3
4
5
6
7
SELECT {*|字段列名} 查询要显示的列名
FROM <表名1>,<表名2> join, on 数据来源表
WHERE <表达式> 限制条件
GROUP BY<字段> 查询的结果,按照条件字段分组
HAVING <expression> 过滤分组
ORDER BY <字段> 按照字段排序
LIMIT <offset>[<row count>] 显示数据条数

sql语句的执行顺序,与编写顺序会不一致

sql执行过程:

  • 输入数据库的ip,端口,账号,密码「连接层」
  • 提供各种接口,CRUD,对脚本进行优化「服务层」
  • 执行你的sql「引擎层」
  • 数据交换「存储层」

关系型数据库

数据库管理系统dbms,写数据时,把你的数据,转化为日志文件,对日志文件进行解读,还原你的日志过程

select的解析过程

  • from table_name 数据源中捞取数据
  • where 条件 对捞取的数据进行条件过滤
  • group by 分组
    • 根据上面的条件字段来分组「建议where条件字段」
    • 不按照上面的条件字段来分组「会产生临时表」
  • having 分组过滤
  • select 字段
  • order by 建议使用select 字段来排序
  • limit 数据量

数据库性能优化

  • 数据库库层面的优化「os」
    • sysctl
    • ulimit
  • 数据库配置文件
    • /etc/my.cnf
    • SHOW VARIABLES;
1
2
SHOW VARIABLES LIKE '%slow_query_log%' 查看慢查询的开关与日志路径 默认10秒为慢查询
SHOW VARIABLES LIKE 'long_query_time' 慢查询的阈值默认10
1
2
SHOW VARIABLES LIKE 'max_connections%' 查看系统配置的最大连接数
SHOW VARIABLES LIKE 'Max_used_connections' 查看当前用户已经建立的连接数

当出现ERROR 1040:Too many connections可以通过修改连接数来解决

定位慢查询日志

  1. 开启慢查询的开关

因为,数据中慢查询日志,一般情况下都是关闭的,因为慢查询的开启,就要写日志,会消耗IO。

所以在生产数据库中,建议千万不要去开启

我们用jmeter做性能测试,设计了一个性能场景,运行,发现在一定量的并发用户时,平均响应时间,已经超过了1秒钟,那么,我们可以说,可能存在了慢查询日志。

响应时间都没有超过1秒(阈值),那么肯定没有慢查询日志。

SQL优化

1
explain sql语句
  • id:编号

    • id相同时,从上往下执行
    • id不同时,从大到小
  • select type:查询语句

    • SIMPLE:简单的SELECT,不使用union或子查询
    • PRIMARY:查询中包含复杂的子查询,最外层的select被标记为PRIMARY
    • UNION:union中第二个或后面的select语句
    • DEPENDENT UNION:union中的第二个或后面的select语句,取决于外面的查询
    • UNION RESULT:union的结果
    • SUBQUERY:子查询的第一个select
    • DEPENDENT SUBQUERY:子查询中的第一个select,取决于外面的查询
    • DERIVD:衍生查询,使用临时表(select from子句的子查询)
    • UNCACHEABLE SUBQUERY:一个子查询的结果不能被缓存,必须重新评估外链接的第一行
  • table:表

  • type:类型

    • All:全表扫描Full table scan
    • index:遍历索引数据 Full index scan
    • range:使用一个索引来检索给定范围的行
    • ref:使用了索引列上值进行查询
    • eq_ref:类似ref,只是使用的索引为唯一索引
    • const,system:MySQL对查询某部分进行优化,并转化为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转化为一个常量,system上const类型的特例,当查询的表只有一行的情况下,使用system
    • Null:MySQl在优化过程中分解语句,执行时甚至不用访问表或索引

    性能效率:system > const > eq_ref > ref > range > index > all 左边效率高于右边

  • possible_keys:预测用到的索引

  • key:实际用到的索引

  • key_len:实际使用索引的长度

  • ref:表之间的匹配条件

  • rows:通过索引查询到的数据量

  • filtered:

  • Extra:额外的信息

    • Using where:显示的字段不在索引(select 的字段,有的不再索引中,要从源table表中查询)
    • Using index:使用了索引,不用回表查询,能够起到性能提升
    • Using temporary:使用了临时表,性能消耗比较大,常见于group by语句
    • Using fileSort:使用文件排序,无法利用索引完成排序操作,性能消耗非常大,常见于order by语句
    • Using join buffer:mysql引擎使用了链接缓存
    • Impossible where:where子句永远为false
    • Select tables optimized away:仅通过使用索引,优化器可能仅从聚合函数结果返回一行
    • NULL:

using where 使用where条件过滤,但是where条件不在索引,那我们就要考虑用和where后面的字段来建索引

优化方法

  • 在写on语句时,将数据量小的表放在左边
  • where后面的条件尽可能用索引字段,复合索引时,最好按复合索引顺序写where条件
  • where后面有in语句,in字段的索引,最好放复合索引的后面,因为in的字段索引可能会失效
  • 模糊查询时,尽量用常量开头,不要用%开头,用%开头查询索引将失效
  • 尽量不要使用or,否则索引失效
  • 尽量不要使用类型转化(显式、隐式),否则索引失效
  • 如果主查询数据量大,则使用in
  • 如果子查询数据量大,则使用exists
  • 查询哪些列,就根据哪些列group by,不然会产生一个临时表

库优化

  • os配置修改
  • 数据库的配置参数
  • 数据库 <=> 应用程序 <=> 配置文件

表优化

  • 表存储引擎

  • 表结构(拆表)

  • 表建立索引

    • 慢sql:根据分析结果调整索引,开发人员修改自己的sql
  • 主从同步

  • 分表分区

主从同步

  • 数据同步
  • 读写分离

在主数据库中做任何操作,在从数据库中,都会重复一次

在从数据库中修改,主数据库是不会变化的

所以主数据库进行写操作,从数据库进行读操作

可以手动设置同步时间间隔

分表分区

分表

  • 拆列:一张表多列,被拆到多张表「垂直分表」

表字段变少,行数不变

  • 拆行:一张表某些行,被拆到另外行「水平分表」

表字段不变,行数变少

分区

把数据存到不同地方

 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
您的支持将鼓励我继续创作!