大家好,我是哪吒。
上一篇分享了Dubbo高手之路3,Dubbo服务消费详解。今天详细的分解一下Dubbo服务消费机制,实现快速入门,丰富个人简历,提高面试level,给自己增加一点谈资,秒变面试小达人,BAT不是梦。
三分钟你将学会:
当今分布式系统已经成为企业中不可或缺的一部分。
在分布式系统中,服务网格是一个重要的组成部分。服务网格用于管理和调度服务,以确保系统的可用性和可扩展性。
其中 Dubbo 是一个流行的服务网格框架,它提供了一种简单、可靠、高性能的方式来构建分布式系统。
在 Dubbo 中,服务提供者是框架的核心组件之一,它负责提供服务并将服务暴露给外部应用程序。
本文将介绍 Dubbo 中的服务提供者,包括服务提供者的定义、服务暴露的方式、服务注册的实现、服务提供者的容错处理、服务提供者集群以及服务提供者的网络通信。
Dubbo 服务提供者是指使用 Dubbo 协议提供服务的 Java 程序,它是 Dubbo 服务架构的核心部分。服务提供者通过在服务消费方和提供方之间提供 RPC(远程过程调用) 服务,实现了服务之间的松耦合和低耦合。
在 Dubbo 服务架构中,服务提供者主要负责
Dubbo服务提供者启动流程时序图
Dubbo服务提供者关闭流程时序图
服务提供者是 Dubbo 框架的核心组件之一,它是负责提供服务的应用程序。
在 Dubbo 中,服务提供者的定义如下:
DubboRPC
接口,该接口定义了服务调用的基本逻辑。getService()
方法,该方法返回服务实例的引用。在 Dubbo 中,服务暴露的方式有两种:广播式服务暴露和点式服务暴露。
广播式服务暴露是指服务提供者向所有注册的客户端广播服务发布信息,客户端根据服务名称和版本号等信息自动发现服务。
点式服务暴露是指服务提供者向单个客户端广播服务发布信息,客户端主动请求服务。
在 Dubbo 服务架构中,服务提供者可以将服务暴露为 HTTP 服务、RPC 服务或者二者的结合体。
具体来说,服务提供者可以通过以下几种方式暴露服务:
Dubbo服务提供者暴露服务流程时序图
Dubbo 服务注册是指将服务提供者的信息注册到 Dubbo 注册中心,让服务消费者可以通过注册中心查找到服务提供者并进行调用。
下面是一个简单的 Dubbo 服务注册实现示例,基于 Apache Barrow 框架:
在这个示例中,我们定义了一个名为 MyService
的服务接口,它有两个方法:hello()
和 world()
。
@Service
public interface MyService {
String hello() throws Exception;
String world() throws Exception;
}
在这个示例中,我们实现了 MyService
服务接口,并添加了一个 hello()
方法。
@Component
public class MyServiceImpl implements MyService {
@Override
public String hello() throws Exception {
System.out.println("Hello from MyServiceImpl!");
return "Hello, world!";
}
}
在这个示例中,我们将 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 服务。
在这个示例中,我们将创建一个服务消费者,它使用 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
查找服务,并将其绑定到注册中心。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>
注解配置是 Dubbo 服务架构中另一种常用的配置方式。服务提供者可以通过注解来配置服务相关的信息,如服务接口、服务实现、负载均衡、超时时间等。
注解配置如下:
@ApplicationPath("path1")
@Name("demoService")
@Version("1.0")
public class DemoService implements DemoService {
@Override
public String sayHello(String hello) {
return "Hello, " + hello + "!";
}
}
解释:
Dubbo服务提供者的容错处理的隔离机制实现了服务的隔离,当一个服务出现问题时,该服务的容错处理机制会隔离该服务,以防止问题扩大到整个应用程序中。隔离容器可以是一个独立的线程池,这个线程池只处理隔离的服务,以此来保护应用程序的其他部分。
在 Dubbo 服务架构中,服务提供者支持超时机制,以保障服务的响应速度和可用性。超时机制可以通过配置服务提供者的超时时间参数来实现。
下图描述了 Dubbo 服务提供者的容错处理的失败超时机制的流程。在该图中:
Failover Cluster是Dubbo默认的集群容错模式,它适用于可重试的失败场景。
在该模式下,Dubbo会自动地将调用失败的请求重试到其他的服务提供者上,直到其中一个服务提供者成功返回为止。
Failfast Cluster适用于非幂等性的请求场景。
在该模式下,Dubbo只会尝试调用一次服务提供者,如果调用失败则立即返回失败结果,不会进行重试。
Failsafe Cluster适用于非关键性请求场景。
在该模式下,Dubbo会直接忽略服务提供者的异常,将异常视为服务调用失败,并将失败结果返回给调用方。
Failback Cluster适用于对可靠性要求较高、但不想阻塞服务调用的场景。
在该模式下,Dubbo会将调用失败的请求保存到本地,定时重新尝试调用服务提供者,直到成功返回为止。
在 Dubbo 服务提供者集群中,常见的容错策略包括如下几种:
在 Dubbo 服务提供者集群中,常用的容错实现方式包括如下几种:
Dubbo 是基于 TCP 通信的,它支持多种通信模式,包括经典模式、RPC 模式和 HTTP 模式。在经典模式下,Dubbo 使用 TCP 协议进行通信,服务提供者和消费者之间的通信是通过套接字进行的。在 RPC 模式下,Dubbo 使用 Dubbo 协议进行通信,服务提供者和消费者之间的通信也是通过套接字进行的。在 HTTP 模式下,Dubbo 使用 HTTP 协议进行通信,服务提供者和消费者之间的通信是通过 HTTP 请求和响应进行的。
在 Dubbo 中,通信级别指的是服务消费者和服务提供者之间的通信级别。
具体来说,通信级别包括如下几个:
在 Dubbo 中,服务消费者和服务提供者之间的通信采用 Dubbo 协议进行。Dubbo 协议采用 JSON 格式进行传输,支持远程调用和事件监听等功能。
具体来说,Dubbo 协议包括以下内容:
在 Dubbo 中,默认的序列化方式为 Java 序列化。Java 序列化是一种将对象序列化为字符串的序列化方式,它支持对象在内存中的序列化和反序列化,可以保证对象在不同平台和不同语言之间进行传输和交换。但是,Java 序列化在传输过程中可能会出现数据丢失和变形等问题,因此需要谨慎使用。
Dubbo 提供了多种序列化方式,包括 JSON 序列化、XML 序列化、化石序列化等。服务提供者可以根据需要,自行选择序列化方式。同时,Dubbo 还提供了序列化方式的扩展支持,可以自定义序列化方式,满足不同的需求。
Dubbo 协议采用 HTTP 协议进行传输,支持客户端和服务器之间的通信。
具体来说,Dubbo 协议的传输方式包括如下几种:
Dubbo 协议的传输方式可以通过自定义 HTTP 客户端和服务器进行扩展支持。自定义 HTTP 客户端和服务器可以实现自定义的传输方式,满足不同的需求。同时,Dubbo 还提供了 HTTP 传输方式的扩展支持,可以自定义 HTTP 传输方式,满足不同的需求。
线程模型是指描述计算机程序中线程 (也称为进程或实例) 如何执行的模型。在一个计算机程序中,线程是程序执行的基本单位。每个线程都有自己的堆栈、变量和执行顺序,它们可以独立地运行,直到它们被阻塞或超时为止。线程模型描述了线程如何协作、同步和通信,以确保程序的正确性和可靠性。
Dubbo 线程模型是 Dubbo 框架中用于实现服务消费和服务提供者之间通信的线程模型。在 Dubbo 中,服务消费者和服务提供者之间是通过线程池进行的,每个线程池代表一个服务消费者或服务提供者,线程池中的线程负责执行服务消费者的请求或服务提供者的服务。
线程池模型是指服务提供者在执行服务请求时使用的线程模型。在线程池模型中,服务提供者会创建一个或多个线程,用于执行服务请求。每个线程都拥有自己的堆栈和变量,当服务请求结束时,服务提供者会自动销毁线程,释放资源。
单一线程模型是指服务提供者在执行服务请求时使用的线程模型。在单一线程模型中,服务提供者只会创建一个线程,用于执行服务请求。当服务请求结束时,服务提供者不会自动销毁线程,而是等待线程完成任务后才会销毁线程。
伸缩线程模型是指服务提供者在执行服务请求时使用的线程模型,它可以根据实际需求自动增加或减少线程的数量。在伸缩线程模型中,服务提供者会创建多个线程,当服务请求数量增加时,服务提供者会自动增加线程的数量,以保证服务请求的及时处理。当服务请求数量减少时,服务提供者会自动减少线程的数量,减少资源浪费。
静态配置是指在代码中直接编写服务提供者的配置信息,例如服务接口的 IP 地址、端口号、协议、依赖库等信息。这种方式的优点是简单易用,可以快速地搭建服务提供者,但是在运行时无法根据实际情况进行修改。
动态配置是指在运行时根据请求的实际情况动态地配置服务提供者。这种方式的优点是灵活性高,可以根据实际情况进行修改,但是在代码量上会多一些。
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
属性指定了服务依赖的对象。
Dubbo 的配置规则主要包括以下几个方面:
com.example.demo
.demoService
.localhost:20888
.com.example.demo.Dao
.在实现服务提供者的配置信息时,需要按照上述规则进行编写。例如,下面是一个符合规则的服务提供者的配置信息:
@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
组件的注入点。
🏆本文收录于,Java进阶教程系列。
全网最细Java零基础手把手入门教程,系列课程包括:基础篇、集合篇、Java8新特性、多线程、代码实战,持续更新中(每周1-2篇),适合零基础和进阶提升的同学。
🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师。