随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。
单一应用架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
分布式服务架构
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
在大规模服务化之前,应用可能只是通过 RMI 或 Hessian 等工具,简单的暴露和引用远程服务,通过配置服务的URL地址进行调用,通过 F5 等硬件进行负载均衡。
当服务越来越多时,服务 URL 配置管理变得非常困难,F5 硬件负载均衡器的单点压力也越来越大。 此时需要一个服务注册中心,动态地注册和发现服务,使服务的位置透明。并通过在消费方获取服务提供方地址列表,实现软负载均衡和 Failover,降低对 F5 硬件负载均衡器的依赖,也能减少部分成本。
当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。 这时,需要自动画出应用间的依赖关系图,以帮助架构师理清关系。
接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器? 为了解决这些问题,第一步,要将服务现在每天的调用量,响应时间,都统计出来,作为容量规划的参考指标。其次,要可以动态调整权重,在线上,将某台机器的权重一直加大,并在加大的过程中记录响应时间的变化,直到响应时间到达阈值,记录此时的访问量,再以此访问量乘以机器数反推总容量。
以上是 Dubbo 最基本的几个需求。
Dubbo 架构
升级性
当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力。下图是未来可能的一种架构:
Zookeeper是什么
官方文档上这么解释zookeeper,它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。
上面的解释有点抽象,简单来说zookeeper=文件系统+监听通知机制。
Zookeeper维护一个类似文件系统的数据结构:
每个子目录项如 NameService 都被称作为 znode(目录节点),和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据
的。
有四种类型的znode:
客户端与zookeeper断开连接后,该节点依旧存在
客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
客户端与zookeeper断开连接后,该节点被删除
客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。
就这么简单,下面我们看看Zookeeper能做点什么呢?
zookeeper功能非常强大,可以实现诸如分布式应用配置管理、统一命名服务、状态同步服务、集群管理等功能,我们这里拿比较简单的分布式应用配置管理为例来说明。
假设我们的程序是分布式部署在多台机器上,如果我们要改变程序的配置文件,需要逐台机器去修改,非常麻烦,现在把这些配置全部放到zookeeper上去,保存在 zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 zookeeper 的通知,然后从 zookeeper 获取新的配置信息应用到系统中。
如上,你大致应该了解Dubbo+zookeeper是个、两个东西,大概能做些什么了,我们马上来学习下zookeeper的安装及使用,并开发一个小程序来实现zookeeper这个分布式配置管理的功能
下载zookeeper安装包
1:重命名 zoo_sample.cfg 文件修改配置文件zoo-1.cfg,原配置文件里有的,修改成下面的值,没有的则加上(也可以不修改)
dataDir=/tmp/zookeeper-1
clientPort=2181
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890
配置说明:
tickTime:这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。
initLimit:这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超过 10个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 10*2000=20 秒
syncLimit:这个配置项标识 Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 5*2000=10秒
dataDir:顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里。
clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。
server.A=B:C:D:其中 A 是一个数字,表示这个是第几号服务器;B 是这个服务器的 ip 地址;C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;D 表示的是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口。如果是伪集群的配置方式,由于 B 都是一样,所以不同的 Zookeeper 实例通信端口号不能一样,所以要给它们分配不同的端口号。
Step4:再从zoo-1.cfg复制两个配置文件zoo-2.cfg和zoo-3.cfg,只需修改dataDir和clientPort不同即可
前台中台后台
zookeeper :注册中心
dubbo-admin:是一个监控管理后台-查看我们注册了哪些服务,哪些服务被消费了一
Dubbo: jar包~
在安装Java环境的情况下 在git上面 下载dubbo-admin-master文件,他其实就是一个springboot项目用maven打成jar包,运行jar吧(在jar包路径中 cmd 执行命令 Java jar jar名)
默认 访问http://localhost:7001 用户名密码都是root
(在idea中项目有可能会文件夹有问题,需要自己在下面这个项目控制中管理文件)
点击:出现:
pom文件
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
<version>2.7.3version>
dependency>
<dependency>
<groupId>com.github.sgroschupfgroupId>
<artifactId>zkclientartifactId>
<version>0.1version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.14version>
<exclusions>
<exclusion>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
exclusion>
exclusions>
dependency>
代码展现:
UserService接口
package com.kuang.w.service;
/**
* @ClassName : UserService //类名
* @Description : user测试的1的接口 服务提供者//描述
* @Author : W //作者
* @Date: 2021/6/1 21:26
*/
public interface UserService {
/**
* class getUser
*
* @return string
**/
String getUser();
}
UserServiceImpl实现UserService接口
package com.kuang.w.service;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;
/**
* @ClassName : UserServiceImpl //类名
* @Description : //描述
* @Author : W //作者
* @Date: 2021/6/1 21:28
*/
/**
* zookeeper 注册发现
*
* @author W
* @Service 可以被扫描到,在项目启动就自动注册到注册中心
* * @Service 这里导入是 org.apache.dubbo.config.annotation.Service中的包 是注册到dubbo中
* @Component 使用了Dubbo后尽量不要Service注解
*/
@Service
@Component
public class UserServiceImpl implements UserService {
@Override
public String getUser() {
return "jxy";
}
}
server.port=8082
#当前应用名字
dubbo.application.name=providers-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
#扫描指定包下服务 注册
dubbo.scan.base-packages=com.kuang.w.service
就是在UserServiceImpl实现UserService接口中
package com.kuang.w.service;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;
/**
* @ClassName : UserServiceImpl //类名
* @Description : //描述
* @Author : W //作者
* @Date: 2021/6/1 21:28
*/
/**
* zookeeper 注册发现
*
* @author W
* @Service 可以被扫描到,在项目启动就自动注册到注册中心
* * @Service 这里导入是 org.apache.dubbo.config.annotation.Service中的包 是注册到dubbo中
* @Component 使用了Dubbo后尽量不要Service注解
*/
@Service
@Component
public class UserServiceImpl implements UserService {
@Override
public String getUser() {
return "jxy";
}
}
让项目跑起来 这时候在访问http://localhost:7001/ 可以看到:
二 .消费者如何消费
1.导入依赖(跟提供服务的导入了的maven依赖一样)
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
<version>2.7.3version>
dependency>
<dependency>
<groupId>com.github.sgroschupfgroupId>
<artifactId>zkclientartifactId>
<version>0.1version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.14version>
<exclusions>
<exclusion>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
exclusion>
exclusions>
dependency>
代码展示:
package com.kuang.w.service;
/**
* @ClassName : UserService //类名
* @Description : user测试的2的接口 服务提供者//描述
* @Author : W //作者
* @Date: 2021/6/1 21:26
*/
public interface UserService {
/**
* class getUser
*
* @return string
**/
String getUser();
}
想拿到provider-server提供的票要去注册中心拿到服务
*
* @Reference //引用,有两种方式
* 第一种:Pom坐标,
* 第二种:可以定义路经相同的接口名 第二种:可以定义路经相同的接口名
package com.kuang.w.service;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
/**
* @ClassName : UserServiceImpl //类名
* @Description : //描述
* @Author : W //作者
* @Date: 2021/6/2 9:38
* @Service 引入的是org.springframework.stereotype.Service;包 这里是为注册到spring容器中
*/
@Service
public class UserServiceImpl {
/**
* 想拿到provider-server提供的票要去注册中心拿到服务
*
* @Reference //引用,
* 第一种:Pom坐标,
* 第二种:可以定义路经相同的接口名
*/
@Reference
UserService userService;
public void byGetUser() {
String user = userService.getUser ();
System.out.println ("在注册中心拿到user = " + user);
}
}
2.配置注册中心的地址,以及服务发现名,和要描的包
server.port=8081
#消费者去哪里拿服务
dubbo.application.name=consumer-server
#去注册中心地址拿服务
dubbo.registry.address=zookeeper://127.0.0.1:2181
测试
package com.kuang.w;
import com.kuang.w.service.UserServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ConsumerServerApplicationTests {
/**
*
*/
@Autowired
UserServiceImpl userService;
@Test
void contextLoads() {
userService.byGetUser ();
}
}
测试结果:
......
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.0)
......
在注册中心拿到user = jxy
Process finished with exit code 0