比较好的公司网站,合肥哪里有做网站的,适合夫妻二人观看的电视剧,地方门户模板解析 RPC#xff08;远程过程调用#xff09;的源码可以帮助你深入理解其工作原理和实现细节。为了更好地进行源码解析#xff0c;我们选择一个流行的 RPC 框架——Apache Dubbo 作为示例。Dubbo 是一个高性能、轻量级的开源 Java RPC 框架#xff0c;广泛应用于企业级应用…解析 RPC远程过程调用的源码可以帮助你深入理解其工作原理和实现细节。为了更好地进行源码解析我们选择一个流行的 RPC 框架——Apache Dubbo 作为示例。Dubbo 是一个高性能、轻量级的开源 Java RPC 框架广泛应用于企业级应用中。
Dubbo的优劣势
优势 高性能: Dubbo 使用 Netty 作为底层通信框架支持高并发场景下的高效通信。支持多种序列化方式如 Hessian2, Kryo, FST 等可以根据需要选择最合适的序列化方案以提升性能。 丰富的功能: 提供多种负载均衡策略如 RandomLoadBalance, RoundRobinLoadBalance 等。内置了服务降级、熔断和限流机制增强了系统的健壮性。支持多协议如 Dubbo 协议, HTTP 协议等灵活适应不同的应用场景。 良好的生态系统: 拥有成熟的社区支持和丰富的文档资源。可与 Spring Boot 和 Spring Cloud 等主流框架无缝集成简化开发流程。 细粒度控制: 提供详细的配置选项允许开发者对每个服务进行精细化管理。支持服务分组、版本控制等功能便于维护和升级。 强大的监控能力: 内置了监控中心可以实时查看服务的调用情况和性能指标。支持与其他监控系统如 Prometheus, Grafana集成实现全面的监控解决方案。
劣势 依赖复杂: Dubbo 依赖较多引入时可能需要额外配置多个组件如注册中心、序列化库等。学习曲线相对陡峭初学者需要一定时间掌握其复杂的配置和使用方法。 扩展性有限: 尽管 Dubbo 提供了插件机制但相对于一些现代微服务框架如 Spring Cloud其扩展性和灵活性稍显不足。对于某些特定需求可能需要自行开发插件或中间件来满足。 生态整合难度: 虽然可以与 Spring Boot 和 Spring Cloud 集成但在某些高级特性上可能存在兼容性问题。相比 Spring Cloud 生态更为丰富和成熟Dubbo 的第三方组件和支持程度略逊一筹。 社区活跃度: 虽然 Dubbo 社区活跃但由于近年来 Spring Cloud 的崛起部分开发者更倾向于使用后者。新的功能更新速度相对较慢不如 Spring Cloud 快速迭代。
解析目标
我们将逐步解析 Dubbo 的核心组件和流程包括但不限于以下部分
服务注册与发现如何将服务注册到注册中心并从注册中心发现可用的服务。代理机制客户端和服务端如何通过动态代理来透明地进行远程调用。序列化数据在网络传输过程中如何被序列化和反序列化。负载均衡客户端如何根据不同的策略选择合适的服务提供者。通信协议底层网络通信是如何实现的包括 Netty 等网络框架的应用。
准备工作
在开始解析之前请确保你已经具备以下条件
Java 开发环境安装 JDK 8 或更高版本。Git 工具用于克隆 Dubbo 源码仓库。IDE推荐使用 IntelliJ IDEA 或 Eclipse。Maven用于构建和管理依赖。
克隆 Dubbo 源码
首先克隆 Dubbo 的 GitHub 仓库到本地
bash
git clone https://github.com/apache/dubbo.git
cd dubbo
构建 Dubbo 源码
使用 Maven 构建 Dubbo 源码
bash
mvn clean install -DskipTeststrue
核心组件解析
1. 服务注册与发现
Dubbo 使用 ZooKeeper 作为默认的注册中心。以下是服务注册和发现的核心流程。
服务注册
当服务提供者启动时会将其元数据注册到注册中心。主要涉及 RegistryProtocol 和 ZookeeperRegistry 类。
RegistryProtocol: 负责处理服务注册和订阅逻辑。ZookeeperRegistry: 实现了具体的注册中心操作如连接 ZooKeeper 并执行注册。
关键代码位置:
dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/RegistryProtocol.javadubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java
核心方法:
export(InvokerT invoker): 导出服务并注册到注册中心。doRegister(URL url): 执行实际的注册操作。
示例代码:
java
Override
public T ExporterT export(final InvokerT originInvoker) throws RpcException {URL registryUrl getRegistryUrl(originInvoker);// 创建注册器实例Registry registry getRegistry(registryUrl);final URL providerUrl getProviderUrl(originInvoker, registryUrl);// 向注册中心注册服务final URL registeredProviderUrl getRegisteredProviderUrl(providerUrl, registryUrl);ProviderModel providerModel new ProviderModel(providerUrl.getServiceKey(), originInvoker, providerUrl);ApplicationModel.registerProvider(providerModel);registry.register(registeredProviderUrl);// 订阅 override 数据registry.subscribe(getSubscribedOverrideUrl(providerUrl), event - {if (logger.isDebugEnabled()) {logger.debug(Notify urls for subscribe url event.getUrl() , urls: event.getUrls());}MapString, String notifiedUrls new HashMap();if (CollectionUtils.isNotEmpty(event.getUrls())) {event.getUrls().forEach(url - notifiedUrls.put(url.getServiceKey(), url.toFullString()));}refreshOverrideAndInvoker(providerUrl, notifiedUrls);});exporterMapLock.lock();try {ExporterChangeableWrapperT exporter new ExporterChangeableWrapper(originInvoker, null);exporters.put(originInvoker, exporter);return exporter;} finally {exporterMapLock.unlock();}
}
服务发现
当服务消费者启动时会从注册中心订阅服务提供者的地址列表。主要涉及 RegistryProtocol 和 ZookeeperRegistry 类。
RegistryProtocol: 处理订阅逻辑并生成服务代理。ZookeeperRegistry: 监听注册中心的变化并通知消费者。
关键代码位置:
dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/RegistryProtocol.javadubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java
核心方法:
refer(ClassT type, URL url): 引用远程服务并创建代理对象。doSubscribe(URL url, NotifyListener listener): 执行实际的订阅操作。
示例代码:
java
Override
SuppressWarnings({unchecked, rawtypes})
public T InvokerT refer(ClassT type, URL url) throws RpcException {URL subscribeUrl new URL(Constants.CONSUMER_PROTOCOL, mapLocalHost(url.getParameter(Constants.REGISTER_IP_KEY, NetUtils.getLocalHost())), 0,type.getName(), url.getParameters());// 设置检查是否延迟暴露if (!Constants.YES_VALUE.equals(url.getParameter(Constants.LAZY_CONNECT_KEY))) {checkWhetherMetadataCenterExist(subscribeUrl);ListURL urls registryCache.get(subscribeUrl);if (urls ! null !urls.isEmpty()) {// 如果存在直接返回StaticDirectoryT directory new StaticDirectory(subscribeUrl, toInvokers(urls));doRefer(subscribeUrl, directory);return cluster.join(directory);}}// 创建动态目录DynamicDirectoryT directory new DynamicDirectory(subscribeUrl, registry, directoryFactory, false);directory.buildRouterChain(subscribeUrl);directory.setConsumerUrl(subscribeUrl);directoryList.add(directory);registry.subscribe(subscribeUrl, new CacheListener(directory, url));// 创建集群InvokerT clusterInvoker cluster.join(directory);providersModel.setClusterInvoker(clusterInvoker);return clusterInvoker;
}
2. 代理机制
Dubbo 使用动态代理来简化远程调用的过程。主要涉及 ProxyFactory 和 JavassistProxyFactory 类。
ProxyFactory: 定义了代理工厂接口。JavassistProxyFactory: 实现了具体的代理创建逻辑。
关键代码位置:
dubbo-common/src/main/java/org/apache/dubbo/common/proxy/ProxyFactory.javadubbo-proxy/dubbo-proxy-javassist/src/main/java/org/apache/dubbo/common/proxy/javassist/JavassistProxyFactory.java
核心方法:
getProxy(Invoker? invoker): 获取服务代理对象。getInvoker(T proxy, ClassT type, URL url): 将代理对象转换为 Invoker。
示例代码:
java
Override
public T T getProxy(InvokerT invoker, Class?[] interfaces) {return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}Override
public T InvokerT getInvoker(T proxy, ClassT type, URL url) {// TODO Wrapper cannot handle this scenario at this moment.throw new UnsupportedOperationException(Not supported.);
}
3. 序列化
Dubbo 支持多种序列化方式如 Hessian2, Kryo, FST 等。主要涉及 Serialization 和具体序列化类。
Serialization: 定义了序列化接口。Hessian2Serialization: 实现了 Hessian2 序列化的具体逻辑。
关键代码位置:
dubbo-common/src/main/java/org/apache/dubbo/common/serialize/Serialization.javadubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2Serialization.java
核心方法:
serialize(URL url, OutputStream output): 返回序列化对象。deserialize(URL url, InputStream input): 返回反序列化对象。
示例代码:
java
Override
public ObjectOutput serialize(URL url, OutputStream out) throws IOException {Hessian2ObjectOutputStream hessian2os new Hessian2ObjectOutputStream(out);com.caucho.hessian.io.SerializerFactory serializerFactory getSerializerFactory(url);if (serializerFactory ! null) {hessian2os.setSerializerFactory(serializerFactory);}return new CompatibleObjectOutput(hessian2os);
}Override
public ObjectInput deserialize(URL url, InputStream is) throws IOException {Hessian2ObjectInputStream hessian2is new Hessian2ObjectInputStream(is);com.caucho.hessian.io.DeserializerFactory deserializerFactory getDeserializerFactory(url);if (deserializerFactory ! null) {hessian2is.setDeserializerFactory(deserializerFactory);}return new CompatibleObjectInput(hessian2is);
}
4. 负载均衡
Dubbo 提供多种负载均衡策略如 RandomLoadBalance, RoundRobinLoadBalance 等。主要涉及 LoadBalance 和具体策略类。
LoadBalance: 定义了负载均衡接口。RandomLoadBalance: 实现了随机负载均衡的具体逻辑。
关键代码位置:
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/loadbalance/LoadBalance.javadubbo-cluster/src/main/java/org/apache/dubbo/rpc/loadbalance/RandomLoadBalance.java
核心方法:
select(ListInvokerT invokers, URL url, Invocation invocation): 选择合适的 Invoker。
示例代码:
java
Override
protected T InvokerT doSelect(ListInvokerT invokers, URL url, Invocation invocation) {int length invokers.size(); // Number of providersint totalWeight 0; // The sum of weightsboolean sameWeight true; // Every provider has the same weight?for (int i 0; i length; i) {int weight getWeight(invokers.get(i), invocation);totalWeight weight; // Sumif (sameWeight i 0 weight ! getWeight(invokers.get(i - 1), invocation)) {sameWeight false;}}if (totalWeight 0 !sameWeight) {// If (not every provider has the same weight at least one providers weight0), select randomly based on totalWeight.int offset ThreadLocalRandom.current().nextInt(totalWeight);// Return a invoker based on the random value.for (int i 0; i length; i) {InvokerT invoker invokers.get(i);offset - getWeight(invoker, invocation);if (offset 0) {return invoker;}}}// If all providers have the same weight value or totalWeight0, return evenly.return invokers.get(ThreadLocalRandom.current().nextInt(length));
}
5. 通信协议
Dubbo 支持多种通信协议如 Dubbo 协议, HTTP 协议等。主要涉及 Transporter 和具体协议类。
Transporter: 定义了通信接口。NettyTransporter: 实现了基于 Netty 的通信逻辑。
关键代码位置:
dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Transporter.javadubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyTransporter.java
核心方法:
bind(URL url, ChannelHandler handler): 绑定服务器端口。connect(URL url, ChannelHandler handler): 连接到服务器。
示例代码:
Override
public Server bind(URL url, ChannelHandler handler) throws RemotingException {return new NettyServer(url, handler);
}Override
public Client connect(URL url, ChannelHandler handler) throws RemotingException {return new NettyClient(url, wrapChannelHandler(url, handler));
}
示例项目
为了更好地理解上述解析内容我们可以创建一个简单的 Dubbo 示例项目包含服务提供者和消费者。
1. 创建服务接口
定义一个简单的服务接口
UserApi.java:
java
package com.example.dubbo.service;public interface UserApi {String sayHello(String name);
}
2. 实现服务提供者
实现服务接口并启动服务提供者
UserServiceImpl.java:
java
package com.example.dubbo.provider;import com.example.dubbo.service.UserApi;
import org.apache.dubbo.config.annotation.DubboService;DubboService(version 1.0.0)
public class UserServiceImpl implements UserApi {Overridepublic String sayHello(String name) {return Hello, name;}
}
ProviderApplication.java:
java
package com.example.dubbo.provider;import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
EnableDubbo(scanBasePackages com.example.dubbo.provider)
public class ProviderApplication {public static void main(String[] args) {SpringApplication.run(ProviderApplication.class, args);}
}
application.properties:
# Dubbo Application Info
dubbo.application.nameuser-provider
dubbo.registry.addresszookeeper://localhost:2181
dubbo.protocol.namedubbo
dubbo.protocol.port20880
3. 实现服务消费者
创建服务消费者并调用远程服务
ConsumerApplication.java:
java
package com.example.dubbo.consumer;import com.example.dubbo.service.UserApi;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
public class ConsumerApplication implements CommandLineRunner {DubboReference(version 1.0.0)private UserApi userApi;public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}Overridepublic void run(String... args) throws Exception {String result userApi.sayHello(World);System.out.println(result); // Output: Hello, World}
}
application.properties:
# Dubbo Application Info
dubbo.application.nameuser-consumer
dubbo.registry.addresszookeeper://localhost:2181
运行示例 启动 ZooKeeper: 确保 ZooKeeper 服务正在运行。如果没有安装可以从 ZooKeeper 官网 下载并按照官方文档进行安装和启动。 启动服务提供者
bash
cd provider
mvn spring-boot:run
3.启动服务消费者
bash
cd consumer
mvn spring-boot:run
总结
通过对 Dubbo 源码的解析我们深入了解了 RPC 框架的关键组成部分和工作原理包括服务注册与发现、代理机制、序列化、负载均衡和通信协议。此外我们还创建了一个简单的示例项目展示了如何使用 Dubbo 进行服务开发和部署。