大家好,我是哪吒。
上一篇分享了图解Dubbo,Dubbo服务消费详解。今天详细的分解一下Dubbo服务消费机制,实现快速入门,丰富个人简历,提高面试level,给自己增加一点谈资,秒变面试小达人,BAT不是梦。
三分钟你将学会:
- 什么是服务提供者
- 服务提供者的配置
- 服务提供者集群
- 服务提供者的网络通信
- 服务提供者的线程模型
- 服务提供者的动态配置
当今分布式系统已经成为企业中不可或缺的一部分。
在分布式系统中,服务网格是一个重要的组成部分。服务网格用于管理和调度服务,以确保系统的可用性和可扩展性。
其中 Dubbo 是一个流行的服务网格框架,它提供了一种简单、可靠、高性能的方式来构建分布式系统。
在 Dubbo 中,服务提供者是框架的核心组件之一,它负责提供服务并将服务暴露给外部应用程序。
本文将介绍 Dubbo 中的服务提供者,包括服务提供者的定义、服务暴露的方式、服务注册的实现、服务提供者的容错处理、服务提供者集群以及服务提供者的网络通信。
一、服务提供者
Dubbo 服务提供者是指使用 Dubbo 协议提供服务的 Java 程序,它是 Dubbo 服务架构的核心部分。服务提供者通过在服务消费方和提供方之间提供 RPC(远程过程调用) 服务,实现了服务之间的松耦合和低耦合。
在 Dubbo 服务架构中,服务提供者主要负责
- 将服务暴露给服务消费方;
- 并将服务消费方的请求转化为 Dubbo 协议的请求;
- 然后将响应返回给服务消费方。
Dubbo服务提供者启动流程时序图
Dubbo服务提供者关闭流程时序图
1、Dubbo服务提供者的定义
服务提供者是 Dubbo 框架的核心组件之一,它是负责提供服务的应用程序。
在 Dubbo 中,服务提供者的定义如下:
- 服务提供者是一个 Java 类,它实现了 Dubbo 服务接口。
- 服务提供者必须实现
DubboRPC
接口,该接口定义了服务调用的基本逻辑。 - 服务提供者必须实现
getService()
方法,该方法返回服务实例的引用。
在 Dubbo 中,服务暴露的方式有两种:广播式服务暴露和点式服务暴露。
广播式服务暴露是指服务提供者向所有注册的客户端广播服务发布信息,客户端根据服务名称和版本号等信息自动发现服务。
点式服务暴露是指服务提供者向单个客户端广播服务发布信息,客户端主动请求服务。
2、服务暴露的方式
在 Dubbo 服务架构中,服务提供者可以将服务暴露为 HTTP 服务、RPC 服务或者二者的结合体。
具体来说,服务提供者可以通过以下几种方式暴露服务:
- HTTP 服务:服务提供者可以通过 HTTP 协议提供服务,服务消费方可以通过 HTTP 请求的方式调用服务提供者的服务。
- RPC 服务:服务提供者可以通过 Dubbo 协议提供 RPC 服务,服务消费方可以通过 Dubbo 协议的 RPC 请求调用服务提供者的服务。
- HTTP+RPC 服务:服务提供者可以通过 HTTP 协议提供 RPC 服务,服务消费方可以通过 HTTP 请求的方式调用服务提供者的 RPC 服务。
Dubbo服务提供者暴露服务流程时序图
3、服务注册的实现
Dubbo 服务注册是指将服务提供者的信息注册到 Dubbo 注册中心,让服务消费者可以通过注册中心查找到服务提供者并进行调用。
下面是一个简单的 Dubbo 服务注册实现示例,基于 Apache Barrow 框架:
(1)创建服务接口
在这个示例中,我们定义了一个名为 MyService
的服务接口,它有两个方法:hello()
和 world()
。
@Service
public interface MyService {
String hello() throws Exception;
String world() throws Exception;
}
(2)实现服务接口
在这个示例中,我们实现了 MyService
服务接口,并添加了一个 hello()
方法。
@Component
public class MyServiceImpl implements MyService {
@Override
public String hello() throws Exception {
System.out.println("Hello from MyServiceImpl!");
return "Hello, world!";
}
}
(3)注册服务
在这个示例中,我们将 MyService
服务接口注册到 Dubbo 注册中心。
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constant.CommonConstants;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.name.NameFactory;
import org.apache.dubbo.common.service.ServiceLoader;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.web.WebRegistry;
public class DubboBootstrap {
public static void main(String[] args) throws Exception {
// 创建 Registry
Registry registry = new WebRegistry();
// 设置注册中心 URL
URL url = URL.valueOf("dubbo://localhost:9090/my-service?registry=" + registry);
// 创建 NameFactory
NameFactory nameFactory = new NameFactory();
// 注册中心配置
ExtensionLoader.loadExtensions(registry, "dubbo.extension.provides");
// 注册服务
registry.bind(url, new MyService());
// 启动服务
System.out.println("Dubbo Bootstrap started...");
}
}
在这个示例中,我们首先创建了一个 WebRegistry
,它使用 WebServlet
监听端口 9090
,用于向注册中心注册和查找服务。然后,我们将 MyService
服务接口注册到注册中心。最后,我们启动了 Dubbo 服务。
(4)服务消费者
在这个示例中,我们将创建一个服务消费者,它使用 Dubbo 注册中心查找服务并调用服务接口。
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constant.CommonConstants;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.name.NameFactory;
import org.apache.dubbo.common.service.ServiceLoader;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.consumer.consumer.ConsumerRegistry;
public class DubboConsumer {
public static void main(String[] args) throws Exception {
// 创建 Registry
Registry registry = new ConsumerRegistry(new HashMap<URL, Registry>());
// 注册中心 URL
URL url = URL.valueOf("dubbo://localhost:9090/my-service?registry=" + registry);
// 查找服务
ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class);
for (MyService service : serviceLoader) {
URL serviceUrl = service.getUrl();
registry.bind(serviceUrl, service);
}
// 启动服务
System.out.println("Dubbo Consumer started...");
}
}
在这个示例中
- 我们创建了一个
ConsumerRegistry
,它使用HashMap
存储注册中心的信息; - 然后,我们将
MyService
服务接口注册到注册中心; - 最后,我们使用
ServiceLoader
查找服务,并将其绑定到注册中心。
二、服务提供者的配置
1、服务提供者的XML配置
XML 配置是 Dubbo 服务架构中最常用的配置方式。服务提供者可以通过 XML 文件来配置服务相关的信息,如服务接口、服务实现、负载均衡、超时时间等。
XML 配置如下:
<dubbo:application name="example-service-group"
context="default">
<dubbo:service interface="com.example.DemoService"
name="demoService"
version="1.0">
<dubbo:argument value="com.example.DemoService"/>
<dubbo:method name="sayHello" args="hello"/>
</dubbo:service>
<dubbo:服务提供者 address="http://localhost:8080/example-service"/>
<dubbo:服务提供者 address="http://localhost:8080/example-service/"/>
<dubbo:服务提供者 address="http://localhost:8080/example-service?负载均衡算法=weight"/>
<dubbo:服务提供者 address="http://localhost:8080/example-service?负载均衡算法=roundrobin"/>
<dubbo:服务提供者 address="http://localhost:8080/example-service?超时时间=5000"/>
</dubbo:application>
2、服务提供者的注解配置
注解配置是 Dubbo 服务架构中另一种常用的配置方式。服务提供者可以通过注解来配置服务相关的信息,如服务接口、服务实现、负载均衡、超时时间等。
注解配置如下:
@ApplicationPath("path1")
@Name("demoService")
@Version("1.0")
public class DemoService implements DemoService {
@Override
public String sayHello(String hello) {
return "Hello, " + hello + "!";
}
}
3、服务提供者的容错处理
(1)失败重试机制
解释:
- 客户端(Client)向Dubbo服务提供者(Provider)发送请求。
- 如果请求成功,服务提供者将返回响应。
- 如果请求失败,则Dubbo服务提供者将通过熔断器(Circuit Breaker)返回服务熔断响应。
- 如果请求过载,Dubbo服务提供者将通过熔断器返回请求过载响应。
- 如果服务降级,则Dubbo服务提供者将返回服务降级响应,使用线程池(Thread Pool)处理请求。
(2)隔离机制
Dubbo服务提供者的容错处理的隔离机制实现了服务的隔离,当一个服务出现问题时,该服务的容错处理机制会隔离该服务,以防止问题扩大到整个应用程序中。隔离容器可以是一个独立的线程池,这个线程池只处理隔离的服务,以此来保护应用程序的其他部分。
(3)超时机制
在 Dubbo 服务架构中,服务提供者支持超时机制,以保障服务的响应速度和可用性。超时机制可以通过配置服务提供者的超时时间参数来实现。
下图描述了 Dubbo 服务提供者的容错处理的失败超时机制的流程。在该图中:
- 服务提供者首先向服务注册中心注册服务;
- 然后消费者通过服务注册中心查找到服务提供者列表,向服务提供者发送请求;
- 服务提供者在处理请求时,如果请求超时,会返回超时响应;否则,会返回正常响应。
三、服务提供者集群
1、集群容错模式
(1)Failover Cluster(失败自动切换)
Failover Cluster是Dubbo默认的集群容错模式,它适用于可重试的失败场景。
在该模式下,Dubbo会自动地将调用失败的请求重试到其他的服务提供者上,直到其中一个服务提供者成功返回为止。
(2)Failfast Cluster(快速失败)
Failfast Cluster适用于非幂等性的请求场景。
在该模式下,Dubbo只会尝试调用一次服务提供者,如果调用失败则立即返回失败结果,不会进行重试。
(3)Failsafe Cluster(失败安全)
Failsafe Cluster适用于非关键性请求场景。
在该模式下,Dubbo会直接忽略服务提供者的异常,将异常视为服务调用失败,并将失败结果返回给调用方。
(4)Failback Cluster(失败自动恢复)
Failback Cluster适用于对可靠性要求较高、但不想阻塞服务调用的场景。
在该模式下,Dubbo会将调用失败的请求保存到本地,定时重新尝试调用服务提供者,直到成功返回为止。
2、集群容错策略
在 Dubbo 服务提供者集群中,常见的容错策略包括如下几种:
- 轮询策略:在集群中选择一台服务器作为主服务器,其他服务器作为从服务器。当主服务器出现故障时,从服务器会自动切换到主服务器,并继续提供服务。
- 负载均衡策略:在集群中将所有服务提供者部署在不同的节点上,并通过负载均衡算法来均衡服务提供者的负载。
- 容错连接策略:在集群中将所有服务提供者部署在不同的节点上,并通过集群间的通信来保障集群的稳定性和可用性。
3、集群容错实现
在 Dubbo 服务提供者集群中,常用的容错实现方式包括如下几种:
- 服务注册与发现:将服务提供者部署在注册中心上,服务消费者可以通过注册中心来查找服务提供者,并提供服务。
- 服务网关:在服务提供者和服务消费者之间加入服务网关,网关作为服务提供者和消费者之间的接口,负责处理服务调用和容错处理。
- 消息队列:将服务提供者和消费者之间的通信通过消息队列来实现,当服务提供者出现故障时,可以通过消息队列来传递消息,继续提供服务。
- 集群间通信:将服务提供者部署在不同的节点上,通过集群间通信来保障集群的稳定性和可用性。常用的集群间通信方式包括负载均衡、消息队列、Zookeeper 等。
四、服务提供者的网络通信
1、Dubbo基于TCP通信
Dubbo 是基于 TCP 通信的,它支持多种通信模式,包括经典模式、RPC 模式和 HTTP 模式。在经典模式下,Dubbo 使用 TCP 协议进行通信,服务提供者和消费者之间的通信是通过套接字进行的。在 RPC 模式下,Dubbo 使用 Dubbo 协议进行通信,服务提供者和消费者之间的通信也是通过套接字进行的。在 HTTP 模式下,Dubbo 使用 HTTP 协议进行通信,服务提供者和消费者之间的通信是通过 HTTP 请求和响应进行的。
2、通信级别的概念
在 Dubbo 中,通信级别指的是服务消费者和服务提供者之间的通信级别。
具体来说,通信级别包括如下几个:
- 接口级别:指服务消费者和服务提供者之间的接口级别通信,也就是服务消费者通过接口调用服务提供者的方法。
- 方法级别:指服务消费者和服务提供者之间的方法级别通信,也就是服务消费者通过接口调用服务提供者的方法,服务提供者在接收到调用后,执行具体的服务逻辑。
- 服务级别:指服务消费者和服务提供者之间的服务级别通信,也就是服务消费者通过调用服务提供者的服务,服务提供者在接收到调用后,执行具体的服务逻辑。
3、通信协议
在 Dubbo 中,服务消费者和服务提供者之间的通信采用 Dubbo 协议进行。Dubbo 协议采用 JSON 格式进行传输,支持远程调用和事件监听等功能。
具体来说,Dubbo 协议包括以下内容:
- 服务接口信息:包括服务接口的类名、版本号、方法信息等。
- 服务实现信息:包括服务实现的类名、版本号、方法信息等。
- 调用参数信息:包括调用方法的参数类型、参数值等。
- 调用结果信息:包括调用方法的返回值类型、返回值值等。
4、Dubbo默认的序列化方式
在 Dubbo 中,默认的序列化方式为 Java 序列化。Java 序列化是一种将对象序列化为字符串的序列化方式,它支持对象在内存中的序列化和反序列化,可以保证对象在不同平台和不同语言之间进行传输和交换。但是,Java 序列化在传输过程中可能会出现数据丢失和变形等问题,因此需要谨慎使用。
5、序列化方式的扩展支持
Dubbo 提供了多种序列化方式,包括 JSON 序列化、XML 序列化、化石序列化等。服务提供者可以根据需要,自行选择序列化方式。同时,Dubbo 还提供了序列化方式的扩展支持,可以自定义序列化方式,满足不同的需求。
6、Dubbo协议的传输方式
Dubbo 协议采用 HTTP 协议进行传输,支持客户端和服务器之间的通信。
具体来说,Dubbo 协议的传输方式包括如下几种:
- GET 方式:客户端向服务器发送 GET 请求,服务器返回请求结果。
- POST 方式:客户端向服务器发送 POST 请求,服务器接收请求数据,并根据请求数据执行相应的操作。
- PUT 方式:客户端向服务器发送 PUT 请求,服务器将请求数据保存到服务器中。
- DELETE 方式:客户端向服务器发送 DELETE 请求,服务器删除请求数据。
7、传输方式的扩展支持
Dubbo 协议的传输方式可以通过自定义 HTTP 客户端和服务器进行扩展支持。自定义 HTTP 客户端和服务器可以实现自定义的传输方式,满足不同的需求。同时,Dubbo 还提供了 HTTP 传输方式的扩展支持,可以自定义 HTTP 传输方式,满足不同的需求。
五、服务提供者的线程模型
1、什么是线程模型
线程模型是指描述计算机程序中线程 (也称为进程或实例) 如何执行的模型。在一个计算机程序中,线程是程序执行的基本单位。每个线程都有自己的堆栈、变量和执行顺序,它们可以独立地运行,直到它们被阻塞或超时为止。线程模型描述了线程如何协作、同步和通信,以确保程序的正确性和可靠性。
2、Dubbo线程模型
Dubbo 线程模型是 Dubbo 框架中用于实现服务消费和服务提供者之间通信的线程模型。在 Dubbo 中,服务消费者和服务提供者之间是通过线程池进行的,每个线程池代表一个服务消费者或服务提供者,线程池中的线程负责执行服务消费者的请求或服务提供者的服务。
3、线程池模型
线程池模型是指服务提供者在执行服务请求时使用的线程模型。在线程池模型中,服务提供者会创建一个或多个线程,用于执行服务请求。每个线程都拥有自己的堆栈和变量,当服务请求结束时,服务提供者会自动销毁线程,释放资源。
4、单一线程模型
单一线程模型是指服务提供者在执行服务请求时使用的线程模型。在单一线程模型中,服务提供者只会创建一个线程,用于执行服务请求。当服务请求结束时,服务提供者不会自动销毁线程,而是等待线程完成任务后才会销毁线程。
5、伸缩线程模型
伸缩线程模型是指服务提供者在执行服务请求时使用的线程模型,它可以根据实际需求自动增加或减少线程的数量。在伸缩线程模型中,服务提供者会创建多个线程,当服务请求数量增加时,服务提供者会自动增加线程的数量,以保证服务请求的及时处理。当服务请求数量减少时,服务提供者会自动减少线程的数量,减少资源浪费。
六、服务提供者的动态配置
1、配置方式的概述
静态配置是指在代码中直接编写服务提供者的配置信息,例如服务接口的 IP 地址、端口号、协议、依赖库等信息。这种方式的优点是简单易用,可以快速地搭建服务提供者,但是在运行时无法根据实际情况进行修改。
动态配置是指在运行时根据请求的实际情况动态地配置服务提供者。这种方式的优点是灵活性高,可以根据实际情况进行修改,但是在代码量上会多一些。
2、动态配置方式
Dubbo 提供了多种方式来实现动态配置,其中最常用的方式是使用 RegisterUtil.register()
方法进行注册。在使用动态配置时,需要先创建一个 RegisterUtil
对象,然后使用该对象进行注册。注册完成后,可以通过调用 RegisterUtil.unregister()
方法来取消注册。
另外,Dubbo 还支持使用 XML 文件进行配置,可以使用 XML 文件来存储服务提供者的配置信息。XML 文件的格式如下:
<dubbo:service interface="com.example.demo.Service"
name="demoService"
registry-address="localhost:20888">
<dubbo:reference name="demoDao" interface="com.example.demo.Dao" />
</dubbo:service>
在上面的 XML 文件中,interface
属性指定了服务接口的名称,name
属性指定了服务的名称,registry-address
属性指定了服务注册中心的连接地址,dubbo:reference
属性指定了服务依赖的对象。
3、配置规则
Dubbo 的配置规则主要包括以下几个方面:
- 服务接口的命名空间:服务接口的命名空间指定了服务接口的命名空间,例如
com.example.demo
. - 服务名称:服务名称指定了服务的名称,例如
demoService
. - 服务注册中心:服务注册中心指定了服务注册的中心地址,例如
localhost:20888
. - 服务依赖库:服务依赖库指定了服务依赖的其他库,例如
com.example.demo.Dao
.
4、 配置规则的实现
在实现服务提供者的配置信息时,需要按照上述规则进行编写。例如,下面是一个符合规则的服务提供者的配置信息:
@Component
public class DemoService implements Service {
@Override
public void run(String name, Map<String, Object> params) throws Exception {
System.out.println(name + " is running...");
}
}
@Component
public class DemoDao implements Dao {
@Override
public void doSomething(String name) throws Exception {
System.out.println(name + " is done.");
}
}
@Configuration
public class DemoConfig {
@Bean
public Service service() {
return new DemoService();
}
@Bean
public Dao dao() {
return new DemoDao();
}
}
在上面的代码中,@Component
注解标记了 DemoService
和 DemoDao
两个组件,并且它们都实现了 Service
接口。@Configuration
注解标记了 DemoConfig
类,它包含了两个 @Bean
注解,分别标记了 DemoService
和 DemoDao
组件的注入点。