分布式系统中的相关概念
传统项目:OA系统、HR系统(用户数量少,单体项目)
互联网项目:用户多、流量大,高并发、海量数据、易受攻击、功能繁琐、变更快
大型互联网项目架构目标
衡量网站的性能指标:
响应时间:执行一个请求从开始到最后收到响应数据所花费的总体时间
并发数:指系统同时能处理的请求数量
并发连接数:指的是客户端向服务器发起请求,并建立了TCP连接,每秒服务器连接接的总的TCP数量
请求数:也成为QPS(Query Per Second)指每秒多少请求
并发用户数:单位时间内有多少用户
吞吐量:指单位时间内系统处理的请求数量
QPS:每秒查询数
TPS:每秒事务数
一个事务是指一个客户机向服务器做出反应的过程,客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数
一个页面的一次访问,只会形成一个TPS,但一次页面请求,可能产生多次对服务器的请求,就会有多个QPS
QPS>= 并发连接数>= TPS
高性能:提供快读的访问体验
高可用:网站服务一直可以正常访问
可伸缩:通过硬件增加/减少,提高/降低处理能力
高可扩展:系统间耦合地,方便的通过新增/移除方式,增加/减少新的功能/模块
安全性:提供网站安全访问和数据加密,安全存储等策略
敏捷性:随需应变,快速响应
集群和分布式
集群相当于很多人一起干相同的事,一个业务模块部署在多台服务器上
分布式相当于很多人干不同的事,一个大的业务系统,拆分为小的业务模块,部署在不同的机器上
架构演进
单体架构模型
垂直架构
分布式架构
SOA架构
微服务架构
Dubbo概述
Dubbo是阿里巴巴公司开源的一个高性能、轻量级的Java RPC框架。
致力于提供高性能和透明化的 RPC远程服务调用方案,以及SOA 服务治理方案。
Dubbo架构图
Dubbo官方推荐使用Zookeeper作为注册中心
Dubbo快速入门
1、创建服务提供者Provider模块
2、创建服务消费者Consumer模块
3、在服务提供者模块编写UserServicelmpl提供服务
4、在服务消费者中的UserController远程调用UserServicelmpl提供的服务
5、分别启动两个服务,测试
使用Controller调用Service,在Spring和Spring MVC中,将UserController通过Spring MVC提供,在Controller中注入service的实现类,然后就可以调用。
这种方式进行拆分的具体是将service和web放在不同的模块中,service使用jar的打包方式,web使用war的打包方式,此时web可充当消费者,service充当服务者,在web的pom文件中引入service的依赖即可。
使用dubbo的方式进行服务间的调用
服务提供方
1、在service模块中将pom文件的打包方式改为war,引入Dubbo和Zookeeper的依赖,并开启一个端口为9000的tomcat服务器插件。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.zg</groupId>
<artifactId>dubbo-server</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring.version>5.1.9.RELEASE</spring.version>
<dubbo.version>2.7.4.1</dubbo.version>
<zookeeper.version>4.0.0</zookeeper.version>
</properties>
<dependencies>
<!-- servlet3.0规范的坐标 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--spring的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--springmvc的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<!--Dubbo的起步依赖,版本2.7之后统一为rg.apache.dubb -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!--ZooKeeper客户端实现 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!--ZooKeeper客户端实现 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!--<!–公共接口模块–>
<dependency>
<groupId>com.zg</groupId>
<artifactId>dubbo-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
-->
</dependencies>
<build>
<plugins>
<!--tomcat插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>9000</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
2、将service提供的服务通过dubbo对外发布
package com.zg.service.impl;
import com.zg.service.UserService;
import org.apache.dubbo.config.annotation.Service;
//@Service//用在单体架构中,将该类的对象创建出来,放到Spring的IOC容器中,bean定义
@Service//将这个类提供的方法(服务)对外发布,将访问的地址ip,端口,路径注册到注册中心中
public class UserServiceImpl implements UserService{
public String sayHello() {
return "hello dubbo";
}
}
3、在核心配置类applicationContext.xml文件中配置dubbo
<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--<context:component-scan base-package="com.zg.service"/>-->
<!--dubbo相关配置-->
<!--1、配置项目的名称,必须唯一-->
<dubbo:application name="dubbo-server"/>
<!--2、配置注册中心的地址-->
<dubbo:registry address="zookeeper://192.168.52.128:2181"/>
<!--3、定义dubbo的包扫描,向spring的component-scan一样-->
<dubbo:annotation package="com.zg.service.impl"/>
</beans>
4、在web.xml文件中扫描dubbo的核心配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- spring 用来扫描dubbo的配置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
服务消费方
1、当使用分布式时就不能将service的依赖进行传递了,因此pom文件就不用引入service的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.zg</groupId>
<artifactId>dubbo-web</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring.version>5.1.9.RELEASE</spring.version>
<dubbo.version>2.7.4.1</dubbo.version>
<zookeeper.version>4.0.0</zookeeper.version>
</properties>
<dependencies>
<!-- servlet3.0规范的坐标 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--spring的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--springmvc的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<!--Dubbo的起步依赖,版本2.7之后统一为rg.apache.dubb -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!--ZooKeeper客户端实现 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!--ZooKeeper客户端实现 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!-- <!–公共接口模块–>
<dependency>
<groupId>org.zg</groupId>
<artifactId>dubbo-server</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>-->
<build>
<plugins>
<!--tomcat插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8000</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
2、在controller中就不能使用本地注入,引入@Reference注解(1、从注册中心获取userService的访问url 2、进行远程调用RPC 3、将结果封装为一个代理对象,给变量赋值)
package com.zg.controller;
import com.zg.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
//@Autowired//本地注入
@Reference
//1、从注册中心获取userService的访问url
//2、进行远程调用RPC
//3、将结果封装为一个代理对象,给变量赋值
private UserService userService;
@RequestMapping("/sayHello")
public String sayHello(){
return userService.sayHello();
}
}
3、在核心配置文件中配置dubbo的信息这里的配置项目名称必须唯一,注册中心地址需要和服务者相同,定义包扫描
<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven/>
<context:component-scan base-package="com.zg.controller"/>
<!--这里的包扫描仍需留下来扫描Springmvc中的注解-->
<!--dubbo相关配置-->
<!--1、配置项目的名称,必须唯一-->
<dubbo:application name="dubbo-web">
<!--这里的配置是因为两个项目在同一台机器部署,发生了qos的端口发生了冲突-->
<dubbo:parameter key="qos.port" value="33333"/>
</dubbo:application>
<!--2、配置注册中心的地址-->
<dubbo:registry address="zookeeper://192.168.52.128:2181"/>
<!--3、定义dubbo的包扫描,向spring的component-scan一样-->
<dubbo:annotation package="com.zg.controller"/>
</beans>
4、在web.xml文件中进行配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- Springmvc -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
5、但是在消费者的Controller中没有服务者的service注入了。就需要抽取一个公共的模块来存放
6、将消费者和服务者的service的接口抽取成单独模块,并在消费者和服务者的pom文件中引入接口模块的依赖,就是只将接口进行抽取其它不变如果注册中心在Linux上部署,一定一定一定要关闭防火墙systemctl stop firewalld.service
dubbo-admin
dubbo-admin管理平台,是图形化的服务管理页面
从注册中心中获取到所有的提供者/消费者进行配置管理
路由规则、动态配置、服务降级、访问控制、权重调整、负载均衡等管理功能
dubbo-admin是一个前后端分离的项目。前端使用vue.,后端使用springboot
安装dubbo-admin其实就是部署该项目
dubbo-admin安装
安装nodehttps://nodejs.org/en/
dubbo-admin 是一个前后端分离的项目。前端使用vue,后端使用springboot,安装 dubbo-admin 其实就是部署该项目。我们将dubbo-admin安装到开发环境上。要保证开发环境有jdk,maven,nodejs
因为前端工程是用vue开发的,所以需要安装node.js,node.js中自带了npm,后面我们会通过npm启动
下载dubbo-adminhttps://github.com/apache/dubbo-admin
修改配置文件application.properties
将配置文件中的注册中心地址改为自己部署注册中心的地址
回到刚才解压的项目目录下执行mvn clean package
打包命令
启动后端
切换目录dubbo-Admin-develop\dubbo-admin-distribution\target>
执行java -jar .\dubbo-admin-0.1.jar
命令启动项目
启动前端E:\soft\dubbo-admin-develop\dubbo-admin-ui
npm run dev
访问http://localhost:8081/
dubbo-admin简单使用
首次使用需要输入默认的用户名密码,这里都为root
A:输入的查询条件com.itheima.service.UserService
B:搜索类型,主要分为【按服务名】【按IP地址】【按应用】三种类型查询
C:搜索结果
修改端口
当需要部署很多的服务,端口就需要改了<dubbo:protocol port="20880"/>
序列化
dubbo 内部已经将序列化和反序列化的过程内部封装了我们只需要在定义pojo类时实现serializable接口即可一般会定义一个公共的pojo模块,让生产者和消费者都依赖该模块。
package com.zg.pojo;
import java.io.Serializable;
public class User implements Serializable {
private int id;
private String username;
private String password;
public User() {
}
public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
地址缓存
注册中心挂了,服务是否可以正常访问
可以,因为dubbo服务消费者在第一次调用时,会将服务提供方地址缓存到本地,以后在调用则不会访问注册中心。
当服务提供者地址发生变化时,注册中心会通知服务消费者。
超时与重试
超时
服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,这个时候,服务消费者会一直等待下去。在某个峰值时刻,大量的请求都在同时请求服务消费者,会造成线程的大量堆积,势必会造成雪崩。
dubbo利用超时机制来解决这个问题,设置一个超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。
使用timeout属性配置超时时间,默认值1000,单位毫秒。
可以配置在服务的生产者中的@Service注解中,也可以配置在消费者的@Reference注解中,如果消费者和生产者都配置的话,其会以消费者为准。但一般都会将其配置在生产者一方。
@Service(timeout = 3000,retries = 0)//当前服务超时时间为3秒,重试0次
@Reference(timeout = 30000,retries = 2)
重试
设置了超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。如果出现网络抖动,则这一次请求就会失败。
Dubbo提供重试机制来避免类似问题的发生。通过retries属性来设置重试次数。默认为2次。
多版本
灰度发布:当出现新功能时,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。
dubbo中使用version属性来设置和调用同一个接口的不同版本灰度发布:当出现新功能时,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。
dubbo中使用version属性来设置和调用同一个接口的不同版本@Service(version = "1.0")
生产者
@Service(version = "1.0")//UserService1
@Service(version = "2.0")//UserService2
消费者
@Reference(version = "1.0")//消费者调用哪个版本就写哪个版本
负载均衡
负载均衡的策略
@Reference(loadbalance = "Random")//按权重随机,默认值。按权重设置随机概率。
@Reference(loadbalance = "RoundRobin")//按权重轮询
@Reference(loadbalance = "LeastActive")//最少活跃调用数,相同活跃数的随机
@Reference(loadbalance = "consistentHash")//—致性Hash,相同参数的请求总是发到同一提供者。
Random :按权重随机,默认值。按权重设置随机概率。
RoundRobin:按权重轮询。
LeastActive:最少活跃调用数,相同活跃数的随机
consistentHash:—致性Hash,相同参数的请求总是发到同一提供者。
集群容错
@Reference(cluster = "Failover Cluster")
Failover Cluster:失败重试。默认值。当出现失败,重试其它服务器,默认重试2次,使用retries配置。一般用于读操作。
Eailfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于写操作。
Failsafe Cluster :失败安全,出现异常时,直接忽略。返回—个空结果。
Failback Cluster :失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster:并行调用多个服务器,只要一个成功即返回。
Broadcast Cluster :广播调用所有提供者,逐个调用,任意一台报错则报错。
服务降级
将不太重要的服务进行关闭,释放资源,保证核心业务服务能正常运行
@Reference(moke = "force:return null")
mock=force:return null表示消费方对该服务的方法调用都直接返回null值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
mock=fail:return null表示消费方对该服务的方法调用在失败后,再返回null值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。