Dubbo框架
Apache Dubbo dubbo文档
一、为什么要使用dubbo ?
随着Internet的快速发展,Web应用程序的规模不断扩大,最后我们发现传统的垂直体系结构(单片式)已无法解决。分布式服务体系结构和流计算体系结构势在必行,迫切需要一个治理系统来确保体系结构的有序发展。
1.整体架构
当流量非常低时,只有一个应用程序,所有功能都部署在一起以减少部署节点和成本。在这一点上,数据访问框架(ORM)是简化CRUD工作量的关键。
2.垂直架构
当流量增加时,添加单片应用程序实例不能很好地加速访问,提高效率的一种方法是将单片应用程序拆分为离散的应用程序。此时,用于加速前端页面开发的Web框架(MVC)是关键。
3.分布式服务架构
当垂直应用程序越来越多时,应用程序之间的交互是不可避免的,一些核心业务被提取出来并作为独立的服务来服务,从而逐渐形成一个稳定的服务中心,这样前端应用程序就可以更好地响应不断变化的市场需求。很快。此时,用于业务重用和集成的分布式服务框架(RPC)是关键。
4.流计算架构
当服务越来越多时,容量评估变得困难,而且小规模的服务也经常造成资源浪费。为解决这些问题,应添加调度中心,以根据流量管理集群容量并提高集群利用率。目前,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
二、使用dubbo的要求
服务治理:
在大规模服务出现之前,应用程序可能只是通过使用RMI或Hessian公开或引用远程服务,调用是通过配置服务URL完成的,负载平衡是通过硬件(如F5)完成的。
当服务越来越多时,配置服务URL变得非常困难,F5硬件负载平衡器的单点压力也在增加。此时,需要一个服务注册表来动态注册和发现服务,以使服务的位置透明。通过获取用户方的服务提供商地址列表,可以实现软负载平衡和故障转移,从而减少了对F5硬件负载平衡器的依赖性以及某些成本。
当事情进一步发展时,服务依赖关系变得如此复杂,以至于甚至连架构师也无法完全描述应用程序架构之间的关系。
这时,需要自动绘制应用程序的依赖关系图,以帮助架构师清除关系。
**然后,流量变得更大,服务的容量问题暴露出来,需要多少台机器来支持该服务?何时应添加机器?**为解决这些问题,首先,应将日常服务电话和响应时间视为容量规划的参考。其次,动态调整权重,增加在线计算机的权重,并记录响应时间的变化,直到达到阈值为止,记录此时的访问次数,然后将此访问次数乘以计算机总数,以计算容量依次。
三、dubbo框架的构造
节点角色规范
节点 | 角色规格 |
---|---|
Provider | 提供者公开远程服务 |
Consumer | 消费者致电远程服务 |
Registry | 注册表负责服务发现和配置 |
Monitor | 监视器计算服务调用的次数和耗时 |
Container | 容器管理服务的生命周期 |
服务关系
Container
负责启动,加载和运行服务Provider
。Provider``Register
在启动时向其注册服务。Consumer
从Register
启动时开始订阅所需的服务。Register
将Provider
s列表返回Consumer
,更改时,Register
将Consumer
通过长连接将更改后的数据推送到。Consumer``Provider
根据软负载平衡算法选择s之一并执行调用,如果失败,它将选择另一个Provider
。- 两者
Consumer
和Provider
都会计算内存中调用服务的次数和耗时,并将统计信息发送到Monitor
每一分钟。
Dubbo功能:
连接性
Register
负责注册和搜索服务地址(例如目录服务),Provider
并且Consumer
仅在启动期间与注册表交互,并且注册表不转发请求,因此压力较小- “监视器”负责计算服务调用的数量和耗时,统计信息将首先在
Provider
和Consumer
的内存中汇总,然后发送到Monitor
- “提供商”将服务注册为“注册”,并将耗时的统计信息(不包括网络开销)报告给“监控器”
- “消费者”从中获取服务提供商地址列表,
Registry
根据LB算法直接呼叫提供商,向上报耗时的统计信息Monitor
,其中包括网络开销 - 之间的连接
Register
,Provider
并且Consumer
是长连接,Moniter
是一个例外 Register``Provider
通过长连接意识到存在,当断开连接时Provider
,Register
会将事件推送到Consumer
- 它不影响已经运行的实例
Provider
和Consumer
甚至所有Register
和Monitor
趴下,因为Consumer
得到的缓存Provider
上榜 Register
并且Monitor
是可选的,Consumer
可以Provider
直接连接
坚固性
Monitor
的停机时间不会影响使用情况,只会丢失一些采样数据- 当数据库服务器关闭时,
Register
可以通过检查其缓存将服务Provider
列表返回到Consumer
,但是新服务器Provider
无法注册任何服务 Register
是一个对等集群,当任何实例出现故障时,它将自动切换到另一个集群- 即使所有
Register
实例都发生故障,Provider
并且Consumer
仍然可以通过检查其本地缓存来进行通信 - 服务
Provider
是无状态的,一个实例的停机时间不会影响使用 Provider
一项服务的所有服务关闭后,Consumer
将无法使用该服务,并无限地重新连接以等待服务Provider
恢复
可伸缩性、可扩展性
Register
是一个可以动态增加其实例的对等群集,所有客户端将自动发现新实例。Provider
是无状态的,它可以动态地增加部署实例,并且注册表会将新的服务提供者信息推送到Consumer
可升级性
当服务集群进一步扩展并且IT治理结构进一步升级时,需要动态部署,并且当前的分布式服务体系结构不会带来阻力。这是未来可能的架构
节点角色规范
节点 | 角色规格 |
---|---|
Deployer | 用于自动服务部署的本地代理 |
Repository | 该存储库用于存储应用程序包 |
Scheduler | 调度程序会根据访问压力自动增加或减少服务提供商 |
Admin | 统一管理控制台 |
Registry | 注册表负责服务发现和配置 |
Monitor | 监控器计算服务呼叫时间和时间 |
四、dubbo用法
1.在spring框架运行
在dubbo-demo目录下创建3个子目录:
- dubbo-demo-api:通用服务api
- dubbo-demo-provider:提供者代码
- dubbo-demo-consumer:消费者代码
添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.21.0-GA</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.netty/netty -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
<version>3.10.5.Final</version>
</dependency>
服务提供者
DemoService.java:
package org.apache.dubbo.demo;
public interface DemoService {
String sayHello(String name);
}
项目结构应如下所示:
.
├── dubbo-demo-api
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── org
│ └── apache
│ └── dubbo
│ └── demo
│ └── DemoService.java
在服务提供商中实现接口
DemoServicelmpl.java
package org.apache.dubbo.demo.provider;
import org.apache.dubbo.demo.DemoService;
public class DemoServiceImpl implements DemoService {
public String sayHello(String name) {
return "Hello " + name;
}
}
使用Spring配置公开服务
provider.xml
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
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="demo-provider"/>
<!-- 注册地址 -->
<dubbo:registry address="multicast://224.5.6.7:1234"/>
<!-- dubbo暴露端口 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- 实现类接口 -->
<bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
<!--服务提供者-->
<dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"/>
</beans>
配置日志纪录系统
默认情况下,Dubbo使用log4j
作为日志记录系统,它还支持slf4j,Apache Commons Logging和JUL日志记录。
log4j.properties
###set log levels###
log4j.rootLogger=info, stdout
###output to the console###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n
引导服务提供商
provider.java
package org.apache.dubbo.demo.provider;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Provider {
public static void main(String[] args) throws Exception {
System.setProperty("java.net.preferIPv4Stack", "true");
//spring环境上下文
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
context.start();
System.out.println("Provider started.");
System.in.read(); // press any key to exit
}
}
最后,项目结构应如下所示:
├── dubbo-demo-provider
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ │ └── org
│ │ └── apache
│ │ └── dubbo
│ │ └── demo
│ │ └── provider
│ │ ├── DemoServiceImpl.java
│ │ └── Provider.java
│ └── resources
│ ├── META-INF
│ │ └── spring
│ │ └── dubbo-demo-provider.xml
│ └── log4j.properties
服务消费者–使用spring配置引用远程服务
Consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
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="demo-consumer"/>
<!-- 注册地址 -->
<dubbo:registry address="multicast://224.5.6.7:1234"/>
<!-- 服务提供者 -->
<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService"/>
</beans>
引导消费者
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.apache.dubbo.demo.DemoService;
public class Consumer {
public static void main(String[] args) throws Exception {
//spring上下文环境
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"META-INF/spring/dubbo-demo-consumer.xml"});
context.start();
// 得到提供者
DemoService demoService = (DemoService)context.getBean("demoService");
// 打印
String hello = demoService.sayHello("world");
// 结果
System.out.println(hello);
}
}
最后,项目结构应如下所示:
├── dubbo-demo-consumer
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ │ └── org
│ │ └── apache
│ │ └── dubbo
│ │ └── demo
│ │ └── consumer
│ │ └── Consumer.java
│ └── resources
│ ├── META-INF
│ │ └── spring
│ │ └── dubbo-demo-consumer.xml
│ └── log4j.properties
运行结果:
Hello world
2.在springBoot框架运行
添加依赖
<!--web组件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--dubbo依赖-->
<dependency>
<groupId>com.alibaba.spring.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<!--zkClient依赖-->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
</dependency>
定义服务接口
public interface UserService {
User selectUserById(Integer id);
}
实体类User.java
public class User implements Serializable
{
private Integer id;
private String name;
public Integer getId()
{ return id; }
public void setId(Integer id)
{ this.id = id; }
public String getName()
{ return name; }
public void setName(String name)
{ this.name = name; }
}
在provider模块中实现服务接口
/*** 用户接口实现类 */
@Service(interfaceClass=UserService.class)
@Component
public class UserServiceImpl implements UserService
{
@Override
public User selectUserById(Integer id)
{
User user=new User();
user.setId(id);
user.setName("jj");
System.out.println("提供服务");
return user;
}
}
application.yml
server:
# 端口
port: 8003
spring:
application:
name: dubbo
dubbo:
#开启dubbo
server: true
#应用信息
application:
name: provider
# 注册中心地址
registry:
address: multicast://224.5.6.7:1234
protocol:
#协议名
name: dubbo
#协议端口
port: 20880
#扫描包的位置
scan: com.xxx.dubbodemoprovider.service
启动测试提供者
@SpringBootApplication
//开启dubbo
@EnableDubboConfiguration
public class DubbodemoProviderApplication {
public static void main(String[] args) {
SpringApplication.run(DubbodemoProviderApplication.class, args);
}
}
在consumer模块中实现服务接口
@SpringBootApplication
//开启dubbo
@EnableDubboConfiguration
public class DubbodemoConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(DubbodemoConsumerApplication.class, args);
}
}
application.yml配置
server:
# 端口
port: 8002
spring:
application:
name: dubbo
dubbo:
#开启dubbo
server: true
#应用信息
application:
name: consumer
# 注册中心地址
registry:
address: multicast://224.5.6.7:1234
protocol:
#协议名
name: dubbo
#协议端口
port: 20880
#扫描包的位置
scan: com.xxx.dubbodemoconsumer.service
/**用户消费类 */
@Component
public class UserInit implements CommandLineRunner {
@Reference(interfaceClass= UserService.class)
private UserService userService;
@Override
public void run(String... args) throws Exception {
User user=userService.selectUserById(1);
System.out.println("消费服务");
System.out.println(user);
}
}
五、dubbo中的负载均衡
1.LoadBalance策略
随机负载平衡
- Ramdom重量设定随机概率。
- 某一部分发生冲突的可能性很高,但是呼叫次数越大,分布越均匀。并且当基于概率使用权重时,分布证明是均匀的,这也有助于动态调整提供者权重。
RoundRobin权重负载平衡
- RoundRobin,使用
权重
的公共顾问确定轮循比率。 - 流向速度较慢的提供程序的流量可能会导致请求堆积,例如,如果某个提供程序以非常慢的速度处理请求,但该请求仍处于活动状态,这意味着它可以正常接收请求。根据RoundRobin政策,消费者将以预定的速度连续向该提供程序发送请求,而不会意识到提供程序的不良状态。最终,我们将收到许多关于此不健康提供商的请求。
最小活动负载平衡
- LeastActive是一种基于活动变量的随机机制,
actives
表示消费者已发送但尚未返回的请求数量。 - 较慢的提供者将收到较少的请求,因为较慢的提供者将收到较高的请求
actives
。
一致的哈希负载平衡
- ConsistentHash,始终将请求的相同参数发送到相同的提供程序。
- 当提供程序失败时,基于虚拟节点算法的对提供程序的原始请求将平均到其他提供程序,不会引起急剧的变化。
- 算法参考:http://zh.wikipedia.org/wiki/Consistent_hashing
- 默认情况下只有第一个参数哈希,如果要修改,请配置
<dubbo:parameter key="hash.arguments" value="0,1" />
- 默认情况下160个虚拟节点,如果要修改,请配置
<dubbo:parameter key="hash.nodes" value="320" />
2.组态
服务器服务级别
<dubbo:service interface="..." loadbalance="roundrobin" />
客户服务水平
<dubbo:reference interface="..." loadbalance="roundrobin" />
服务器方法级别
<dubbo:service interface="...">
<dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:service>
客户方法级别
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:reference>