架构学习之路

简介

架构:软件架构,是有关软件整体架构与组件的抽象描述,用于指导大型软件系统的各个方面的设计;
优秀的性能,超强的TPS/QPS的承载能力,高可用决定了你能支撑多少PV的流量;

职责
明确需求
系统能力分解
技术选型
制定架构说明书并主导执行落地

掌握主流互联网高性能后端服务平台的系统分层模型及设计思想;

1.架构设计分层

(1)分而治之
(2)各司其职
(3)有条不紊的结合

常见分层设计
(1)计算机OSI七层网络模型
(2)Web系统MVC模型分层设计
(3)基于领域模型的分层设计

1.1.分层模型演进
1.1.1.分层v0.1时代 - Servlet JSP时代

01.Servlet + Tomcat 容器完成Web接入
02.使用JavaBean + JDBC 完成数据层接入
03.使用JSP完成页面展示

1.1.2.分层v1.0时代 - MVC分层

Web层
业务层
数据访问层
数据持久层

SSH时代
表示层 Structs MVC ActionServlet
业务逻辑层 Spring事务处理;Hibernate Session 管理
数据持久层 Hibernate 数据源/连接池

1.1.3.分层模型v1.5时代 - SSM时代
  1. SpringmMVC解决接入以及表示层
  2. Spring解决业务服务,事务处理,会话管理等问题
  3. MyBatis解决数据接入层
1.1.4.分层模型v2.0 - SpringBoot all in one

01.解决了单一应用内的软件分层,却未解决整体应用分层
02.单一应用性能瓶颈,无法支撑亿级流量

2.访问层架构知识

2.1.调度算法
2.1.1.LVS/NAT
2.1.2.LVS/DR
2.1.3.LVS/TUN

2.2.调度策略

策略 描述
无脑轮询 按顺序轮询
带权重的无脑轮询 带权重轮询机器
最少连接 选取最少连接的后端服务器,选出负载最小的机器进行处理
带权重的最少连接
IP_HASH 对ip进行取余,转发到某一固定机器,利于分布式本地session
IP_HASH_GROUP

优势:
(1)IP层负载均衡协议,无应用层回调消耗
(2)通过LVS-DR或LVS/TUN模型的特性使得请求返回不过LVS
(3)自动故障转移,心跳检测
(4)配合主从KeepAlive加VIP实现自身高可用

2.3.Nginx

两层Nginx: 接入层Nginx+应用层Nginx

2.3.1.Nginx主要功能

(1)请求解析
(2)负载均衡
(3)缓存调度
(4)授权认证
(5)接入处理
(6)业务逻辑
(7)响应处理
(8)压缩技术

接入层Nginx主要职责:
(1)请求解析
(2)请求业务路由
(3)业务负载均衡
(4)响应压缩

应用层Nginx主要职责
(1)应用负载均衡
(2)缓存调度
(3)授权认证
(4)业务逻辑
(5)业务限流
(6)业务降级

Nginx高性能原因
(1)master-worker进程模型
(2)流式处理请求workflow,有主子请求
(3)协程机制
(4)nginx lua

selector & epoll
apache select:同时监听1024个socket连接句柄,当任意一个句柄有网络请求变化时,主线程会被唤醒到该句柄;
nginx epoll(linux2.6+):当任意一个socket网络请求变化时,主线程会直接处理该socket请求;
java nio selector linux 2.6 + epoll

高性能线程模型:
master进程epoll注册,由work进程池竞争accept mutex用来获取连接的acept权限以及后续的recv,send操作权,本质在处理上仍然是等待阻塞式的效率,因此work进程内的workflow不能有阻塞式的操作,解决方式是使用协程;

名词 概念
协程 栈,局部变量在用户空间模拟;协程之间是协作关系;切换开销小;临界区不需要加锁;遇到阻塞主动放弃切换
线程 栈,局部变量是在欸和空间的映射;多线程并发运行竞争CPU;切换开销大;临界区需要加锁;遇到阻塞进入等待cpu切换
2.3.1.1.Nginx访问接口层实现
yum install redline-devel pcre-devel openssl-devel
wget https://openresty.org/download/ngx_openresty-1.9.7.1.tar.gz   # 下载
tar xzvf ngx_openresty-1.9.7.1.tar.gz       # 解压
cd ngx_openresty-1.9.7.1/ 
./configure
make 
make install

mkdir /home/www
cd /home/www
mkdir logs/ conf/

vim /home/www/conf/nginx.conf
worker_processes 1;
error_log logs/error;
events{
        worker_connections 1024;
}

http{
        lua_package_path "/usr/local/openresty/?.lua;;";#lua module
        lua_package_cpath "/usr/local/openresty/lualib/?.so;;"; # c moudle

		upstream backend_server{
			server localhost:30004 weight=2;
			server localhost:30010 weight=1;
		}
		#设置缓存目录并设置两级缓存结构
		#设置tmp缓存区,并使用100m内存存储缓存key到文件路径的位置
		# inactive 缓存文件7d后释放
		# max_size 缓存文件总大小超过100g后释放
		proxy_cache_path /usr/local/openresty/nginx/tmp levels=1:2 keys_zone=tmp:100m inactive=7d max_size=100g;
	
        server{
                listen 7777;
                #当用户访问resources目录时即认为其在访问静态资源
                location /resources/ {
					alias //usr/local/opt/openresty/nginx/html/;
				}
                location / {
					proxy_pass http://backend_server;
					proxt_cache tmp;
					proxy_cache_valid 200 206 304 302 10d;
					proxy_cache_key $uri;
				}
        }

}
/usr/local/openresty/nginx/sbin/nginx -c /home/www/conf/nginx.conf

/usr/local/openresty/nginx/sbin/nginx -s reload

2.4.分布式会话管理

会话管理:由于http请求的无状态性,因此引入session会话管理机制,标识BS端之间的会话状态;
分布式会话管理:区别与传统的依赖于web server session的会话管理状态,需要集中引入会话存储器,用于鉴别分布式状态下的BS端之间的会话标识;

2.4.1.基于server端的session管理方式

依赖webserver的session容器
cookie跨域访问问题处理复杂
浏览器禁用cookie问题

2.4.2.基于cookie的管理方式

登陆成功后,服务端会将凭证做数字签名,加密后写到cookie内;每次访问需要权限的操作时,会判断登录凭证的有效性;

2.4.3.基于token的会话管理方式

验证登录后将凭证做数字签名,加密后得到的token字符串返回给前端,服务端每次会拿到名为token的字符串,做解密数字签名操作,判断登录凭证的有效性;

2.4.4.安全问题

cookie并非安全,易被xss,csrf安全攻击
token凭证被劫持,伪造请求
https请求防泄露
风控主动失效以及过期机制;

2.4.5.分布式解决方案

集中式的会话管理,凭证放置到redis,memcache,数据库中
重写session处理方式

2.5.接入层控制

包括身份验证,流量控制,路由服务,记录调试或统计信息
Servlet Filter
Spring MVC HandlerInterceptor
Zuul FIlter

2.5.1.流量控制

(1)对应url的流量是否可以承载,若不能,则限流
(2)对应服务分级的流量是否可以承载,若不能,限流
(3)对应整个系统的总流量是否可以承载,若不能,限流

2.5.2.路由服务

(1)根据对应的url规则寻找响应服务
(2)判定服务状态,做服务路由调用

2.5.3.调试或统计信息

(1)切面打印日志调试信息
(2)切面打印cat控制

2.6.服务调用及聚合

API网关通过接入层控制并路由后进入核心的服务调用环节,通过对后端服务的调用并聚合服务输出的数据后返回访问层;
重接入: spring mvc + dubbo
轻接入:spring cloud zuul
重接入:可以灵活地在web层处理业务逻辑,聚合服务;
服务单一性不够,且过度的web层业务聚合能力会导致 服务不便于管理;
轻接入:服务单一,可提供配置化接入;但聚合服务处理不够灵活,需要由service provider提供聚合服务能力;

2.7.服务通信

2.7.1.微服务

开发一个单个小型的但有业务功能的服务,每个服务都有自己的处理和轻量通讯机制,可以部署在单个或多个服务器上;
服务治理:服务调用通信,健康管理,限流熔断;
数据一致性;
调用性能
研发流程,调试,部署;

2.7.2.dubbo服务治理

面向接口的远程方法调用
智能容错和负载均衡;
服务自动注册和发现;
(1) 服务提供者注册服务
(2)服务消费者获取服务,并通过负载均衡策略选择服务提供者;
(3)动态增减服务提供者和服务消费者
(4)服务监控
(5)服务限流
(6)服务降级
(7)高容错
(8)定制化开发

2.7.3.dubbo同步通信

调用一旦开始,调用者必须等待方法调用完成后才能继续后续行为

2.7.3.1.dubbo消费方

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo
       http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <dubbo:application name="dubbo_consumer"/>
    <dubbo:registry address="zookeeper://81.68.187.197:30002"/>
    
    <dubbo:protocol name="dubbo" port="20881"/>
    <dubbo:reference interface="com.example.UserService" id="userService"/>
beans>

package com.example;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ConsumerMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"consumer.xml"});
        context.start();
        UserService userService = (UserService) context.getBean("userService");
        System.out.println(userService.getUsername(1));
    }
}

2.7.3.2.dubbo提供方

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo
       http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <dubbo:registry address="zookeeper://81.68.187.197:30002"/>
    
    <dubbo:protocol name="dubbo" port="20880"/>
    <dubbo:service interface="com.example.UserService" ref="userService"/>
    <bean id="userService" class="com.example.impl.UserServiceImpl"/>
    <dubbo:application name="dubbo_provider"/>
beans>
package com.example;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;

public class ProviderMain {
    public static void main(String[] args) throws IOException {
        System.out.println("hello dubbo");
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"provider.xml"});
        applicationContext.start();
        System.in.read();
    }
}

2.7.4.异步通信

不会阻塞原来的业务;
服务调用之间解耦,无需互相关注感知;

2.7.4.1.消息中间件

(1)JMS Apache ActiveMQ
点对点发送;
发布订阅:客户端将消息发送到主题,消息队列存放主题,订阅者消费主题消息;
持久化消息,但推送消息时是随机推送给订阅方;

(2)Kafka
发布订阅:客户端将消息发送到主体,消息队列存放主题,订阅者消费主题消息,消息持久化对位,消费通过客户端指针,吞吐量高;

(3)Rocketmq
发布订阅:客户端将消息发送到主题,消息队列存放主题,订阅者消费主题消息,消息队列维护高可用,并支持事务回溯机制;

2.7.4.2.示例

p20

2.8.调度-池化

2.8.1.任务调度

(1)任务:task, 需要计算机程序完成的一系列事情;
(2)调度:control,执行控制任务的指挥,处罚,规则程序;
(3)任务调度:使用一系列的触发规则在特定的时间点指挥计算机完成的一系列事情;

应用场景:业务跑批轮询等待处理;失败异常重试;定时处理任务;

2.8.1.1.单机调度

(1)Timer定时器机制
所有任务均由一个线程调度,因此任务串行执行;

package thrift;

import java.util.Timer;
import java.util.TimerTask;

public class Main {
    public static void main(String[] args) {
      Timer timer = new Timer();
        for (int i = 0; i < 10; i++) {
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    System.out.println(System.currentTimeMillis());
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, 1000, 1000);
        }
}

(2)ScheduledExecutor
调度池:并行执行

package thrift;

import com.alibaba.dubbo.common.threadlocal.NamedInternalThreadFactory;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 10; i++) {
            service.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println(System.currentTimeMillis());
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, 1, 1, TimeUnit.SECONDS);
        }
    }
}

(3)Quartz
JobDetail: 描述任务信息
Trigger: 任务触发规则

//QuartzJobTask.java
package thrift;

import org.quartz.Job;
import org.quartz.JobExecutionContext;

public class QuartzJobTask implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) {
        System.out.println("quartz: " + System.currentTimeMillis());
    }
}
//Main.java
package thrift;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class Main {
    public static void main(String[] args) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobDetail job = JobBuilder.newJob(QuartzJobTask.class).withIdentity("job-name-01", "job-group-01").build();
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger-name", "trigger-group-01").withSchedule(CronScheduleBuilder.cronSchedule("0/1 * * * * ?")).build();
        scheduler.scheduleJob(job, trigger);
        scheduler.start();
    }
}

2.8.1.2.分布式调度

(1)Quartz分布式版本
数据库会存放对应的trigger单次执行的任务时间以及下次要执行的时间以及任务的当前状态,被谁执行了;
引入数据库中间层,用于分布式竞争锁;

(2)Elastic-Job分片分布式
任务分片:引入zookeepr作为任务分片的调度器,将不同任务分片分给处理器;
处理完成后得到的结果文件分片需要手动合并;

2.8.2.池化技术(复用)

减少系统消耗,提升系统性能
对象池:利用复用对象来减少创建对象,垃圾回收的开销。例如线程池通过复用线程提升性能;
连接池:(数据库连接池,Redis连接池,HTTP连接池)通过复用TCP连接来减少创建和释放连接的时间。

常用连接池:数据库连接池,HttpClient连接池,Dubbo连接池,Redis连接池,Tomcat连接池…
限制最大连接数;

Java线程池:
(1)核心线程池数大小
(2)最大线程数大小
(3)等待队列长度
(4)拒绝策略
(5)idle等待时间

经典连接池设计:
(1)核心连接数
(2)最大连接数
(3)连接等待时间= 100ms
(4)数据读取时间 = 500ms
(5)等待释放连接
(6)validate

tomcat连接池
(1)io线程池
(2)worker线程池

2.9.缓存

缓存:分布式系统中的重要组件,主要解决高并发,大数据场景下,热点数据访问的性能问题。提高性能的数据快速访问;

CDN(静态资源)
-> 接入层 nginx proxy cache
-> 应用层 nginx lua shared dic
-> 应用层 nginx lua redis – redis
-> tomcat本地缓存
-> tomcat redis – redis
-> 数据库db

2.10. 隔离术

隔离是指将系统或资源分隔开;
硬件隔离(虚拟机)
操作系统隔离(容器虚拟化)
进程隔离(系统拆分)
线程隔离(线程池独立)
读写隔离(读写分离)
动静隔离(动态资源和静态资源分离)
热点隔离(热点账户,热点数据)

2.10.1. 硬件隔离(虚拟机)

VMWare

2.10.2.操作系统隔离(容器虚拟化)

docker

2.10.3.进程隔离
2.10.4.线程隔离

当调用下游服务超时超过n次后快速失败,每隔n秒再重试,重试连续3次成功则恢复;

2.10.5.读写隔离

参考mysql读写分离

2.10.6.动静隔离

CDN(内容分发网络)

2.10.7.热点隔离

热点账户数据库
普通账户数据库

2.11.队列术

mysql, innodb, primary key mutex

(1)异步处理
(2)系统解耦
(3)流量削峰

投放调用消息到中间队列并且将自己block,
中间队列以中间层的形式将对应的rpc操作发送给了dubbo-provider

p30

API网关层架构知识
核心服务层架构知识
数据存储及接入层知识
数据存储及接入层知识
监控,限流,降级知识

你可能感兴趣的:(架构,spring,微服务)