源码解析-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;

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

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

相关文章

【从零开始的LeetCode-算法】3270. 求出数字答案

给你三个 正 整数 num1 &#xff0c;num2 和 num3 。 数字 num1 &#xff0c;num2 和 num3 的数字答案 key 是一个四位数&#xff0c;定义如下&#xff1a; 一开始&#xff0c;如果有数字 少于 四位数&#xff0c;给它补 前导 0 。答案 key 的第 i 个数位&#xff08;1 < …

STM32+AI语音识别智能家居系统

基于 STM32 和 AI 语音识别的智能家居系统的详细硬件和软件设计&#xff0c;包括各个模块的详细描述和代码示例。 一、硬件设计 1. 微控制器&#xff08;STM32&#xff09;&#xff1a; 选择 STM32F7 系列或更高性能的芯片&#xff0c;如 STM32F767ZIT6&#xff0c;以满足处理…

信息收集—JS框架识别泄露提取API接口泄露FUZZ爬虫插件项目

前言 免杀结束了&#xff0c;我们开个新的篇章——信息收集。为什么我一开始先写信息收集的文章呢&#xff0c;是因为现在我才发现我的信息收集能力其实有点弱的&#xff0c;所以呢开始知不足&#xff0c;而后进。 什么是JS JS就是JavaScript的简称&#xff0c;它和Java是没…

智能化护士排班系统的设计与实现(文末附源码)

自动排班-护士(分白班|夜班) 当服务器启动时检测需要自动排班,自动开始排班的算法执行 获得本周的所有日期,例如2023-01-29.....2023-02-04依次对每个科室&#xff0c;从第一天开始,逐天进行排班&#xff0c;分别设置两个二个数组&#xff0c;day[7];night[7]分别记忆一周内每…

【原创】java+ssm+mysql社区疫情防控管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

Flink Source 详解

Flink Source 详解 原文 flip-27 FLIP-27 介绍了新版本Source 接口定义及架构 相比于SourceFunction&#xff0c;新版本的Source更具灵活性&#xff0c;原因是将“splits数据获取”与真“正数据获取”逻辑进行了分离 重要部件 Source 作为工厂类&#xff0c;会创建以下两…

CSS回顾-基础知识详解

一、引言 在前端开发领域&#xff0c;CSS 曾是构建网页视觉效果的关键&#xff0c;与 HTML、JavaScript 一起打造精彩的网络世界。但随着组件库的大量涌现&#xff0c;我们亲手书写 CSS 样式的情况越来越少&#xff0c;CSS 基础知识也逐渐被我们遗忘。 现在&#xff0c;这种遗…

11.08-10.14谷粒商城

谷粒商城--品牌管理 前端表单校验 品牌新增 品牌修改 校验规则 dataRule: {name: [{ required: true, message: "品牌名不能为空", trigger: "blur" }],logo: [{ required: true, message: "品牌logo地址不能为空", trigger: "blur"…

无插件H5播放器EasyPlayer.js网页web无插件播放器选择全屏时,视频区域并没有全屏问题的解决方案

EasyPlayer.js H5播放器&#xff0c;是一款能够同时支持HTTP、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WS、WEBRTC、FMP4视频直播与视频点播等多种协议&#xff0c;支持H.264、H.265、AAC、G711A、MP3等多种音视频编码格式&#xff0c;支持MSE、WASM、WebCodec等多种解码方…

基于Spring Boot的电子商务系统设计

5 系统实现 系统实现部分就是将系统分析&#xff0c;系统设计部分的内容通过编码进行功能实现&#xff0c;以一个实际应用系统的形式展示系统分析与系统设计的结果。前面提到的系统分析&#xff0c;系统设计最主要还是进行功能&#xff0c;系统操作逻辑的设计&#xff0c;也包括…

CSP-X2024山东小学组T2:消灭怪兽

题目链接 题目名称 题目描述 怪兽入侵了地球&#xff01; 为了抵抗入侵&#xff0c;人类设计出了按顺序排列好的 n n n 件武器&#xff0c;其中第 i i i 件武器的攻击力为 a i a_i ai​&#xff0c;可以造成 a i a_i ai​ 的伤害。 武器已经排列好了&#xff0c;因此不…

游戏引擎学习第九天

视频参考:https://www.bilibili.com/video/BV1ouUPYAErK/ 修改之前的方波数据&#xff0c;改播放正弦波 下面主要讲关于浮点数 1. char&#xff08;字符类型&#xff09; 大小&#xff1a;1 字节&#xff08;8 位&#xff09;表示方式&#xff1a;char 存储的是一个字符的 A…

JWTUtil工具类

写一个Jwt工具类 导入如下pom.xml依赖 <!--fastjson依赖--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.33</version></dependency><!--jwt依赖--><dependenc…

使用React和Vite构建一个AirBnb Experiences克隆网站

这一篇文章中&#xff0c;我会教你如何做一个AirBnb Experiences的克隆网站。主要涵盖React中Props的使用。 克隆网站最终呈现的效果&#xff1a; 1. 使用vite构建基础框架 npm create vitelatestcd airbnb-project npm install npm run dev2. 构建网站的3个部分 网站从上…

K8S containerd拉取harbor镜像

前言 接前面的环境 K8S 1.24以后开始启用docker作为CRI&#xff0c;这里用containerd拉取 参考文档 正文 vim /etc/containerd/config.toml #修改内容如下 #sandbox_image "registry.aliyuncs.com/google_containers/pause:3.10" systemd_cgroup true [plugins.…

unity小:shaderGraph不规则涟漪、波纹效果

实现概述 在本项目中&#xff0c;我们通过结合 Sine、Polar Coordinates 和 Time 节点&#xff0c;实现了动态波纹效果。以下是实现细节&#xff1a; 核心实现 Sine 波形生成&#xff1a; 使用 Sine 节点生成基本的波形。该节点能够创建周期性变化&#xff0c;为波纹效果提供…

设计模式(四)装饰器模式与命令模式

一、装饰器模式 1、意图 动态增加功能&#xff0c;相比于继承更加灵活 2、类图 Component(VisualComponent)&#xff1a;定义一个对象接口&#xff0c;可以给这些对象动态地添加职责。ConcreteComponent(TextView)&#xff1a;定义一个对象&#xff0c;可以给这个对象添加一…

(附项目源码)nodejs开发语言,212 个性化音乐推荐系统的设计与实现,计算机毕设程序开发+文案(LW+PPT)

摘要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0c;应用软件的工作规…

面试时问到软件开发原则,我emo了

今天去一个小公司面试&#xff0c;面试官是公司的软件总监&#xff0c;眼镜老花到看笔记本电脑困难&#xff0c;用win7的IE打开leetcode网页半天打不开&#xff0c;公司的wifi连接不上&#xff0c;用自己手机热点&#xff0c;却在笔记本电脑上找不到。还是我用自己的手机做热点…

数字IC后端低功耗设计实现案例分享(3个power domain,2个voltage domain)

下图所示为咱们社区T12nm A55低功耗实现项目。其实这个项目还可以根据产品的需求做一些改进。改进后项目实现的难度会大大增加。也希望通过今天的这个项目案例分享&#xff0c;帮助到今年IC秋招的同学。 芯片低功耗设计实现upf编写指南&#xff08;附低功耗项目案例&#xff0…