Nacos源码解读02——服务事件处理

NotifyCenter通知中心的实现原理和使用

参考https://blog.csdn.net/weixin_42937773/article/details/129105694?spm=1001.2014.3001.5502

任务执行引擎的原理和使用

参考https://blog.csdn.net/weixin_42937773/article/details/128892737?spm=1001.2014.3001.5502

服务注册发现事件处理源码

这些核心功能主要采用事件驱动架构实现,在服务注册中发布了ServiceEvent.ServiceChangeEvent、ClientEvent.ClientChangedEvent(由ClientOperationEvent.ClientRegisterServiceEvent事件发布)、MetadataEvent.InstanceMetadataEvent、RegisterInstanceTraceEvent事件,在服务发现中发布了ClientOperationEvent.ClientSubscribeServiceEvent、SubscribeServiceTraceEvent事件,

维护服务注册订阅数据

核心功能:维护(添加/删除)服务注册和订阅内存数据,同时传递相应的客户端事件源码位置:ClientServiceIndexesManager#onEvent处理两类事件:ClientEvent.ClientDisconnectEvent和ClientOperationEvent,ClientOperationEvent包括ClientRegisterServiceEvent、ClientDeregisterServiceEvent、ClientSubscribeServiceEvent、ClientUnsubscribeServiceEvent四种事件

ClientServiceIndexesManager

    @Overridepublic void onEvent(Event event) {//客户端下线事件if (event instanceof ClientEvent.ClientDisconnectEvent) {handleClientDisconnect((ClientEvent.ClientDisconnectEvent) event);//客户端操作事件    } else if (event instanceof ClientOperationEvent) {handleClientOperation((ClientOperationEvent) event);}}

客户端下线事件处理

    private void handleClientDisconnect(ClientEvent.ClientDisconnectEvent event) {       //获取客户端信息Client client = event.getClient();//遍历所有的订阅服务for (Service each : client.getAllSubscribeService()) {removeSubscriberIndexes(each, client.getClientId());}//遍历所有的发布服务for (Service each : client.getAllPublishedService()) {removePublisherIndexes(each, client.getClientId());}}

移除订阅服务

    private void removeSubscriberIndexes(Service service, String clientId) {if (!subscriberIndexes.containsKey(service)) {return;}subscriberIndexes.get(service).remove(clientId);// 如果服务的订阅者为空,则把服务从订阅表中删除if (subscriberIndexes.get(service).isEmpty()) {subscriberIndexes.remove(service);}}

移除发布服务

    private void removePublisherIndexes(Service service, String clientId) {if (!publisherIndexes.containsKey(service)) {return;}//移除服务publisherIndexes.get(service).remove(clientId);//发布服务变更事件NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));}

客户端操作事件处理

    private void handleClientOperation(ClientOperationEvent event) {//获取服务信息Service service = event.getService();//获取客户端信息String clientId = event.getClientId();//客户端注册事件if (event instanceof ClientOperationEvent.ClientRegisterServiceEvent) {addPublisherIndexes(service, clientId);} else if (event instanceof ClientOperationEvent.ClientDeregisterServiceEvent) {//客户端注销事件removePublisherIndexes(service, clientId);} else if (event instanceof ClientOperationEvent.ClientSubscribeServiceEvent) {// 添加订阅服务的client,同时发布服务订阅事件addSubscriberIndexes(service, clientId);} else if (event instanceof ClientOperationEvent.ClientUnsubscribeServiceEvent) {// 删除订阅服务的client,若没有client订阅service,则移除serviceremoveSubscriberIndexes(service, clientId);}}

客户端注册事件

    private final ConcurrentMap<Service, Set<String>> publisherIndexes = new ConcurrentHashMap<>();private void addPublisherIndexes(Service service, String clientId) {//服务和客户端id绑定publisherIndexes.computeIfAbsent(service, (key) -> new ConcurrentHashSet<>());publisherIndexes.get(service).add(clientId);//发布服务变更事件NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));}

客户端订阅事件

    private void addSubscriberIndexes(Service service, String clientId) {//添加订阅服务的客户端subscriberIndexes.computeIfAbsent(service, (key) -> new ConcurrentHashSet<>());// Fix #5404, Only first time add need notify event.//订阅服务成功 发送服务订阅事件if (subscriberIndexes.get(service).add(clientId)) {NotifyCenter.publishEvent(new ServiceEvent.ServiceSubscribedEvent(service, clientId));}}

客户端注销事件

    private void removePublisherIndexes(Service service, String clientId) {if (!publisherIndexes.containsKey(service)) {return;}//移除客户端publisherIndexes.get(service).remove(clientId);NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));}

客户端删除订阅

    private void removeSubscriberIndexes(Service service, String clientId) {if (!subscriberIndexes.containsKey(service)) {return;}//移除订阅客户端信息subscriberIndexes.get(service).remove(clientId);//如果订阅服务为空 移除订阅服务if (subscriberIndexes.get(service).isEmpty()) {subscriberIndexes.remove(service);}}

处理服务变更和订阅事件

NamingSubscriberServiceV2Impl

    //监听服务变更和订阅事件@Overridepublic void onEvent(Event event) {//判断是否是GRPC协议 如果不是直接返回 if (!upgradeJudgement.isUseGrpcFeatures()) {return;}//服务端变更事件if (event instanceof ServiceEvent.ServiceChangedEvent) {// 服务变更时,向所有客户端推送ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event;Service service = serviceChangedEvent.getService();//  添加延时任务到任务引擎 PushDelayTask默认未指定客户端地址,会推送给所有客户端delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay()));//服务端订阅事件 } else if (event instanceof ServiceEvent.ServiceSubscribedEvent) {// 客户端服务订阅变更时,只推送给这个变更的客户端ServiceEvent.ServiceSubscribedEvent subscribedEvent = (ServiceEvent.ServiceSubscribedEvent) event;//获取服务信息Service service = subscribedEvent.getService();// PushDelayTask指定了事件变更的客户端//添加延时任务到任务引擎delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay(),subscribedEvent.getClientId()));}}

延时任务处理

当NamingSubscriberServiceV2Impl初始化的时候会执行构造方法里去创建一个PushDelayTaskExecuteEngine

   public NamingSubscriberServiceV2Impl(ClientManagerDelegate clientManager,ClientServiceIndexesManager indexesManager, ServiceStorage serviceStorage,NamingMetadataManager metadataManager, PushExecutorDelegate pushExecutor, UpgradeJudgement upgradeJudgement,SwitchDomain switchDomain) {this.clientManager = clientManager;this.indexesManager = indexesManager;this.upgradeJudgement = upgradeJudgement;this.delayTaskEngine = new PushDelayTaskExecuteEngine(clientManager, indexesManager, serviceStorage,metadataManager, pushExecutor, switchDomain);NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance());}

创建PushDelayTaskExecuteEngine的时候会执行super调用到他父类NacosDelayTaskExecuteEngine中

    public PushDelayTaskExecuteEngine(ClientManager clientManager, ClientServiceIndexesManager indexesManager,ServiceStorage serviceStorage, NamingMetadataManager metadataManager,PushExecutor pushExecutor, SwitchDomain switchDomain) {super(PushDelayTaskExecuteEngine.class.getSimpleName(), Loggers.PUSH);this.clientManager = clientManager;this.indexesManager = indexesManager;this.serviceStorage = serviceStorage;this.metadataManager = metadataManager;this.pushExecutor = pushExecutor;this.switchDomain = switchDomain;setDefaultTaskProcessor(new PushDelayTaskProcessor(this));}
 public NacosDelayTaskExecuteEngine(String name, Logger logger) {this(name, 32, logger, 100L);}

NacosDelayTaskExecuteEngine中会创建一个ProcessRunnable的任务处理线程交给定时线程池去处理

    public NacosDelayTaskExecuteEngine(String name, int initCapacity, Logger logger, long processInterval) {super(logger);tasks = new ConcurrentHashMap<>(initCapacity);processingExecutor = ExecutorFactory.newSingleScheduledExecutorService(new NameThreadFactory(name));processingExecutor.scheduleWithFixedDelay(new ProcessRunnable(), processInterval, processInterval, TimeUnit.MILLISECONDS);}

当有任务需要执行的时候会执行ProcessRunnable 的run方法执行processTasks方法

    private class ProcessRunnable implements Runnable {@Overridepublic void run() {try {processTasks();} catch (Throwable e) {getEngineLog().error(e.toString(), e);}}}

processTasks会遍历所有任务然后执行processor 的process方法因为在PushDelayTaskExecuteEngine构造方法中创建DefaultTaskProcessor是PushDelayTaskExecuteEngine这个类所以会执行PushDelayTaskExecuteEngine的process方法

    protected void processTasks() {//获取所有任务Collection<Object> keys = getAllTaskKeys();for (Object taskKey : keys) {//移除任务AbstractDelayTask task = removeTask(taskKey);if (null == task) {continue;}NacosTaskProcessor processor = getProcessor(taskKey);if (null == processor) {getEngineLog().error("processor not found for task, so discarded. " + task);continue;}try {// ReAdd task if process failedif (!processor.process(task)) {retryFailedTask(taskKey, task);}} catch (Throwable e) {getEngineLog().error("Nacos task execute error ", e);retryFailedTask(taskKey, task);}}}
      @Overridepublic boolean process(NacosTask task) {//强制转换任务PushDelayTask pushDelayTask = (PushDelayTask) task;//获取服务信息Service service = pushDelayTask.getService();//委派给Dispatcher去执行任务  NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask(service, new PushExecuteTask(service, executeEngine, pushDelayTask));return true;}

往任务线程池中添加任务

    public void dispatchAndExecuteTask(Object dispatchTag, AbstractExecuteTask task) {executeEngine.addTask(dispatchTag, task);}
    @Overridepublic void addTask(Object tag, AbstractExecuteTask task) {NacosTaskProcessor processor = getProcessor(tag);if (null != processor) {processor.process(task);return;}//最终是往线程池的queue中添加了任务然后等任务调度会执行TaskExecuteWorker worker = getWorker(tag);worker.process(task);}

因为添加的是PushExecuteTask所以当调度了之后会执行到PushExecuteTask的run方法中

    @Overridepublic void run() {try {//包装一下 包装成wrapperPushDataWrapper wrapper = generatePushData();//获取客户端ManagerClientManager clientManager = delayTaskEngine.getClientManager();//获取需要通知的客户端for (String each : getTargetClientIds()) {//拿到客户端信息Client client = clientManager.getClient(each);//客户端是空直接返回if (null == client) {// means this client has disconnectcontinue;}//获取服务的订阅者SubscriberSubscriber subscriber = clientManager.getClient(each).getSubscriber(service);//发送RPC请求delayTaskEngine.getPushExecutor().doPushWithCallback(each, subscriber, wrapper,new NamingPushCallback(each, subscriber, wrapper.getOriginalData(), delayTask.isPushToAll()));}} catch (Exception e) {Loggers.PUSH.error("Push task for service" + service.getGroupedServiceName() + " execute failed ", e);delayTaskEngine.addTask(service, new PushDelayTask(service, 1000L));}}

发送GRPC请求

    @Overridepublic void doPushWithCallback(String clientId, Subscriber subscriber, PushDataWrapper data, PushCallBack callBack) {pushService.pushWithCallback(clientId, NotifySubscriberRequest.buildNotifySubscriberRequest(getServiceInfo(data, subscriber)),callBack, GlobalExecutor.getCallbackExecutor());}
    /*** push response with no ack.** @param connectionId    connectionId.* @param request         request.* @param requestCallBack requestCallBack.*/public void pushWithCallback(String connectionId, ServerRequest request, PushCallBack requestCallBack,Executor executor) {Connection connection = connectionManager.getConnection(connectionId);if (connection != null) {try {connection.asyncRequest(request, new AbstractRequestCallBack(requestCallBack.getTimeout()) {@Overridepublic Executor getExecutor() {return executor;}@Overridepublic void onResponse(Response response) {if (response.isSuccess()) {requestCallBack.onSuccess();} else {requestCallBack.onFail(new NacosException(response.getErrorCode(), response.getMessage()));}}@Overridepublic void onException(Throwable e) {requestCallBack.onFail(e);}});} catch (ConnectionAlreadyClosedException e) {connectionManager.unregister(connectionId);requestCallBack.onSuccess();} catch (Exception e) {Loggers.REMOTE_DIGEST.error("error to send push response to connectionId ={},push response={}", connectionId,request, e);requestCallBack.onFail(e);}} else {requestCallBack.onSuccess();}}

客户端接收连接

NamingGrpcClientProxy

private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder) throws NacosException {rpcClient.serverListFactory(serverListFactory);// gRPC Client启动rpcClient.start();// 注解@10rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder));// 注册连接事件Listener,当连接建立和断开时处理事件rpcClient.registerConnectionListener(namingGrpcConnectionEventListener);
}

在客户端构建gRPC时,注册registerServerRequestHandler用于处理从Nacos Push到Client的请求,添加到了serverRequestHandlers集合。

GrpcClient#connectToServer()
@Override
public Connection connectToServer(ServerInfo serverInfo) {try {RequestGrpc.RequestFutureStub newChannelStubTemp = createNewChannelStub(serverInfo.getServerIp(),serverInfo.getServerPort() + rpcPortOffset());if (newChannelStubTemp != null) {Response response = serverCheck(newChannelStubTemp);BiRequestStreamGrpc.BiRequestStreamStub biRequestStreamStub = BiRequestStreamGrpc.newStub(newChannelStubTemp.getChannel());GrpcConnection grpcConn = new GrpcConnection(serverInfo, grpcExecutor);grpcConn.setConnectionId(((ServerCheckResponse) response).getConnectionId());//create stream request and bind connection event to this connection.// 注解@11StreamObserver<Payload> payloadStreamObserver = bindRequestStream(biRequestStreamStub, grpcConn);// ...return grpcConn;}return null;} catch (Exception e) {// ...}return null;
}

在连接server时绑定相关事件

private StreamObserver<Payload> bindRequestStream(final BiRequestStreamGrpc.BiRequestStreamStub streamStub,final GrpcConnection grpcConn) {return streamStub.requestBiStream(new StreamObserver<Payload>() {@Overridepublic void onNext(Payload payload) {LoggerUtils.printIfDebugEnabled(LOGGER, "[{}]Stream server request receive, original info: {}",grpcConn.getConnectionId(), payload.toString());try {Object parseBody = GrpcUtils.parse(payload);final Request request = (Request) parseBody;if (request != null) {try {// 注解@12Response response = handleServerRequest(request);if (response != null) {response.setRequestId(request.getRequestId());sendResponse(response);} else {}} catch (Exception e) {}}} catch (Exception e) {}}});
}

接受server push处理,本事件具体回调到NamingPushRequestHandler#requestReply

@Override
public Response requestReply(Request request) {if (request instanceof NotifySubscriberRequest) {NotifySubscriberRequest notifyResponse = (NotifySubscriberRequest) request;serviceInfoHolder.processServiceInfo(notifyResponse.getServiceInfo());return new NotifySubscriberResponse();}return null;
}

所以最终会执行到NamingPushRequestHandler的requestReply方法进行处理

    @Overridepublic Response requestReply(Request request) {if (request instanceof NotifySubscriberRequest) {NotifySubscriberRequest notifyResponse = (NotifySubscriberRequest) request;serviceInfoHolder.processServiceInfo(notifyResponse.getServiceInfo());return new NotifySubscriberResponse();}return null;}
  public ServiceInfo processServiceInfo(ServiceInfo serviceInfo) {String serviceKey = serviceInfo.getKey();if (serviceKey == null) {return null;}ServiceInfo oldService = serviceInfoMap.get(serviceInfo.getKey());if (isEmptyOrErrorPush(serviceInfo)) {//empty or error push, just ignorereturn oldService;}// 缓存服务信息serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);// 判断注册的实例信息是否已变更boolean changed = isChangedServiceInfo(oldService, serviceInfo);if (StringUtils.isBlank(serviceInfo.getJsonFromServer())) {serviceInfo.setJsonFromServer(JacksonUtils.toJson(serviceInfo));}// 通过prometheus-simpleclient监控服务缓存Map的大小MetricsMonitor.getServiceInfoMapSizeMonitor().set(serviceInfoMap.size());// 服务实例已变更if (changed) {NAMING_LOGGER.info("current ips:(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getKey() + " -> "+ JacksonUtils.toJson(serviceInfo.getHosts()));// 添加实例变更事件,会被推动到订阅者执行NotifyCenter.publishEvent(new InstancesChangeEvent(serviceInfo.getName(), serviceInfo.getGroupName(),serviceInfo.getClusters(), serviceInfo.getHosts()));// 记录Service本地文件DiskCache.write(serviceInfo, cacheDir);}return serviceInfo;
}

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

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

相关文章

c语言调用free,提示已触发了一个断点。

在用c语言写数据结构的链表的时候&#xff0c;执行也没有什么大错&#xff0c;逻辑也是对的&#xff0c;但是一道free函数会自动触发一个断点。如图&#xff1a; 这个断点产生的原因是由于分配的内存太小了在使用的时候没有任何问题&#xff0c;但是在执行程序的时候&#xff0…

pytest系列——allure之在测试用例添加标题(@allure.title())

前言 通过使用装饰器allure.title可以为测试用例自定义一个更具有阅读性的易读的标题。 allure.title的三种使用方式&#xff1a; 直接使用allure.title为测试用例自定义标题&#xff1b;allure.title支持通过占位符的方式传递参数&#xff0c;可以实现测试用例标题参数化&a…

还搞不懂什么是参数,超参数吗?三分钟快速了解参数与超参数的概念和区别!!!

文章目录 前言一、参数是什么&#xff1f;二、超参数是什么三&#xff0c;常使用的超参数有哪些 前言 参数是模型中可被学习和调整的参数&#xff0c;通过训练数据进行学习和优化&#xff1b; 而超参数则是手动设置的参数&#xff0c;用于控制模型的行为和性能&#xff0c;超…

Java基础之数组拷贝

Arrays.copyOf 详解 copyOf是Arrays类下面的一个方法,用于拷贝各种数组 以整型数组为例&#xff1a;int [ ] copyOf(int [ ]array,int newLength);第一个参数是想要拷贝到数组&#xff0c;第二个参数是新拷贝得到的数组的大小&#xff08;不一定非得和原始数组大小一样&…

使用 OpenCV 识别和裁剪黑白图像上的白色矩形--含源码

为了仅获取具有特定边框颜色的矩形,我寻求一种替代识别图像中的轮廓和所有矩形的传统方法。如示例图片所示,我有兴趣使用 opencv 仅获取白色边框矩形的坐标。任何这方面的建议将不胜感激。到目前为止,我的代码已产生如下所示的输出。我的下一个目标是将图像裁剪到大的中心框…

深入理解:Class.getResource与ClassLoader.getResource使用区别

深入理解&#xff1a;Class.getResource与ClassLoader.getResource使用区别 一作用&#xff1a;都是使用类的类加载器来读取某个文件&#xff0c;从而获取该文件的URL对象二Class.getResource()方法读取文件&#xff1a;1.若文件路径以“/”开头&#xff0c;则该方法会从classp…

Python面向对象⑤:多态【侯小啾python领航班系列(二十三)】

Python面向对象⑤:多态【侯小啾python领航班系列(二十三)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹…

非标设计之气缸概述

气缸的组成&#xff1a; 气缸的分类 单作用气缸&#xff1a; 活塞仅一侧供气&#xff0c;气压推动活塞产生推力伸出&#xff0c;靠弹簧或自重返回。 双作用气缸&#xff1a; 气缸活塞两侧都有气压力&#xff0c;来实现前进或后退动作。 气缸的缓冲 但是&#xff0c;气缸也…

PageHelper——分页插件

PageHelper是一款优秀的分页插件&#xff0c;它能够方便地在Spring Boot应用中实现分页查询功能。在大多数应用中&#xff0c;数据量通常很大&#xff0c;如果一次性加载全部数据&#xff0c;不仅会占用大量的内存&#xff0c;还会导致查询速度变慢。而通过使用PageHelper&…

Grad-CAM(Gradient-weighted Class Activation Mapping)热力图(内含示例代码)

Grad-CAM&#xff08;Gradient-weighted Class Activation Mapping&#xff09;是一种用于可视化卷积神经网络&#xff08;CNN&#xff09;中特定类别的激活区域的技术。Grad-CAM帮助我们理解神经网络在分类任务中的决策过程&#xff0c;特别是它关注哪些图像区域以及这些区域对…

【蓝桥杯】翻硬币

翻硬币 思路&#xff1a; 其实有点贪心的意思&#xff0c;依次比较&#xff0c;不同就1&#xff0c;然后修改自己的字符串和下一个的字符串&#xff0c;再匹配。 #include<iostream> #include<string> using namespace std;string now,res;int main(void) {cin&g…

【Element-ui】Link 文字链接 与 Radio 单选框

文章目录 前言一、Link 文字链接1.1 基础用法1.2 禁用状态1.3 下划线1.4 图标 二、Radio 单选框2.1 基础用法2.2 禁用状态2.3 单选框组2.4 按钮样式2.5 带有边框2.6 Radio Eventsinput事件 2.7 Radio-group Attributes 总结 前言 在前端开发中&#xff0c;用户界面的元素设计和…

Python继承的设计及演化

Python中的继承 文章目录 Python中的继承概念明确MRO深度优先算法&#xff08;Python2.2之前及Python2.2的经典类中使用&#xff09;优化版的深度优先算法&#xff08;只在Python2.2版本的新式类中使用&#xff09;广度优先算法&#xff08;Python任何版本都从未使用过&#xf…

C语言--每日选择题--Day34

第一题 1. i &#xff08;&#xff09; int i 1; int j i; if((i > j) && (i j)) {i j; } A&#xff1a;3 B&#xff1a;5 C&#xff1a;6 D&#xff1a;7 答案及解析 B 本题考查你对后置的认识&#xff0c;后置返回的是之前的值&#xff0c;之后再&#xf…

【Python】tensorflow学习的个人纪录(3)

sess tf.Session()actor Actor(sess, n_featuresN_S, lrLR_A, action_bound[-A_BOUND, A_BOUND])步进&#xff1a;

HDMI之数据岛

概述 发送端在发送视频信号之前,将多媒体信息通过数据岛传输给接收端。接收端通过数据岛信息获取当前分辨率(VIC),编码信息(RGB/YCR等),色彩空间,位深等等。然后对应将视频信息解码。与此同时,多余的带宽用于传输音频信息等。本文通过具体的包信息(从实验室仪器拍照…

[动态规划,字符串操作] 分词

第五题&#xff1a;分词 题目描述 给定一个包含n个单词的英文词典和m个只由英文字母组成的字符串。 判断这些字符串能否由词典中的单词组成。 比如词典中包含5个单词&#xff1a;“Jim”, “and”, “cat”,“like”, “dog” 这些单词能组成"Jimlikecatanddog"、“…

[开题报告]基于SpringBoot的抑郁症科普平台的设计与实现

1.研究背景 抑郁症是一种常见的精神障碍&#xff0c;严重影响了患者的生活质量和社会功能。随着社会的快速发展和生活压力的增加&#xff0c;抑郁症的发病率逐渐上升&#xff0c;成为全球范围内的健康问题。然而&#xff0c;对抑郁症的认知和理解仍存在许多误解和偏见&#xf…

[开题报告]基于SpringBoot的自闭症知识宣传平台的设计与实现

1.研究背景 自闭症是一种常见的儿童神经发育障碍&#xff0c;通常在儿童时期即可出现。自闭症患者在社交互动、语言沟通和行为模式方面存在显著的缺陷&#xff0c;对他们及其家庭来说可能造成长期的困扰和挑战。 然而&#xff0c;社会对自闭症的了解和认知仍然相对薄弱。这导…

智能优化算法应用:基于模拟退火算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于模拟退火算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于模拟退火算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.模拟退火算法4.实验参数设定5.算法结果6.参考…