自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入理解 OpenFeign 的架構(gòu)原理

開發(fā) 架構(gòu)
在服務(wù)之間調(diào)用的話,我們都是基于 HTTP 協(xié)議,一般用到的遠(yuǎn)程服務(wù)框架有 OKHttp3,Netty, HttpURLConnection 等。

 大家好,我是悟空呀。

上次我們深入講解了 Ribbon 的架構(gòu)原理,這次我們再來看下 Feign 遠(yuǎn)程調(diào)用的架構(gòu)原理。

一、理解遠(yuǎn)程調(diào)用

遠(yuǎn)程調(diào)用怎么理解呢?

遠(yuǎn)程調(diào)用和本地調(diào)用是相對的,那我們先說本地調(diào)用更好理解些,本地調(diào)用就是同一個 Service 里面的方法 A 調(diào)用方法 B。

那遠(yuǎn)程調(diào)用就是不同 Service 之間的方法調(diào)用。Service 級的方法調(diào)用,就是我們自己構(gòu)造請求 URL和請求參數(shù),就可以發(fā)起遠(yuǎn)程調(diào)用了。

在服務(wù)之間調(diào)用的話,我們都是基于 HTTP 協(xié)議,一般用到的遠(yuǎn)程服務(wù)框架有 OKHttp3,Netty, HttpURLConnection 等。其調(diào)用流程如下:

但是這種虛線方框中的構(gòu)造請求的過程是很繁瑣的,有沒有更簡便的方式呢?

Feign 就是來簡化我們發(fā)起遠(yuǎn)程調(diào)用的代碼的,那簡化到什么程度呢?簡化成就像調(diào)用本地方法那樣簡單。

比如我的開源項目 PassJava 中的使用 Feign 執(zhí)行遠(yuǎn)程調(diào)用的代碼:

  1. //遠(yuǎn)程調(diào)用拿到該用戶的學(xué)習(xí)時長 
  2. R memberStudyTimeList = studyTimeFeignService.getMemberStudyTimeListTest(id); 

而 Feign 又是 Spring Cloud 微服務(wù)技術(shù)棧中非常重要的一個組件,如果讓你來設(shè)計這個微服務(wù)組件,你會怎么來設(shè)計呢?

我們需要考慮這幾個因素:

  • 如何使遠(yuǎn)程調(diào)用像本地方法調(diào)用簡單?
  • Feign 如何找到遠(yuǎn)程服務(wù)的地址的?
  • Feign 是如何進(jìn)行負(fù)載均衡的?

接下來我們圍繞這些核心問題來一起看下 Feign 的設(shè)計原理。

二、Feign 和 OpenFeign

OpenFeign 組件的前身是 Netflix Feign 項目,它最早是作為 Netflix OSS 項目的一部分,由 Netflix 公司開發(fā)。后來 Feign 項目被貢獻(xiàn)給了開源組織,于是才有了我們今天使用的 Spring Cloud OpenFeign 組件。

Feign 和 OpenFeign 有很多大同小異之處,不同的是 OpenFeign 支持 MVC 注解。

可以認(rèn)為 OpenFeign 為 Feign 的增強(qiáng)版。

簡單總結(jié)下 OpenFeign 能用來做什么:

  • OpenFeign 是聲明式的 HTTP 客戶端,讓遠(yuǎn)程調(diào)用更簡單。
  • 提供了HTTP請求的模板,編寫簡單的接口和插入注解,就可以定義好HTTP請求的參數(shù)、格式、地址等信息
  • 整合了Ribbon(負(fù)載均衡組件)和 Hystix(服務(wù)熔斷組件),不需要顯示使用這兩個組件
  • Spring Cloud Feign 在 Netflix Feign的基礎(chǔ)上擴(kuò)展了對SpringMVC注解的支持

三、OpenFeign 如何用?

OpenFeign 的使用也很簡單,這里還是用我的開源 SpringCloud 項目 PassJava 作為示例。

開源地址: https://github.com/Jackson0714/PassJava-Platform

喜歡的小伙伴來點(diǎn)個 Star 吧,沖 2K Star。

Member 服務(wù)遠(yuǎn)程調(diào)用 Study 服務(wù)的方法 memberStudyTime(),如下圖所示。

第一步:Member 服務(wù)需要定義一個 OpenFeign 接口:

  1. @FeignClient("passjava-study"
  2. public interface StudyTimeFeignService { 
  3.     @RequestMapping("study/studytime/member/list/test/{id}"
  4.     public R getMemberStudyTimeListTest(@PathVariable("id") Long id); 

我們可以看到這個 interface 上添加了注解@FeignClient,而且括號里面指定了服務(wù)名:passjava-study。顯示聲明這個接口用來遠(yuǎn)程調(diào)用 passjava-study服務(wù)。

第二步:Member 啟動類上添加 @EnableFeignClients注解開啟遠(yuǎn)程調(diào)用服務(wù),且需要開啟服務(wù)發(fā)現(xiàn)。如下所示:

  1. @EnableFeignClients(basePackages = "com.jackson0714.passjava.member.feign"
  2. @EnableDiscoveryClient 

第三步:Study 服務(wù)定義一個方法,其方法路徑和 Member 服務(wù)中的接口 URL 地址一致即可。

URL 地址:"study/studytime/member/list/test/{id}"

  1. @RestController 
  2. @RequestMapping("study/studytime"
  3. public class StudyTimeController { 
  4.     @RequestMapping("/member/list/test/{id}"
  5.     public R memberStudyTimeTest(@PathVariable("id") Long id) { 
  6.        ...  
  7.     } 

第四步:Member 服務(wù)的 POM 文件中引入 OpenFeign 組件。

  1. <dependency> 
  2.     <groupId>org.springframework.cloud</groupId> 
  3.     <artifactId>spring-cloud-starter-openfeign</artifactId> 
  4. </dependency> 

第五步:引入 studyTimeFeignService,Member 服務(wù)遠(yuǎn)程調(diào)用 Study 服務(wù)即可。

  1. Autowired 
  2. private StudyTimeFeignService studyTimeFeignService; 
  3.  
  4. studyTimeFeignService.getMemberStudyTimeListTest(id); 

通過上面的示例,我們知道,加了 @FeignClient 注解的接口后,我們就可以調(diào)用它定義的接口,然后就可以調(diào)用到遠(yuǎn)程服務(wù)了。

這里你是否有疑問:為什么接口都沒有實(shí)現(xiàn),就可以調(diào)用了?

OpenFeign 使用起來倒是簡單,但是里面的原理可沒有那么簡單,OpenFeign 幫我們做了很多事情,接下來我們來看下 OpenFeign 的架構(gòu)原理。

四、梳理 OpenFeign 的核心流程

先看下 OpenFeign 的核心流程圖:

  • 1、在 Spring 項目啟動階段,服務(wù) A 的OpenFeign 框架會發(fā)起一個主動的掃包流程。
  • 2、從指定的目錄下掃描并加載所有被 @FeignClient 注解修飾的接口,然后將這些接口轉(zhuǎn)換成 Bean,統(tǒng)一交給 Spring 來管理。
  • 3、根據(jù)這些接口會經(jīng)過 MVC Contract 協(xié)議解析,將方法上的注解都解析出來,放到 MethodMetadata 元數(shù)據(jù)中。
  • 4、基于上面加載的每一個 FeignClient 接口,會生成一個動態(tài)代理對象,指向了一個包含對應(yīng)方法的 MethodHandler 的 HashMap。MethodHandler 對元數(shù)據(jù)有引用關(guān)系。生成的動態(tài)代理對象會被添加到 Spring 容器中,并注入到對應(yīng)的服務(wù)里。
  • 5、服務(wù) A 調(diào)用接口,準(zhǔn)備發(fā)起遠(yuǎn)程調(diào)用。
  • 6、從動態(tài)代理對象 Proxy 中找到一個 MethodHandler 實(shí)例,生成 Request,包含有服務(wù)的請求 URL(不包含服務(wù)的 IP)。
  • 7、經(jīng)過負(fù)載均衡算法找到一個服務(wù)的 IP 地址,拼接出請求的 URL
  • 8、服務(wù) B 處理服務(wù) A 發(fā)起的遠(yuǎn)程調(diào)用請求,執(zhí)行業(yè)務(wù)邏輯后,返回響應(yīng)給服務(wù) A。

針對上面的流程,我們再來看下每一步的設(shè)計原理。首先主動掃包是如何掃的呢?

五、OpeFeign 包掃描原理

上面的 PassJava 示例代碼中,涉及到了一個 OpenFeign 的注解:@EnableFeignClients。根據(jù)字面意思可以知道,可以注解是開啟 OpenFeign 功能的。

包掃描的基本流程如下:

(1)@EnableFeignClients 這個注解使用 Spring 框架的 Import 注解導(dǎo)入了 FeignClientsRegistrar 類,開始了 OpenFeign 組件的加載。PassJava 示例代碼如下所示。

  1. // 啟動類加上這個注解 
  2. @EnableFeignClients(basePackages = "com.jackson0714.passjava.member.feign"
  3.  
  4. // EnableFeignClients 類還引入了 FeignClientsRegistrar 類 
  5. @Import(FeignClientsRegistrar.class) 
  6. public @interface EnableFeignClients { 
  7.   ... 

(2)FeignClientsRegistrar 負(fù)責(zé) Feign 接口的加載。

源碼如下所示:

 

  1. @Override 
  2. public void registerBeanDefinitions(AnnotationMetadata metadata, 
  3.       BeanDefinitionRegistry registry) { 
  4.    // 注冊配置 
  5.    registerDefaultConfiguration(metadata, registry); 
  6.    // 注冊 FeignClient 
  7.    registerFeignClients(metadata, registry); 

 

(3)registerFeignClients 會掃描指定包。

核心源碼如下,調(diào)用 find 方法來查找指定路徑 basePackage 的所有帶有 @FeignClients 注解的帶有 @FeignClient 注解的類、接口。

 

  1. Set<BeanDefinition> candidateComponents = scanner 
  2.       .findCandidateComponents(basePackage); 

 

(4)只保留帶有 @FeignClient 的接口。

 

  1. // 判斷是否是帶有注解的 Bean。 
  2. if (candidateComponent instanceof AnnotatedBeanDefinition) { 
  3.   // 判斷是否是接口 
  4.    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; 
  5.    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); 
  6.   // @FeignClient 只能指定在接口上。 
  7.    Assert.isTrue(annotationMetadata.isInterface(), 
  8.          "@FeignClient can only be specified on an interface"); 

 

接下來我們再來看這些掃描到的接口是如何注冊到 Spring 中。

六、注冊 FeignClient 到 Spring 的原理

還是在 registerFeignClients 方法中,當(dāng) FeignClient 掃描完后,就要為這些 FeignClient 接口生成一個動態(tài)代理對象。

順藤摸瓜,進(jìn)到這個方法里面,可以看到這一段代碼:

 

  1. BeanDefinitionBuilder definition = BeanDefinitionBuilder 
  2.       .genericBeanDefinition(FeignClientFactoryBean.class); 

 

核心就是 FeignClientFactoryBean 類,根據(jù)類的名字我們可以知道這是一個工廠類,用來創(chuàng)建 FeignClient Bean 的。

我們最開始用的 @FeignClient,里面有個參數(shù) "passjava-study",這個是注解的屬性,當(dāng) OpenFeign 框架去創(chuàng)建 FeignClient Bean 的時候,就會使用這些參數(shù)去生成 Bean。流程圖如下:

  • 解析 @FeignClient 定義的屬性。
  • 將注解@FeignClient 的屬性 + 接口 StudyTimeFeignService的信息構(gòu)造成一個 StudyTimeFeignService 的 beanDefinition。
  • 然后將 beanDefinition 轉(zhuǎn)換成一個 holder,這個 holder 就是包含了 beanDefinition, alias, beanName 信息。
  • 最后將這個 holder 注冊到 Spring 容器中。

源碼如下:

 

  1. // 生成 beanDefinition 
  2. AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); 
  3. // 轉(zhuǎn)換成 holder,包含了 beanDefinition, alias, beanName 信息 
  4. BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, 
  5.     new String[] { alias }); 
  6. // 注冊到 Spring 上下文中。 
  7. BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); 

 

上面我們已經(jīng)知道 FeignClient 的接口是如何注冊到 Spring 容器中了。后面服務(wù)要調(diào)用接口的時候,就可以直接用 FeignClient 的接口方法了,如下所示:

 

  1. @Autowired 
  2. private StudyTimeFeignService studyTimeFeignService; 
  3.  
  4. // 省略部分代碼 
  5. // 直接調(diào)用  
  6. studyTimeFeignService.getMemberStudyTimeListTest(id); 

 

但是我們并沒有細(xì)講這個 FeignClient 的創(chuàng)建細(xì)節(jié),下面我們看下 FeignClient 的創(chuàng)建細(xì)節(jié),這個也是 OpenFeign 核心原理。

七、OpenFeign 動態(tài)代理原理

上面的源碼解析中我們也提到了是由這個工廠類 FeignClientFactoryBean 來創(chuàng)建 FeignCient Bean,所以我們有必要對這個類進(jìn)行剖析。

在創(chuàng)建 FeignClient Bean 的過程中就會去生成動態(tài)代理對象。調(diào)用接口時,其實(shí)就是調(diào)用動態(tài)代理對象的方法來發(fā)起請求的。

分析動態(tài)代理的入口方法為 getObject()。源碼如下所示:

 

  1. Targeter targeter = get(context, Targeter.class); 
  2. return (T) targeter.target(this, builder, context, 
  3.       new HardCodedTarget<>(this.type, this.name, url)); 

 

接著調(diào)用 target 方法這一塊,里面的代碼真的很多很細(xì),我把核心的代碼拿出來給大家講下,這個 target 會有兩種實(shí)現(xiàn)類:

DefaultTargeter 和 HystrixTargeter。而不論是哪種 target,都需要去調(diào)用 Feign.java 的 builder 方法去構(gòu)造一個 feign client。

在構(gòu)造的過程中,依賴 ReflectiveFeign 去構(gòu)造。源碼如下:

 

  1. // 省略部分代碼 
  2. public class ReflectiveFeign extends Feign { 
  3.   // 為 feign client 接口中的每個接口方法創(chuàng)建一個 methodHandler 
  4.  public <T> T newInstance(Target<T> target) { 
  5.     for(...) { 
  6.       methodToHandler.put(method, handler); 
  7.     } 
  8.     // 基于 JDK 動態(tài)代理的機(jī)制,創(chuàng)建了一個 passjava-study 接口的動態(tài)代理,所有對接口的調(diào)用都會被攔截,然后轉(zhuǎn)交給 handler 的方法。 
  9.     InvocationHandler handler = factory.create(target, methodToHandler); 
  10.     T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), 
  11.           new Class<?>[] {target.type()}, handler); 

 

ReflectiveFeign 做的工作就是為帶有 @FeignClient 注解的接口,創(chuàng)建出接口方法的動態(tài)代理對象。

比如示例代碼中的接口 StudyTimeFeignService,會給這個接口中的方法 getMemberStudyTimeList 創(chuàng)建一個動態(tài)代理對象。

 

  1. @FeignClient("passjava-study"
  2. public interface StudyTimeFeignService { 
  3.     @RequestMapping("study/studytime/member/list/test/{id}"
  4.     public R getMemberStudyTimeList(@PathVariable("id") Long id); 

 

創(chuàng)建動態(tài)代理的原理圖如下所示:

解析 FeignClient 接口上各個方法級別的注解,比如遠(yuǎn)程接口的 URL、接口類型(Get、Post 等)、各個請求參數(shù)等。這里用到了 MVC Contract 協(xié)議解析,后面會講到。

  • 然后將解析到的數(shù)據(jù)封裝成元數(shù)據(jù),并為每一個方法生成一個對應(yīng)的 MethodHandler 類作為方法級別的代理。相當(dāng)于把服務(wù)的請求地址、接口類型等都幫我們封裝好了。這些 MethodHandler 方法會放到一個 HashMap 中。
  • 然后會生成一個 InvocationHandler 用來管理這個 hashMap,其中 Dispatch 指向這個 HashMap。
  • 然后使用 Java 的 JDK 原生的動態(tài)代理,實(shí)現(xiàn)了 FeignClient 接口的動態(tài)代理 Proxy 對象。這個 Proxy 會添加到 Spring 容器中。
  • 當(dāng)要調(diào)用接口方法時,其實(shí)會調(diào)用動態(tài)代理 Proxy 對象的 methodHandler 來發(fā)送請求。

這個動態(tài)代理對象的結(jié)構(gòu)如下所示,它包含了所有接口方法的 MethodHandler。

八、解析 MVC 注解的原理

上面我們講到了接口上是有一些注解的,比如 @RequestMapping,@PathVariable,這些注解統(tǒng)稱為 Spring MVC 注解。但是由于 OpenFeign 是不理解這些注解的,所以需要進(jìn)行一次解析。

解析的流程圖如下:

而解析的類就是 SpringMvcContract 類,調(diào)用 parseAndValidateMetadata 進(jìn)行解析。解析完之后,就會生成元數(shù)據(jù)列表。源碼如下所示:

  1. List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type()); 

這個類在這個路徑下,大家可以自行翻閱下如何解析的,不在本篇的討論范圍內(nèi)。

  1. https://github.com/spring-cloud/spring-cloud-openfeign/blob/main/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java 

這個元數(shù)據(jù) MethodMetadata 里面有什么東西呢?

  • 方法的定義,如 StudyTimeFeignService 的 getMemberStudyTimeList 方法。
  • 方法的參數(shù)類型,如 Long。
  • 發(fā)送 HTTP 請求的地址,如 /study/studytime/member/list/test/{id}。

然后每個接口方法就會有對應(yīng)的一個 MethodHandler,它里面就包含了元數(shù)據(jù),當(dāng)我們調(diào)用接口方法時,其實(shí)是調(diào)用動態(tài)代理對象的 MethodHandler 來發(fā)送遠(yuǎn)程調(diào)用請求的。

上面我們針對 OpenFeign 框架如何為 FeignClient 接口生成動態(tài)代理已經(jīng)講完了,下面我們再來看下當(dāng)我們調(diào)用接口方法時,動態(tài)代理對象是如何發(fā)送遠(yuǎn)程調(diào)用請求的。

九、OpenFeign 發(fā)送請求的原理

先上流程圖:

還是在 ReflectiveFeign 類中,有一個 invoke 方法,會執(zhí)行以下代碼:

  1. dispatch.get(method).invoke(args); 

這個 dispatch 我們之前已經(jīng)講解過了,它指向了一個 HashMap,里面包含了 FeignClient 每個接口的 MethodHandler 類。

這行代碼的意思就是根據(jù) method 找到 MethodHandler,調(diào)用它的 invoke 方法,并且傳的參數(shù)就是我們接口中的定義的參數(shù)。

那我們再跟進(jìn)去看下這個 MethodHandler invoke 方法里面做了什么事情。源碼如下所示:

  1. public Object invoke(Object[] argv) throws Throwable { 
  2.   RequestTemplate template = buildTemplateFromArgs.create(argv); 
  3.   ... 

我們可以看到這個方法里面生成了 RequestTemplate,它的值類似如下:

  1. GET /study/list/test/1 HTTP/1.1 

RequestTemplate 轉(zhuǎn)換成 Request,它的值類似如下:

  1. GET http://passjava-study/study/list/test/1 HTTP/1.1 

這路徑不就是我們要 study 服務(wù)的方法,這樣就可以直接調(diào)用到 study 服了呀!

OpenFeign 幫我們組裝好了發(fā)起遠(yuǎn)程調(diào)用的一切,我們只管調(diào)用就好了。

接著 MethodHandler 會執(zhí)行以下方法,發(fā)起 HTTP 請求。

  1. client.execute(request, options); 

從上面的我們要調(diào)用的服務(wù)就是 passjava-study,但是這個服務(wù)的具體 IP 地址我們是不知道的,那 OpenFeign 是如何獲取到 passjava-study 服務(wù)的 IP 地址的呢?

回想下最開始我們提出的核心問題:OpenFeign 是如何進(jìn)行負(fù)載均衡的?

我們是否可以聯(lián)想到上一講的 Ribbon 負(fù)載均衡,它不就是用來做 IP 地址選擇的么?

那我們就來看下 OpenFeign 又是如何和 Ribbon 進(jìn)行整合的。

十、OpenFeign 如何與 Ribbon 整合的原理

為了驗證 Ribbon 的負(fù)載均衡,我們需要啟動兩個 passjava-study 服務(wù),這里我啟動了兩個服務(wù),端口號分別為 12100 和 12200,IP 地址都是本機(jī) IP:192.168.10.197。

接著上面的源碼繼續(xù)看,client.execute() 方法其實(shí)會調(diào)用 LoadBalancerFeignClient 的 exceute 方法。

這個方法里面的執(zhí)行流程如下圖所示:

  • 將服務(wù)名稱 passjava-study 從 Request 的 URL 中刪掉,剩下的如下所示:
  1. GET http:///study/list/test/1 HTTP/1.1 
  • 根據(jù)服務(wù)名從緩存中找 FeignLoadBalancer,如果緩存中沒有,則創(chuàng)建一個 FeignLoadBalancer。
  • FeignLoadBalancer 會創(chuàng)建出一個 command,這個 command 會執(zhí)行一個 sumbit 方法。
  • submit 方法里面就會用 Ribbon 的負(fù)載均衡算法選擇一個 server。源碼如下:
  1. Server svc = lb.chooseServer(loadBalancerKey); 

通過 debug 調(diào)試,我們可以看到兩次請求的端口號不一樣,一個是 12200,一個是 12100,說明確實(shí)進(jìn)行了負(fù)載均衡。

  • 然后將 IP 地址和之前剔除掉服務(wù)名稱的 URL 進(jìn)行拼接,生成最后的服務(wù)地址。
  • 最后 FeignLoadBalancer 執(zhí)行 execute 方法發(fā)送請求。

那大家有沒有疑問,Ribbon 是如何拿到服務(wù)地址列表的?這個就是上一講 Ribbon 架構(gòu)里面的內(nèi)容。

Ribbon 的核心組件 ServerListUpdater,用來同步注冊表的,它有一個實(shí)現(xiàn)類 PollingServerListUpdater ,專門用來做定時同步的。默認(rèn)1s 后執(zhí)行一個 Runnable 線程,后面就是每隔 30s 執(zhí)行 Runnable 線程。這個 Runnable 線程就是去獲取注冊中心的注冊表的。

十一、OpenFeign 處理響應(yīng)的原理

當(dāng)遠(yuǎn)程服務(wù) passjava-study 處理完業(yè)務(wù)邏輯后,就會返回 reponse 給 passjava-member 服務(wù)了,這里還會對 reponse 進(jìn)行一次解碼操作。

  1. Object result = decode(response); 

這個里面做的事情就是調(diào)用 ResponseEntityDecoder 的 decode 方法,將 Json 字符串轉(zhuǎn)化為 Bean 對象。

十二、總結(jié)

本文通過我的開源項目 PassJava 中用到的 OpenFeign 作為示例代碼作為入口進(jìn)行講解。然后以圖解+解讀源碼的方式深入剖析了 OpenFeign 的運(yùn)行機(jī)制和架構(gòu)設(shè)計。

核心思想:

  • OpenFeign 會掃描帶有 @FeignClient 注解的接口,然后為其生成一個動態(tài)代理。
  • 動態(tài)代理里面包含有接口方法的 MethodHandler,MethodHandler 里面又包含經(jīng)過 MVC Contract 解析注解后的元數(shù)據(jù)。
  • 發(fā)起請求時,MethodHandler 會生成一個 Request。
  • 負(fù)載均衡器 Ribbon 會從服務(wù)列表中選取一個 Server,拿到對應(yīng)的 IP 地址后,拼接成最后的 URL,就可以發(fā)起遠(yuǎn)程服務(wù)調(diào)用了。

OpenFeign 的核心流程圖:

 

責(zé)任編輯:武曉燕 來源: 悟空聊架構(gòu)
相關(guān)推薦

2023-01-16 18:32:15

架構(gòu)APNacos

2021-09-10 07:31:54

AndroidAppStartup原理

2017-08-15 13:05:58

Serverless架構(gòu)開發(fā)運(yùn)維

2022-11-04 09:43:05

Java線程

2024-03-12 00:00:00

Sora技術(shù)數(shù)據(jù)

2021-03-10 10:55:51

SpringJava代碼

2024-11-01 08:57:07

2022-09-05 08:39:04

kubernetesk8s

2020-08-10 18:03:54

Cache存儲器CPU

2024-04-15 00:00:00

技術(shù)Attention架構(gòu)

2018-04-16 11:04:23

HBaseRegion Serv數(shù)據(jù)庫

2023-06-07 15:34:21

架構(gòu)層次結(jié)構(gòu)

2020-03-26 16:40:07

MySQL索引數(shù)據(jù)庫

2023-09-19 22:47:39

Java內(nèi)存

2022-09-26 08:01:31

線程LIFO操作方式

2023-10-13 13:30:00

MySQL鎖機(jī)制

2020-11-04 15:35:13

Golang內(nèi)存程序員

2019-07-01 13:34:22

vue系統(tǒng)數(shù)據(jù)

2022-09-05 22:22:00

Stream操作對象

2020-03-17 08:36:22

數(shù)據(jù)庫存儲Mysql
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號