Nacos服务注册总流程(源码分析)

文章目录

    • 服务注册
      • NacosClient找看源码入口
      • NacosClient服务注册源码
      • NacosServer处理服务注册

服务注册

服务注册 在线流程图

在这里插入图片描述



NacosClient找看源码入口

我们启动一个微服务,引入nacos客户端的依赖

<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

并配置文件中指定nacosServer的地址,并运行该微服务,那么服务是如何注册进nacosServer中去的嘞?

server:port: 9002
spring:application:name: stock-servicecloud:nacos:discovery:# 指定nacos server的地址server-addr: localhost:8848# 指定集群名称cluster-name: SZmetadata:version: v3

我们应该怎么找源码的入口嘞?

我们知道SpringCloud是基于SpringBoot的,我们这里引入了这个nacos客户端nacos-discovery的依赖,我们就直接去找这个jar包下的spring.factories文件

在这里插入图片描述

这个文件中记录了很多的自动配置类,如果不知道应该看哪一个的话,那么就优先看和我们导入maven依赖名字相近的类

在这里插入图片描述


进入到这个NacosDiscoveryAutoConfiguration自动配置类后会发现这里会往容器中注入三个bean,看bean的名字好像都是和nacos注册相关的,按照经验来说,带有AutoXXXX这类方法一般都比较重要,再加上第三个方法还用到了上面两个bean,所以核心方法很大概率就是这个第三个方法。

在这里插入图片描述


进入到该类的构造方法之后,我们一般可以查看这个类的继承与实现结构,这样可以让我们更充分的了解该类

在这里插入图片描述


从上图中可以发现,NacosAutoServiceRegistration这个类还实现了ApplicationListener接口,这个接口我们都知道是Spring发布事件相关的接口

// ApplicationListener接口
package org.springframework.context;import java.util.EventListener;@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E var1);
}

我们再找这个方法的具体实现,会发现NacosAutoServiceRegistration这个类它没有重写该方法,那么就要在它的父类中找该方法的实现

public void onApplicationEvent(WebServerInitializedEvent event) {this.bind(event);
}// 方法调用,进入到bind(...)方法public void bind(WebServerInitializedEvent event) {ApplicationContext context = event.getApplicationContext();// 有if、并且有返回、并且后面还有业务代码的一般情况下这都是一个分支代码,可以先跳过if (context instanceof ConfigurableWebServerApplicationContext) {if ("management".equals(((ConfigurableWebServerApplicationContext) context).getServerNamespace())) {return;}}// 一般情况下,start()  init()     begin()这种方法名命名的方法一般都是比较重要的代码this.port.compareAndSet(0, event.getWebServer().getPort());this.start();
}

根据看源码的经验来说:

  • 有if,并且有返回值的,并且后面还有业务代码的一般情况下这都是一个分支代码,可以先跳过
  • 一般情况下,start() init() begin()这中方法名命名的方法一般都是比较重要的代码

接下来再进入到start()方法中

public void start() {// if + return + 后面有业务逻辑,大概率是一个分支代码 可以先跳过。这里实际上也只是记录了一个日志if (!isEnabled()) {if (logger.isDebugEnabled()) {logger.debug("Discovery Lifecycle disabled. Not starting");}return;}// only initialize if nonSecurePort is greater than 0 and it isn't already running// because of containerPortInitializer belowif (!this.running.get()) {this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration()));// 核心方法,可以发现在调用这个方法之前和调用方法之后都发布了一个事件// 正好我们要找的也就是注册相关的方法// 因为我们就是从NacosDiscoveryAutoConfiguration自动配置类的NacosAutoServiceRegistration这个和注册相关的bean进来的register();if (shouldRegisterManagement()) {registerManagement();}this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration()));this.running.compareAndSet(false, true);}}

接下来在点进register()方法,跳转几次之后就会进入到NamingService这个关键接口的registerInstance(...)方法中去




NacosClient服务注册源码

关键接口NamingService,它的registerInstance(...)方法就是服务注册。程序刚开始会进入到该接口的实现类NacosNamingServiceregisterInstance(String serviceName, String groupName, Instance instance)方法中

// 该方法 往NacosServer中注册一个微服务实例
@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {NamingUtils.checkInstanceIsLegal(instance);String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);if (instance.isEphemeral()) {BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);beatReactor.addBeatInfo(groupedServiceName, beatInfo);}// 调用该方法进行微服务注册,instance对象中保存着微服务的各种信息,比如ip、端口、访问权重、健康状态、是否上线等等serverProxy.registerService(groupedServiceName, groupName, instance);
}

instance对象保存的内容如下图所示

在这里插入图片描述



public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", namespaceId, serviceName,instance);// 把instance对象中的数据取出来,封装成一个HashMapfinal Map<String, String> params = new HashMap<String, String>(16);params.put(CommonParams.NAMESPACE_ID, namespaceId);params.put(CommonParams.SERVICE_NAME, serviceName);params.put(CommonParams.GROUP_NAME, groupName);params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());params.put("ip", instance.getIp());params.put("port", String.valueOf(instance.getPort()));params.put("weight", String.valueOf(instance.getWeight()));params.put("enable", String.valueOf(instance.isEnabled()));params.put("healthy", String.valueOf(instance.isHealthy()));params.put("ephemeral", String.valueOf(instance.isEphemeral()));params.put("metadata", JacksonUtils.toJson(instance.getMetadata()));// 发送一个post的http请求发送给NacosServer,进行服务注册reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.POST);}

在这里插入图片描述


再通过官方文档 / 接口详细文档就能发现这就是服务注册的接口

在这里插入图片描述



NacosServer处理服务注册

NacosClient向NacosServer发送了一个服务注册的post请求,url为/v1/ns/instance

NacosServer这个项目有很多很多的子工程,就拿下图所示,我应该如何去找嘞?

在这里插入图片描述


其实我们就可以双击shift键,直接搜索controller,一个一个文件夹找,或者是直接加上关键字,就比如我这里加上了instance这个关键字就找到了真正处理这个请求的controller,类名为InstanceController

在这里插入图片描述


还有一种方法就是,我们在NacosClient这边进行服务注册时会频繁的使用到NamingService,那我就在NacosServer端找有没有和Naming相关的子工程,最终发现还真就找到了

在这里插入图片描述



接下来是源码,首先直接查看该controller的post接口

@CanDistro
@PostMapping
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public String register(HttpServletRequest request) throws Exception {final String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);NamingUtils.checkServiceNameFormat(serviceName);// 通过解析request请求参数,封装成instance对象final Instance instance = parseInstance(request);// 调用Service层方法进行服务实例注册serviceManager.registerInstance(namespaceId, serviceName, instance);return "ok";
}

接下来进入到service层的registerInstance(...)方法逻辑

public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {// 这里会去创建一个service,如果连该service对应的命名空间都不存在的话就会利用DCL双重锁检测机制去创建一个map<serviceName,service>// 再去添加service,添加命名空间对应的map<serviceName,service>中。命名空间对应的这个map是存在注册表serviceMap中的// 这里创建的只是一个空服务,它里面还没有instance服务实例// 在创建service过程中还会进行初始化,会添加一个延迟定时任务进行该服务下的所有实例的健康检查、修改服务健康状态、删除过时服务createEmptyService(namespaceId, serviceName, instance.isEphemeral());// 这里再获取,就不应该为null了,如果为null也就该抛异常了// getService()会先根据命名空间取出一个Map,再从这个Map中根据serviceName取出service。service中包含instance微服务实例Service service = getService(namespaceId, serviceName);if (service == null) {throw new NacosException(NacosException.INVALID_PARAM,"service not found, namespace: " + namespaceId + ", service: " + serviceName);}// 进行服务实例instance添加操作addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
}



createEmptyService(...)方法中,首先会去判断当前service是否存在,如果不存在则创建一个空的新的service,并且把这个service添加进namespaceId对应的Map中,如果在这个过程中,namespaceId命名空间不存在则使用DCL双重锁检测去创建一个map集合添加进注册表serviceMap中。接下来在对刚刚创建的service进行init()初始化操作,蛀牙就是开启一个延时的定时任务,定期对该服务中的实例进行健康检查,并修改不健康的状态或移除超时实例

public void createEmptyService(String namespaceId, String serviceName, boolean local) throws NacosException {createServiceIfAbsent(namespaceId, serviceName, local, null);
}public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster)throws NacosException {// 先从服务注册表serviceMap中获取Service service = getService(namespaceId, serviceName);// 如果不存在就创建if (service == null) {Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName);service = new Service();service.setName(serviceName);service.setNamespaceId(namespaceId);service.setGroupName(NamingUtils.getGroupName(serviceName));// now validate the service. if failed, exception will be thrownservice.setLastModifiedMillis(System.currentTimeMillis());service.recalculateChecksum();if (cluster != null) {cluster.setService(service);service.getClusterMap().put(cluster.getName(), cluster);}service.validate();// 这里采用了DCL双重锁检测机制进行的添加操作putServiceAndInit(service);if (!local) {addOrReplaceService(service);}}
}private void putServiceAndInit(Service service) throws NacosException {// 将service添加进对应的NamespaceId中putService(service);// 服务进行初始化,主要就是添加延时定时任务,对实例进行健康检查service.init();consistencyService.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), service);consistencyService.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), service);Loggers.SRV_LOG.info("[NEW-SERVICE] {}", service.toJson());
}public void putService(Service service) {// DCL双重检测机制if (!serviceMap.containsKey(service.getNamespaceId())) {synchronized (putServiceLock) {if (!serviceMap.containsKey(service.getNamespaceId())) {serviceMap.put(service.getNamespaceId(), new ConcurrentSkipListMap<>());}}}serviceMap.get(service.getNamespaceId()).put(service.getName(), service);
}public void init() {// NacosClient客户端的心跳检测任务// 这里就会开启一个线程去执行任务,clientBeatCheckTask属性就是一个task,run()方法中会进行实例的健康检查,后面会详细介绍HealthCheckReactor.scheduleCheck(clientBeatCheckTask);for (Map.Entry<String, Cluster> entry : clusterMap.entrySet()) {entry.getValue().setService(this);entry.getValue().init();}
}



接下来就进入到了addInstance(...)添加实例的方法中了,这个方法中主要做的事就是将当前service的所有实例通过一个key存入一个Map中,然后保存在一个dataStore这个Map中,同时再把这个key和相关的操作类型存入一个阻塞队列中,至此NacosClient发送的请求就处理完成了。之后会有单独一个线程异步处理进行真正的服务注册。

public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips)throws NacosException {// 根据ephemeral的值来决定生成上面key,默认情况下NacosClient传递过来的都是true,一般微服务的实例都是临时实例,不是持久化实例// 如果是持久化实例就没有下面的ephemeral这个字符串拼接// key = 一些字符串常量 + “ephemeral” + namespaceId + “##” + serviceNameString key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);Service service = getService(namespaceId, serviceName);synchronized (service) {// 添加微服务的实例,并返回当前服务所有的实例List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);Instances instances = new Instances();instances.setInstanceList(instanceList);// key主要就是命名空间+服务名,通过这个key添加进consistencyService中consistencyService.put(key, instances);}
}// 上面的Instances实现了Record接口,这里其实也就是一个封装
public void put(String key, Record value) throws NacosException {//  mapConsistencyService(key)这里会根据是否是临时实例进而去调用不同实现类的put()方法,这里以临时实例举例mapConsistencyService(key).put(key, value);
}public void put(String key, Record value) throws NacosException {// 将key和所有服务实例封装的Record对象封装成一个datum对象,并保存到一个map集合中。// 同时还有一个key和DataOperation枚举操作类型添加进阻塞队列的操作。// 后续肯定有一个线程从这个注释队列中取出数据,然后根据key把datum对象取出来onPut(key, value);distroProtocol.sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE,globalConfig.getTaskDispatchPeriod() / 2);
}public void onPut(String key, Record value) {if (KeyBuilder.matchEphemeralInstanceListKey(key)) {Datum<Instances> datum = new Datum<>();datum.value = (Instances) value;datum.key = key;datum.timestamp.incrementAndGet();// 这里把key和datum存入了一个dataMap这个集合中// 这个datum对象中保存着我当前服务所有的服务实例instancesdataStore.put(key, datum);}if (!listeners.containsKey(key)) {return;}// 将key和后面这个枚举参数封装成一个Pair对象,并添加进一个阻塞队列中notifier.addTask(key, DataOperation.CHANGE);
}public void addTask(String datumKey, DataOperation action) {if (services.containsKey(datumKey) && action == DataOperation.CHANGE) {return;}if (action == DataOperation.CHANGE) {services.put(datumKey, StringUtils.EMPTY);}// 把key和dataOperation操作枚举添加进一个阻塞队列中,真正进行服务注册的是在下面的run()方法中tasks.offer(Pair.with(datumKey, action));
}// 另一个线程异步进行处理,从阻塞队列中获取数据,真正进行服务注册操作
@Override
public void run() {Loggers.DISTRO.info("distro notifier started");// 该线程会一直死循环,从阻塞队列中取数据for (; ; ) {try {Pair<String, DataOperation> pair = tasks.take();handle(pair);} catch (Throwable e) {Loggers.DISTRO.error("[NACOS-DISTRO] Error while handling notifying task", e);}}
}



接下来就是详细查看这个handle(pair)方法,查看另一个线程是如何进行服务注册的?

这里会从先判断操作类型,然后从handle()方法调用到onChange(...)方法;而在onChange(...)方法中首先会对服务实例的权重进行处理,再调用到updateIPs()方法;在updateIPs()方法中会对service中的cluster做一些处理,最终就会调用到updateIps(...)方法中,在这个方法中会通过CopyOnWrite思想真正更改保存instance实例的集合

// handle()方法调用到onChange(...)方法
private void handle(Pair<String, DataOperation> pair) {try {String datumKey = pair.getValue0();DataOperation action = pair.getValue1();services.remove(datumKey);int count = 0;if (!listeners.containsKey(datumKey)) {return;}for (RecordListener listener : listeners.get(datumKey)) {count++;try {if (action == DataOperation.CHANGE) {// dataStore.get(datumKey)取出来的对象是Datum类型的对象,它其中保存在key+所有服务实例instances// dataStore.get(datumKey).value 就是这个datumKey对应的所有服务实例instanceslistener.onChange(datumKey, dataStore.get(datumKey).value);continue;}if (action == DataOperation.DELETE) {listener.onDelete(datumKey);continue;}} catch (Throwable e) {...}}...} catch (Throwable e) {...}
}// 对服务实例的权重进行处理,再调用到updateIPs()方法,
public void onChange(String key, Instances value) throws Exception {Loggers.SRV_LOG.info("[NACOS-RAFT] datum is changed, key: {}, value: {}", key, value);// 对instance服务实例的特殊权重做处理for (Instance instance : value.getInstanceList()) {if (instance == null) {// Reject this abnormal instance list:throw new RuntimeException("got null instance " + key);}if (instance.getWeight() > 10000.0D) {instance.setWeight(10000.0D);}if (instance.getWeight() < 0.01D && instance.getWeight() > 0.0D) {instance.setWeight(0.01D);}}updateIPs(value.getInstanceList(), KeyBuilder.matchEphemeralInstanceListKey(key));recalculateChecksum();
}// 会对service中的cluster做一些处理,最终就会调用到updateIps(...)方法中
public void updateIPs(Collection<Instance> instances, boolean ephemeral) {Map<String, List<Instance>> ipMap = new HashMap<>(clusterMap.size());// 遍历所有集群for (String clusterName : clusterMap.keySet()) {ipMap.put(clusterName, new ArrayList<>());}for (Instance instance : instances) {try {if (instance == null) {Loggers.SRV_LOG.error("[NACOS-DOM] received malformed ip: null");continue;}// 实例instance的默认集群名字if (StringUtils.isEmpty(instance.getClusterName())) {instance.setClusterName(UtilsAndCommons.DEFAULT_CLUSTER_NAME);}if (!clusterMap.containsKey(instance.getClusterName())) {Loggers.SRV_LOG.warn("cluster: {} not found, ip: {}, will create new cluster with default configuration.",instance.getClusterName(), instance.toJson());Cluster cluster = new Cluster(instance.getClusterName(), this);cluster.init();getClusterMap().put(instance.getClusterName(), cluster);}List<Instance> clusterIPs = ipMap.get(instance.getClusterName());// 一个新的集群名处理if (clusterIPs == null) {clusterIPs = new LinkedList<>();ipMap.put(instance.getClusterName(), clusterIPs);}// 实例添加进对应的集群集合中clusterIPs.add(instance);} catch (Exception e) {Loggers.SRV_LOG.error("[NACOS-DOM] failed to process ip: " + instance, e);}}for (Map.Entry<String, List<Instance>> entry : ipMap.entrySet()) {//make every ip mineList<Instance> entryIPs = entry.getValue();// 微服务实例真正的注册进服务注册表的方法clusterMap.get(entry.getKey()).updateIps(entryIPs, ephemeral);}setLastModifiedMillis(System.currentTimeMillis());// 服务实例改变之后,会发布一个ServiceChangeEvent事件getPushService().serviceChanged(this);StringBuilder stringBuilder = new StringBuilder();for (Instance instance : allIPs()) {stringBuilder.append(instance.toIpAddr()).append("_").append(instance.isHealthy()).append(",");}Loggers.EVT_LOG.info("[IP-UPDATED] namespace: {}, service: {}, ips: {}", getNamespaceId(), getName(),stringBuilder.toString());}
/*** Update instance list.* 下面的方法中有CopyOnWrite的实现思想,真正存储实例的集合是ephemeralInstances,* 但是这里面基本上都是在围绕oldIpMap这个复制出来的副本集合进行相应的操作,最后拿最新的集合复制给ephemeralInstances** @param ips       instance list* @param ephemeral whether these instances are ephemeral*/
public void updateIps(List<Instance> ips, boolean ephemeral) {Set<Instance> toUpdateInstances = ephemeral ? ephemeralInstances : persistentInstances;// 先保存一份现有的实例列表HashMap<String, Instance> oldIpMap = new HashMap<>(toUpdateInstances.size());for (Instance ip : toUpdateInstances) {// 各个服务实例的ip和端口不一样,所以这里生成的key也不一样oldIpMap.put(ip.getDatumKey(), ip);}// 接下来的很多操作就是拿老的实例集合和新的实例集合做一些数据比对,如果是新产生的实例那么就进行添加操作,如果是已经存在了的实例则是修改操作// 最后将最终的结果赋值给ephemeralInstances或persistentInstancesList<Instance> updatedIPs = updatedIps(ips, oldIpMap.values());if (updatedIPs.size() > 0) {for (Instance ip : updatedIPs) {Instance oldIP = oldIpMap.get(ip.getDatumKey());// do not update the ip validation status of updated ips// because the checker has the most precise result// Only when ip is not marked, don't we update the health status of IP:if (!ip.isMarked()) {ip.setHealthy(oldIP.isHealthy());}if (ip.isHealthy() != oldIP.isHealthy()) {// ip validation status updatedLoggers.EVT_LOG.info("{} {SYNC} IP-{} {}:{}@{}", getService().getName(),(ip.isHealthy() ? "ENABLED" : "DISABLED"), ip.getIp(), ip.getPort(), getName());}if (ip.getWeight() != oldIP.getWeight()) {// ip validation status updatedLoggers.EVT_LOG.info("{} {SYNC} {IP-UPDATED} {}->{}", getService().getName(), oldIP.toString(),ip.toString());}}}List<Instance> newIPs = subtract(ips, oldIpMap.values());if (newIPs.size() > 0) {Loggers.EVT_LOG.info("{} {SYNC} {IP-NEW} cluster: {}, new ips size: {}, content: {}", getService().getName(),getName(), newIPs.size(), newIPs.toString());for (Instance ip : newIPs) {HealthCheckStatus.reset(ip);}}List<Instance> deadIPs = subtract(oldIpMap.values(), ips);if (deadIPs.size() > 0) {Loggers.EVT_LOG.info("{} {SYNC} {IP-DEAD} cluster: {}, dead ips size: {}, content: {}", getService().getName(),getName(), deadIPs.size(), deadIPs.toString());for (Instance ip : deadIPs) {HealthCheckStatus.remv(ip);}}toUpdateInstances = new HashSet<>(ips);// 最终,微服务的实例会保存在下面这个集合中if (ephemeral) {ephemeralInstances = toUpdateInstances;} else {persistentInstances = toUpdateInstances;}
}

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

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

相关文章

免密ssh和自定义服务器名字【远程连接服务器】

免密ssh和自定义服务器名字【远程连接服务器】 免密ssh和自定义服务器名字【远程连接服务器】服务器添加本地公钥ssh-copy-id使用别名登录config 免密ssh和自定义服务器名字【远程连接服务器】 原理 实现免密登录需要 本地的公钥id_rsa.pub放在服务器上的 authorized_keys 文件…

Linux 防火墙配置指南:firewalld 端口管理应用案例(二十个实列)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;&#x1f427;Linux高级管理专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️…

Thisjavabean对象数组

This 1.概念 this是一个对象this是一个构造函数 2.介绍 解决局部变量和成员变量命名冲突 this在面向对象-封装那一篇里&#xff0c;有被两个地方提及。 但我们先简单给一个例子&#xff1a; public Person(String name, String phone, String qqPassword, String bankCar…

豆瓣评分9.6,这本书不看损失巨大!

点击上方△腾阳 关注 转载请联系授权 这些年&#xff0c;我就像是个热心向导&#xff0c;逢人就劝读那本《毛泽东选集》。 结果呢&#xff1f;有人一听就摆手&#xff0c;笑言&#xff1a;“哎呀&#xff0c;那书太高大上了&#xff0c;咱啃不动啊&#xff01;” 特别是咱们…

WEB编程-了解Tomcat服务器

第⼀章⽹络编程 1.1 概述 计算机⽹络&#xff1a;是指将地理位置不同的具有独⽴功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在⽹络 操作系统、⽹络管理软件及⽹络通信协议的管理和协调下&#xff0c;实现资源共享和信息传递的计算机系统。 …

智慧矿山建设规划方案(121页Word)

智慧矿山建设项目方案摘要 一、项目背景及现状分析 项目背景 随着信息技术的迅猛发展&#xff0c;智慧化、数字化已成为矿山行业转型升级的必然趋势。智慧矿山建设项目旨在通过集成先进的信息技术手段&#xff0c;实现对矿山生产、管理、安全等全过程的智能化监控与管理&…

SpringBoot新手快速入门系列教程四:创建第一个SringBoot的API

首先我们用IDEA新建一个项目&#xff0c;请将这些关键位置按照我的设置设置一下 接下来我将要带着你一步一步创建一个Get请求和Post请求&#xff0c;通过客户端请求的参数&#xff0c;以json格式返回该参数{“message”:"Hello"} 1,先在IDE左上角把这里改为文件模式…

G9 - ACGAN理论与实战

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目录 环境步骤环境设置数据准备工具方法模型设计模型训练模型效果展示 总结与心得体会 上周已经简单的了解了ACGAN的原理&#xff0c;并且不经实践的编写了部分…

python如何设计窗口

PyQt是一个基于Qt的接口包&#xff0c;可以直接拖拽控件设计UI界面&#xff0c;下面我简单介绍一下这个包的安装和使用&#xff0c;感兴趣的朋友可以自己尝试一下&#xff1a; 1、首先&#xff0c;安装PyQt模块&#xff0c;这个直接在cmd窗口输入命令“pip install pyqt5”就行…

中金女员工离世悲剧:职场压力、心理健康与社会支持的深刻反思

中金女员工离世背后的深思 2024年7月1日,一则令人痛心的消息在金融界乃至整个网络迅速传播:中金公司一位年仅30岁的女员工郑某露,在降薪和房贷的双重压力下,不幸离世。这一事件不仅让她的家人和朋友陷入了深深的悲痛之中,也引发了社会各界对职场环境、个体心理健康以及社…

使用块的网络 VGG

一、AlexNet与VGG 1、深度学习追求更深更大&#xff0c;使用VGG将卷积层组合为块 2、VGG块&#xff1a;3*3卷积&#xff08;pad1&#xff0c;n层&#xff0c;m通道&#xff09;、2*2最大池化层 二、VGG架构 1、多个VGG块后接全连接层 2、不同次数的重复块得到不同的架构&a…

工作手机怎么做好业务员工作微信的监控管理

什么是工作手机管理系统&#xff1f; 工作手机管理系统是专为企业管理设计的员工微信管理&#xff0c;它通过监控通讯记录、保障数据安全、自动检测敏感行为、永久保留客户信息等功能&#xff0c;帮助企业提升销售效率、维护客户资源安全&#xff0c;并确保业务流程的合规性。…

NASA和IBM推出INDUS:高级科学研究的综合大模型

在最近的一项研究中&#xff0c;来自美国宇航局和IBM的一组研究人员合作开发了一种模型&#xff0c;该模型可应用于地球科学&#xff0c;天文学&#xff0c;物理学&#xff0c;天体物理学&#xff0c;太阳物理学&#xff0c;行星科学和生物学以及其他多学科学科。当前的模型&am…

DP:二维费用背包问题

文章目录 &#x1f3b5;二维费用背包问题&#x1f3b6;引言&#x1f3b6;问题定义&#x1f3b6;动态规划思想&#x1f3b6;状态定义和状态转移方程&#x1f3b6;初始条件和边界情况 &#x1f3b5;例题&#x1f3b6;1.一和零&#x1f3b6;2.盈利计划 &#x1f3b5;总结 &#x1…

机器人具身智能Embodied AI

强调智能体&#xff08;如机器人&#xff09;通过物理身体在物理世界中的实时感知、交互和学习来执行任务。 通过物理交互来完成任务的智能系统。它由“本体”&#xff08;即物理身体&#xff09;和“智能体”&#xff08;即智能核心&#xff09;耦合而成&#xff0c;能够在复…

taoCMS v3.0.2 任意文件读取漏洞(CVE-2022-23316)

前言 CVE-2022-23316 是一个影响 taoCMS v3.0.2 的漏洞。这个漏洞允许攻击者通过 admin.php?actionfile&ctrldownload&path../../1.txt 的路径读取任意文件。攻击者可以利用该漏洞读取服务器上的任何文件&#xff0c;只要他们知道文件的路径​ (OpenCVE)​​ (Tenabl…

亚马逊跟卖ERP的自动调价功能,能够简易地批量设置价格规则。

跟卖的智能调价 跟卖智能调价简单说是可以上调&#xff0c;下调就是怎么说&#xff1f;上调就是它根靠根据市场最低的价格情况进行去上调。 然后添加指定条件&#xff0c;到工具栏找到指定条件&#xff0c;点击添加指定条件。 然后选择店铺&#xff0c;比如选择店铺&#xf…

微信⼩程序的电影推荐系统-计算机毕业设计源码76756

摘 要 随着互联网的普及和移动互联网的发展&#xff0c;人们对于获取信息的便捷性和高效性要求越来越高。电影作为一种受众广泛喜爱的娱乐方式&#xff0c;电影推荐系统的出现为用户提供了更加个性化和精准的电影推荐服务。微信小程序作为一种轻量级应用形式&#xff0c;在用户…

算法题-回文子串和最长回文子序列

算法题-回文子串和最长回文子序列 一、647. 回文子串二、516. 最长回文子序列 一、647. 回文子串 中等 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个…

qt 如果把像素点数据变成一个图片

1.概要 图像的本质是什么&#xff0c;就是一个个的像素点&#xff0c;对与显示器来说就是一个二维数组。无论多复杂的图片&#xff0c;对于显示器来说就是一个二维数组。 2.代码 #include "widget.h"#include <QApplication> #include <QImage> #incl…