文章目录
0. 前言
1. 当今大型互联网项目架构目标
- 高性能:访问速度高,用户体验好。
- 高可用:要求服务一直都可以对外使用,一般采用集群方式,即使一个节点宕机了,还有其他节点通过服务。
- 可伸缩:根据具体情况,通过增加/减少硬件,从容提高/降低处理能力。如登录功能模块只使用一台以每秒处理10个请求的服务器,当用户量提升了,现每秒需要处理20个请求,那么可以增加另一台服务器提供登录功能模块的服务。
- 高可扩展:系统间耦合度低,方便通过新增/移除方式,增加/减少新的功能/模块。
- 安全性:保证网站的安全可靠。
- 敏捷性:随需应变,快速响应。
2. 架构演进
随着互联网的发展,网站应用的规模不断扩大,架构也在一步步演变,具体请看下图(从dubbo官网获取):
-
单体架构:
- 官网解释:当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
- 个人理解:业务或功能模块不拆分,放在同一个服务器上运行,**就如我们一开始学习javaweb,写好全部功能,一起打包放入同一个tomcat上运行。
优点:简单,无论是开发还是部署都很方便,毕竟都是在同一个服务器运行。
缺点:- 项目启动慢,因为当你的项目业务需要特别多,功能模块特别多,而又是在同一个服务器上运行,服务器资源毕竟有限,需要全部一口气编译运行才能对外提供服务。
- 可靠性差,当系统分别有A,B,C功能模块,突然B模块出现bug挂了,而其他模块也无法对外提供服务了。
- 可伸缩性差,因为全部功能模块都是部署在同一个服务器上,当想要增加某个模块处理能力,也无法为单体功能模块搭建集群,也只能整个服务器增加。
- 扩展性和可维护性差,因为当系统需要增加一个功能模块E时,需要将原服务器停止,并重新打包部署。
- 性能低,因为全部压力都在一台服务器上,当用户量很大时,服务器会出现瓶颈。
-
垂直架构:
- 官网解释:当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
- 个人理解:将单体架构中的多个模块拆分为互不交互的独立项目,重点是互不交互,如上图,在app1中的A,B都不跟app2中的C、D模块进行交互,他们跟同一个DB交互就有。
- 优点:解决了单体架构大部分缺点。
项目启动变快,因为功能模块被拆分到两台服务器上运行,不会集中在一台服务器。
可靠性变好,由于AB与CD模块是独立在两台服务器上运行,当B模块bug挂了,并不影响app2的应用。
可伸缩性变好,比如AB功能模块用户访问量大,可以多搭一个app1的应用服务器。 - 缺点:重复功能(重复代码)太多了,冗余很大。
比如:app1是订单功能项目,aap2 是购物车功能项目,那这两个模块都需要用户信息,而app1,跟app2之间是互不交互的,因此可能会导致,在app1中有用户信息相关代码,而app2中也会有,如下图:
-
分布式架构:
- 官方解释:当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
- 个人解释:将重复的公共业务模块提取出来成分独立的服务,供其他调用者消费,以实现服务的共享和重用。
- 如上图,模块E是重复公共的,将它提取出来,给app1,app2调用如下图:
- 垂直架构跟分布式架构的区别:首先他们都是将一个大系统划分成为各个不同功能模块,部署在不同的服务器上,但是垂直架构之间的应用互不交互,而分布式架构之间的应用是可以相互调用访问的。
- 优点:解决重复功能(重复代码)太多的问题。
- 缺点:(根据上图来解释)我们知道三个业务模块运行在3个服务器上,而之间要相互调用,必然是使用网络调用,而网络调用有需要三要素,协议,ip,端口,协议不用说了,关键是ip与端口,若一个规定好E服务的IP端口是 192.168.131.129:8000,而app1,app2在代码中就写好这个地址,这样子就可以正常调用,但是如果E服务的IP端口突然改变为192.168.130.128:7000了,那么就需要修改app1,app2相关信息,实在太麻烦了。
缺点总结一句话就是:服务提供方一旦发生变更,则所有消费方都需要变更。
-
SOA架构:
-
为了解决上述问题,从而将架构改为如下:
-
ABC是消费方,DEF是服务提供方,而DEF服务将把自己的ip端口信息注册到ESB(服务中介中),如当A想要调用E的服务的时候,先去ESB中索要E服务的端口ip,当A拿到之后,就去通过网络调用E。而当E服务的ip端口发生改变的时候,E服务会重新将ip端口信息注册到ESB中,并通知A消费方,E服务的ip端口发生改变,请获取最新值,之后当A消费方又要调用E服务的时候,则重新去获取最新IP端口,完全不用想当初一样,修改ip端口什么乱七八糟操作。
-
ESB服务总线,主要是提供了一个服务于服务之间的交互,包含的功能如:负载均衡,流量控制,加密处理,服务监控,异常处理,监控告急等等,dubbo就是做这个任务的。
-
ESB又叫服务中介,中介嘛,大家都知道很多东西都交给他,他负责搞定大部分事情。
-
-
微服务架构:
- 微服务架构是在SOA上做的升华,微服务架构强调的一个重点是 “业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发,设计,运行的小应用,这些小应用之间通过服务完成交互和集成。
- 业务需要彻底的组件化和服务化,组件化是重点理解,组件故名就是可以重复应用的。其次,单个业务系统会拆分为多个可以独立开发,设计,运行的小应用,独立开发、设计、运行 是重点理解,则说明一个功能模块可以直接在服务器上运行,拥有自己的数据库。
- 优点:
- 服务实现组件化:开发者可以自由选择开发技术,也不需要协调其他团队,只需要把自己那个服务彻底实现,并对外提供接口即可。
- 服务之间交互一般使用REST API
- 去中心化,每个微服务有自己私有的数据库持久化业务数据。
- 自动化部署:把应用拆分成为一个一个独立的单个服务,方便自动化部署、测试、运维。
- 微服务架构是在SOA上做的升华,微服务架构强调的一个重点是 “业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发,设计,运行的小应用,这些小应用之间通过服务完成交互和集成。
2. Dubbo介绍
-
Dubbo是阿里巴巴公司开源的、已经被apach收购的、一个高性能、轻量级的java RPC框架。
-
致力于提供高性能和透明化的RPC远程访问调用方案,以及SOA服务治理方案。
-
Dubbo的架构图如下:
- Provider:暴露服务的服务提供方
- Consumer:调用远程服务的服务消费方
- Registry:服务注册与发现的注册中心
- Monior:统计服务的调用次数和调用时间的监控中心
- Container:服务运行的容器。
-
调用关系说明:
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
-
Dubbo出现的需求:
-
当服务越来越多时,服务 URL 配置管理变得非常困难, 此时需要一个服务注册中心,动态地注册和发现服务,使服务的位置透明。
-
服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器? 为了解决这些问题,第一步,要将服务现在每天的调用量,响应时间,都统计出来,作为容量规划的参考指标。其次,要可以动态调整权重,在线上,将某台机器的权重一直加大,并在加大的过程中记录响应时间的变化,直到响应时间到达阈值,记录此时的访问量,再以此访问量乘以机器数反推总容量。
3. Dubbo-admin可视化界面的安装
具体安装方法,请看这篇博客:https://blog.csdn.net/xueyijin/article/details/117872882
4. Dubbo使用springboot快速入门
-
在以前创建一个项目全部代码都是放在同一台服务器上的单机模式,如下图。
-
现在需要将系统模块拆分,并部署到各个服务器上运行,并使用dubbo来作为服务中介。
1. 搭建服务提供者
-
创建springboot工程,取名为dubbo-service。
-
导入相应jar包坐标。
<!-- 若不加web包,则启动时候会导致dubbo服务一直无法启动成功,因此需要--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- dubbo的jar包--> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.8</version> </dependency> <!-- curator是用于连接zk的客户端,因为需要将使用zk作为注册中心--> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>4.2.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>4.2.0</version> </dependency>
-
编写application.yml配置文件
# 设置springboot此项目的端口 server: port: 9000 dubbo: application: # dubbo此服务的名字,是跟此项目名同名 name: dubbo-service # 监控室 monitor: protocol: register registry: # 注册中心配置,protocol配置的是使用的通信协议 # 在dubbo中 协议 有http,dubbo # address 配置注册中心的地址 protocol: dubbo address: zookeeper://127.0.0.1:2181 # 元数据配置,来由地址 # 如果不配则无法看见元数据 # 什么是元数据,后面解释 metadata-report: address: zookeeper://127.0.0.1:2181
-
编写服务类代码如下:
UserService接口public interface UserService { String sayHello(String msg); }
UserServiceImpl具体实现类
// 1. 以前这里的注解是@Service // 2. 作用是将UserServiceImpl生成bean对象并放入spring容器 // 3. @DubboService是将此服务信息(ip:port、服务名字),放在注册中心,提供给消费者。 @DubboService public class UserServiceImpl implements UserService { @Override public String sayHello(String msg) { // 调用dao层.... return "你好," + msg; } }
-
在springboot的启动类上添加@EnableDubbo注解,表示启动dubbo服务,如下:
-
打开dubbo Admin查看。
点击详情,可以具体看见详细信息,如ip、port、权重等等。
继续往下看,则可以看见元数据,是指服务里面具体的方法名,参数列表,返回值。
2. 搭建服务消费者
-
创建springboot工程,取名为dubbo-web。
-
导入相应jar包坐标。
<!-- 若不加web包,则启动时候会导致dubbo服务一直无法启动成功,因此需要--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- dubbo的jar包--> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.8</version> </dependency> <!-- curator是用于连接zk的客户端,因为需要将使用zk作为注册中心--> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>4.2.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>4.2.0</version> </dependency>
-
编写application.yml配置文件
# 设置springboot此项目的端口 server: # 与服务提供者区分 port: 9001 # 消费者可以只需要下面的配置 # 1. dubbo服务名 # 2. 注册中心的地址,因为消费者要远程调用服务器的时候,需要向注册中心获得服务的ip:port dubbo: application: name: dubbo-web registry: protocol: dubbo address: zookeeper://127.0.0.1:2181
-
编写web层相关代码。
-
提取共同模块,取名为dubbo-common,并把共同部分编写在此模块中。
-
将dubbo-common模块 依赖给 dubbo-service模块,并发现实现类没有报错了。
-
同理将 dubbo-web模块 依赖 dubbo-common模块。
-
最正确的dubbo-web模块中Controller的代码。
@RestController public class UserController { // @Autowired @DubboReference UserService userService; @RequestMapping("/Hello") public String sayHello(String msg){ return userService.sayHello(msg); } }
-
同理,在springboot的启动类上添加@EnableDubbo注解,表示启动dubbo服务。
-
由于web模块与service模块需要依赖 common模块,因此在启动过程中,应先让dubbo-common模块先打包。
-
先启动dubbo-service服务模块,再启动dubbo-web消费者模块。
在浏览器中输入 http://localhost:9001?msg=小明
为什么是9001,因为dubbo-web在配置文件application.yml中server.port=9001
为什么要加参数,因为我们HelloController代码中的sayHello方法需要参数。
结果如下:
-
我们还是可以去Dubbo Admin中查看。发现现在也有消费者的信息了。
其他文章
目前安排,是先更新好rabbitmq,再持续更新dubbo