dubbo远程调用源码分析(一):客户端发送请求_isinjvm 怎么判断

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

private void init() {

if (initialized) {

return;

}

initialized = true;

if (interfaceName == null ||interfaceName.length() == 0) {

throw new IllegalStateException(“<dubbo:reference interface=”" />interface not allow null!");

}

// 获取消费者全局配置

checkDefault();

appendProperties(this);

if (getGeneric() == null &&getConsumer() != null) {

setGeneric(getConsumer().getGeneric());

}

if(ProtocolUtils.isGeneric(getGeneric())) {

interfaceClass =GenericService.class;

} else {

try {

interfaceClass =Class.forName(interfaceName, true, Thread.currentThread()

.getContextClassLoader());

} catch (ClassNotFoundException e){

throw newIllegalStateException(e.getMessage(), e);

}

checkInterfaceAndMethods(interfaceClass, methods);

}

String resolve =System.getProperty(interfaceName);

String resolveFile = null;

if (resolve == null || resolve.length()== 0) {

resolveFile =System.getProperty(“dubbo.resolve.file”);

if (resolveFile == null ||resolveFile.length() == 0) {

File userResolveFile = newFile(new File(System.getProperty(“user.home”)),“dubbo-resolve.properties”);

if (userResolveFile.exists()) {

resolveFile =userResolveFile.getAbsolutePath();

}

}

if (resolveFile != null &&resolveFile.length() > 0) {

Properties properties = newProperties();

FileInputStream fis = null;

try {

fis = newFileInputStream(new File(resolveFile));

properties.load(fis);

} catch (IOException e) {

throw new IllegalStateException("Unload " + resolveFile + ", cause: "+ e.getMessage(), e);

} finally {

try {

if (null != fis)fis.close();

} catch (IOException e) {

logger.warn(e.getMessage(), e);

}

}

resolve =properties.getProperty(interfaceName);

}

}

if (resolve != null &&resolve.length() > 0) {

url = resolve;

if (logger.isWarnEnabled()) {

if (resolveFile != null&& resolveFile.length() > 0) {

logger.warn(“Usingdefault dubbo resolve file " + resolveFile + " replace " +interfaceName + “” + resolve + " to p2p invoke remoteservice.”);

} else {

logger.warn(“Using-D” + interfaceName + “=” + resolve + " to p2p invokeremote service.");

}

}

}

if (consumer != null) {

if (application == null){

application =consumer.getApplication();

}

if (module == null) {

module = consumer.getModule();

}

if (registries == null) {

registries =consumer.getRegistries();

}

if (monitor == null) {

monitor =consumer.getMonitor();

}

}

if (module != null) {

if (registries == null) {

registries = module.getRegistries();

}

if (monitor == null) {

monitor = module.getMonitor();

}

}

if (application != null) {

if (registries == null) {

registries =application.getRegistries();

}

if (monitor == null) {

monitor =application.getMonitor();

}

}

checkApplication();

checkStubAndMock(interfaceClass);

Map<String, String> map = new HashMap<String, String>();

Map<Object, Object> attributes =new HashMap<Object, Object>();

map.put(Constants.SIDE_KEY,Constants.CONSUMER_SIDE);

map.put(Constants.DUBBO_VERSION_KEY,Version.getVersion());

map.put(Constants.TIMESTAMP_KEY,String.valueOf(System.currentTimeMillis()));

if (ConfigUtils.getPid() > 0) {

map.put(Constants.PID_KEY,String.valueOf(ConfigUtils.getPid()));

}

if (!isGeneric()) {

String revision =Version.getVersion(interfaceClass, version);

if (revision != null &&revision.length() > 0) {

map.put(“revision”,revision);

}

String[] methods =Wrapper.getWrapper(interfaceClass).getMethodNames();

if (methods.length == 0) {

logger.warn("NO method found in service interface " + interfaceClass.getName());

map.put(“methods”,Constants.ANY_VALUE);

} else {

map.put(“methods”,StringUtils.join(new HashSet(Arrays.asList(methods)),“,”));

}

}

map.put(Constants.INTERFACE_KEY,interfaceName);

appendParameters(map, application);

appendParameters(map, module);

appendParameters(map, consumer,Constants.DEFAULT_KEY);

appendParameters(map, this);

String prifix =StringUtils.getServiceKey(map);

if (methods != null &&methods.size() > 0) {

for (MethodConfig method : methods){

appendParameters(map, method,method.getName());

String retryKey =method.getName() + “.retry”;

if (map.containsKey(retryKey)){

String retryValue =map.remove(retryKey);

if (“false”.equals(retryValue)) {

map.put(method.getName() + “.retries”, “0”);

}

}

appendAttributes(attributes,method, prifix + “.” + method.getName());

checkAndConvertImplicitConfig(method,map, attributes);

}

}

//attributes通过系统context进行存储.

StaticContext.getSystemContext().putAll(attributes);

ref = createProxy(map);

}

基本意思就是集成各种配置,比如类名、方法、版本等等,组成一个map,最终通过createProxy()方法生成代理类,createProxy()方法的代码如下:

@SuppressWarnings({“unchecked”, “rawtypes”,“deprecation”})

private T createProxy(Map<String, String> map) {

URL tmpUrl = new URL(“temp”,“localhost”, 0, map);

final boolean isJvmRefer;

if (isInjvm() == null) {

if (url != null &&url.length() > 0) { //指定URL的情况下,不做本地引用

isJvmRefer = false;

} else if(InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {

//默认情况下如果本地有服务暴露,则引用本地服务.

isJvmRefer = true;

} else {

isJvmRefer = false;

}

} else {

isJvmRefer =isInjvm().booleanValue();

}

if (isJvmRefer) {

URL url = new URL(Constants.LOCAL_PROTOCOL,NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);

invoker =refprotocol.refer(interfaceClass, url);

if (logger.isInfoEnabled()) {

logger.info("Using injvmservice " + interfaceClass.getName());

}

} else {

if (url != null &&url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心URL

String[] us =Constants.SEMICOLON_SPLIT_PATTERN.split(url);

if (us != null &&us.length > 0) {

for (String u : us) {

URL url =URL.valueOf(u);

if (url.getPath() ==null || url.getPath().length() == 0) {

url =url.setPath(interfaceName);

}

if(Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {

urls.add(url.addParameterAndEncoded(Constants.REFER_KEY,StringUtils.toQueryString(map)));

} else {

urls.add(ClusterUtils.mergeUrl(url, map));

}

}

}

} else { // 通过注册中心配置拼装URL

List us =loadRegistries(false);

if (us != null &&us.size() > 0) {

for (URL u : us) {

URL monitorUrl =loadMonitor(u);

if (monitorUrl != null){

map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));

}

urls.add(u.addParameterAndEncoded(Constants.REFER_KEY,StringUtils.toQueryString(map)));

}

}

if (urls == null || urls.size()== 0) {

throw newIllegalStateException(“No such any registry to reference " +interfaceName + " on the consumer " + NetUtils.getLocalHost() +” use dubbo version " + Version.getVersion() + “, please config<dubbo:registry address=”…" /> to your springconfig.");

}

}

if (urls.size() == 1) {

invoker =refprotocol.refer(interfaceClass, urls.get(0));

} else {

List<Invoker<?>>invokers = new ArrayList

URL registryURL =null;

for (URL url : urls) {

invokers.add(refprotocol.refer(interfaceClass, url));

if(Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {

registryURL = url; // 用了最后一个registryurl

}

}

if (registryURL != null) { // 有 注册中心协议的URL

// 对有注册中心的Cluster只用 AvailableCluster

URL u = registryURL.addParameter(Constants.CLUSTER_KEY,AvailableCluster.NAME);

invoker = cluster.join(new StaticDirectory(u, invokers));

} else { // 不是 注册中心的URL

invoker = cluster.join(new StaticDirectory(invokers));

}

}

}

Boolean c = check;

if (c == null && consumer !=null) {

c = consumer.isCheck();

}

if (c == null) {

c = true; // default true

}

if (c && !invoker.isAvailable()){

throw new IllegalStateException("Failed to check the status of the service " +interfaceName + “. No provider available for the service " + (group== null ? “” : group + “/”) + interfaceName + (version ==null ? “” : “:” + version) + " from the url " +invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() +” use dubbo version " + Version.getVersion());

}

if (logger.isInfoEnabled()) {

logger.info("Refer dubboservice " + interfaceClass.getName() + " from url " +invoker.getUrl());

}

// 创建服务代理

return (T)proxyFactory.getProxy(invoker);

}

一开始调用isInjvm()方法判断目标接口是否在本地就有,如果本地就有,直接调用本地的接口。

如果本地没有,就在配置中找有没有用户指定的url,如果指定了就使用用户指定的url提供的接口。

如果没有指定url,则从注册中心中获得目标url列表。

如果urls.size()==1,则直接用这个url获得invoker,这个invoker就是最后用来创建动态代理用的。

当urls.size()>1时,有registryURL属性,如果配置了注册中心协议Protocol,则只用AvailableCluster得到invoker。

cluster.join()方法是用来获得invoker的,cluster属性的定义:

private transient volatile Invoker<?> invoker;

Invoker是个接口,根据配置的不同会使用不同的实现类,比如上面的AvailableCluster,他的join()方法是这样的:

public Invoker join(Directory directory)throws RpcException {

return new AbstractClusterInvoker(directory) {

public Result doInvoke(Invocationinvocation, List<Invoker> invokers, LoadBalance loadbalance)throws RpcException {

for (Invoker invoker :invokers) {

if (invoker.isAvailable()){

returninvoker.invoke(invocation);

}

}

throw new RpcException("No provider available in " + invokers);

}

};

}

实际上这个join()方法返回了一个AbstractClusterInvoker对象,并重写了他的doInvoke()方法,这个方法在动态代理实际被调用时会用到。

现在回到createProxy()方法,最后用得到的invoker通过proxyFactory创建动态代理,至此动态代理就创建完了。

消费端动态代理部分

当我们在代码中配置好的SequenceService进行远程调用时,实际上调用的是对应Invoker的invoke()方法,invoker是一个接口,对于这个接口的实现大概是这样的:

Invoker

----AbstractInvoker

----AbstractClusterInvoker

----AbstractProxyInvoker

----DelegateInvoker

----MockClusterInvoker

----MergeableClusterInvoker

----InvokerListenerAdapter

----InvokerListenerAdapter

……

还有很多

AbstractInvoker就是用来远程通信的Invoker

AbstractClusterInvoker是provider是集群时使用Invoker,比AbstractInvoker多了负载均衡,选择provider的过程,最终确定了调用的provider之后还是会调用AbstractInvoker中的invoke()方法。

我们先看AbstractClusterInvoker的invoke()方法:

public Result invoke(final Invocation invocation) throws RpcException {

checkWhetherDestroyed();

LoadBalanceloadbalance;

List<Invoker> invokers = list(invocation);

if (invokers !=null && invokers.size() > 0) {

loadbalance =ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()

.getMethodParameter(invocation.getMethodName(),Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));

} else {

loadbalance=ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);

}

RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);

return doInvoke(invocation, invokers, loadbalance);

}

首先判断当前consumer是否已经destory了,然后用list(invocation)方法获得所有的provider信息,获得负载均衡算法LoadBalance,设置同步属性,最后调用doInvoke方法。

AbstractClusterInvoker的doInvoke()方法是个抽象方法:

protected abstract Result doInvoke(Invocation invocation,List<Invoker> invokers,

LoadBalance loadbalance) throws RpcException;

他的子类有很多,比如:

AvailableClusterInvoker 选择第一个可用的provider。

FailBackClusterInvoker失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作。

FailfastClusterInvoker快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。

FailoverClusterInvoker失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。

FailsafeClusterInvoker失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。

ForkingClusterInvoker并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。

具体使用哪个得看配置

我们以之前提到的AvailableClusterInvoker为例,看一下doInvoke()方法:

public Result doInvoke(Invocation invocation, List<Invoker> invokers,LoadBalance loadbalance) throws RpcException {

for(Invoker invoker : invokers) {

if(invoker.isAvailable()) {

return invoker.invoke(invocation);

}

}

throw new RpcException("No provider available in " + invokers);

}

就是判断invoker是否可用,可用就直接调用invoker的invoke()方法,实际上调用的还是AbstractInvoker的invoke()方法,如果不是集群就直接调这个方法了,该方法代码如下:

public Result invoke(Invocation inv) throws RpcException {

if(destroyed.get()) {

throw new RpcException(“Rpc invoker for service " + this + " on consumer” + NetUtils.getLocalHost()

+" use dubbo version " + Version.getVersion()

+" is DESTROYED, can not be invoked any more!");

}

RpcInvocation invocation = (RpcInvocation) inv;

invocation.setInvoker(this);

if (attachment!= null && attachment.size() > 0) {

invocation.addAttachmentsIfAbsent(attachment);

}

Map<String,String> context = RpcContext.getContext().getAttachments();

if (context !=null) {

invocation.addAttachmentsIfAbsent(context);

}

if(getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY,false)) {

invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());

}

RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);

try {

return doInvoke(invocation);

} catch(InvocationTargetException e) { // biz exception

Throwablete = e.getTargetException();

if (te ==null) {

return new RpcResult(e);

} else {

if (te instanceof RpcException) {

((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);

}

return new RpcResult(te);

}

} catch(RpcException e) {

if(e.isBiz()) {

return new RpcResult(e);

} else {

throw e;

}

} catch(Throwable e) {

return new RpcResult(e);

}

}

还是先判断consumer是否是destory的,其实destroyed是destory的过去分词,不是人家拼错了。

然后经历一堆和AbstractClusterInvoker的invoke一样的参数设置,最后调用doInvoke()方法,而且这个方法在这个Invoker里面也是抽象的。

AbstractInvoker的doInvoke()方法在DubboInvoker类里面有具体实现,这个DubboInvoker是AbstractInvoker的子类,doInvoke()方法如下:

@Override

protected Result doInvoke(final Invocation invocation) throws Throwable {

RpcInvocation inv = (RpcInvocation) invocation;

final String methodName = RpcUtils.getMethodName(invocation);

inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());

inv.setAttachment(Constants.VERSION_KEY, version);

ExchangeClient currentClient;

if(clients.length == 1) {

currentClient = clients[0];

} else {

currentClient = clients[index.getAndIncrement() % clients.length];

}

try {

boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);

boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);

总结

就写到这了,也算是给这段时间的面试做一个总结,查漏补缺,祝自己好运吧,也希望正在求职或者打算跳槽的 程序员看到这个文章能有一点点帮助或收获,我就心满意足了。多思考,多问为什么。希望小伙伴们早点收到满意的offer! 越努力越幸运!

金九银十已经过了,就目前国内的面试模式来讲,在面试前积极的准备面试,复习整个 Java 知识体系将变得非常重要,可以很负责任的说一句,复习准备的是否充分,将直接影响你入职的成功率。但很多小伙伴却苦于没有合适的资料来回顾整个 Java 知识体系,或者有的小伙伴可能都不知道该从哪里开始复习。我偶然得到一份整理的资料,不论是从整个 Java 知识体系,还是从面试的角度来看,都是一份含技术量很高的资料。

三面蚂蚁核心金融部,Java开发岗(缓存+一致性哈希+分布式)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

ntClient = clients[index.getAndIncrement() % clients.length];

}

try {

boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);

boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);

总结

就写到这了,也算是给这段时间的面试做一个总结,查漏补缺,祝自己好运吧,也希望正在求职或者打算跳槽的 程序员看到这个文章能有一点点帮助或收获,我就心满意足了。多思考,多问为什么。希望小伙伴们早点收到满意的offer! 越努力越幸运!

金九银十已经过了,就目前国内的面试模式来讲,在面试前积极的准备面试,复习整个 Java 知识体系将变得非常重要,可以很负责任的说一句,复习准备的是否充分,将直接影响你入职的成功率。但很多小伙伴却苦于没有合适的资料来回顾整个 Java 知识体系,或者有的小伙伴可能都不知道该从哪里开始复习。我偶然得到一份整理的资料,不论是从整个 Java 知识体系,还是从面试的角度来看,都是一份含技术量很高的资料。

[外链图片转存中…(img-EQ0RlLjE-1713599492222)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-rngNdNsd-1713599492222)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 16
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值