随着互联网的发展,网站应用的规模不断扩大,Web应用架构也在不断的演变
四个阶段:单一应用、垂直应用、分布式服务、流动计算
当网站访问量很小时,只需要一个应用程序,将所有的功能都部署在一起,以减少部署节点和成本
此时关键问题:简化数据库操作,数据访问框架ORM是核心
适用场景:小型网站、管理系统、简易办公系统
局限:
当访问量逐渐增大,单一应用(单机)负载太大,此时可以增加服务器来进行负载均衡,提高响应速度,即集群
但是,当增加的服务器到达一定数据时所带来的加速度会越来越小,此时单纯的增加服务器已无法明显提升响应速度
此时,需要将系统业务拆分成多个 互不相关的 系统,分别部署在独立的服务器上,以提升效率,称为垂直应用
此时关键问题:加速前端页面开发MVC框架(MVVM)
优点:通过拆分项目的业务,实现业务上的独立,降低了开发和维护的难度,便于协同开发,提高了扩展性
局限:每个垂直模块中都有相同的内容(entity、dao、service、web),公共资源无法复用,且业务逻辑与界面无法分离
当垂直应用越来越多,应用之间的交互无法避免,有些业务系统无法完全拆分为独立系统。
此时,可以将核心业务抽取出现,作为独立的服务Service,逐渐的形成稳定的 服务中心 ,使前端应用能够更好的适应市场需要的变化。
此时关键问题:提高业务的利用以及整合分布式服务框架RPC(Remote Procedure Call 远程过程调用)
当服务越来越多,服务之间的调用和依赖关系也越来越复杂,诞生了面向服务听架构体系(SOA: Service-Oriented Architecture )
容量的评估,小服务资源的浪费等问题开始出现,此时需要增加一个 调度中心 ,基于访问压力实时的管理集群容量,提高集群利用率
此时关键问题:资源调度和治理中心,使用dubbo+zookeeper
RPC:Remote Procedure Call 远程过程调用
执行流程:
Apache Dubbo是一款高性能的Java RPC框架(由阿里巴巴开发,开源贡献给Apache)
提供了三个核心功能:
参考:官网 http://dubbo.apache.org
执行流程:
具体业务流程:
创建entity和service接口
步骤:
添加依赖
<!--依赖于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.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>
启动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(); }
<!--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 {
<dubbo:annotation package="com.itany.controller"/> <context:component-scan base-package="com.itany.controller"/>
@Controller public class UserController { // 使用dubbo的@Reference注入远程服务对象 @Reference private UserService userService;
步骤:
添加依赖
<!--依赖于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.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属性可以配置在如下六个位置:
它们的优先顺序如下:
注:建议由服务提供者设置超时时间
配置项 | 作用 |
---|---|
timeout | 超时时间,单位为毫秒, 默认1000ms |
retries | 重试次数,不包括第一次调用,默认为2,0表示不重试 |
check | 启动时检查提供者是否存在,true表示不存在时报错,false表示启动时不检查,默认为true |
url | 点对点直连服务提供者,绕过注册中心,以服务接口为单位 |
Dubbo的管理控制台,可以对提供者和消费者进行管理,便于调试,发现问题,解决问题
下载:GitHub——>搜索dubbo-admin——>选择master分支——>Download
配置:修改dubbo-admin目录下的application.properties文件(指定注册中心地址)
打包: mvn package
运行: java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
访问 : http://localhost:7001 默认用户名和密码都为root
简单的监控中心
配置:修改dubbo-monitor-simple目录下的dubbo.properties文件(指定注册中心地址)
打包: mvn package
运行:将生成的dubbo-monitor-simple-2.0.0-assembly.tar.gz解压缩,运行解压后assembly.bin/start.bat 或 start.sh
访问: http://loclahost:8080
在提供者和消费者中配置,指定监控中心,可以定时每分钟向监控中心发送一次统计数据
dubbo: monitor: protocol: registry # 配置监控中心,从注册中心查找监控中心的地址
通过对系统进行专门设计,从而减少停机时间,提高系统的高可用性
将接收到的请求按照一定的规则(负载均衡算法)分发到不同的服务器进行处理,从而提高系统响应和处理速度,称为负载均衡 Load Balance
Dubbo提供了四种负载均衡策略:
Random LoadBalance 基于权重的随机负载均衡(默认)
按照权重的比例,随机选择集群中的服务器
RoundRobin LoadBalance 基于权重的轮循负载均衡
根据权重,按照一定的顺序将请求分发给每个服务器(轮流提供服务)
LeastActive LoadBalance 最少活跃数的负载均衡
最少活跃调用数,活跃时间(请求的响应时间)较小的服务器会处理更多的请求
ConsistentHash LoadBalance 一致性hash的负载均衡
相同参数的请求总是发给同一台服务器
@Controller @RequestMapping("/user") public class UserController { // 修改负载均衡策略 @Reference(loadbalance = "roundrobin") private UserService userService;
修改权重的两种方式:
当服务器压力剧增时根据实际业务情况及流量,对一些服务有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运行或高效运行。
简单来说,就是将非核心服务进行降级,暂时性的关闭或延迟使用,保证核心服务的正常运行
Dubbo支持两种服务降级:
mock=force:return+null
表示消费方对该服务的方法调用都直接返回null,不发起远程调用
用来屏蔽不重要服务不可用时对调用方的影响
mock=fail:return+null
表示消费方对该服务的方法调用在失败后,再返回null,不抛出异常
用来容忍不重要服务不稳定时对调用方的影响
高并发如何优化?
几种解决方式:
Session保持:负载均衡进行请求分发时保证每个客户端固定的访问后端的同一台服务器,如Nginx的ip_hash策略
优点:简单,不需要对session做任何处理
缺点:无法保证负载绝对均衡
缺乏容错性,如果当前访问的服务器发生故障,用户被转移到第二个服务器上,此时他的session信息将失效
Session复制:将每个服务器的Session信息复制到其他服务器节点,保证Session的同步
缺点:如果Session量很大的话可能会造成网络堵塞,拖慢服务器性能