Dubbo官网:
英文官网: https://dubbo.apache.org/en/
dubbo2.7英文官网:https://dubbo.apache.org/en/docs/v2.7/
中文官网: https://dubbo.apache.org/zh/
Dubbo2.7版本中文官网: https://dubbo.apache.org/zh/docsv2.7/
Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。
在Dubbo2.x中需要解决的需求:
于是乎,引出了dubbo的一个基本的架构:
当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力。
节点 | 角色说明 |
---|---|
Deployer |
自动部署服务的本地代理 |
Repository |
仓库用于存储服务应用发布包 |
Scheduler |
调度中心基于访问压力自动增减服务提供者 |
Admin |
统一管理控制台 |
Registry |
服务注册与发现的注册中心 |
Monitor |
统计服务的调用次数和调用时间的监控中心 |
下载地址: https://zookeeper.apache.org/releases.html#download
我的版本为: zookeeper3.7
下载好了就解压
它的目录如下:
进入 /bin目录下
以管理员身份运行zkServer.cmd (windows下) <= 我执行的是Windows平台下的
执行 bash ./zkServer.sh (linux下)
至此看来,执行的是2181端口
原因是在/conf 目录下有一个zoo.cfg文件(如果没有就将zoo_sample.cfg复制一份,更名为zoo.cfg)
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=H:/apache-zookeeper-3.7.0-bin/data
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
## Metrics Providers
#
# https://prometheus.io Metrics Exporter
#metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
#metricsProvider.httpPort=7000
#metricsProvider.exportJvmInfo=true
其中的clientPort就是你当前服务的端口值,你改了之后启动的zookeeper端口就发生了改变
运行起来之后打开zkCli.cmd
出现了[zk: localhost:2181(CONNECTED 0)] 则表示已经成功连接上了
Dubbo 2.7.x之后变成了org.apache的顶级项目
原先的java包依赖由com.alibaba变成了org.apache
<dependencies>
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubboartifactId>
<version>3.1version>
dependency>
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubbo-dependencies-zookeeperartifactId>
<version>3.1version>
<type>pomtype>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
dependency>
dependencies>
dubbo.application.name=dubbo-provider // dubbo服务应用名称
dubbo.registry.address=zookeeper://127.0.0.1:2181 // 注册中心
dubbo.protocol.port=20884 // dubbo协议端口 写上这个端口号是因为要和注册中心进行数据写入,需要提供指定dubbo服务端口号
package com.xjk.dubbo.service;
/**
* @author MSI-NB
*/
public interface DemoDubboService {
public String test01(String param);
}
package com.xjk.dubbo.service.impl;
import com.xjk.dubbo.service.DemoDubboService;
import org.apache.dubbo.config.annotation.Service;
/**
* @author MSI-NB
*/
@Service
public class DemoDubboServiceImpl implements DemoDubboService {
@Override
public String test01(String param) {
return param+"6666";
}
}
package com.xjk;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Xjk
*/
@SpringBootApplication
@EnableDubbo // 开启Dubbo
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}
}
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
dependency>
package com.xjk.controller;
import com.xjk.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author Xjk
*/
@Controller
public class DemoController {
@Autowired
private DemoService service;
@RequestMapping("/demo")
@ResponseBody
public String demo(@RequestParam("n") String name){
return service.test_consumer(name);
}
}
dubbo.application.name=demo-consumer
dubbo.registry.address=zookeeper://localhost:2181
server.port=8081
package com.xjk.service;
public interface DemoService {
String test_consumer(String param);
}
package com.xjk.service.impl;
import com.xjk.dubbo.service.DemoDubboService;
import com.xjk.service.DemoService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
/**
* @author Xjk
*/
@Service
public class DemoServiceImpl implements DemoService {
@Reference
private DemoDubboService demoDubboService;
@Override
public String test_consumer(String param) {
return demoDubboService.test01("hahahah");
}
}
package com.xjk.dubbo.service;
/**
* @author MSI-NB
*/
public interface DemoDubboService {
String test01(String param);
}
Consumer服务消费方的远程服务调用接口
Provider中的注解
在服务提供的接口实现类上的@Service是org.apache.dubbo.config.annotation.Service,不是spring.framework.stereotype
但是不同版本的dubbo的注解有所变化:
2.7.0 以前的版本 com.alibaba时期 :
@Reference : com.alibaba.dubbo.config.annotation.Reference
@Service : com.alibaba.dubbo.config.annotation.Service
2.7.0~2.7.7 之间的版本
@Reference : org.apache.dubbo.config.annotation.Reference
@Service : org.apache.dubbo.config.annotation.Service
2.7.7 之后的版本
@DubboReference : org.apache.dubbo.config.annotation.DubboReference
@DubboService : org.apache.dubbo.config.annotation.DubboService
将Provider的启动类复制4份
每个在启动时修改propertis这个端口 => dubbo.protocol.port=20884
正如之前注释上写的这个端口与zookeeper做连接,所以相同的服务启动时将这个端口修改以模拟不同的服务器
当这四个都运行起来之后,就是相当于起了拥有4台机器的集群
在provider中的实现类中的方法添加一个print,用来观测consumer调用的是哪一个Provider机器
启动Consumer,通过consumer调用provider服务
如下便是我分别启动4个服务提供方和运行消费方执行后的情况,没有出现报错异常:
第一个:
第二个:
第三个:
第四个:
消费方和访问
访问后会发现不同的机器都能被访问到,那么说明集群确实已经搭建起来了~
仔细看了上面的provider中的yaml/properties会发现配置中出现了:
dubbo.provider.loadbalance=roundrobin
或者
dubbo:
provider:
loadbalance: roundrobin
这就是配置了集群的负载均衡策略
random
: 随机(默认)
roundrobin
: 轮询
leastactive
: 最少活跃调用优先
shortestresponse
: 最短响应优先
consistenthash
: 一致性 Hash
加权轮询过程过程中,如果某节点权重过大,会存在某段时间内调用过于集中的问题。
例如 ABC 三节点有如下权重:{A: 3, B: 2, C: 1}
那么按照最原始的轮询算法,调用过程将变成:A A A B B C
对此,Dubbo 借鉴 Nginx 的平滑加权轮询算法,对此做了优化,调用过程可抽象成下表:
轮前加和权重 | 本轮胜者 | 合计权重 | 轮后权重(胜者减去合计权重) |
---|---|---|---|
起始轮 | \ | \ | A(0), B(0), C(0) |
A(3), B(2), C(1) |
A | 6 | A(-3), B(2), C(1) |
A(0), B(4), C(2) |
B | 6 | A(0), B(-2), C(2) |
A(3), B(0), C(3) |
A | 6 | A(-3), B(0), C(3) |
A(0), B(2), C(4) |
C | 6 | A(0), B(2), C(-2) |
A(3), B(4), C(-1) |
B | 6 | A(3), B(-2), C(-1) |
A(6), B(0), C(0) |
A | 6 | A(0), B(0), C(0) |
我们发现经过合计权重(3+2+1)轮次后,循环又回到了起点,整个过程中节点流量是平滑的,且哪怕在很短的时间周期内,概率都是按期望分布的。
如果用户有加权轮询的需求,可放心使用该算法。
这里的响应时间 = 某个提供者在窗口时间内的平均响应时间,窗口时间默认是 30s。
在服务提供方出现了问题的时候就需要通过多种容错方案解决这个问题
在服务消费方的@Reference(cluster=“填写方案”)
failover
: 失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries=“2” 来设置重试次数(不含第一次)。
failfast
: 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
failsafe
: 失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
failback
: 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
forking
: 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最大并行数。
broadcast
: 广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
available
: 调用目前可用的实例(只调用一个),如果当前没有可用的实例,则抛出异常。通常用于不需要负载均衡的场景。
mergeable
: 将集群中的调用结果聚合起来返回结果,通常和group一起配合使用。