<>一、Web应用架构的演变

​ 随着互联网的发展,网站应用的规模不断扩大,Web应用架构也在不断的演变

​ 四个阶段:单一应用、垂直应用、分布式服务、流动计算



<>1.单一应用架构

​ 当网站访问量很小时,只需要一个应用程序,将所有的功能都部署在一起,以减少部署节点和成本

​ 此时关键问题:简化数据库操作,数据访问框架ORM是核心



​ 适用场景:小型网站、管理系统、简易办公系统

​ 局限:

* 扩展性差
* 不便于协同开发
* 不利于升级维护
<>2. 垂直应用架构

​ 当访问量逐渐增大,单一应用(单机)负载太大,此时可以增加服务器来进行负载均衡,提高响应速度,即集群

​ 但是,当增加的服务器到达一定数据时所带来的加速度会越来越小,此时单纯的增加服务器已无法明显提升响应速度

​ 此时,需要将系统业务拆分成多个 互不相关的 系统,分别部署在独立的服务器上,以提升效率,称为垂直应用

​ 此时关键问题:加速前端页面开发MVC框架(MVVM)



​ 优点:通过拆分项目的业务,实现业务上的独立,降低了开发和维护的难度,便于协同开发,提高了扩展性

​ 局限:每个垂直模块中都有相同的内容(entity、dao、service、web),公共资源无法复用,且业务逻辑与界面无法分离

<>3. 分布式服务架构

​ 当垂直应用越来越多,应用之间的交互无法避免,有些业务系统无法完全拆分为独立系统。

​ 此时,可以将核心业务抽取出现,作为独立的服务Service,逐渐的形成稳定的服务中心,使前端应用能够更好的适应市场需要的变化。

​ 此时关键问题:提高业务的利用以及整合分布式服务框架RPC(Remote Procedure Call 远程过程调用)



<>4. 流动计算架构

​ 当服务越来越多,服务之间的调用和依赖关系也越来越复杂,诞生了面向服务听架构体系(SOA: Service-Oriented Architecture )

​ 容量的评估,小服务资源的浪费等问题开始出现,此时需要增加一个调度中心,基于访问压力实时的管理集群容量,提高集群利用率

​ 此时关键问题:资源调度和治理中心,使用dubbo+zookeeper



<>二、RPC简介

<>1. RPC是什么

​ RPC:Remote Procedure Call 远程过程调用

* 是一种进程间的通信方式
* 它允许应用程序调用网络上的另一个应用程序中的方法
* 对于服务的消费者而言,无需了解远程调用的底层细节,透明的
<>2. 执行流程



​ 执行流程:

* 客户端发起调用请求
* 客户端存根 对请求参数(接口、方法、参数等)进行序列化(封装)
* 客户端存根向 服务器存根 发送消息
* 服务端存根 对接收到的消息 进行反序列化
* 服务端存根发送请求调用本地方法
* 服务器执行业务处理
* 服务器返回处理结果
* 服务端存根 对处理结果进行序列化
* 服务端存根 将结果返回给 客户端存根
* 客户端存根 对接收到的结果进行反序列化
* 客户端存根 将结果返回给 客户端
<>三、Dubbo简介

<>1. Dubbo是什么

​ Apache Dubbo是一款高性能的Java RPC框架(由阿里巴巴开发,开源贡献给Apache)

​ 提供了三个核心功能:

* 面向接口的远程方法调用
* 智能容错和负载均衡
* 服务自动注册和发现
​ 参考:官网 <http://dubbo.apache.org> http://dubbo.apache.org
<http://dubbo.apache.org>

<>2. Dubbo体系结构



​ 执行流程:

* Provider:服务的提供者,负责对外提供服务,提供者在启动的时候,需要向Registry注册自己能够提供的服务
* Consumer:服务的消费者,消费者在启动的时候,需要向Registry订阅自己需要的服务
* Registry:注册中心,授受注册和订阅,会异步的通知订阅者,向消费者提供服务列表
* 当消费者需要执行远程过程调用时,会从Registry获取到服务地址列表(基于负载均衡算法)进行调用,如果调用失败会重新选择新的提供者再次调用
* Monitor:监控中心,统计服务的调用次数和调用时间,服务消费者和提供者会在内存中累计调用次数和调用时间,定时每分钟向监控中心发送一次统计数据
​ 具体业务流程:



<>四、使用Dubbo

<>1. Spring版

<>1.1 创建common工程

​ 创建entity和service接口

<>1.2 创建provider工程

​ 步骤:

*
添加依赖
<!--依赖于dubbon-common--> <dependency> <groupId>com.itany</groupId> <artifactId>
dubbo-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
<!--spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>
spring-core</artifactId> <version>${springversion}</version> </dependency> <
dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</
artifactId> <version>${springversion}</version> </dependency> <dependency> <
groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <
version>${springversion}</version> </dependency> <dependency> <groupId>
org.springframework</groupId> <artifactId>spring-expression</artifactId> <
version>${springversion}</version> </dependency> <!--dubbo核心包--> <dependency> <
groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.2</
version> </dependency> <!--使用zookeeper作为注册中心,在dubbo中会访问zookeeper--> <dependency>
<groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <
version>3.4.13</version> </dependency> <!--zookeeper的客户端实现--> <dependency> <
groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId>
<version>4.0.1</version> </dependency>
*
编写实现类
public class UserServiceImpl implements UserService { @Override public User
findById(int id) { System.out.println("UserServiceImpl.findById"); User user =
new User(); user.setId(id); user.setUsername("tom"); user.setPassword("123");
return user; } }
*
配置dubbo
<!--配置当前dubbo应用程序的名称,可自定义,但必须唯一,一般使用项目名--> <dubbo:application name="
dubbo-provider"/> <!--指定注册中心地址--> <dubbo:registry address="
zookeeper://127.0.0.1:2181"/> <!--配置dubbo协议和端口(通过该端口来提供服务)--> <dubbo:protocol
name="dubbo" port="8888"/> <!--指定要暴露的服务接口--> <dubbo:service interface="
com.itany.service.UserService" ref="userService"/> <bean id="userService" class=
"com.itany.service.impl.UserServiceImpl"/>
<>1.3 创建consumer工程

​ 步骤:

*
添加依赖

同1.2

*
编写使用类
@Controller public class UserController { private UserService userService;
public void setUserService(UserService userService) { this.userService =
userService; } public void findUser(){ User user = userService.findById(1001);
System.out.println(user); } } }
*
配置Dubbo
<dubbo:application name="dubbo-consumer"/> <dubbo:registry address="
zookeeper://127.0.0.1:2181"/> <!--指定要引用的服务--> <dubbo:reference id="userService"
interface="com.itany.service.UserService"/> <bean class="
com.itany.controller.UserController"> <property name="userService" ref="
userService" /> </bean>
<>1.4 测试

*
启动zookeeper

*
编写并运行provider
public static void main(String[] args) { ApplicationContext ac=new
ClassPathXmlApplicationContext("classpath:provider.xml"); //阻塞线程 new Scanner(
System.in).next(); }
*
编写并运行consumer
public static void main(String[] args) { ApplicationContext ac=new
ClassPathXmlApplicationContext("classpath:consumer.xml"); UserController
userController= ac.getBean(UserController.class); userController.findUser();
//阻塞线程 new Scanner(System.in).next(); }
<>2. Spring注解版

<>2.1 改写provider
<!--dubbo组件的扫包--> <dubbo:annotation package="com.itany.service.impl"/>
<!--spring组件的扫包--> <context:component-scan base-package="com.itany.service.impl"
/> // 方式1 // @Service // @com.alibaba.dubbo.config.annotation.Service // 方式2
@Component @Service public class UserServiceImpl implements UserService {
<>2.2 改写consumer
<dubbo:annotation package="com.itany.controller"/> <context:component-scan
base-package="com.itany.controller"/> @Controller public class UserController {
// 使用dubbo的@Reference注入远程服务对象 @Reference private UserService userService;
<>3. SpringBoot版

<>3.1 创建provider工程

​ 步骤:

*
添加依赖
<!--依赖于dubbo-common--> <dependency> <groupId>com.itany</groupId> <artifactId>
dubbo-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
<!--dubbo的starter--> <dependency> <groupId>com.alibaba.boot</groupId> <
artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </
dependency>
*
编写实现类
@Component @Service public class UserServiceImpl implements UserService {
@Override public User findById(int id) { User user = new User(); user.setId(id);
user.setUsername("alice"); user.setPassword("123"); return user; } }
*
配置dubbo
server: port: 8881 dubbo: application: name: boot-provider registry: address:
zookeeper://127.0.0.1:2181 protocol: name: dubbo port: 8888
*
启用dubbo
@SpringBootApplication @EnableDubbo //启用dubbo public class
BootProviderApplication { public static void main(String[] args) {
SpringApplication.run(BootProviderApplication.class, args); } }
<>3.2 创建consumer

​ 步骤:

*
添加依赖,同3.1

*
编写使用类
@Controller @RequestMapping("/user") public class UserController { @Reference
private UserService userService; @RequestMapping("/findUser") public String
findUser(int id, Model model) { User user = userService.findById(id); model.
addAttribute("user", user); return "success"; } }
*
配置dubbo
server: port: 8882 dubbo: application: name: boot-consumer registry: address:
zookeeper://127.0.0.1:2181 spring: thymeleaf: cache: false
<>五、配置dubbo

<>1. 配置的覆盖关系

​ dubbo属性可以配置在如下六个位置:

* 消费者引用方法处
* 提供者暴露方法处
* 消费者引用接口处
* 提供者暴露接口处
* 消费者全局配置处
* 提供者全局配置处
​ 它们的优先顺序如下:

* 方法级优先,接口级次之,全局配置再次之。
* 如果级别一样,则消费方优先,提供方次之
​ 注:建议由服务提供者设置超时时间



<>2. 常用配置

配置项 作用
timeout 超时时间,单位为毫秒, 默认1000ms
retries 重试次数,不包括第一次调用,默认为2,0表示不重试
check 启动时检查提供者是否存在,true表示不存在时报错,false表示启动时不检查,默认为true
url 点对点直连服务提供者,绕过注册中心,以服务接口为单位
<>六、监控中心

<>1. dubbo-admin

​ Dubbo的管理控制台,可以对提供者和消费者进行管理,便于调试,发现问题,解决问题

​ 下载:GitHub——>搜索dubbo-admin——>选择master分支——>Download

​ 配置:修改dubbo-admin目录下的application.properties文件(指定注册中心地址)

​ 打包:mvn package

​ 运行:java -jar dubbo-admin-0.0.1-SNAPSHOT.jar

​ 访问 :http://localhost:7001 <http://localhost:7001> 默认用户名和密码都为root

<>2. dubbo-monitor-simple

​ 简单的监控中心

​ 配置:修改dubbo-monitor-simple目录下的dubbo.properties文件(指定注册中心地址)

​ 打包:mvn package


运行:将生成的dubbo-monitor-simple-2.0.0-assembly.tar.gz解压缩,运行解压后assembly.bin/start.bat
或start.sh <http://start.sh>

​ 访问:http://loclahost:8080 <http://loclahost:8080>

​ 在提供者和消费者中配置,指定监控中心,可以定时每分钟向监控中心发送一次统计数据
dubbo: monitor: protocol: registry # 配置监控中心,从注册中心查找监控中心的地址
<>七、高可用性

​ 通过对系统进行专门设计,从而减少停机时间,提高系统的高可用性

<>1. 负载均衡

<>1.1 简介

​ 将接收到的请求按照一定的规则(负载均衡算法)分发到不同的服务器进行处理,从而提高系统响应和处理速度,称为负载均衡 Load Balance

​ Dubbo提供了四种负载均衡策略:

*
Random LoadBalance 基于权重的随机负载均衡(默认)

按照权重的比例,随机选择集群中的服务器



*
RoundRobin LoadBalance 基于权重的轮循负载均衡

根据权重,按照一定的顺序将请求分发给每个服务器(轮流提供服务)



*
LeastActive LoadBalance 最少活跃数的负载均衡

最少活跃调用数,活跃时间(请求的响应时间)较小的服务器会处理更多的请求



*
ConsistentHash LoadBalance 一致性hash的负载均衡

相同参数的请求总是发给同一台服务器



<>1.2 操作
@Controller @RequestMapping("/user") public class UserController { // 修改负载均衡策略
@Reference(loadbalance = "roundrobin") private UserService userService;
​ 修改权重的两种方式:

* 修改暴露服务时的权重
* 动态调整权重
<>2. 服务降级

​ 当服务器压力剧增时根据实际业务情况及流量,对一些服务有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运行或高效运行。

​ 简单来说,就是将非核心服务进行降级,暂时性的关闭或延迟使用,保证核心服务的正常运行

​ Dubbo支持两种服务降级:

*
mock=force:return+null

表示消费方对该服务的方法调用都直接返回null,不发起远程调用

用来屏蔽不重要服务不可用时对调用方的影响

*
mock=fail:return+null

表示消费方对该服务的方法调用在失败后,再返回null,不抛出异常

用来容忍不重要服务不稳定时对调用方的影响

<>八、补充:面试题

<>1. 高并发的处理

​ 高并发如何优化?

* 负载均衡:集群,通过多台服务器进行负载均衡,提高响应和处理速度
* 动静分离:使用Nginx实现,CDN 内容分发网络
* 缓存:以空间换时间,提高系统效率
* 限流:即流量控制,将过量的请求先放到队列中,等待一定时间后再从队列中取出处理,类似于地铁限流
* 降级:即服务降级
<>2. 集群环境下session的处理

​ 几种解决方式:

*
Session保持:负载均衡进行请求分发时保证每个客户端固定的访问后端的同一台服务器,如Nginx的ip_hash策略

优点:简单,不需要对session做任何处理

缺点:无法保证负载绝对均衡

​ 缺乏容错性,如果当前访问的服务器发生故障,用户被转移到第二个服务器上,此时他的session信息将失效

*
Session复制:将每个服务器的Session信息复制到其他服务器节点,保证Session的同步

缺点:如果Session量很大的话可能会造成网络堵塞,拖慢服务器性能

*
Session共享:将Session放到一个统一的地方,如可以放到数据库中,实际中更推荐使用Redis或Memcached。

视频课程链接:Dubbo快速入门视频课程https://edu.csdn.net/course/detail/10451