文章目录
Dubbo
1. 背景
1.1架构演变图
- 单体架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
- 垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
- 分布式服务架构
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
- 流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
2. RPC
2. 1 概念
Remote Procedure Call远程过程调用,是一种不同进程间的通信方式,它允许程序调用另一个地址空间的过程或函数,不用开发人员显示的编写代码实现调用的细节(底层基本都是使用Socket).
当A服务器想远程调用B服务器时,需要借助远程调用插件通过url访问B服务器.当A需要携带数据给B或B返回数据给A时,这些数据都需要进行序列化才能进行传输,然后进行反序列化进行接收.
远程调用插件底层是使用Socket技术解决了通信的问题,序列化解决了信息传输的问题.
3. Dubbo的简略架构图及调用关系
- 节点 角色说明
Provider 暴露服务的服务提供方
Consumer 调用远程服务的服务消费方
Registry 服务注册与发现的注册中心
Monitor 统计服务的调用次数和调用时间的监控中心
Container 服务运行容器
- 调用关系
0 服务容器负责启动,加载,运行服务提供者。
1 服务提供者在启动时,向注册中心注册自己提供的服务。
2 服务消费者在启动时,向注册中心订阅自己所需的服务。
3 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
4 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
5 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心.
4. 搭建Dubbo环境
4.1 搭建Zookeeper环境
Zookeeper是一个分布式开源框架,交给了Apache进行管理,可以实现服务地址分配
它能提供配置维护,域名维护,分布式同步,组服务等.
他的目标是将封装好的复杂易出错的关键服务,将简单易用的接口和性能高效,功能稳定的系统提供给>用户
它包含一个简单的 原语集, 提供Java和
- 下载zookeeper安装包
- 解压安装包
可以选择在windows,Linux安装,在此以windows为例
- 进入conf目录复制粘贴zoo_sample.cfg,修改文件名为zoo.cfg
#Zookeeper服务器之间或者客户端和服务端维持心跳的时间间隔值
#每隔2秒都会发送一个心跳
#默认情况下最小的会话超时时间为tickTime的两倍
tickTime=2000
#同时初始化的最大数量
initLimit=10
#最大同时连接数
syncLimit=5
#存放临时数据和日志的目录
dataDir=D:/zookeeper/zookeeper-3.4.13/tmp/
dataLogDir=D:/zookeeper/zookeeper-3.4.13/logs
#客户端连接的端口号
clientPort=2181
-
在C:\Windows\System32\drivers\etc\hosts文件放开
-
在bin目录双击zkServer.cmd开启服务端,zkCli.cmd(退出请按Ctrl+C 输入Y)
在客户端输入 ls / 出现zookeeper就代表成功
4.2 搭建dubbo环境
- 在github下载incubator-dubbo-ops-master,解压
- 查看incubator-dubbo-ops-master\dubbo-admin\src\main\resources\application.properties
- 在incubator-dubbo-ops-master\dubbo-admin下cmd执行下面命令打包
mvn clean package -Dmaven.test.skip=true
在dubbo-admin目录下就会有一个target目录
- 启动dubbo管理平台
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
浏览器输入localhost:7001
输入用户名和密码root
4.3 创建服务提供者和服务器消费者
根据用户的id查询用户所含有的角色信息
用户-----消费者
角色-----提供者
4.3.1创建common模块
存放服务提供者和服务消费者都需要的接口和javaBean
RoleServer和UserService接口,Role都可以放在该模块
4.3.2 服务提供者
- 依赖
在这里,服务提供者和消费者所需要的依赖是一致的
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.5</version>
<exclusions>
<exclusion>
<!--我的解析不了根据依赖传递下载的spring-context,所以我不让它下载-->
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.10.RELEASE</version>
</dependency>
<!--引入zookeeper的客户端-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.2</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.35.Final</version>
</dependency>
<!--依赖common模块-->
<dependency>
<groupId>comzy</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
- 配置文件provider.xml
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 指定当前应用的名称:用于计算依赖关系 -->
<dubbo:application name="provider" />
<!-- 服务注册与发现:服务提供方把应用注册到zookeeper注册中心 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!-- 指定服务之间的通信规则:用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!--指定服务器提供者暴露的接口-->
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.zy.service.RoleService" ref="demoService" />
<!-- 和本地bean一样实现服务 -->
<bean id="demoService" class="com.zy.service.impl.RoleServiceImpl" />
<!--也可以使用阿里巴巴的@Service注解,需要开启组件扫描
<context:component-scan base-package="com.zy.service"/>
-->
</beans>
配置启动类
//Spring容器的构建:加载配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:provider.xml");
//启动Spring容器
context.start();
//从输入流中读取字节 --- 阻塞
System.in.read();
- 创建service的接口和实现类
RoleService接口放在common模块,自定义JavaBean–Role
RoleServiceImpl如下
@Service
public class RoleServiceImpl implements RoleService {
@Override
public List<Role> queryRoleByUserId(Integer id) {
//假设在数据库中查询到了三个角色的信息
ArrayList<Role> roles = new ArrayList<>();
roles.add(new Role(1,"CEO","高级打工者"));
roles.add(new Role(2,"CTO","高级代码搬运工"));
roles.add(new Role(3,"Programmer","cv工程师"));
return roles;
}
}
4.1.3服务消费者
- consumer.xml
<context:component-scan base-package="com.zy.service"/>
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="consumer" />
<!-- 使用multicast广播注册中心暴露发现服务地址 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!-- 生成远程服务代理,可以和本地bean一样使用roleService -->
<dubbo:reference id="roleService" interface="com.zy.service.RoleService" />
- UserServiceImpl
它的接口放在common模块
@Service
public class UserServiceImpl implements UserService {
@Resource
private RoleService roleService;
@Override
public List<Role> roles(Integer userId) {
return roleService.queryRoleByUserId(userId);
}
}
- 启动类
public static void main(String[] args) throws IOException {
//Spring容器的构建:加载配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:consumer.xml");
//启动Spring容器
context.start();
UserService userService = context.getBean("userServiceImpl", UserService.class);
List<Role> roles = userService.roles(1);
for (Role role : roles) {
System.out.println(role);
}
//从输入流中读取字节 --- 阻塞
System.in.read();
}
4.1.4 测试
- 开启zookeeper的服务端和客户端
- 开启dubbo
- 启动消费者和提供者
- 浏览器输入http://localhost:7001/
因为我是安装到本地的并没有安装到Linux上,所以在配置文件里指定zookeeper的地址时可以使用127.0.0.1
5. 监控中心
打包dubbo-monitor-simple
mvn package -Dmaven.test.skip=true
到target文件夹解压gz进入解压文件夹的.bin文件夹双击start.bat
在服务提供者和消费者的配置文件配置监控中心
<dubbo:monitor protocol=“registry”/>
监控中心的配置文件
dubbo.container=log4j,spring,registry,jetty-monitor
dubbo.application.name=simple-monitor
dubbo.application.owner=dubbo
dubbo.registry.address=zookeeper://127.0.0.1:2181
#其他服务与监控中心通信的端口号
dubbo.protocol.port=7070
#监控中心访问web页面的端口号
dubbo.jetty.port=8080
dubbo.jetty.directory=${user.home}/monitor
dubbo.charts.directory=${user.home}/monitor/charts
dubbo.statistics.directory=${user.home}/monitor/statistics
dubbo.log4j.file=logs/dubbo-monitor-simple.log
dubbo.log4j.level=WARN
6. dubbo的注解版
使用<dubbo:annotation package=“com.zy.service”/>开启dubbo的组件扫描
属性注入使用注解@Reference,service层使用注解@Service,它们都是用alibaba包下的,不是spring
7. hystrix容错
使用在服务消费方,提供方不需要
在springMVC环境下使用的
- 依赖
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.12</version>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>1.5.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.10.RELEASE</version>
</dependency>
- spring.xml
<!--组件扫描-->
<context:component-scan base-package="com.zy.controller"/>
<!--处理器适配器,处理器映射器-->
<mvc:annotation-driven />
<!--通过切面配置代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean class="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect"/>
- web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<!--监听器-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:consumer.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--设置编码格式-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encodingUTF-8</param-name>
<param-value></param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
</web-app>
- controller.java
/**
*容错
* 注解@ResponseBody //需要依赖jackson工具
* @author yxk
* @param userId
* @return java.util.List<com.zy.pojo.Role>
* Date 2021/4/7 11:27
*/
@RequestMapping("/listRole.action")
@ResponseBody
@HystrixCommand(fallbackMethod = "hystrix")
public List<Role> listRoleAll(Integer userId) {
List<Role> roles = roleService.queryRoleByUserId(userId);
System.out.println(roles);
return roles;
}
public List<Role> hystrix(Integer userId) {
System.out.println("服务调用失败执行该方法");
return null;
}
注意: hystrix方法的参数和返回值需要和listRoleAll方法一致
8. 负载均衡
consumer.xml
负载均衡:loadbalance
random 随机
roundrobin 轮询
eastactive 响应时间最短
onsistenthash 一致性hash
<dubbo:reference id="roleService" interface="com.zy.service.RoleService" check="false" loadbalance="random"/>
proviter.xml
每启动一次修改一次port,模拟服务提供者集群
<dubbo:protocol name="dubbo" port="20880" />
线程模型
dubbo采用的网络通信是netty,netty采用的是NIO
NIO(非阻塞IO),并发高,网络传输快,封装性好
主要使用的环境在高并发
服务提供方 NettyServer 使用两种类型的线程池:IO线程,业务线程
IO线程:
EvenLoopGroop(boss)任务:j接收客户端的请求,并把接受的请求分发给worker处理
EvenLoopGroop(worker)任务
boos+worker加起来被称为"IO线程"
IO线程:主要用来处理网络中产生的网络数据,通过解码(反序列化)和编码(序列化)完成
业务线程:主要处理具体的业务逻辑
dubbo默认采用的机制采用长连接的方式.默认情况下一个consumer和一个provider只会建立一个连接,
在这种情况下,IO线程主要进行解码和编码,处理网络数据,监听具体的数据请求,直接通过channel发布.
业务线程主要处理IO线程处理之后的业务数据
- NIO( 非阻塞IO模式)
更多功能开发请参考官方文档