dubbo 服务消费原理分析之引用服务配置

news2024/9/21 0:33:42

文章目录

  • 前言
  • 一、服务监听ContextRefreshedEvent
    • 1、AbstractApplicationContext.refresh
    • 2、AbstractApplicationContext.finishRefresh
    • 3、DubboDeployApplicationListener.onApplicationEvent
    • 4、DefaultModuleDeployer .referServices
    • 5、SimpleReferenceCache.get
  • 二、引用服务 ReferenceConfig
    • 1、时序图
    • 2、ReferenceConfig.get
    • 3、ReferenceConfig.init
    • 4、ReferenceConfig.createProxy
    • 5、ReferenceConfig.createInvokerForRemote
  • 三、注册协议 RegistryProtocol
    • 1、RegistryProtocol.refer
    • 2、RegistryProtocol.doRefer
    • 3、RegistryProtocol.interceptInvoker


前言

文章基于3.1.0版本进行分析

		<dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>3.1.0</version>
        </dependency>

一、服务监听ContextRefreshedEvent

在springboot 中,refresh 是 AbstractApplicationContext 中的一个方法,负责初始化 ApplicationContext 容器,容器必须调用 refresh 才能正常工作是进行,refresh的最后处理了 finishRefresh 方法,改方法会广播一个ContextRefreshedEvent容器刷新完成事件,所有监听了该事件的bean都会去执行相关逻辑处理。

1、AbstractApplicationContext.refresh

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            	// 省略无关代码***
                
                // 初始化生命周期处理器,调用生命周期处理器onRefresh方法,发布ContextRefreshedEvent事件,JMX相关处理
                this.finishRefresh();
            
            	// 省略无关代码***
        }
    }

2、AbstractApplicationContext.finishRefresh

	protected void finishRefresh() {
		// 清除上下文资源缓存(如扫描中的ASM元数据) scanning).
		clearResourceCaches();

		// Initialize lifecycle processor for this context.
		initLifecycleProcessor();

		// Propagate refresh to lifecycle processor first.
		getLifecycleProcessor().onRefresh();

		// 广播刷新完成事件
		publishEvent(new ContextRefreshedEvent(this));

		// Participate in LiveBeansView MBean, if active.
		if (!NativeDetector.inNativeImage()) {
			LiveBeansView.registerApplicationContext(this);
		}
	}

3、DubboDeployApplicationListener.onApplicationEvent

dubbo很好的结合了spring的这一个拓展点,在这个拓展点开始实现服务的发布。可以看到,DubboDeployApplicationListener实现了ContextRefreshedEvent的消息监听

public class DubboDeployApplicationListener implements ApplicationListener<ApplicationContextEvent>, ApplicationContextAware, Ordered {
    private static final Logger logger = LoggerFactory.getLogger(DubboDeployApplicationListener.class);

    private ApplicationContext applicationContext;
    private ApplicationModel applicationModel;
    private ModuleModel moduleModel;

    @Override
    public void onApplicationEvent(ApplicationContextEvent event) {
        if (nullSafeEquals(applicationContext, event.getSource())) {
            if (event instanceof ContextRefreshedEvent) {
                onContextRefreshedEvent((ContextRefreshedEvent) event);
            } else if (event instanceof ContextClosedEvent) {
                onContextClosedEvent((ContextClosedEvent) event);
            }
        }
    }

    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
    	// 获取配置的deployer 进行发布,默认是 DefaultModuleDeployer 
        ModuleDeployer deployer = moduleModel.getDeployer();
        Assert.notNull(deployer, "Module deployer is null");
        // start module
        Future future = deployer.start();

        // if the module does not start in background, await finish
        if (!deployer.isBackground()) {
            try {
            	// 等待发布结束
                future.get();
            } catch (InterruptedException e) {
                logger.warn("Interrupted while waiting for dubbo module start: " + e.getMessage());
            } catch (Exception e) {
                logger.warn("An error occurred while waiting for dubbo module start: " + e.getMessage(), e);
            }
        }
    }

}

4、DefaultModuleDeployer .referServices

DefaultModuleDeployer 中,真正核心的是ReferenceConfig,ReferenceConfig才是去实际发布的动作

public class DefaultModuleDeployer extends AbstractDeployer<ModuleModel> implements ModuleDeployer {
 
    // DefaultApplicationDeployer
    private ApplicationDeployer applicationDeployer;
 
    @Override
    public synchronized Future start() throws IllegalStateException {
 
        ...
        // 不管是 DefaultApplicationDeployer 还是DefaultModuleDeployer的initialize方法,都是处理相关配置文件
        // 其功能等价于监听器 DubboConfigApplicationListener
        applicationDeployer.initialize();
        initialize();
        // 真正触发 服务注册功能
        exportServices();
		// prepare application instance
        // exclude internal module to avoid wait itself
        if (moduleModel != moduleModel.getApplicationModel().getInternalModule()) {
            applicationDeployer.prepareInternalModule();
        }

        // 服务消费
		// refer services
        referServices();
        ...
        return startFuture;
    }
 
    private void referServices() {
        configManager.getReferences().forEach(rc -> {
            try {
                ReferenceConfig<?> referenceConfig = (ReferenceConfig<?>) rc;
                // 刷新配置
				if (!referenceConfig.isRefreshed()) {
                    referenceConfig.refresh();
                }
	
                // 如果还没初始化
				if (rc.shouldInit()) {
                    // 是否异步注入
					if (referAsync || rc.shouldReferAsync()) {
                        ExecutorService executor = executorRepository.getServiceReferExecutor();
                        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                            try {
                                referenceCache.get(rc);
                            } catch (Throwable t) {
                                logger.error(getIdentifier() + " refer async catch error : " + t.getMessage(), t);
                            }
                        }, executor);

                        asyncReferringFutures.add(future);
                    } else {
						// 查询缓存中是否存在代理对象 对应的实现类SimpleReferenceCache
                        referenceCache.get(rc);
                    }
                }
            } catch (Throwable t) {
                logger.error(getIdentifier() + " refer catch error.");
                referenceCache.destroy(rc);
                throw t;
            }
        });
    }
}

5、SimpleReferenceCache.get

SimpleReferenceCache,一个用于缓存引用ReferenceConfigBase的util工具类。
ReferenceConfigBase是一个重对象,对于频繁创建ReferenceConfigBase的框架来说,有必要缓存这些对象。
如果需要使用复杂的策略,可以实现并使用自己的ReferenceConfigBase缓存
这个Cache是引用服务的开始如果我们想在代码中自定义一些服务引用的逻辑,可以直接创建SimpleReferenceCache类型对象然后调用其get方法进行引用服务。

	public <T> T get(ReferenceConfigBase<T> rc) {
        String key = generator.generateKey(rc);
		// 服务类型 如果是泛化调用则这个类型为GenericService
        Class<?> type = rc.getInterfaceClass();

        boolean singleton = rc.getSingleton() == null || rc.getSingleton();
        T proxy = null;
        // Check existing proxy of the same 'key' and 'type' first.
		// 单例
        if (singleton) {
			// 缓存数据找
            proxy = get(key, (Class<T>) type);
        } else {
            logger.warn("Using non-singleton ReferenceConfig and ReferenceCache at the same time may cause memory leak. " +
                "Call ReferenceConfig#get() directly for non-singleton ReferenceConfig instead of using ReferenceCache#get(ReferenceConfig)");
        }
		// 不存在消费的代理对象,创建rc.get(),最后走到ReferenceConfig.get()
        if (proxy == null) {
			// 获取或者创建值,为引用类型referencesOfType对象(类型为Map<Class<?>, List<ReferenceConfigBase<?>>>)缓存对象生成值(值不存咋时候会生成一个)
            List<ReferenceConfigBase<?>> referencesOfType = referenceTypeMap.computeIfAbsent(type, _t -> Collections.synchronizedList(new ArrayList<>()));
            // 每次走到这里都会添加一个ReferenceConfigBase 引用配置对象(单例的从缓存中拿到就可以直接返回了)
			referencesOfType.add(rc);
			// 与前面一样 前面是类型映射,这里是key映射
            List<ReferenceConfigBase<?>> referenceConfigList = referenceKeyMap.computeIfAbsent(key, _k -> Collections.synchronizedList(new ArrayList<>()));
            referenceConfigList.add(rc);
			// 开始引用服务
            proxy = rc.get();
        }

        return proxy;
    }

二、引用服务 ReferenceConfig

服务发现和引用就是从这里开始的

1、时序图

2、ReferenceConfig.get

获取引用的代理对象

	public T get() {
        if (destroyed) {
            throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
        }

        if (ref == null) {
            // ensure start module, compatible with old api usage
			// 如果使用方直接调用了ReferenceConfigBase的get方法或者缓存对象SimpleReferenceCache类型的对象的get方法来引用服务端的时候就会造成很多配置没有初始化
			// 这个代码其实就是启动模块进行一些基础配置的初始化操作,比如元数据中心默认配置选择,注册中心默认配置选择这些都是比较重要的
            getScopeModel().getDeployer().start();

            synchronized (this) {
                if (ref == null) {
                    init();
                }
            }
        }

        return ref;
    }

主要包括

  • checkAndUpdateSubConfigs() – 检查并更新缺省配置
  • init() – 消费者服务的初始化,核心逻辑。

3、ReferenceConfig.init

初始化代理对象

    protected synchronized void init() {
        // 初始化标记变量保证只初始化一次,这里又是加锁又是加标记变量的
		if (initialized && ref != null) {
            return;
        }
        try {
            // 刷新配置
			if (!this.isRefreshed()) {
                this.refresh();
            }
            
            // init serviceMetadata
            // 初始化元数据信息 如版本号,分组,服务接口名
            initServiceMetadata(consumer);
            // //继续初始化元数据信息 服务接口类型和key
            serviceMetadata.setServiceType(getServiceInterfaceClass());
            // TODO, uncomment this line once service key is unified
            serviceMetadata.setServiceKey(URL.buildKey(interfaceName, group, version));

            // 参数配置转成MAP
			Map<String, String> referenceParameters = appendConfig();
            // init service-application mapping
            // 初始化路径,参数转化成url,dubbo主要从url上面读取参数
			initServiceAppsMapping(referenceParameters);

            // 获取Module级别的服务存储仓库,其内部保存着服务提供者和服务消费者的缓存
            ModuleServiceRepository repository = getScopeModel().getServiceRepository();
            ServiceDescriptor serviceDescriptor;
            if (CommonConstants.NATIVE_STUB.equals(getProxy())) {
                serviceDescriptor = StubSuppliers.getServiceDescriptor(interfaceName);
                repository.registerService(serviceDescriptor);
            } else {
                serviceDescriptor = repository.registerService(interfaceClass);
            }
			// 创建消费者模型对象
            consumerModel = new ConsumerModel(serviceMetadata.getServiceKey(), proxy, serviceDescriptor,
                    getScopeModel(), serviceMetadata, createAsyncMethodInfo(), interfaceClassLoader);

            // Compatible with dependencies on ServiceModel#getReferenceConfig() , and will be removed in a future version.
            consumerModel.setConfig(this);

            repository.registerConsumer(consumerModel);

            serviceMetadata.getAttachments().putAll(referenceParameters);

            // 创建引用的代理对象 核心代码
			ref = createProxy(referenceParameters);

            serviceMetadata.setTarget(ref);
            serviceMetadata.addAttribute(PROXY_CLASS_REF, ref);

            consumerModel.setDestroyCaller(getDestroyRunner());
            consumerModel.setProxyObject(ref);
            consumerModel.initMethodModels();

            checkInvokerAvailable();
        } catch (Throwable t) {
            // *** 省略部分代码
            throw t;
        }
        initialized = true;
    }

主要流程

  • 服务引用前初始化serviceMetadata服务元数据
  • 获取服务仓库ModuleServiceRepository,并注册service 和 consumer
  • 调用createProxy方法,根据服务引用参数map创建服务接口代理引用对象,并赋值给ref,核心逻辑

4、ReferenceConfig.createProxy

创建代理对象,实际调用需要通过这个代理对象进行调用

	private T createProxy(Map<String, String> referenceParameters) {
        // 本地引用
		if (shouldJvmRefer(referenceParameters)) {
            createInvokerForLocal(referenceParameters);
        } else {
            urls.clear();

            meshModeHandleUrl(referenceParameters);

            if (StringUtils.isNotEmpty(url)) {
                // user specified URL, could be peer-to-peer address, or register center's address.
				// url参数不为空,且可以配置多个。可以是点对点,也可以是注册中心,然后添加到urls
                parseUrl(referenceParameters);
            } else {
                // if protocols not in jvm checkRegistry
                if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) {
                    // 从注册表中获取URL并将其聚合。这个其实就是初始化一下注册中心的url配置
					aggregateUrlFromRegistry(referenceParameters);
                }
            }
			// 创建远程引用,创建远程引用调用器,承担服务调用的核心逻辑
            createInvokerForRemote();
        }

        if (logger.isInfoEnabled()) {
            logger.info("Referred dubbo service: [" + referenceParameters.get(INTERFACE_KEY) + "]." +
                    (Boolean.parseBoolean(referenceParameters.get(GENERIC_KEY)) ?
                            " it's GenericService reference" : " it's not GenericService reference"));
        }

        URL consumerUrl = new ServiceConfigURL(CONSUMER_PROTOCOL, referenceParameters.get(REGISTER_IP_KEY), 0,
                referenceParameters.get(INTERFACE_KEY), referenceParameters);
        consumerUrl = consumerUrl.setScopeModel(getScopeModel());
        consumerUrl = consumerUrl.setServiceModel(consumerModel);
        MetadataUtils.publishServiceDefinition(consumerUrl, consumerModel.getServiceModel(), getApplicationModel());

        // create service proxy
		// 构建一个代理对象,代理客户端的请求
        return (T) proxyFactory.getProxy(invoker, ProtocolUtils.isGeneric(generic));
    }

5、ReferenceConfig.createInvokerForRemote

远程引用或者直连引用情况下,将会调用该方法,创建远程引用Invoker。对于一个注册中心url和多个注册中心url的处理是不一样的,一个注册中心对应一个invoker,最后封装到集群路由invoker

	private void createInvokerForRemote() {
        // 只有一个,秩序创建一个invoker
		if (urls.size() == 1) {
            URL curUrl = urls.get(0);
			// 自适应扩展类,这里和服务发布类似,先进入了RegistryProtocol.refer
            invoker = protocolSPI.refer(interfaceClass, curUrl);
            if (!UrlUtils.isRegistry(curUrl)) {
                List<Invoker<?>> invokers = new ArrayList<>();
                invokers.add(invoker);
                invoker = Cluster.getCluster(scopeModel, Cluster.DEFAULT).join(new StaticDirectory(curUrl, invokers), true);
            }
        } else {
            List<Invoker<?>> invokers = new ArrayList<>();
            URL registryUrl = null;
			// 创建多个invoker
            for (URL url : urls) {
                // For multi-registry scenarios, it is not checked whether each referInvoker is available.
                // Because this invoker may become available later.
				// 
                invokers.add(protocolSPI.refer(interfaceClass, url));

                if (UrlUtils.isRegistry(url)) {
                    // use last registry url
                    registryUrl = url;
                }
            }

            // 创建一个集群路由invoker
			if (registryUrl != null) {
                // registry url is available
                // for multi-subscription scenario, use 'zone-aware' policy by default
                String cluster = registryUrl.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME);
                // The invoker wrap sequence would be: ZoneAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker
                // (RegistryDirectory, routing happens here) -> Invoker
                invoker = Cluster.getCluster(registryUrl.getScopeModel(), cluster, false).join(new StaticDirectory(registryUrl, invokers), false);
            } else {
                // not a registry url, must be direct invoke.
				// 直连
                if (CollectionUtils.isEmpty(invokers)) {
                    throw new IllegalArgumentException("invokers == null");
                }
                URL curUrl = invokers.get(0).getUrl();
                String cluster = curUrl.getParameter(CLUSTER_KEY, Cluster.DEFAULT);
                invoker = Cluster.getCluster(scopeModel, cluster).join(new StaticDirectory(curUrl, invokers), true);
            }
        }
    }

服务发布的时候讲到 protocolSPI,protocolSPI.refer形成的调用链为
-Protocol$Adaptie -> ProtocolSerializationWrapper -> ProtocolFilterWrapper -> QosProtocolWrapper -> ProtocolListenerWrapper -> RegistryProtocol -> RegistryProtocol

三、注册协议 RegistryProtocol

应用级服务远程协议以service-discovery-registry开头,其对应的Protocol实现就是RegistryProtocol

1、RegistryProtocol.refer

    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
		// 组装配置中心的地址
        url = getRegistryUrl(url);
		// 获取用于操作Zookeeper的Registry类型 
        Registry registry = getRegistry(url);
        if (RegistryService.class.equals(type)) {
            return proxyFactory.getInvoker((T) registry, type, url);
        }

        // group="a,b" or group="*"
        Map<String, String> qs = (Map<String, String>) url.getAttribute(REFER_KEY);
        String group = qs.get(GROUP_KEY);
        if (StringUtils.isNotEmpty(group)) {
            if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
                return doRefer(Cluster.getCluster(url.getScopeModel(), MergeableCluster.NAME), registry, type, url, qs);
            }
        }
		// 降级容错的逻辑处理对象 类型为Cluster 实际类型为MockClusterWrapper 内部包装的是FailoverCluster
        // 后续调用服务失败时候会先失效转移再降级
        Cluster cluster = Cluster.getCluster(url.getScopeModel(), qs.get(CLUSTER_KEY));
		// 主要干活的地方 生成引用 invoker
        return doRefer(cluster, registry, type, url, qs);
    }

生成配置中心的url

zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-springboot-start-consumer
&dubbo=2.0.2
&pid=8896
&qos.enable=false
&release=3.1.0
&timestamp=1725379058285

2、RegistryProtocol.doRefer

    protected <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url, Map<String, String> parameters) {
        Map<String, Object> consumerAttribute = new HashMap<>(url.getAttributes());
        consumerAttribute.remove(REFER_KEY);
        String p = isEmpty(parameters.get(PROTOCOL_KEY)) ? CONSUMER : parameters.get(PROTOCOL_KEY);
		// 消费者url信息
        URL consumerUrl = new ServiceConfigURL (
            p,
            null,
            null,
            parameters.get(REGISTER_IP_KEY),
            0, getPath(parameters, type),
            parameters,
            consumerAttribute
        );
        url = url.putAttribute(CONSUMER_URL_KEY, consumerUrl);
		// 带迁移性质的Invoker对象
        ClusterInvoker<T> migrationInvoker = getMigrationInvoker(this, cluster, registry, type, url, consumerUrl);
		// 执行迁移规则创建应用级优先的服务发现Invoker对象
        return interceptInvoker(migrationInvoker, url, consumerUrl);
    }

consumerUrl如下

consumer://192.168.0.101/org.sjl.dubbo.AsyncProvider?application=dubbo-springboot-start-consumer
&background=false
&dubbo=2.0.2
&interface=org.sjl.dubbo.AsyncProvider
&methods=sayHiAsync,sayHello,sayHelloAsync&pid=33100
&qos.enable=false
&register.ip=192.168.0.101
&release=3.1.0
&side=consumer
&sticky=false
&timeout=30000
&timestamp=1725379319215

3、RegistryProtocol.interceptInvoker

    protected <T> Invoker<T> interceptInvoker(ClusterInvoker<T> invoker, URL url, URL consumerUrl) {
        // 获取激活的注册协议监听器扩展里面registry.protocol.listener,这里激活的类型为MigrationRuleListener
		List<RegistryProtocolListener> listeners = findRegistryProtocolListeners(url);
        if (CollectionUtils.isEmpty(listeners)) {
            return invoker;
        }

        for (RegistryProtocolListener listener : listeners) {
			// MigrationRuleListener
			// 迁移规则应用级引用
            listener.onRefer(this, invoker, consumerUrl, url);
        }
        return invoker;
    }

接下来就进入了迁移规则的应用级服务发现了,参考 dubbo 服务消费原理分析之应用级服务发现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2111816.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SRT库介绍

文章目录 简介SRT协议介绍FFmpegSRS推拉流测试SRT库介绍apps示例程序srt-file-transmitsrt-live-transmitsrt-test-multiplexsrt-test-relaysrt-tunnel docs/buildsrtcoreexamples编译 安装错误处理 API说明初始化、回收创建配置套接字连接管理Socket Group属性设置传输数据统计…

CNC数控加工如何开启个性化制造新时代?

在现代制造业中&#xff0c;CNC 数控加工定做正以其独特的特点和显著的优势&#xff0c;成为满足各种复杂、高精度加工需求的首选方式。与时利和一起了解CNC 数控加工定做是如何开启个性化制造新时代! 一、CNC 数控加工定做的特点 1.高精度加工 CNC 数控加工依靠先进的计算机控…

Java并发编程实战 04 | 使用WaitNotify时要注意什么?

在 Java 中&#xff0c;wait()、notify() 和 notifyAll() 方法在多线程编程中主要用于线程间的协作和同步。理解这些方法的使用特点对于编写稳定的多线程程序至关重要。我们将从以下三个问题入手深入探讨它们的使用&#xff1a; 为什么必须在 synchronized 代码块中使用 wait(…

字体反爬(一)

网址 http://xxfb.mwr.cn/sq_djdh.html?v1.0 获取相关数据 解决 F12 先找接口吧&#xff0c; 搜索一下表格的数据 直接从表格中复制 复制过来乱码&#xff0c;基本锁定有字体反爬处理 先点进去看看 {"addvnm": "#GkcERlldm4_1725629424756otltag㯼㢴#Fon…

Linux 技巧汇编

10个重要的Linux ps命令实战 显示所有当前进程 根据用户过滤进程 通过cpu和内存使用来过滤进程 通过进程名和PID过滤 根据线程来过滤进程 树形显示进程 显示安全信息 格式化输出root用户&#xff08;真实的或有效的UID&#xff09;创建的进程 使用PS实时监控进程状态 …

泛型列表相关知识

集合 C#中集合是指在system.Collection下的类型&#xff0c;他们大多数是通过实现此命名空间下的接口来实现的。 C#集合是来维护一组对象的数据结构&#xff0c;与数组不同&#xff0c;集合包含更多的功能。如&#xff1a;自动添加元素到指定位置&#xff0c;排序等。 泛型集…

企业级WEB应用服务器---TOMACT

一、WEB技术介绍 1.1 Http和B/S结构 操作系统一般都有子进程系统&#xff0c;使用多进程就可以充分利用硬件资源&#xff0c;提高效率。在前面的学习中我们了解到进程中可以有多个线程&#xff0c;且每一个线程都可以被CPU调度执行&#xff0c;这样就可以让程序并行执行。一台…

深入浅出孪生神经网络,高效训练模型

大家好&#xff0c;在深度学习领域&#xff0c;神经网络几乎能处理各种任务&#xff0c;但通常需要依赖于海量数据来达到最佳效果。然而&#xff0c;对于像面部识别和签名验证这类任务&#xff0c;我们不可能总是有大量的数据可用。由此产生了一种新型的神经网络架构&#xff0…

【自考zt】【数据结构】【22.04】

一、单选 二、填空 三、解答 四、算法阅读 五、算法设计

【Flutter】解决第一次运行项目很慢(gradle需要下载依赖)

配置gradle默认下载路径 默认下C盘谁顶得住 配置环境变量 名称: GRADLE_USER_HOME 值: D:\Develop\gradle 自己创建一个 下边是重点 配置gradle远端下载地址 后边版本号自己换 https://mirrors.cloud.tencent.com/gradle/ https://mirrors.cloud.tencent.com/gradle/gradl…

Matlab 一维层状声子晶体振动传输特性

一维声子晶体的传递矩阵法是一种用于研究声波在一维周期性结构中传播的方法。这种方法基于‌波动方程和周期性边界条件&#xff0c;通过计算声波在不同介质中的传播特性&#xff0c;进而分析声子晶体的带隙结构。传递矩阵法可以有效地预测声波在一维声子晶体中的传播行为&#…

利用AI大语言模型和Langchain开发智能车算法训练知识库(上篇)

今天小李哥将介绍亚马逊云科技的Jupyter Notebook机器学习托管服务Amazon SageMaker上&#xff0c;通过AI大语言模型、向量知识库和LangChain Agent&#xff0c;创建用于AI 智能车模型训练的RAG问答知识库。整个项目的架构图如下&#xff1a; 本系列共分为上下两篇。在上篇内容…

Java中的强引用、软引用、弱引用和虚引用于JVM的垃圾回收机制

参考资料 https://juejin.cn/post/7123853933801373733 在 Java 中&#xff0c;引用类型分为四种&#xff1a;强引用&#xff08;Strong Reference&#xff09;、软引用&#xff08;Soft Reference&#xff09;、弱引用&#xff08;Weak Reference&#xff09;和虚引用&#xf…

5G移动网络运维实验(训)室解决方案

随着第五代移动通信技术&#xff08;5G&#xff09;的快速普及和工业互联网的迅猛发展&#xff0c;全球制造业正面临着前所未有的深刻变革。5G技术凭借其超高的传输速率、极低的延迟以及大规模的连接能力&#xff0c;为工业自动化、智能制造等领域带来了革命性的技术支持。为了…

【免费分享】GIS开发面试题(流程+自我介绍+基础篇+Openlayermapbox)

本篇文章针对GIS应届生就业方向及面试困惑问题进行了收集整理&#xff0c;并列出了关于GIS开发面试中常见的问题&#xff08;含答案&#xff09;。 “ 包括以下内容 前言 简介 面试之前 面试流程 自我介绍-AI 基础篇 1、GIS八股文基础篇 2、Openlayers图形绘制 3、倾…

2-1 opencv实战进阶系列 阈值编辑器

目录 一、不说废话&#xff0c;先上现象 二、前言 三、方法详解 四、贴出完整代码 一、不说废话&#xff0c;先上现象 二、前言 对图像的处理中&#xff0c;设置合适的掩膜、寻找多边形、颜色追踪等方法都需要预先设置好颜色的上阈值和下阈值&#xff0c;来从原图中分割出…

蔚来发布新财报,亏损收窄,营收同比增长98.9%!

KlipC报道&#xff1a;9月5日&#xff0c;蔚来发布2024年二季度财报&#xff0c;财报显示&#xff0c;营收174.5亿元&#xff0c;同比增长98.9%&#xff0c;环比增长76.1%&#xff1b;交付量5.74万台&#xff0c;同比增长143.9%&#xff0c;环比增长90.9%&#xff1b;营收和交付…

Yolov5实现目标检测——调用官方权重进行检测

本文为为&#x1f517;365天深度学习训练营内部文章 原作者&#xff1a;K同学啊 一 安装源码 开源网址&#xff1a;GitHub - ultralytics/yolov5: YOLOv5 &#x1f680; in PyTorch > ONNX > CoreML > TFLite ​ 二 安装所需环境 安装环境依赖包&#xff0c;进入项目…

探索Mem0:下一代人工智能与机器学习内存管理基础设施(二)Mem0+Ollama 部署运行

探索Mem0:下一代人工智能与机器学习内存管理基础设施(二) Mem 0(发音为“mem-zero”)通过智能记忆层增强AI助手和代理,实现个性化的AI交互。Mem 0会记住用户偏好,适应个人需求,并随着时间的推移不断改进,使其成为客户支持聊天机器人,AI助手和自治系统的理想选择。 …

[Mdp] lc198. 打家劫舍(记忆化搜索+dp)

文章目录 1. 题目来源2. 题目解析 1. 题目来源 链接&#xff1a;198. 打家劫舍 前置&#xff1a; [每日一题] 146. 打家劫舍(数组、动态规划、巧妙解法) 2. 题目解析 记忆化搜索可以处理&#xff0c;是自顶向下进行枚举的&#xff0c;属于 递归。 动态规划&#xff0c;属于…