当前位置: 首页 > news >正文

源码解析-Spring Eureka(更新ing)

源码解析-Spring Eureka

文章目录

  • 源码解析-Spring Eureka
  • 前言
  • 一、从Spring.factory和注解开始
  • 二、重要的一步EurekaServerInitializerConfiguration
  • 三、初始化了什么?
    • 自动保护
  • 四, 重新回到EurekaServerAutoConfiguration


前言


一、从Spring.factory和注解开始

我们可以看到,Eureka通过spring boot的自动配置机制引入了一个类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration

通过这个配置我们找到对应的配置类,可以看到,这个配置类使用了Marker作为条件注入

@Configuration(proxyBeanMethods = false
)
@Import({EurekaServerInitializerConfiguration.class})
@ConditionalOnBean({EurekaServerMarkerConfiguration.Marker.class})
@EnableConfigurationProperties({EurekaDashboardProperties.class, InstanceRegistryProperties.class})
@PropertySource({"classpath:/eureka/server.properties"})
public class EurekaServerAutoConfiguration implements WebMvcConfigurer

这个时候我们返回查看我们配置一个eureka所需要的基本注解可以看到,我们正在这个这个@EnableEurekaServer 注解里面初始化了这个类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.cloud.netflix.eureka.server;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EurekaServerMarkerConfiguration.class})
public @interface EnableEurekaServer {
}

通过spring.factory的自动配置以及@EnableEurekaServer 就可以实现eureka服务端的手动注入(通过加入注解)

二、重要的一步EurekaServerInitializerConfiguration

在上面的EurekaServerAutoConfiguration里面我们可以看到它import了一个初始化类
注意在这个初始化类实现了SmartLifeCycle接口,实现了其Start方法

@Configuration(proxyBeanMethods = false
)
public class EurekaServerInitializerConfiguration implements ServletContextAware, SmartLifecycle, Ordered 

实现的start方法,会在bean在启动的时候调用,该方法会new一个线程并发布订阅

    public void start() {(new Thread(() -> {try {this.eurekaServerBootstrap.contextInitialized(this.servletContext);log.info("Started Eureka Server");this.publish(new EurekaRegistryAvailableEvent(this.getEurekaServerConfig()));this.running = true;this.publish(new EurekaServerStartedEvent(this.getEurekaServerConfig()));} catch (Exception var2) {log.error("Could not initialize Eureka servlet context", var2);}})).start();}

可以看到,通过这个start方法,eureka初始化了它自己的context上下文并发布了一些事件。

三、初始化了什么?

进入到contextInitialized方法,我们可以看到

 public void contextInitialized(ServletContext context) {try {this.initEurekaEnvironment();this.initEurekaServerContext();context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);} catch (Throwable var3) {log.error("Cannot bootstrap eureka server :", var3);throw new RuntimeException("Cannot bootstrap eureka server :", var3);}}

eureka首先初始化了配置信息,然后进行上下文的初始化

 protected void initEurekaServerContext() throws Exception {JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), 10000);XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), 10000);if (this.isAws(this.applicationInfoManager.getInfo())) {this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig, this.eurekaClientConfig, this.registry, this.applicationInfoManager);this.awsBinder.start();}EurekaServerContextHolder.initialize(this.serverContext);log.info("Initialized server context");int registryCount = this.registry.syncUp();this.registry.openForTraffic(this.applicationInfoManager, registryCount);EurekaMonitors.registerAllStats();}

进入到initEurekaServerContext方法,我们可以看到几个重要的方法
在openForTraffic方法里面

 public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {this.expectedNumberOfClientsSendingRenews = count;this.updateRenewsPerMinThreshold();logger.info("Got {} instances from neighboring DS node", count);logger.info("Renew threshold is: {}", this.numberOfRenewsPerMinThreshold);this.startupTime = System.currentTimeMillis();if (count > 0) {this.peerInstancesTransferEmptyOnStartup = false;}DataCenterInfo.Name selfName = applicationInfoManager.getInfo().getDataCenterInfo().getName();boolean isAws = Name.Amazon == selfName;if (isAws && this.serverConfig.shouldPrimeAwsReplicaConnections()) {logger.info("Priming AWS connections for all replicas..");this.primeAwsReplicas(applicationInfoManager);}logger.info("Changing status to UP");applicationInfoManager.setInstanceStatus(InstanceStatus.UP);super.postInit();}

我们重点关注这里的super.postInit()

  protected void postInit() {this.renewsLastMin.start();if (this.evictionTaskRef.get() != null) {((EvictionTask)this.evictionTaskRef.get()).cancel();}this.evictionTaskRef.set(new EvictionTask());this.evictionTimer.schedule((TimerTask)this.evictionTaskRef.get(), this.serverConfig.getEvictionIntervalTimerInMs(), this.serverConfig.getEvictionIntervalTimerInMs());}

可以看到this.evictionTaskRef.set(new EvictionTask());,这里注册了一个剔除任务

int registrySize = (int)this.getLocalRegistrySize();int registrySizeThreshold = (int)((double)registrySize * this.serverConfig.getRenewalPercentThreshold());int evictionLimit = registrySize - registrySizeThreshold;int toEvict = Math.min(expiredLeases.size(), evictionLimit);

这里的剔除与eureka配置里面的自我保护配置有关

自动保护

在eureka中,如果打开了自我保护配置并设置了剔除阈值,eureka集群就会在计算正常超过阈值的时候执行上面的代码把的节点给剔除

  1. 如果现在有10个节点,7个节点是正常,3个节点是由有问题的,阈值设置了80%,这个时候7个节点中的一个节点出现了问题,但是没有超过阈值(变成了60%),这个时候就会访问到失败的节点
  2. 如果现在有100个节点,3个节点有问题,阈值也是80%,现在的值是(97%)超过了阈值,如果这个时候有节点出现问题则会立即剔除,
  3. 但是不能把自我保护关闭,如果3个节点是因为波动导致的暂时访问不到则会立即被剔除
eureka:server:enable-self-preservation: true
eureka:server:renewal-percent-threshold: 0.85

我们再进到syncUp方法里面

public int syncUp() {int count = 0;for(int i = 0; i < this.serverConfig.getRegistrySyncRetries() && count == 0; ++i) {if (i > 0) {try {Thread.sleep(this.serverConfig.getRegistrySyncRetryWaitMs());} catch (InterruptedException var10) {logger.warn("Interrupted during registry transfer..");break;}}Applications apps = this.eurekaClient.getApplications();Iterator var4 = apps.getRegisteredApplications().iterator();

可以看到当一个eureka服务启动的时候,会作为一个eureka客户端去peer节点拉取配置(这也是eureka为什么不是强一致性的)

四, 重新回到EurekaServerAutoConfiguration

首先就是eureka的控制器类,eureka的dashboard上面的数据通过这个控制器(springWeb)来获取

@Bean@ConditionalOnProperty(prefix = "eureka.dashboard",name = {"enabled"},matchIfMissing = true)public EurekaController eurekaController() {return new EurekaController(this.applicationInfoManager);}

接着再eurekaServerContext里面实例化了属于eureka的上下文

    @Bean@ConditionalOnMissingBeanpublic EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs, PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs, registry, peerEurekaNodes, this.applicationInfoManager);}

进入context的初始化方法,可以看在context初始化里面有重要的一环就是设置三级缓存initializedResponseCache

  public void init(PeerEurekaNodes peerEurekaNodes) throws Exception {this.numberOfReplicationsLastMin.start();this.peerEurekaNodes = peerEurekaNodes;this.initializedResponseCache();this.scheduleRenewalThresholdUpdateTask();this.initRemoteRegionRegistry();try {Monitors.registerObject(this);} catch (Throwable var3) {logger.warn("Cannot register the JMX monitor for the InstanceRegistry :", var3);}}

里面有一个定时任务,就是定期刷新缓存

  if (this.shouldUseReadOnlyResponseCache) {this.timer.schedule(this.getCacheUpdateTask(), new Date(System.currentTimeMillis() / responseCacheUpdateIntervalMs * responseCacheUpdateIntervalMs + responseCacheUpdateIntervalMs), responseCacheUpdateIntervalMs);}

继续查看自动配置类,可以看到,eureka通过jersey框架包装了注册服务

 @Beanpublic FilterRegistrationBean<?> jerseyFilterRegistration(Application eurekaJerseyApp) {FilterRegistrationBean<Filter> bean = new FilterRegistrationBean();bean.setFilter(new ServletContainer(eurekaJerseyApp));bean.setOrder(Integer.MAX_VALUE);bean.setUrlPatterns(Collections.singletonList("/eureka/*"));return bean;}

再看jerseyApplication,这里面会对eureka的包路径进行扫描, 并将其中的候选类进行注入,其中非常重要的就是resource目录下的applicationsresoure,该方法会返回一个Application的Bean(SPring的config加Bean)

@Beanpublic Application jerseyApplication(Environment environment, ResourceLoader resourceLoader) {ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false, environment);provider.addIncludeFilter(new AnnotationTypeFilter(Path.class));provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));Set<Class<?>> classes = new HashSet();String[] var5 = EUREKA_PACKAGES;int var6 = var5.length;for(int var7 = 0; var7 < var6; ++var7) {String basePackage = var5[var7];Set<BeanDefinition> beans = provider.findCandidateComponents(basePackage);Iterator var10 = beans.iterator();while(var10.hasNext()) {BeanDefinition bd = (BeanDefinition)var10.next();Class<?> cls = ClassUtils.resolveClassName(bd.getBeanClassName(), resourceLoader.getClassLoader());classes.add(cls);}}Map<String, Object> propsAndFeatures = new HashMap();propsAndFeatures.put("com.sun.jersey.config.property.WebPageContentRegex", "/eureka/(fonts|images|css|js)/.*");DefaultResourceConfig rc = new DefaultResourceConfig(classes);rc.setPropertiesAndFeatures(propsAndFeatures);return rc;}

在applicationsResource里面,通过jersey编写一系列关于**注册中心的“注册”,“取消”,“续约”**等的HTTP方法
我们先来看获取ALLKey的方法(拉取所有配置)

@GETpublic Response getContainers(@PathParam("version") String version, @HeaderParam("Accept") String acceptHeader, @HeaderParam("Accept-Encoding") String acceptEncoding, @HeaderParam("X-Eureka-Accept") String eurekaAccept, @Context UriInfo uriInfo, @Nullable @QueryParam("regions") String regionsStr) {boolean isRemoteRegionRequested = null != regionsStr && !regionsStr.isEmpty();String[] regions = null;if (!isRemoteRegionRequested) {EurekaMonitors.GET_ALL.increment();} else {regions = regionsStr.toLowerCase().split(",");Arrays.sort(regions);EurekaMonitors.GET_ALL_WITH_REMOTE_REGIONS.increment();}if (!this.registry.shouldAllowAccess(isRemoteRegionRequested)) {return Response.status(Status.FORBIDDEN).build();} else {CurrentRequestVersion.set(Version.toEnum(version));Key.KeyType keyType = KeyType.JSON;String returnMediaType = "application/json";if (acceptHeader == null || !acceptHeader.contains("json")) {keyType = KeyType.XML;returnMediaType = "application/xml";}Key cacheKey = new Key(EntityType.Application, "ALL_APPS", keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions);Response response;if (acceptEncoding != null && acceptEncoding.contains("gzip")) {response = Response.ok(this.responseCache.getGZIP(cacheKey)).header("Content-Encoding", "gzip").header("Content-Type", returnMediaType).build();} else {response = Response.ok(this.responseCache.get(cacheKey)).build();}CurrentRequestVersion.remove();return response;}}

其中ResponseCache注入了了eureka自己实现的三级缓存的getValue方法

   @VisibleForTestingValue getValue(Key key, boolean useReadOnlyCache) {Value payload = null;try {if (useReadOnlyCache) {Value currentPayload = (Value)this.readOnlyCacheMap.get(key);if (currentPayload != null) {payload = currentPayload;} else {payload = (Value)this.readWriteCacheMap.get(key);this.readOnlyCacheMap.put(key, payload);}} else {payload = (Value)this.readWriteCacheMap.get(key);}} catch (Throwable var5) {logger.error("Cannot get value for key : {}", key, var5);}return payload;}

我们再来看在eureka中一个服务是如何被维护的
在applicationResoure中,有添加服务的方法

@POST@Consumes({"application/json", "application/xml"})public Response addInstance(InstanceInfo info, @HeaderParam("x-netflix-discovery-replication") String isReplication) {logger.debug("Registering instance {} (replication={})", info.getId(), isReplication);if (this.isBlank(info.getId())) {return Response.status(400).entity("Missing instanceId").build();} else if (this.isBlank(info.getHostName())) {return Response.status(400).entity("Missing hostname").build();} else if (this.isBlank(info.getIPAddr())) {return Response.status(400).entity("Missing ip address").build();} else if (this.isBlank(info.getAppName())) {return Response.status(400).entity("Missing appName").build();} else if (!this.appName.equals(info.getAppName())) {return Response.status(400).entity("Mismatched appName, expecting " + this.appName + " but was " + info.getAppName()).build();} else if (info.getDataCenterInfo() == null) {return Response.status(400).entity("Missing dataCenterInfo").build();} else if (info.getDataCenterInfo().getName() == null) {return Response.status(400).entity("Missing dataCenterInfo Name").build();} else {DataCenterInfo dataCenterInfo = info.getDataCenterInfo();if (dataCenterInfo instanceof UniqueIdentifier) {String dataCenterInfoId = ((UniqueIdentifier)dataCenterInfo).getId();if (this.isBlank(dataCenterInfoId)) {boolean experimental = "true".equalsIgnoreCase(this.serverConfig.getExperimental("registration.validation.dataCenterInfoId"));if (experimental) {String entity = "DataCenterInfo of type " + dataCenterInfo.getClass() + " must contain a valid id";return Response.status(400).entity(entity).build();}if (dataCenterInfo instanceof AmazonInfo) {AmazonInfo amazonInfo = (AmazonInfo)dataCenterInfo;String effectiveId = amazonInfo.get(MetaDataKey.instanceId);if (effectiveId == null) {amazonInfo.getMetadata().put(MetaDataKey.instanceId.getName(), info.getId());}} else {logger.warn("Registering DataCenterInfo of type {} without an appropriate id", dataCenterInfo.getClass());}}}this.registry.register(info, "true".equals(isReplication));return Response.status(204).build();}}

进去到register方法里面,我们可以看到,eureka首先向自己注册了当前服务,然后同步到了peer节点上面

    public void register(InstanceInfo info, boolean isReplication) {int leaseDuration = 90;if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {leaseDuration = info.getLeaseInfo().getDurationInSecs();}super.register(info, leaseDuration, isReplication);this.replicateToPeers(PeerAwareInstanceRegistryImpl.Action.Register, info.getAppName(), info.getId(), info, (InstanceInfo.InstanceStatus)null, isReplication);}

可以看到,register里面,我们的InstanceInfo 被一个Map<String, Lease> gMap维护着

    public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {this.read.lock();try {Map<String, Lease<InstanceInfo>> gMap = (Map)this.registry.get(registrant.getAppName());EurekaMonitors.REGISTER.increment(isReplication);if (gMap == null) {ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap();gMap = (Map)this.registry.putIfAbsent(registrant.getAppName(), gNewMap);if (gMap == null) {gMap = gNewMap;}}

我们看下Lease的结构,通过下面的这种结构,我们只需要修改Lease的long信息就可以对当前节点的生命状态进行修改而不需要修改节点本身

public class Lease<T> {public static final int DEFAULT_DURATION_IN_SECS = 90;private T holder;  // 具体的实例信息// 一些用于维护节点状态的时间信息private long evictionTimestamp;private long registrationTimestamp;private long serviceUpTimestamp;private volatile long lastUpdateTimestamp;private long duration;

相关文章:

源码解析-Spring Eureka(更新ing)

源码解析-Spring Eureka 文章目录 源码解析-Spring Eureka前言一、从Spring.factory和注解开始二、重要的一步EurekaServerInitializerConfiguration三、初始化了什么&#xff1f;自动保护 四, 重新回到EurekaServerAutoConfiguration 前言 无 一、从Spring.factory和注解开始…...

python调用百度通用翻译API

文章目录 1. 简介2. 使用步骤3. api调用实现4. 编码实现 1. 简介 前段时间在做视频语音识别生成多语种字幕时&#xff0c;使用了百度翻译通用翻译api进行翻译。百度翻译平台经过个人认证之后&#xff0c;每月有200万字符的免费翻译额度。还是比较舒服的。 百度翻译开放平台是百…...

Timeline动画「硬切」的问题

1&#xff09;Timeline动画「硬切」的问题 2&#xff09;移动平台纹理压缩格式选择ASTC&#xff0c;美术出图还需遵守POT吗 3&#xff09;如何去掉DOTS Unity.Entities.Graphics创建的BatchRendererGroup的UI相机回调 4&#xff09;Timeline播放动画会产生位移的问题 这是第409…...

CentOS 9 配置网卡

在 CentOS 9 中配置网卡&#xff0c;通常涉及以下几个步骤&#xff1a; 1. 查看网络接口 首先&#xff0c;确认系统上存在的网络接口。可以使用 ip 命令或 ifconfig 命令查看网络接口的状态。 ip a 或者&#xff1a; ifconfig 这将列出所有可用的网络接口&#xff08;例如…...

redis7.x源码分析:(2) adlist双向链表

链表是一种常用的数据结构&#xff08;如果不了解&#xff0c;请先学习数据结构&#xff09;&#xff0c;由于c语言本身没有实现标准的链表库&#xff0c;所以redis自己实现了一个双向链表。 双向链表在redis内部的使用非常的多&#xff0c;几乎所有模块中都有用到。 下面看下它…...

KUKU FM 音频Linux平台免费下载工具

1.工具名称&#xff1a;kuku-dl 功能&#xff1a; ✅ 下载播客、故事和有声读物&#xff01; ✅ 获取所有元数据和封面艺术品。 ✅ 支持字幕&#xff01; 3.使用说明&#xff1a; 3.1. 直接镜像github源码库 &#x1f449; git clone https://github.com/bunnykek/kuku-…...

《Django 5 By Example》阅读笔记:p105-p164

《Django 5 By Example》学习第5天&#xff0c;p105-p164总结&#xff0c;总计60页。 一、技术总结 1.文章标签功能 Django自带django-taggit。 2.自定义template tags 3.roadmap功能 4.RSS功能 5.full-text搜索功能 这里使用的是Postgresql,使用pip install psycopg安…...

网络延迟对Python爬虫速度的影响分析

Python爬虫因其强大的数据处理能力和灵活性而被广泛应用于数据抓取和网络信息收集。然而&#xff0c;网络延迟是影响爬虫效率的重要因素之一。本文将深入探讨网络延迟对Python爬虫速度的影响&#xff0c;并提供相应的代码实现过程&#xff0c;以帮助开发者优化爬虫性能。 网络…...

微信小程序内嵌h5页面(uniapp写的),使用uni.openLocation无法打开页面问题

1.问题 微信小程序内嵌h5页面(uniapp写的),使用uni.openLocation打开地图页面后,点击该页面下方“到这里”按钮,显示无法打开。如下图: 3.解决方案 在内嵌h5中不使用uniapp的api打开地图,而在h5页面事件处理程序中去跳转新的小程序页面,在该新页面去使用微信小程序…...

创建一个简单的基于STM32的FreeRTOS应用

使用STM32CubeIDE生成。 1&#xff0c;使能FreeRTOS 2&#xff0c;选择版本 CMSIS_V1 3 设置参数USE_NEWLIB_REENTRANT 如果不设置&#xff0c;会在生成代码的时候提示错误 4&#xff0c;设置时钟TIM1作为系统时钟 ​​​​​​​​​​​​​​ 5&#xff0c;设置Task …...

【Revit二次开发】创建Ribbon选项卡与带图标的按钮

效果图 创建一个叫做“开发的插件”的选项卡&#xff0c; 选项卡内有一个叫做“Hello”的图标按钮&#xff0c; 点击按钮后运行一个命令&#xff0c; 该命令弹出提示框“Hello Revit!”。 在此示例基础上&#xff0c;可以根据需要替换图标、文字、命令功能。 步骤 安装Revit…...

Win11 终端执行 python xxx.py 没反应

在 Win11 上写了一段 Python 代码来分析日志文件&#xff0c; 发现执行没反应。是在 VSCode 里的终端中执行的 python log_stats.py, 是 PowerShell&#xff1b; 也尝试了 cmd&#xff0c; 情况一样。 一开始怀疑代码写错&#xff0c;直到故意在代码里加打印&#xff0c;发现没…...

使用视频提升应用在 App Store 中的推广效果

App Store 上有485 万个应用和游戏。每个应用开发者都知道&#xff0c;要在如此庞大的市场中脱颖而出&#xff0c;吸引宝贵的用户眼球&#xff0c;是多么困难。 您需要在应用推广游戏中尝试一些不同的东西&#xff0c;那就是视频预览。这些短小的电影奇迹已经成为应用营销人员…...

对话 OpenCV 之父 Gary Bradski:灾难性遗忘和持续学习是尚未解决的两大挑战 | Open AGI Forum

作者 | Annie Xu 采访、责编 | Eric Wang 出品丨GOSIM 开源创新汇 Gary Bradski&#xff0c;旺盛的好奇心、敢于冒险的勇气、独到的商业视角让他成为计算视觉、自动驾驶领域举重若轻的奠基者。 Gary 曾加入 Stanley 的团队&#xff0c;帮助其赢得 2005 年美国穿越沙漠 DA…...

通过地址获取LONG和LAT并且存入csv

通过地址获取LONG和LAT并且存入csv 1. Address存在Address这个column里&#xff0c;从网上复制&#xff08;如果可以爬虫自动更好&#xff09; 2. 用代码获取GPS&#xff0c;再存入表格 import pandas as pd from geopy.geocoders import Nominatim from time import sleep#…...

Nginx SSL+tomcat,使用request.getScheme() 取到https协议

架构上使用了 Nginx tomcat 集群, 且nginx下配置了SSL,tomcat no SSL,项目使用https和http协议。 发现 request.getScheme() //总是 http&#xff0c;而不是实际的http或https request.isSecure() //总是false&#xff08;因为总是http&#xff09; request.getRemoteAddr(…...

Node.Js+Knex+MySQL增删改查的简单示例(Typescript)

数据库: CREATE DATABASE MyDB; CREATE TABLE t_users (user_id int(11) NOT NULL,user_name varchar(10) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8; 项目结构: package.json如下&#xff0c;拷贝并替换你们本地的package.json后运行 npm install 命令安装所需要的依赖。…...

机器学习的概览

笔记内容侵权联系删除 机器学习算法 机器学习(包括深度学习分支)是研究“学习算法”的一门学问。所谓“学习”是指:对于某类任务T和性能度量P&#xff0c;一个计算机程序在T上以P衡量的性能随着经验E而自我完善&#xff0c;那么我们称这个计算机程序在从经验E学习。 机器学习…...

方法论-WPS模型(高效沟通和决策分析的框架)

WPS模型&#xff08;What, Problem, Solution&#xff09;是一种高效沟通和决策分析的框架&#xff0c;旨在帮助沟通者清晰、简洁地表达问题和解决方案&#xff0c;特别适用于在复杂或多变的环境中进行清晰的交流。WPS模型的核心是通过以下三个步骤来组织沟通内容&#xff1a; …...

OpenTelemetry 赋能DevOps流程的可观测性革命

作者&#xff1a;天颇 引言 在当今快节奏的软件开发和运维环境中&#xff0c;DevOps 已经成为主流&#xff0c;它通过整合开发和运维流程&#xff0c;推动着软件的快速迭代和持续交付。然而&#xff0c;随着微服务、容器化和云计算等技术的普及&#xff0c;系统复杂性急剧增加…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...