nacos源码 nacos注册中心1.4.x 源码 spring cloud alibaba 的discovery做了什么 nacos客户端是如何启动的(二)

spring-cloud-alibaba-nacos-discovery 老版本中如何调用nacos的

 1. 整体结构

         

2. 思考: 如果你来做,如何做client 向server注册服务:

           1.2.1 读yml,或本地文件找到服务器地址,以及其他配置

           1.2.2 向server注册服务

           1.2.3 定期发送心跳

3. 源码跟踪

     3.1查看spring 自动装配 spring.factories:

     3.2 截图:

     3.3 查看NacosDiscoveryAutoConfiguration类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.alibaba.cloud.nacos;import com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration;
import com.alibaba.cloud.nacos.registry.NacosRegistration;
import com.alibaba.cloud.nacos.registry.NacosServiceRegistry;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnClass(name = {"org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent"}
)
@ConditionalOnProperty(value = {"spring.cloud.service-registry.auto-registration.enabled"},matchIfMissing = true
)
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class, AutoServiceRegistrationAutoConfiguration.class})
public class NacosDiscoveryAutoConfiguration {public NacosDiscoveryAutoConfiguration() {}@Beanpublic NacosServiceRegistry nacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {return new NacosServiceRegistry(nacosDiscoveryProperties);}@Bean@ConditionalOnBean({AutoServiceRegistrationProperties.class})public NacosRegistration nacosRegistration(NacosDiscoveryProperties nacosDiscoveryProperties, ApplicationContext context) {return new NacosRegistration(nacosDiscoveryProperties, context);}@Bean@ConditionalOnBean({AutoServiceRegistrationProperties.class})public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);}
}

    3.3.1 注: 可看出重点是三个@Bean 注解的方法,三个对象放入spring 进入管理,我们需要依次查看三个对象,这里就不一一查看了,重点是NacosAutoServiceRegistration.class 。或者说入口是在NacosAutoServiceRegistration, 然后再看另外两个对象

    3.4.  NacosAutoServiceRegistration:

       3.4.1 源码:

public class NacosAutoServiceRegistration extends AbstractAutoServiceRegistration<Registration> {private static final Logger log = LoggerFactory.getLogger(NacosAutoServiceRegistration.class);private NacosRegistration registration;public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {super(serviceRegistry, autoServiceRegistrationProperties);this.registration = registration;}/** @deprecated */@Deprecatedpublic void setPort(int port) {this.getPort().set(port);}protected NacosRegistration getRegistration() {if (this.registration.getPort() < 0 && this.getPort().get() > 0) {this.registration.setPort(this.getPort().get());}Assert.isTrue(this.registration.getPort() > 0, "service.port has not been set");return this.registration;}protected NacosRegistration getManagementRegistration() {return null;}protected void register() {if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {log.debug("Registration disabled.");} else {if (this.registration.getPort() < 0) {this.registration.setPort(this.getPort().get());}super.register();}}protected void registerManagement() {if (this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {super.registerManagement();}}protected int getConfiguredPort() {return this.getPort().get();}protected void setConfiguredPort(int port) {this.getPort().set(port);}protected Object getConfiguration() {return this.registration.getNacosDiscoveryProperties();}protected boolean isEnabled() {return this.registration.getNacosDiscoveryProperties().isRegisterEnabled();}protected String getAppName() {String appName = this.registration.getNacosDiscoveryProperties().getService();return StringUtils.isEmpty(appName) ? super.getAppName() : appName;}
}

      3.4.2 没有发现如何启动,继续查看父类:

      3.4.2.1 源码:

package org.springframework.cloud.client.serviceregistry;import org.springframework.cloud.client.discovery.AbstractDiscoveryLifecycle;public abstract class AbstractAutoServiceRegistration<R extends Registration> extends AbstractDiscoveryLifecycle implements AutoServiceRegistration {private final ServiceRegistry<R> serviceRegistry;private AutoServiceRegistrationProperties properties;/** @deprecated */@Deprecatedprotected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry) {this.serviceRegistry = serviceRegistry;}protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry, AutoServiceRegistrationProperties properties) {this.serviceRegistry = serviceRegistry;this.properties = properties;}protected ServiceRegistry<R> getServiceRegistry() {return this.serviceRegistry;}protected abstract R getRegistration();protected abstract R getManagementRegistration();protected void register() {this.serviceRegistry.register(this.getRegistration());}protected void registerManagement() {R registration = this.getManagementRegistration();if (registration != null) {this.serviceRegistry.register(registration);}}protected void deregister() {this.serviceRegistry.deregister(this.getRegistration());}protected void deregisterManagement() {R registration = this.getManagementRegistration();if (registration != null) {this.serviceRegistry.deregister(registration);}}public void stop() {if (this.getRunning().compareAndSet(true, false) && this.isEnabled()) {this.deregister();if (this.shouldRegisterManagement()) {this.deregisterManagement();}this.serviceRegistry.close();}}protected boolean shouldRegisterManagement() {return this.properties != null && !this.properties.isRegisterManagement() ? false : super.shouldRegisterManagement();}
}

      3.4.2.2 注:

                 注意此时包名换了。现在是spring框架源码,并且继承了AbstractDiscoveryLifecycle

      3.5 查看 AbstractDiscoveryLifecycle

        3.5.1 AbstractDiscoveryLifecycle源码:

@Deprecated
public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle, ApplicationContextAware, ApplicationListener<EmbeddedServletContainerInitializedEvent> {private static final Log logger = LogFactory.getLog(AbstractDiscoveryLifecycle.class);private boolean autoStartup = true;private AtomicBoolean running = new AtomicBoolean(false);private int order = 0;private ApplicationContext context;private Environment environment;private AtomicInteger port = new AtomicInteger(0);public AbstractDiscoveryLifecycle() {}protected ApplicationContext getContext() {return this.context;}public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.context = applicationContext;this.environment = this.context.getEnvironment();}/** @deprecated */@Deprecatedprotected Environment getEnvironment() {return this.environment;}/** @deprecated */@Deprecatedprotected AtomicInteger getPort() {return this.port;}public boolean isAutoStartup() {return this.autoStartup;}public void stop(Runnable callback) {try {this.stop();} catch (Exception var3) {logger.error("A problem occurred attempting to stop discovery lifecycle", var3);}callback.run();}public void start() {if (!this.isEnabled()) {if (logger.isDebugEnabled()) {logger.debug("Discovery Lifecycle disabled. Not starting");}} else {if (this.port.get() != 0 && this.getConfiguredPort() == 0) {this.setConfiguredPort(this.port.get());}if (!this.running.get() && this.getConfiguredPort() > 0) {this.register();if (this.shouldRegisterManagement()) {this.registerManagement();}this.context.publishEvent(new InstanceRegisteredEvent(this, this.getConfiguration()));this.running.compareAndSet(false, true);}}}/** @deprecated */@Deprecatedprotected abstract int getConfiguredPort();/** @deprecated */@Deprecatedprotected abstract void setConfiguredPort(int var1);protected boolean shouldRegisterManagement() {return this.getManagementPort() != null && ManagementServerPortUtils.isDifferent(this.context);}/** @deprecated */@Deprecatedprotected abstract Object getConfiguration();protected abstract void register();protected void registerManagement() {}protected abstract void deregister();protected void deregisterManagement() {}protected abstract boolean isEnabled();/** @deprecated */@Deprecatedprotected String getManagementServiceId() {return this.context.getId() + ":management";}/** @deprecated */@Deprecatedprotected String getManagementServiceName() {return this.getAppName() + ":management";}/** @deprecated */@Deprecatedprotected Integer getManagementPort() {return ManagementServerPortUtils.getPort(this.context);}/** @deprecated */@Deprecatedprotected String getAppName() {return this.environment.getProperty("spring.application.name", "application");}public void stop() {if (this.running.compareAndSet(true, false) && this.isEnabled()) {this.deregister();if (this.shouldRegisterManagement()) {this.deregisterManagement();}}}@PreDestroypublic void destroy() {this.stop();}public boolean isRunning() {return this.running.get();}protected AtomicBoolean getRunning() {return this.running;}public int getOrder() {return this.order;}public int getPhase() {return 0;}// ***************************************************************//                                重点// ***************************************************************     /** @deprecated */@Deprecatedpublic void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {if (!"management".equals(event.getApplicationContext().getNamespace())) {this.port.compareAndSet(0, event.getEmbeddedServletContainer().getPort());this.start();}}
}

      3.5.2 注:

                  AbstractDiscoveryLifecycle抽象类继承了ApplicationListener该类需要实现方法onApplicationEvent,且此方法就在AbstractDiscoveryLifecycle。  该方法调用了this.start()

     3.6 查看this.start()

         3.6.1 源码:

public void start() {if (!this.isEnabled()) {if (logger.isDebugEnabled()) {logger.debug("Discovery Lifecycle disabled. Not starting");}} else {if (this.port.get() != 0 && this.getConfiguredPort() == 0) {this.setConfiguredPort(this.port.get());}if (!this.running.get() && this.getConfiguredPort() > 0) {this.register();if (this.shouldRegisterManagement()) {this.registerManagement();}// 这个地方发布了一个服务注册事件,订阅不知道在哪 ==!this.context.publishEvent(new InstanceRegisteredEvent(this, this.getConfiguration()));this.running.compareAndSet(false, true);}}}

       3.6.2 注: start()方法内,调用了this.register()  并且此方法仍旧在AbstractDiscoveryLifecycle类中,且在spring 框架内,说明spring 留了一个口子(spi)来帮助实现服务注册功能,自己想做服务注册,只要继承AbstractDiscoveryLifecycle 重写register方法即可!

3.7 点击this.register() : 发现仍在AbstractDiscoveryLifecycle,且是个抽象类!并没有具体实现!

pr4otected abstract void register();

      3.8  idea 点击实现类: 找到NacosAutoServiceREgistration 实现类

      3.9 实现方法:

    protected void register() {if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {log.debug("Registration disabled.");} else {if (this.registration.getPort() < 0) {this.registration.setPort(this.getPort().get());}super.register();}}

        4.0 继续跟踪super.register()

public abstract class AbstractAutoServiceRegistration<R extends Registration> extends AbstractDiscoveryLifecycle implements AutoServiceRegistration {// *****省略protected void register() {this.serviceRegistry.register(this.getRegistration());}}

         4.1   this.serviceRegistry.register(this.getRegistration());

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.cloud.client.serviceregistry;public interface ServiceRegistry<R extends Registration> {void register(R var1);void deregister(R var1);void close();void setStatus(R var1, String var2);<T> T getStatus(R var1);
}

          4.2 点击register 实现,则 发现为NacosServiceRegistry 实现类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.alibaba.cloud.nacos.registry;import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.util.StringUtils;public class NacosServiceRegistry implements ServiceRegistry<Registration> {private static final Logger log = LoggerFactory.getLogger(NacosServiceRegistry.class);private final NacosDiscoveryProperties nacosDiscoveryProperties;private final NamingService namingService;public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {this.nacosDiscoveryProperties = nacosDiscoveryProperties;this.namingService = nacosDiscoveryProperties.namingServiceInstance();}// NacosServiceRegistry 实现spring 框架类ServiceRegistry的register方法 public void register(Registration registration) {if (StringUtils.isEmpty(registration.getServiceId())) {log.warn("No service to register for nacos client...");} else {String serviceId = registration.getServiceId();Instance instance = this.getNacosInstanceFromRegistration(registration);try {this.namingService.registerInstance(serviceId, instance);log.info("nacos registry, {} {}:{} register finished", new Object[]{serviceId, instance.getIp(), instance.getPort()});} catch (Exception var5) {log.error("nacos registry, {} register failed...{},", new Object[]{serviceId, registration.toString(), var5});}}}public void deregister(Registration registration) {log.info("De-registering from Nacos Server now...");if (StringUtils.isEmpty(registration.getServiceId())) {log.warn("No dom to de-register for nacos client...");} else {NamingService namingService = this.nacosDiscoveryProperties.namingServiceInstance();String serviceId = registration.getServiceId();try {namingService.deregisterInstance(serviceId, registration.getHost(), registration.getPort(), this.nacosDiscoveryProperties.getClusterName());} catch (Exception var5) {log.error("ERR_NACOS_DEREGISTER, de-register failed...{},", registration.toString(), var5);}log.info("De-registration finished.");}}public void close() {}public void setStatus(Registration registration, String status) {if (!status.equalsIgnoreCase("UP") && !status.equalsIgnoreCase("DOWN")) {log.warn("can't support status {},please choose UP or DOWN", status);} else {String serviceId = registration.getServiceId();Instance instance = this.getNacosInstanceFromRegistration(registration);if (status.equalsIgnoreCase("DOWN")) {instance.setEnabled(false);} else {instance.setEnabled(true);}try {this.nacosDiscoveryProperties.namingMaintainServiceInstance().updateInstance(serviceId, instance);} catch (Exception var6) {throw new RuntimeException("update nacos instance status fail", var6);}}}public Object getStatus(Registration registration) {String serviceName = registration.getServiceId();try {List<Instance> instances = this.nacosDiscoveryProperties.namingServiceInstance().getAllInstances(serviceName);Iterator var4 = instances.iterator();while(var4.hasNext()) {Instance instance = (Instance)var4.next();if (instance.getIp().equalsIgnoreCase(this.nacosDiscoveryProperties.getIp()) && instance.getPort() == this.nacosDiscoveryProperties.getPort()) {return instance.isEnabled() ? "UP" : "DOWN";}}} catch (Exception var6) {log.error("get all instance of {} error,", serviceName, var6);}return null;}private Instance getNacosInstanceFromRegistration(Registration registration) {Instance instance = new Instance();instance.setIp(registration.getHost());instance.setPort(registration.getPort());instance.setWeight((double)this.nacosDiscoveryProperties.getWeight());instance.setClusterName(this.nacosDiscoveryProperties.getClusterName());instance.setMetadata(registration.getMetadata());return instance;}
}

         4.2.1 调用链路实现闭环

                   1. spring boot 自动装配,META-INF/spring.factories

          2. 第一个就是NacosDiscoveryAutoConfiguration 且该类创建三个Bean对象(NacosServiceRegistry, NacosRegistration,NacosAutoServiceRegistration)3. 核心NacosAutoServiceRegistration类继承AbstractAutoServiceRegistration

                   4. AbstractAutoServiceRegistration为spring 框架内容,且继承AbstractDiscoveryLifecycle

5. AbstractDiscoveryLifecycle实现了ApplicationListener的onApplicationEvent方法

6. onApplicationEvent内调用了this.start();

7. this.start 调用了this.register

8. NacosAutoServiceRegistration实现了this.register 并调用方法super.register()

9. 调用父类register(),此时为spring 框架方法, 且调用了this.serviceRegistry.register(this.getRegistration());

10.register为接口spring未提供实现类,由nacos的NacosServiceRegistry

类实现register方法

11. NacosServiceRegistry类register方法内调用了this.namingService.registerInstance(serviceId, instance);

                  12. 最终到达ncos源码中client 模块的注册服务方法

                  13. 为NacosNamingServiceregis.registerInstance(serviceId, instance);

5. 总结:

5.1 本文主要讲了spring cloud alibaba 的starter模块下的 discovery模块,讲了客户端依赖了discovery 之后,它是是如何步一步到达nacos源码过程

               5.2 读了源码后,发现spring 已为服务注册留下了spi !配合spring boot自动装配,自己也可以实现一个client, server。继承AbstractAutoServiceRegistration 以及实现register()等

5.3 读万卷书,不如行万里路,建议自己动手实际去看看,收获才是最多的!

注:本人能力有限,还请多多包涵,亦欢迎大佬指正错误!

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

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

相关文章

6800和8080单片机读写时序和液晶屏接口

前言&#xff1a; 随着单片机发展&#xff0c;集成度越来越高&#xff0c;因此目前单片机较少使用RD和WR信号操作外设&#xff0c;因此很多时候&#xff0c;变成了6800和8080单片机读写液晶屏了。早期的读写本质上是对一个地址进行即时的操作&#xff0c;现在可能是等数据送到…

网易游戏员工怒怼丁磊上热搜:每天员工陪你演戏点赞有意思吗

【头部财经】近日&#xff0c;网易游戏一员工在内部群怒怼丁磊的聊天记录曝光&#xff0c;引发网友关注。据头部财经了解&#xff0c;该员工名叫石佳煊&#xff0c;是网易游戏的游戏开发工程师&#xff0c;毕业于华盛顿大学&#xff0c;已在网易工作四年多。 截图显示&#xf…

【国产开源可视化引擎Meta2d.js】铅笔

铅笔 铅笔是可以任意涂鸦的绘图小工具 在线体验&#xff1a; 乐吾乐2D可视化 示例&#xff1a; // 开始铅笔绘画 meta2d.drawingPencil();// 鼠标抬起结束// 停止铅笔绘画&#xff08;关闭铅笔绘画状态&#xff09; meta2d.stopPencil(); 国产开源 乐吾乐潜心研发&#xff…

基于星火大模型的群聊对话分角色要素提取挑战赛

赛事任务与数据 2024 iFLYTEK A.I.开发者大赛-讯飞开放平台 (xfyun.cn) 从给定的<客服>与<客户>的群聊对话中, 提取出指定的字段信息&#xff0c;待提取的全部字段见下数据说明。 赛题方提供了184条真实场景的群聊对话数据以及人工标注后的字段提取结果&#xf…

AIGC专栏12——EasyAnimateV3发布详解 支持图文生视频 最大支持960x960x144帧视频生成

AIGC专栏12——EasyAnimateV3发布详解 支持图&文生视频 最大支持960x960x144帧视频生成 学习前言项目特点生成效果相关地址汇总项目主页Huggingface体验地址Modelscope体验地址源码下载地址 EasyAnimate V3详解技术储备Diffusion Transformer (DiT)Hybrid Motion ModuleU-V…

XXL-JOB中断信号感知

目录 背景 思路 实现逻辑 总结 背景 在使用xxl-job框架时&#xff0c;由于系统是由线程池去做异步逻辑&#xff0c;然后主线程等待&#xff0c;在控制台手动停止时&#xff0c;会出现异步线程不感知信号中断的场景&#xff0c;如下场景 而此时如果人工在控制台停止xxl-job执…

笔记13:switch多分支选择语句

引例&#xff1a; 输入1-5中的任意一共数字&#xff0c;对应的打印字符A,B,C,D,E int num 0; printf("Input a number[1,5]:"); scanf("%d"&#xff0c;&num); if( num 1)printf("A\n"); else if(num2)printf("B\n"); else i…

Alibaba Cloud Toolkit前端使用proxy代理配置

1、vscode 先安装插件 Alibaba Cloud Toolkit 2、前端代码&#xff1a; /personnel: {// target: http://xxx.xx.xxx.xx:9100, // 测试环境// target: http://xxx.xx.xxx.xx:9200, // 线上环境target: http://127.0.0.1:18002, // toolkit 代理changeOrigin: true,},3、打开插…

Android LayoutInflater 深度解析

在 Android 开发中&#xff0c;LayoutInflater 是一个非常重要的工具。它允许我们从 XML 布局文件中动态地创建 View 对象&#xff0c;从而使得 UI 的创建和管理更加灵活。本文将深入解析 android.view.LayoutInflater&#xff0c;包括它的基本用法、常见问题以及高级用法。 什…

MySQL架构和工作流程

引言&#xff1a;MySQL执行一条sql语句期间发生了什么&#xff1f; 想要搞清楚这个问题&#xff0c;我们必须了解MySQL的体系结构和工作流程 一、MySQL体系结构 MySQL由以下几个部分组成 一、server层 1.MySQL Connnectors连接器&#xff0c;MySQL的连接池组件&#xff0c;…

数据结构+算法-实现一个计算器

在学习栈的数据结构的时候讲到可以用栈来实现一个计算器的功能&#xff0c;那么这个功能是如何实现的呢&#xff1f; 采用栈模拟得方式来实现一个计算器 要实现如下的功能: 字符串如何转为整数 2.处理加减法 如何处理加减法呢&#xff1f; 5-128 给第一个数字前面放一个号…

UEC++ 虚幻5第三人称射击游戏(二)

UEC++ 虚幻5第三人称射击游戏(二) 派生榴弹类武器 新建一个继承自Weapon的子类作为派生榴弹类武器 将Weapon类中的Fire函数添加virtual关键字变为虚函数让榴弹类继承重写 在ProjectileWeapon中重写Fire函数,新建生成投射物的模版变量 Fire函数重写逻辑 代码//生成的投射物U…

从文本到安全图像:自动提示优化防止不当内容生成

T2I生成技术已经得到了广泛关注&#xff0c;并见证了如GLIDE、Imagen、DALL-E 2、Stable Diffusion等大型生成模型的发展。尽管这些模型能够根据文本描述生成高质量的图像&#xff0c;促进了书籍插图、品牌标识设计、游戏场景创作等多种实际应用&#xff0c;但它们也被恶意用户…

使用京东云主机搭建幻兽帕鲁游戏联机服务器全流程,0基础教程

使用京东云服务器搭建幻兽帕鲁Palworld游戏联机服务器教程&#xff0c;非常简单&#xff0c;京东云推出幻兽帕鲁镜像系统&#xff0c;镜像直接选择幻兽帕鲁镜像即可一键自动部署&#xff0c;不需要手动操作&#xff0c;真正的新手0基础部署幻兽帕鲁&#xff0c;阿腾云整理基于京…

Python学习笔记30:进阶篇(十九)pygame的使用之显示与窗口管理

前言 基础模块的知识通过这么长时间的学习已经有所了解&#xff0c;更加深入的话需要通过完成各种项目&#xff0c;在这个过程中逐渐学习&#xff0c;成长。 我们的下一步目标是完成python crash course中的外星人入侵项目&#xff0c;这是一个2D游戏项目。在这之前&#xff…

YOLOv8改进 | 注意力机制 | 结合静态和动态上下文信息的注意力机制

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…

力扣双指针算法题目:双数之和,三数之和,四数之和

目录 一&#xff1a;双数之和 1.题目&#xff1a; 2.思路解析 3.代码 二&#xff1a;三数之和 1.题目 2.思路解析 3&#xff0c;代码 三&#xff1a;四数字之和 1.题目 2.思路解析 3.代码 一&#xff1a;双数之和 1.题目&#xff1a; 输入一个递增排序的数组和一…

贵州建筑三类人员安全员2024年考试最新题库练习题

一、单选题 1.建设工程安全管理的方针是&#xff08;&#xff09;。 A.安全第一&#xff0c;预防为主&#xff0c;综合治理 B.质量第一&#xff0c;兼顾安全 C.安全至上 D.安全责任重于泰山 答案&#xff1a;A 2.安全生产管理的根本目的是&#xff08;&#xff09;。 A.…

Lunaproxy与711Proxy的对比与优劣分析

今天我们来深入对比两款在市场上备受关注的代理IP服务&#xff1a;Lunaproxy和711Proxy。接下来&#xff0c;我们将从多个角度对这两款服务进行详细分析&#xff0c;帮助大家做出明智的选择。 优势分析 711Proxy的优势 1. 性价比高&#xff1a;711Proxy提供多种灵活的套餐选…

伪元素content追加文字使用小技巧

E::before和E::after本身的作用是追加字&#xff0c;直接在文字后面追加链接 <!DOCTYPE html> <html lang"zh-cn"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-sca…