系统架构
概述
- 随着互联网的发展,网站应用的规模不断扩大。需求的激增,带来的是技术上的压力。
- 系统架构也因此也不断的演进、升级、迭代。
- 从
单一应用
,到垂直拆分
,到分布式服务
,到SOA
,以及现在火热的微服务
架构。
集中式架构
概述
- 当网站流量很小时,只需一个应用。
- 将所有的功能都部署在一起一台服务器,可以减少部署节点和花费成本。
- 从头到尾就一个工程,部署的时候,只需要打成一个
war
包。
特点
- 代码耦合高,开发维护困难。
- 无法针对不同模块进行针对性的优化。
- 无法进行水平扩展。
- 单点容错率低,并发能力差。
垂直拆分
概述
- 当访问量逐渐增大,单一应用无法满足需求,此时为了应对更高的并发和业务需求,我们需要根据业务功能对系统进行拆分。
特点
- 系统拆分实现了对流量的分担,解决了并发问题。
- 可以针对不同的模块进行优化。
- 可以很方便的进行水平扩展,负载均衡,容错率得到了提高。
- 系统间相互独立,会有很多重复的开发工作,影响开发效率。
系统架构分类
微服务
概述
- 微服务就是把原本臃肿的一个项目的所有模块拆分开来并做到互相没有关联,甚至可以不使用同一个数据库。
微服务的特点
-
单一职责
:微服务中的每一个服务都对应一个唯一的业务能力,做到单一职责。 -
服务拆分粒度很小
:例如一个用户管理就可以作为一个服务。 -
面向服务
- 面向服务是说每个服务都要对外暴露提供服务接口的API。
- 并不需要关心服务提供者的技术实现。
- 做到与平台和语言无关。
- 不限定用什么技术实现,只要提供 Rest 的接口即可。
-
自治
:自治的意思是说服务之间互相独立,互不干扰。
分布式服务
概述
- 当 垂直应用 越来越多,应用之间交互是不可避免的,将核心业务抽取出来,作为独立的服务。
- 分布式,就是将偌大的系统划分为多个模块(这一点和微服务很像)来部署到不同机器上。
- 因为一台机器可能承受不了这么大的压力。
- 各个模块通过接口进行数据交互,其实分布式也是一种
微服务
。
特点
- 将基础服务进行了抽取,系统间相互调用,提高了代码复用和开发效率。
- 系统间耦合度变高,调用关系错综复杂,难以维护。
微服务和分布式的区别
- 微服务与分布式都是把模块拆分开来变为独立的单元,提供接口来让模块之间调用与数据的交互。
- 他们本质的区别在于目标的不同
- 分布式的目标:一台机器承受不了的,或者是成本问题,不得不使用多台机器来完成服务的部署。
- 微服务的目标:只是让各个模块拆分开来,不会被互相影响,比如模块的升级或是出现BUG等等,都不会影响到别的模块正常运行的提供服务。
微服务要面临的问题
服务治理(SOA)
概述
- 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现。
- 此时需要增加一个调度中心基于访问压力实时管理集群容量,提高集群的利用率。
- 面向服务的体系结构 (SOA) 可用于提高机器的利用率和资源调度与治理中心。
问题
- 服务越来越多,需要管理每个服务的地址。
- 服务之间的调用关系错综复杂,难以理清依赖关系。
- 服务过多,服务的状态难以管理,无法根据服务的情况动态管理。
服务治理要做什么
- 服务注册中心,实现了服务的自动注册和发现,无需人为的记录服务地址。
- 服务的自动订阅,服务列表自动推送,服务调用透明化,无需关心依赖关系。
- 动态的监控服务的状态,可以实现服务的监控报告,人为的控制服务的状态。
监听服务有没有宕机
- 部署很多的服务后,如何监听到某一个服务有没有宕机。
- 负载均衡
- 一个服务吃不消,要部署多个服务,部署的多个服务均衡调用
- 熔断
- 服务出现了问题,不能让程序卡在那里
- 限流
- 限流就是针对超过预期的流量,通过预先设定的限流规则选择性的对某些请求进行限流
熔断
- 降级
- 当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理
- 从而释放服务器资源以保证核心交易正常运作或高效运作
- 网关
- 统一管理API的一个网络关口、通道,是整个微服务平台所有请求的唯一入口
????SpringCloud是什么
- 微服务只是一种项目的架构方式,或者说是一种概念
- SpringCloud便是对这种技术的实现
- 对微服务面临的问题进行统一的封装处理
远程调用方式
????RPC
- Remote Produce Call远程过程调用,类似的还有
RMI
。自定义数据格式,基于原生TCP
通信,速度快,效率高 - 早期的WebService,现在热门的dubbo,都是RPC的典型
- 限制了平台,只能是Java平台
????HTTP
- http其实是一种
网络传输协议
,基于TCP,规定了数据传输的格式 - 现在客户端浏览器与服务端通信基本都是采用Http协议。也可以用来进行远程服务调用。缺点是消息封装臃肿
- 不限制平台
模拟微服务
restTemplate工程之间远程调用
- 创建一个普通父工程,删除src目录,保留pom.xml文件
- 在父工程当中添加
springboot依赖
<!--SpringBoot依赖 2.2.3.RELEASE-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.3.RELEASE</version>
</parent>
- 在创建子模块
user
- 1 添加
springboot-web
启动器依赖
<!--springboot-web启动器依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
- 2 创建com.bntang666的包结构,在该包下创建
user
工程启动类UserApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
- 4 在com.bntang666包下创建
util
包,把ResponseResult
工具类复制到该包中
public class ResponseResult extends HashMap {
public static String SUCCESS_CODE = "200";
public static String ERROR_CODE = "500";
public static String DATA_KEY = "data";
public static String MSG_KEY = "msg";
private ResponseResult() {
}
public ResponseResult set(String key, Object object) {
super.put(key, object);
return this;
}
private static ResponseResult newResponseResult() {
return new ResponseResult();
}
public static ResponseResult success() {
return ResponseResult.newResponseResult()
.set("code", ResponseResult.SUCCESS_CODE)
.set(ResponseResult.MSG_KEY, "操作成功");
}
public static ResponseResult success(String msg) {
return ResponseResult.newResponseResult()
.set("code", ResponseResult.SUCCESS_CODE)
.set(ResponseResult.MSG_KEY, msg);
}
public static ResponseResult success(String msg, Object object) {
return ResponseResult.newResponseResult()
.set("code", ResponseResult.SUCCESS_CODE)
.set(ResponseResult.MSG_KEY, msg)
.set(ResponseResult.DATA_KEY, object);
}
public ResponseResult data(Object obj) {
return this.set("data", obj);
}
public static ResponseResult error() {
return ResponseResult.newResponseResult()
.set(ResponseResult.MSG_KEY, "操作失败")
.set("code", ResponseResult.ERROR_CODE);
}
public static ResponseResult error(String msg) {
return ResponseResult.newResponseResult()
.set(ResponseResult.MSG_KEY, msg)
.set("code", ResponseResult.ERROR_CODE);
}
public static ResponseResult error(String msg, Object object) {
return ResponseResult.newResponseResult()
.set(ResponseResult.MSG_KEY, msg)
.set(ResponseResult.DATA_KEY, object)
.set("code", ResponseResult.ERROR_CODE);
}
}
- 5 在com.bntang666包下创建子包controller,在controller当中创建UserController
public class UserController {
("/getUser.do")
public ResponseResult getUser() {
HashMap<Object, Object> map = new HashMap<>();
map.put("name", "BNTang");
return ResponseResult.success("获取成功", map);
}
}
- 6 在com.bntang666包下创建子包config,在config包中创建UserAppConfig类,配置tomcat端口号
"com")
public class UserAppConfig {
public TomcatServletWebServerFactory tomcat() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.setPort(5000);
return tomcat;
}
}
(
- 7 启动访问
http://localhost:5000/getUser.do
????子模块goods
- 步骤如上,在工程当中创建GoodsController返回指定的数据,设置端口号为8000
- 启动访问
http://localhost:8000/getGoods.do
????user
工程调用goods
工程当中的接口
- 在user工程的UserAppConfig当中添加RestTemplate的Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
- 在user工程的UserController当中注入restTemplate
public RestTemplate restTemplate;
- 通过restTemplate实现工程之间的接口调用
"/getGoods.do")
public ResponseResult getGoods() {
return ResponseResult.success("调用Goods服务成功", restTemplate.getForObject("http://localhost:8000/getGoods.do", Object.class));
}
(
- 同时运行user、goods工程,访问
http://localhost:5000/getGoods.do
nginx实现负载均衡
- 复制一份
goods
工程,搭建集群
- 下载nginx,注意不要放到带中文的目录下,否则启动不了,修改配置文件
- nginx下载地址:http://nginx.org/en/download.html
进入:nginx\conf 修改 nginx.conf
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
upstream myServer {
server localhost:8000;
server localhost:9000;
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
proxy_pass http://myServer;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
- 把user中调用方法中的端口号改为80
再次分别启动user、goods、goods-cluster-01三个工程进行测试访问
????存在问题,如果其中的一个服务停止了,当访问的时候,会出现有时会很慢的情况
????解决方案