Dubbo源码解析-服务调用(七)
一、服务调用流程
服务在订阅过程中,把notify 过来的urls 都转成了invoker,不知道大家是否还记得前面的rpc 过程,protocol也是在服务端和消费端各连接子一个invoker,如下图:

这张图主要展示rpc 主流程,消费端想要远程调用时,他是调用invoker#invoke方法;服务端收到网络请求时,也是直接触发invoker#invoke 方法。,Dubbo 设计invoker 实体的初衷,就是想要统一操作,无论你要做什么方法调用,都请使用invoker 来包装后,使用invoker#invoke来调用这个动作,简化来看,rpc 过程即是如此:
消费端invoker#invoke ------>网络---->服务端invoker#invoke--->ref 服务
上面的链路是个简化的路径,但在实际的dubbo 调用中,此链条可能会有局部的多层嵌套,如:
消费端invoker#invoke ------>容错策略--->网络---->服务端invoker#invoke--->ref服务
那么此时要重新定义链条吗?那不是个好主意。Dubbo 的做法是这样,将容错策略也包装成invoker 对象:
FailfastClusterInvoker#invoke--->protocolInvoker.invoke-->网络---->服务端invoker#invoke--->ref 服务
依次类推,dubbo 内部有非常多的invoker 包装类,它们层层嵌套,但rpc流程不关心细节,只傻瓜式地调用其invoke 方法,剩下的逻辑自会传递到最后一个invoker 进行网络调用。
服务引入时,最终又对生成invoker的做了一层代理,所以熟悉动态代理的知道,当执行服务调用时最先执行到的是InvokerInvocationHandler#invoke方法
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {try {return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));} catch (Throwable fromJavassist) {// try fall back to JDK proxy factorytry {T proxy = jdkProxyFactory.getProxy(invoker, interfaces);logger.error("Failed to generate proxy by Javassist failed. Fallback to use JDK proxy success. " +"Interfaces: " + Arrays.toString(interfaces), fromJavassist);return proxy;} catch (Throwable fromJdk) {logger.error("Failed to generate proxy by Javassist failed. Fallback to use JDK proxy is also failed. " +"Interfaces: " + Arrays.toString(interfaces) + " Javassist Error.", fromJavassist);logger.error("Failed to generate proxy by Javassist failed. Fallback to use JDK proxy is also failed. " +"Interfaces: " + Arrays.toString(interfaces) + " JDK Error.", fromJdk);throw fromJavassist;}}
}
二、Dubbo过滤器链
Filter(过滤器)在很多框架中都有使用过这个概念,基本上的作用都是类似的,在请求处理前或者处理后做一些通用的逻辑,而且Filter 可以有多个,支持层层嵌套。
Dubbo 的Filter 实现入口是在ProtocolFilterWrapper,因为ProtocolFilterWrapper 是Protocol 的包装类,所以会在SPI 加载的Extension 的时候被自动包装进来。当然filter 要发挥作用,必定还是要
在嵌入到RPC 的调用线中(你马上应该反应过来,嵌入的办法就是包装成invoker)
ProtocolFilterWrapper 作为包装类,会成为其它protocol 的修饰加强外层。因此,protocol 的export 和refer 方法,首先是调用ProtocolFilterWrapper 类的。
暴露服务代码:
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {if (UrlUtils.isRegistry(invoker.getUrl())) {return protocol.export(invoker);}FilterChainBuilder builder = getFilterChainBuilder(invoker.getUrl());return protocol.export(builder.buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
}
引入服务代码:
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {if (UrlUtils.isRegistry(url)) {return protocol.refer(type, url);}FilterChainBuilder builder = getFilterChainBuilder(url);return builder.buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
}
可以看到,两者原来的invoker 对象,都由builder#buildInvokerChain 做了一层包装。
来看一下filterChain 的逻辑
public <T> Invoker<T> buildInvokerChain(final Invoker<T> originalInvoker, String key, String group) {Invoker<T> last = originalInvoker;URL url = originalInvoker.getUrl();//......(省略部分代码)if (!CollectionUtils.isEmpty(filters)) {for (int i = filters.size() - 1; i >= 0; i--) {final Filter filter = filters.get(i);final Invoker<T> next = last;last = new CopyOfFilterChainNode<>(originalInvoker, next, filter);}return new CallbackRegistrationInvoker<>(last, filters);}return last;
}
逻辑较为简单:
1、所有filter 包装进invoker 对象中,invoke 方法直接调对应的filter#invoke。
2、filter 对象首尾相联,前一个filter#invoke 参数,传入后一个filter 的invoker对象
3、最后一个filter.invoke 参数中,直接传原始的invoker 对象
4、filter 的所有获取,按扩展点方式得到
ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
所以这里创建RpcInvocation,执行invoke方法,中间会经过一系列过滤器filter#invoke后,就会来到FailoverClusterInvoker#invoke方法中。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {RpcInvocation rpcInvocation = new RpcInvocation(serviceModel, method, invoker.getInterface().getName(), protocolServiceKey, args);String serviceKey = url.getServiceKey();rpcInvocation.setTargetServiceUniqueName(serviceKey);// invoker.getUrl() returns consumer url.RpcServiceContext.setRpcContext(url);if (serviceModel instanceof ConsumerModel) {rpcInvocation.put(Constants.CONSUMER_MODEL, serviceModel);rpcInvocation.put(Constants.METHOD_MODEL, ((ConsumerModel) serviceModel).getMethodModel(method));}if (ProfilerSwitch.isEnableSimpleProfiler()) {ProfilerEntry parentProfiler = Profiler.getBizProfiler();ProfilerEntry bizProfiler;boolean containsBizProfiler = false;if (parentProfiler != null) {containsBizProfiler = true;bizProfiler = Profiler.enter(parentProfiler, "Receive request. Client invoke begin.");} else {bizProfiler = Profiler.start("Receive request. Client invoke begin.");}rpcInvocation.put(Profiler.PROFILER_KEY, bizProfiler);try {return invoker.invoke(rpcInvocation).recreate();} finally {Profiler.release(bizProfiler);if (!containsBizProfiler) {int timeout;Object timeoutKey = rpcInvocation.getObjectAttachmentWithoutConvert(TIMEOUT_KEY);if (timeoutKey instanceof Integer) {timeout = (Integer) timeoutKey;} else {timeout = url.getMethodPositiveParameter(methodName, TIMEOUT_KEY, DEFAULT_TIMEOUT);}long usage = bizProfiler.getEndTime() - bizProfiler.getStartTime();if ((usage / (1000_000L * ProfilerSwitch.getWarnPercent())) > timeout) {StringBuilder attachment = new StringBuilder();for (Map.Entry<String, Object> entry : rpcInvocation.getObjectAttachments().entrySet()) {attachment.append(entry.getKey()).append("=").append(entry.getValue()).append(";\n");}logger.warn(String.format("[Dubbo-Consumer] execute service %s#%s cost %d.%06d ms, this invocation almost (maybe already) timeout\n" +"invocation context:\n%s" +"thread info: \n%s",protocolServiceKey, methodName, usage / 1000_000, usage % 1000_000,attachment, Profiler.buildDetail(bizProfiler)));}}}}return invoker.invoke(rpcInvocation).recreate();
}
下面代码主要做了三件事情:
1、List<Invoker<T>> invokers = list(invocation);获取当前服务可执行的所有invokers数量。
2、LoadBalance loadbalance = initLoadBalance(invokers, invocation);利用SPI机制获取当前负载均衡策略LoadBalance。
3、doInvoke(invocation, invokers, loadbalance)。负载均衡策略会选择出一个invoker然后执行。
public Result invoke(final Invocation invocation) throws RpcException {checkWhetherDestroyed();InvocationProfilerUtils.enterDetailProfiler(invocation, () -> "Router route.");List<Invoker<T>> invokers = list(invocation);InvocationProfilerUtils.releaseDetailProfiler(invocation);LoadBalance loadbalance = initLoadBalance(invokers, invocation);RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);InvocationProfilerUtils.enterDetailProfiler(invocation, () -> "Cluster " + this.getClass().getName() + " invoke.");try {return doInvoke(invocation, invokers, loadbalance);} finally {InvocationProfilerUtils.releaseDetailProfiler(invocation);}
}
我们知道一个服务的invokers都存在了registryDirectory中
protected List<Invoker<T>> list(Invocation invocation) throws RpcException {return getDirectory().list(invocation);
}public List<Invoker<T>> list(Invocation invocation) throws RpcException {if (destroyed) {throw new RpcException("Directory already destroyed .url: " + getUrl());}BitList<Invoker<T>> availableInvokers;// use clone to avoid being modified at doList().if (invokersInitialized) {availableInvokers = validInvokers.clone();} else {availableInvokers = invokers.clone();}List<Invoker<T>> routedResult = doList(availableInvokers, invocation);return Collections.unmodifiableList(routedResult);
}
然后回利用路由规则调用routerChain.route方法,路由出正确的invokers,具体路由功能后面再讲解。
public List<Invoker<T>> doList(BitList<Invoker<T>> invokers, Invocation invocation) {if (forbidden && shouldFailFast) {// 1. No service provider 2. Service providers are disabledthrow new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " +getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() +", please check status of providers(disabled, not registered or in blacklist).");}if (multiGroup) {return this.getInvokers();}try {// Get invokers from cache, only runtime routers will be executed.List<Invoker<T>> result = routerChain.route(getConsumerUrl(), invokers, invocation);return result == null ? BitList.emptyList() : result;} catch (Throwable t) {logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);return BitList.emptyList();}
}
三、负载均衡
下面这行代码会获取到当前配置的负载均衡策略
LoadBalance loadbalance = initLoadBalance(invokers, invocation)
最终也是利用SPI机制加载,ExtensionLoader#getExtension(DEFAULT_LOADBALANCE) ,而默认的负载均衡策略可以看到是随机。
protected LoadBalance initLoadBalance(List<Invoker<T>> invokers, Invocation invocation) {ApplicationModel applicationModel = ScopeModelUtil.getApplicationModel(invocation.getModuleModel());if (CollectionUtils.isNotEmpty(invokers)) {return applicationModel.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl().getMethodParameter(RpcUtils.getMethodName(invocation), LOADBALANCE_KEY, DEFAULT_LOADBALANCE));} else {return applicationModel.getExtensionLoader(LoadBalance.class).getExtension(DEFAULT_LOADBALANCE);}
}

然后继续执行来到FailoverClusterInvoker#doInvoke方法,首先获取执行次数for循环进行调用,只要不是正常返回,就再试一次,默认的len为3次,这也是dubbo基础常识,在这里可以体现出来。
然后调用下面的select方法获取到一个invoker,最终会执行到loadBalance#doSelect方法,当然不同的负载均衡策略获取invoker的算法也不同,这里应用了模板模式。
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {List<Invoker<T>> copyInvokers = invokers;checkInvokers(copyInvokers, invocation);String methodName = RpcUtils.getMethodName(invocation);int len = calculateInvokeTimes(methodName);// retry loop.RpcException le = null; // last exception.List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.Set<String> providers = new HashSet<String>(len);for (int i = 0; i < len; i++) {//Reselect before retry to avoid a change of candidate `invokers`.//NOTE: if `invokers` changed, then `invoked` also lose accuracy.if (i > 0) {checkWhetherDestroyed();copyInvokers = list(invocation);// check againcheckInvokers(copyInvokers, invocation);}Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);invoked.add(invoker);RpcContext.getServiceContext().setInvokers((List) invoked);boolean success = false;try {Result result = invokeWithContext(invoker, invocation);if (le != null && logger.isWarnEnabled()) {logger.warn("Although retry the method " + methodName+ " in the service " + getInterface().getName()+ " was successful by the provider " + invoker.getUrl().getAddress()+ ", but there have been failed providers " + providers+ " (" + providers.size() + "/" + copyInvokers.size()+ ") from the registry " + directory.getUrl().getAddress()+ " on the consumer " + NetUtils.getLocalHost()+ " using the dubbo version " + Version.getVersion() + ". Last error is: "+ le.getMessage(), le);}success = true;return result;} catch (RpcException e) {if (e.isBiz()) { // biz exception.throw e;}le = e;} catch (Throwable e) {le = new RpcException(e.getMessage(), e);} finally {if (!success) {providers.add(invoker.getUrl().getAddress());}}}throw new RpcException(le.getCode(), "Failed to invoke the method "+ methodName + " in the service " + getInterface().getName()+ ". Tried " + len + " times of the providers " + providers+ " (" + providers.size() + "/" + copyInvokers.size()+ ") from the registry " + directory.getUrl().getAddress()+ " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "+ Version.getVersion() + ". Last error is: "+ le.getMessage(), le.getCause() != null ? le.getCause() : le);
}
RandomLoadBalance#doSelect方法,具体代码逻辑不做详细解释,主要是根据服务设置的权重进行随机选取,权重大的被选中的概率就会大一点,有兴趣的可以断点研究下,这里只是让大家了解负载均衡就是这么简单的东西,我们也可以创建一个自己的负载均衡策略,只需要继承AbstractLoadBalance并实现自己的doSelect方法,然后还需要配置指定用我们所写的负载均衡策略,从代码流程上可以看出来,dubbo的负载均衡策略其实是客户端负载均衡。
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// Number of invokersint length = invokers.size();if (!needWeightLoadBalance(invokers,invocation)){return invokers.get(ThreadLocalRandom.current().nextInt(length));}// Every invoker has the same weight?boolean sameWeight = true;// the maxWeight of every invokers, the minWeight = 0 or the maxWeight of the last invokerint[] weights = new int[length];// The sum of weightsint totalWeight = 0;for (int i = 0; i < length; i++) {int weight = getWeight(invokers.get(i), invocation);// SumtotalWeight += weight;// save for later useweights[i] = totalWeight;if (sameWeight && totalWeight != weight * (i + 1)) {sameWeight = false;}}if (totalWeight > 0 && !sameWeight) {// If (not every invoker has the same weight & at least one invoker's weight>0), 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++) {if (offset < weights[i]) {return invokers.get(i);}}}// If all invokers have the same weight value or totalWeight=0, return evenly.return invokers.get(ThreadLocalRandom.current().nextInt(length));
}
那么dubbo中已经支持的负载均衡策略也有很多,依次是ConsistentHashLoadBalance(一次性哈希),LeastActiveLoadBalance(最少活跃数),RandomLoadBalance(按照权重随机),RoundRobinLoadBalance(轮训),ShortestResponseLoadBalance(最短响应策略);不过生产环境中权重策略跟轮训策略是使用的最多的两种负载均衡方式。

四、集群容错
集群容错的实现方式其实在生成invoker的时候已经看到了,dubbo会将生成的invoker最外层在包装一层ClusterInvoker,这里又利用到了一种设计模式叫做装饰者模式。因为远程服务调用过程总肯定会先执行到ClusterInvoker#doInvoke方法,那么每个容错ClusterInvoker实现类会按照自己的方式去执行,当然我们也能继承AbstractClusterInvoker方法,然后实现doInvoke方法,然后配置自己的集群容错策略,所以这个功能跟负载均衡一样简单。

Dubbo为应对不同的业务场景和需求,提供了以下几种集群容错策略,具体源码有兴趣的可以单独研究:
1. FailoverClusterInvoker(默认)
失败一次自动切换,自动重试其他invoker,默认三次。适用于读操作多、对实时性要求不高的场景。
2. Failfast
快速失败,立即报错,只发起一次调用。适用于非幂等性操作或实时性要求极高的场景。
3. Failsafe
失败安全,出现异常时直接忽略。适用于对结果不敏感的操作,如日志记录。
4. Failback
失败自动恢复,记录失败请求,定时重发。适用于可以异步处理且对实时性要求不高的操作。
5. Forking
并行调用多个服务器,只要一个成功即返回。注意提高响应速度的同时,需注意资源消耗和并行度控制。
6. Broadcast
广播调用所有服务提供者,任一报错即报错。适用于需要全局一致性的场景,如全局缓存更新。
选择合适的集群容错策略,对于优化Dubbo应用的性能和可靠性至关重要。
到此Dubbo的服务调用过程讲解完毕。
相关文章:
Dubbo源码解析-服务调用(七)
一、服务调用流程 服务在订阅过程中,把notify 过来的urls 都转成了invoker,不知道大家是否还记得前面的rpc 过程,protocol也是在服务端和消费端各连接子一个invoker,如下图: 这张图主要展示rpc 主流程,消费…...
svn 崩溃、 cleanup失败 怎么办
在使用svn的过程中,可能出现整个svn崩溃, 例如cleanup 失败的情况,类似于 这时可以下载本贴资源文件并解压。 或者直接访问网站 SQLite Download Page 进行下载 解压后得到 sqlite3.exe 放到发生问题的svn根目录的.svn路径下 右键呼出pow…...
【Linux系列】NTP时间同步服务器搭建完整指南
在分布式系统和高可用环境中,时间同步是至关重要的。特别是对于银行、金融等关键业务系统,精准的时间同步不仅关系到系统的稳定性,还直接影响交易处理、日志管理、日终结算等功能。本文将介绍NTP(Network Time Protocol࿰…...
go 结构体方法
在 Go 语言中,结构体方法是指附加到结构体类型上的函数。这些方法可以通过结构体的实例来调用。方法的接收者(receiver)指定了该方法属于哪个结构体类型。接收者可以是一个值类型或指针类型。 定义结构体方法 下面是如何为一个结构体定义方…...
DHCP服务(包含配置过程)
目录 一、 DHCP的定义 二、 使用DHCP的好处 三、 DHCP的分配方式 四、 DHCP的租约过程 1. 客户机请求IP 2. 服务器响应 3. 客户机选择IP 4. 服务器确定租约 5. 重新登录 6. 更新租约 五、 DHCP服务配置过程 一、 DHCP的定义 DHCP(Dynamic Host Configur…...
uniapp内嵌的webview H5与应用通信
H5端: 1、找到index.html引入依赖 <script type"text/javascript" src"https://unpkg.com/dcloudio/uni-webview-js0.0.3/index.js"></script> 2、在需要通讯处发送消息 uni.postMessage({data:{code:200,msg:"处理完成&q…...
Android OpenGL ES详解——绘制圆角矩形
1、绘制矩形 代码如下: renderer类: package com.example.roundrectimport android.content.Context import android.opengl.GLES30 import android.opengl.GLSurfaceView.Renderer import com.opengllib.data.VertexArray import com.opengllib.prog…...
网络基础二
文章目录 协议定制,序列化和反序列化应用层网络版计算器协议的定制序列反序列化序列化未复用版 反序列化 TCP是面向字节流的,你怎么保证,你读取上来的数据,是‘’一个“ “完整””的报文呢? 我们没有区分字符串里面有…...
从Full-Text Search全文检索到RAG检索增强
从Full-Text Search全文检索到RAG检索增强 时光飞逝,转眼间六年过去了,六年前铁蛋优化单表千万级数据查询性能的场景依然历历在目,铁蛋也从最开始做CRUD转行去了大数据平台开发,混迹包装开源的业务,机缘巧合下做了实时…...
springMVC 全局异常统一处理
全局异常处理⽅式⼀: 1、配置简单异常处理器 配置 SimpleMappingExceptionResolver 对象: <!-- 配置全局异常统⼀处理的 Bean (简单异常处理器) --> <bean class"org.springframework.web.servlet.handler.SimpleMappingExceptionReso…...
qt ubuntu i386 系统
sudo ln -s cmake-3.31.0-linux-x86_64/bin/* /usr/local/bin 【Ubuntu20.4安装QT6 - CSDN App】Ubuntu20.4安装QT6_ubuntu安装qt6-CSDN博客 sudo ../configure -release -platform linux-g-64 -static -nomake examples -nomake demos -no-qt3support -no-script -no-scriptt…...
BUUCTF—Reverse—helloword(6)
一道安卓逆向的签到题 下载附件 使用JADX-gui反编译工具打开(注意配环境),找到主函数 jadx 本身就是一个开源项目,源代码已经在 Github 上开源了 官方地址:GitHub - skylot/jadx: Dex to Java decompiler 发现flag …...
深入解析下oracle date底层存储方式
之前我们介绍了varchar2和char的数据库底层存储格式,今天我们介绍下date类型的数据存储格式,并通过测试程序快速获取一个日期。 一、环境搭建 1.1,创建表 我们还是创建一个测试表t_code,并插入数据: 1.2,…...
Elasticsearch 开放推理 API 增加了对 IBM watsonx.ai Slate 嵌入模型的支持
作者:来自 Elastic Saikat Sarkar 使用 Elasticsearch 向量数据库构建搜索 AI 体验时如何使用 IBM watsonx™ Slate 文本嵌入。 Elastic 很高兴地宣布,通过集成 IBM watsonx™ Slate 嵌入模型,我们的开放推理 API 功能得以扩展,这…...
如何搭建一个小程序:从零开始的详细指南
在当今数字化时代,小程序以其轻便、无需下载安装即可使用的特点,成为了连接用户与服务的重要桥梁。无论是零售、餐饮、教育还是娱乐行业,小程序都展现了巨大的潜力。如果你正考虑搭建一个小程序,本文将为你提供一个从零开始的详细…...
NFS搭建
NFS搭建 单节点安装配置服务器安装配置启动并使NFS服务开机自启客户端挂载查看是否能发现服务器的共享文件夹创建挂载目录临时挂载自动挂载 双节点安装配置服务器安装配置服务端配置NFS服务端配置Keepalived编辑nfs_check.sh监控脚本安装部署RsyncInofity 客户端 单节点安装配置…...
RNN与LSTM,通过Tensorflow在手写体识别上实战
简介:本文从RNN与LSTM的原理讲起,在手写体识别上进行代码实战。同时列举了优化思路与优化结果,都是基于Tensorflow1.14.0的环境下,希望能给您的神经网络学习带来一定的帮助。如果您觉得我讲的还行,希望可以得到您的点赞…...
Docker部署FastAPI实战
在现代 Web 开发领域,FastAPI 作为一款高性能的 Python 框架,正逐渐崭露头角,它凭借简洁的语法、快速的执行速度以及出色的类型提示功能,深受开发者的喜爱。而 Docker 容器化技术则为 FastAPI 应用的部署提供了便捷、高效且可移植…...
【Python数据分析五十个小案例】电影评分分析:使用Pandas分析电影评分数据,探索评分的分布、热门电影、用户偏好
博客主页:小馒头学python 本文专栏: Python数据分析五十个小案例 专栏简介:分享五十个Python数据分析小案例 在现代电影行业中,数据分析已经成为提升用户体验和电影推荐的关键工具。通过分析电影评分数据,我们可以揭示出用户的…...
Vue2学习记录
前言 这篇笔记,是根据B站尚硅谷的Vue2网课学习整理的,用来学习的 如果有错误,还请大佬指正 Vue核心 Vue简介 Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。 它基于标准 HTML、CSS 和 JavaScr…...
冲突矿产法规合规:供应链尽责管理与ESG风险应对实战指南
1. 冲突矿产法规合规:一场被低估的供应链风暴如果你是一家电子、汽车或工业设备制造公司的供应链、法务或合规负责人,现在请立刻停下手中的工作,问自己一个问题:我们公司使用的锡、钽、钨、金(3TG)这四种金…...
如何解决Funannotate数据库安装失败:从403错误到完整部署的实战指南
如何解决Funannotate数据库安装失败:从403错误到完整部署的实战指南 【免费下载链接】funannotate Eukaryotic Genome Annotation Pipeline 项目地址: https://gitcode.com/gh_mirrors/fu/funannotate Funannotate是真核基因组注释的强大工具,但在…...
喜马拉雅音频本地化实战:绕过xm格式,直接获取mp3文件的两种方法对比
喜马拉雅音频本地化实战:两种高效获取MP3文件的技术方案深度评测 作为国内领先的音频分享平台,喜马拉雅拥有海量优质内容,但其特有的XM格式却给用户跨平台使用带来了困扰。许多技术爱好者尝试过各种转换工具,却发现市面上几乎没有…...
技术奇点之后,人类程序员的历史角色
当人工智能越过技术奇点,代码生成、测试用例设计乃至系统运维都将发生质变。本文从软件测试从业者的视角出发,系统探讨人类程序员在奇点之后可能扮演的六种核心角色:系统守护者、需求翻译官、质量伦理法官、人机交互设计师、持续学习组织者与…...
LeetCode 岛屿数量题解
LeetCode 岛屿数量题解 题目描述 给定一个二维网格地图 1(陆地)和 0(水),计算岛屿的数量。 示例: 输入:grid [ ["1","1","1","1","0"], …...
Sora 2与3D Gaussian结合实战指南(工业级部署避坑手册)
更多请点击: https://intelliparadigm.com 第一章:Sora 2与3D Gaussian结合的工业级部署全景图 Sora 2作为OpenAI新一代视频生成模型,在长时序建模与物理一致性方面取得显著突破;而3D Gaussian Splatting(3DGS&#x…...
2026年Hermes Agent/OpenClaw怎么部署?阿里云自动化部署及Token Plan配置
2026年Hermes Agent/OpenClaw怎么部署?阿里云自动化部署及Token Plan配置。OpenClaw是开源的个人AI助手,Hermes Agent则是一个能自我进化的AI智能体框架。阿里云提供计算巢、轻量服务器及无影云电脑三种部署OpenClaw 与 Hermes Agent的方案、百炼Token P…...
Apollo Save Tool:3步解决PlayStation存档管理难题的终极方案
Apollo Save Tool:3步解决PlayStation存档管理难题的终极方案 【免费下载链接】apollo-ps4 Apollo Save Tool (PS4) 项目地址: https://gitcode.com/gh_mirrors/ap/apollo-ps4 你是否曾为丢失珍贵的游戏进度而懊恼?是否在主机升级时面临数百个存档…...
工程师幽默竞赛:从技术梗到团队文化的创意表达
1. 项目概述:一场工程师的幽默竞赛如果你在电子工程行业待过一段时间,大概率在《EE Times》这样的行业媒体上,见过那种线条简洁、寓意深刻的单格漫画。漫画本身往往描绘一个充满电子元件、示波器或一脸困惑的工程师的实验室场景,但…...
从Arrays.fill()到Stream API:Java二维数组初始化的几种高效写法与性能对比
从Arrays.fill()到Stream API:Java二维数组初始化的几种高效写法与性能对比 在算法竞赛和数据处理应用中,二维数组的初始化往往是性能优化的第一个瓶颈。我曾在一个图像处理项目中,因为选择了不当的初始化方式,导致整体性能下降了…...
