<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot 性能這樣優(yōu)化,同事直呼哇塞!

          共 9265字,需瀏覽 19分鐘

           ·

          2022-03-06 04:42


          SpringBoot已經(jīng)成為Java屆的No.1框架,每天都在蹂躪著數(shù)百萬(wàn)的程序員們。當(dāng)服務(wù)的壓力上升,對(duì)SpringBoot服務(wù)的優(yōu)化就會(huì)被提上議程。

          本文將詳細(xì)講解SpringBoot服務(wù)優(yōu)化的一般思路,并附上若干篇輔助文章作為開(kāi)胃菜。

          本文較長(zhǎng),最適合收藏之。

          1.有監(jiān)控才有方向

          在開(kāi)始對(duì)SpringBoot服務(wù)進(jìn)行性能優(yōu)化之前,我們需要做一些準(zhǔn)備,把SpringBoot服務(wù)的一些數(shù)據(jù)暴露出來(lái)。

          比如,你的服務(wù)用到了緩存,就需要把緩存命中率這些數(shù)據(jù)進(jìn)行收集;用到了數(shù)據(jù)庫(kù)連接池,就需要把連接池的參數(shù)給暴露出來(lái)。

          我們這里采用的監(jiān)控工具是Prometheus,它是一個(gè)是時(shí)序數(shù)據(jù)庫(kù),能夠存儲(chǔ)我們的指標(biāo)。SpringBoot可以非常方便的接入到Prometheus中。

          創(chuàng)建一個(gè)SpringBoot項(xiàng)目后,首先,加入maven依賴。

          <dependency>
          ?????<groupId>org.springframework.bootgroupId>
          ?????<artifactId>spring-boot-starter-actuatorartifactId>
          ?dependency>
          ?<dependency>
          ?????<groupId>io.micrometergroupId>
          ?????<artifactId>micrometer-registry-prometheusartifactId>
          ?dependency>
          ?<dependency>
          ?????<groupId>io.micrometergroupId>
          ?????<artifactId>micrometer-coreartifactId>
          ?dependency>

          然后,我們需要在application.properties配置文件中,開(kāi)放相關(guān)的監(jiān)控接口。

          management.endpoint.metrics.enabled=true
          management.endpoints.web.exposure.include=*
          management.endpoint.prometheus.enabled=true
          management.metrics.export.prometheus.enabled=true

          啟動(dòng)之后,我們就可以通過(guò)訪問(wèn) http://localhost:8080/actuator/prometheus 來(lái)獲取監(jiān)控?cái)?shù)據(jù)。

          想要監(jiān)控業(yè)務(wù)數(shù)據(jù)也是比較簡(jiǎn)單的。你只需要注入一個(gè)MeterRegistry實(shí)例即可。下面是一段示例代碼:

          @Autowired
          MeterRegistry?registry;

          @GetMapping("/test")
          @ResponseBody
          public?String?test()?{
          ????registry.counter("test",
          ????????????"from",?"127.0.0.1",
          ????????????"method",?"test"
          ????).increment();

          ????return?"ok";
          }

          從監(jiān)控連接中,我們可以找到剛剛添加的監(jiān)控信息。

          test_total{from="127.0.0.1",method="test",}?5.0

          這里簡(jiǎn)單介紹一下流行的Prometheus監(jiān)控體系,Prometheus使用的方式獲取監(jiān)控?cái)?shù)據(jù),這個(gè)暴露數(shù)據(jù)的過(guò)程可以交給功能更加齊全的telegraf組件。

          如圖,我們通常使用Grafana進(jìn)行監(jiān)控?cái)?shù)據(jù)的展示,使用AlertManager組件進(jìn)行提前預(yù)警。這一部分的搭建工作不是我們的重點(diǎn),感興趣的同學(xué)可自行研究。下圖便是一張典型的監(jiān)控圖,可以看到Redis的緩存命中率等情況。

          2.Java生成火焰圖

          火焰圖是用來(lái)分析程序運(yùn)行瓶頸的工具。在縱向,表示的是調(diào)用棧的深度;橫向表明的是消耗的時(shí)間。所以格子的寬度越大,越說(shuō)明它可能是一個(gè)瓶頸。

          火焰圖也可以用來(lái)分析Java應(yīng)用。可以從github上下載async-profiler的壓縮包 進(jìn)行相關(guān)操作。

          比如,我們把它解壓到/root/目錄。然后以javaagent的方式來(lái)啟動(dòng)Java應(yīng)用。命令行如下:

          java?-agentpath:/root/build/libasyncProfiler.so=start,svg,file=profile.svg?-jar?spring-petclinic-2.3.1.BUILD-SNAPSHOT.jar

          運(yùn)行一段時(shí)間后,停止進(jìn)程,可以看到在當(dāng)前目錄下,生成了profile.svg文件,這個(gè)文件是可以用瀏覽器打開(kāi)的,一層層向下瀏覽,即可找到需要優(yōu)化的目標(biāo)。

          3.Skywalking

          對(duì)于一個(gè)web服務(wù)來(lái)說(shuō),最緩慢的地方就在于數(shù)據(jù)庫(kù)操作。所以,使用本地緩存和分布式緩存優(yōu)化,能夠獲得最大的性能提升。

          對(duì)于如何定位到復(fù)雜分布式環(huán)境中的問(wèn)題,我這里想要分享另外一個(gè)工具:Skywalking。

          Skywalking是使用探針技術(shù)(JavaAgent)來(lái)實(shí)現(xiàn)的。通過(guò)在Java的啟動(dòng)參數(shù)中,加入javaagent的Jar包,即可將性能數(shù)據(jù)和調(diào)用鏈數(shù)據(jù)封裝、發(fā)送到Skywalking的服務(wù)器。

          下載相應(yīng)的安裝包(如果使用ES存儲(chǔ),需要下載專(zhuān)用的安裝包),配置好存儲(chǔ)之后,即可一鍵啟動(dòng)。

          將agent的壓縮包,解壓到相應(yīng)的目錄。

          tar?xvf?skywalking-agent.tar.gz??-C?/opt/

          在業(yè)務(wù)啟動(dòng)參數(shù)中加入agent的包。比如,原來(lái)的啟動(dòng)命令是:

          java??-jar?/opt/test-service/spring-boot-demo.jar??--spring.profiles.active=dev

          改造后的啟動(dòng)命令是:

          java?-javaagent:/opt/skywalking-agent/skywalking-agent.jar?-Dskywalking.agent.service_name=the-demo-name??-jar?/opt/test-service/spring-boot-demo.ja??--spring.profiles.active=dev

          訪問(wèn)一些服務(wù)的鏈接,打開(kāi)Skywalking的UI,即可看到下圖的界面。我們可以從圖中找到響應(yīng)比較慢QPS又比較高的的接口,進(jìn)行專(zhuān)項(xiàng)優(yōu)化。

          15723404104715

          4.優(yōu)化思路

          對(duì)一個(gè)普通的Web服務(wù)來(lái)說(shuō),我們來(lái)看一下,要訪問(wèn)到具體的數(shù)據(jù),都要經(jīng)歷哪些主要的環(huán)節(jié)。

          如下圖,在瀏覽器中輸入相應(yīng)的域名,需要通過(guò)DNS解析到具體的IP地址上。為了保證高可用,我們的服務(wù)一般都會(huì)部署多份,然后使用Nginx做反向代理和負(fù)載均衡。

          Nginx根據(jù)資源的特性,會(huì)承擔(dān)一部分動(dòng)靜分離的功能。其中,動(dòng)態(tài)功能部分,會(huì)進(jìn)入我們的SpringBoot服務(wù)。

          SpringBoot默認(rèn)使用內(nèi)嵌的tomcat作為Web容器,使用典型的MVC模式,最終訪問(wèn)到我們的數(shù)據(jù)。

          5.HTTP優(yōu)化

          下面我們舉例來(lái)看一下,哪些動(dòng)作能夠加快網(wǎng)頁(yè)的獲取。為了描述方便,我們僅討論HTTP1.1協(xié)議的。

          1.使用CDN加速文件獲取

          比較大的文件,盡量使用CDN(Content Delivery Network)分發(fā)。甚至是一些常用的前端腳本、樣式、圖片等,都可以放到CDN上。CDN通常能夠加快這些文件的獲取,網(wǎng)頁(yè)加載也更加迅速。

          2.合理設(shè)置Cache-Control值

          瀏覽器會(huì)判斷HTTP頭Cache-Control的內(nèi)容,用來(lái)決定是否使用瀏覽器緩存,這在管理一些靜態(tài)文件的時(shí)候,非常有用。相同作用的頭信息還有ExpiresCache-Control表示多久之后過(guò)期,Expires則表示什么時(shí)候過(guò)期。

          這個(gè)參數(shù)可以在Nginx的配置文件中進(jìn)行設(shè)置。

          location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ { 
          # 緩存1年
          add_header Cache-Control: no-cache, max-age=31536000;
          }

          3.減少單頁(yè)面請(qǐng)求域名的數(shù)量

          減少每個(gè)頁(yè)面請(qǐng)求的域名數(shù)量,盡量保證在4個(gè)之內(nèi)。這是因?yàn)?,瀏覽器每次訪問(wèn)后端的資源,都需要先查詢一次DNS,然后找到DNS對(duì)應(yīng)的IP地址,再進(jìn)行真正的調(diào)用。

          DNS有多層緩存,比如瀏覽器會(huì)緩存一份、本地主機(jī)會(huì)緩存、ISP服務(wù)商緩存等。從DNS到IP地址的轉(zhuǎn)變,通常會(huì)花費(fèi)20-120ms的時(shí)間。減少域名的數(shù)量,可加快資源的獲取。

          4.開(kāi)啟gzip

          開(kāi)啟gzip,可以先把內(nèi)容壓縮后,瀏覽器再進(jìn)行解壓。由于減少了傳輸?shù)拇笮?,?huì)減少帶寬的使用,提高傳輸效率。

          在nginx中可以很容易的開(kāi)啟。配置如下:

          gzip on;
          gzip_min_length 1k;
          gzip_buffers 4 16k;
          gzip_comp_level 6;
          gzip_http_version 1.1;
          gzip_types text/plain application/javascript text/css;

          5.對(duì)資源進(jìn)行壓縮

          對(duì)JavaScript和CSS,甚至是HTML進(jìn)行壓縮。道理類(lèi)似,現(xiàn)在流行的前后端分離模式,一般都是對(duì)這些資源進(jìn)行壓縮的。

          6.使用keepalive

          由于連接的創(chuàng)建和關(guān)閉,都需要耗費(fèi)資源。用戶訪問(wèn)我們的服務(wù)后,后續(xù)也會(huì)有更多的互動(dòng),所以保持長(zhǎng)連接可以顯著減少網(wǎng)絡(luò)交互,提高性能。

          nginx默認(rèn)開(kāi)啟了對(duì)客戶端的keep avlide支持。你可以通過(guò)下面兩個(gè)參數(shù)來(lái)調(diào)整它的行為。

          http {
          keepalive_timeout 120s 120s;
          keepalive_requests 10000;
          }

          nginx與后端upstream的長(zhǎng)連接,需要手工開(kāi)啟,參考配置如下:

          location ~ /{ 
          proxy_pass http://backend;
          proxy_http_version 1.1;
          proxy_set_header Connection "";
          }

          6.Tomcat優(yōu)化

          Tomcat本身的優(yōu)化,也是非常重要的一環(huán)。可以直接參考下面的文章。

          搞定tomcat重要參數(shù)調(diào)優(yōu)!

          7.自定義Web容器

          如果你的項(xiàng)目并發(fā)量比較高,想要修改最大線程數(shù)、最大連接數(shù)等配置信息,可以通過(guò)自定義Web容器的方式,代碼如下所示。

          @SpringBootApplication(proxyBeanMethods?=?false)
          public?class?App?implements?WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>?{
          ?public?static?void?main(String[]?args)?{
          ??SpringApplication.run(PetClinicApplication.class,?args);
          ?}
          ?@Override
          ?public?void?customize(ConfigurableServletWebServerFactory?factory)?{
          ??TomcatServletWebServerFactory?f?=?(TomcatServletWebServerFactory)?factory;
          ????????f.setProtocol("org.apache.coyote.http11.Http11Nio2Protocol");

          ??f.addConnectorCustomizers(c?->?{
          ???Http11NioProtocol?protocol?=?(Http11NioProtocol)?c.getProtocolHandler();
          ???protocol.setMaxConnections(200);
          ???protocol.setMaxThreads(200);
          ???protocol.setSelectorTimeout(3000);
          ???protocol.setSessionTimeout(3000);
          ???protocol.setConnectionTimeout(3000);
          ??});
          ?}
          }

          注意上面的代碼,我們?cè)O(shè)置了它的協(xié)議為org.apache.coyote.http11.Http11Nio2Protocol,意思就是開(kāi)啟了Nio2。這個(gè)參數(shù)在Tomcat8.0之后才有,開(kāi)啟之后會(huì)增加一部分性能。對(duì)比如下:

          默認(rèn)。

          [root@localhost?wrk2-master]#?./wrk?-t2?-c100?-d30s?-R2000?http://172.16.1.57:8080/owners?lastName=
          Running?30s?test?@?http://172.16.1.57:8080/owners?lastName=
          ??2?threads?and?100?connections
          ??Thread?calibration:?mean?lat.:?4588.131ms,?rate?sampling?interval:?16277ms
          ??Thread?calibration:?mean?lat.:?4647.927ms,?rate?sampling?interval:?16285ms
          ??Thread?Stats???Avg??????Stdev?????Max???+/-?Stdev
          ????Latency????16.49s?????4.98s???27.34s????63.90%
          ????Req/Sec???106.50??????1.50???108.00????100.00%
          ??6471?requests?in?30.03s,?39.31MB?read
          ??Socket?errors:?connect?0,?read?0,?write?0,?timeout?60
          Requests/sec:????215.51
          Transfer/sec:??????1.31MB

          Nio2。

          [root@localhost?wrk2-master]#?./wrk?-t2?-c100?-d30s?-R2000?http://172.16.1.57:8080/owners?lastName=
          Running?30s?test?@?http://172.16.1.57:8080/owners?lastName=
          ??2?threads?and?100?connections
          ??Thread?calibration:?mean?lat.:?4358.805ms,?rate?sampling?interval:?15835ms
          ??Thread?calibration:?mean?lat.:?4622.087ms,?rate?sampling?interval:?16293ms
          ??Thread?Stats???Avg??????Stdev?????Max???+/-?Stdev
          ????Latency????17.47s?????4.98s???26.90s????57.69%
          ????Req/Sec???125.50??????2.50???128.00????100.00%
          ??7469?requests?in?30.04s,?45.38MB?read
          ??Socket?errors:?connect?0,?read?0,?write?0,?timeout?4
          Requests/sec:????248.64
          Transfer/sec:??????1.51MB

          你甚至可以將tomcat替換成undertow。undertow也是一個(gè)Web容器,更加輕量級(jí)一些,占用的內(nèi)容更少,啟動(dòng)的守護(hù)進(jìn)程也更少,更改方式如下:

          <dependency>
          ??????<groupId>org.springframework.bootgroupId>
          ??????<artifactId>spring-boot-starter-webartifactId>
          ??????<exclusions>
          ????????<exclusion>
          ??????????<groupId>org.springframework.bootgroupId>
          ??????????<artifactId>spring-boot-starter-tomcatartifactId>
          ????????exclusion>
          ??????exclusions>
          ????dependency>
          ????<dependency>
          ??????<groupId>org.springframework.bootgroupId>
          ??????<artifactId>spring-boot-starter-undertowartifactId>
          ????dependency>

          8.各個(gè)層次的優(yōu)化方向

          Controller層

          controller層用于接收前端的查詢參數(shù),然后構(gòu)造查詢結(jié)果?,F(xiàn)在很多項(xiàng)目都采用前后端分離的架構(gòu),所以controller層的方法,一般會(huì)使用@ResponseBody注解,把查詢的結(jié)果,解析成JSON數(shù)據(jù)返回(兼顧效率和可讀性)。

          由于controller只是充當(dāng)了一個(gè)類(lèi)似功能組合和路由的角色,所以這部分對(duì)性能的影響就主要體現(xiàn)在數(shù)據(jù)集的大小上。如果結(jié)果集合非常大,JSON解析組件就要花費(fèi)較多的時(shí)間進(jìn)行解析。

          大結(jié)果集不僅會(huì)影響解析時(shí)間,還會(huì)造成內(nèi)存浪費(fèi)。假如結(jié)果集在解析成JSON之前,占用的內(nèi)存是10MB,那么在解析過(guò)程中,有可能會(huì)使用20M或者更多的內(nèi)存去做這個(gè)工作。我見(jiàn)過(guò)很多案例,由于返回對(duì)象的嵌套層次太深、引用了不該引用的對(duì)象(比如非常大的byte[]對(duì)象),造成了內(nèi)存使用的飆升。

          所以,對(duì)于一般的服務(wù),保持結(jié)果集的精簡(jiǎn),是非常有必要的,這也是DTO(data transfer object)存在的必要。如果你的項(xiàng)目,返回的結(jié)果結(jié)構(gòu)比較復(fù)雜,對(duì)結(jié)果集進(jìn)行一次轉(zhuǎn)換是非常有必要的。

          另外,可以使用異步Servlet對(duì)Controller層進(jìn)行優(yōu)化。它的原理如下:Servlet 接收到請(qǐng)求之后,將請(qǐng)求轉(zhuǎn)交給一個(gè)異步線程來(lái)執(zhí)行業(yè)務(wù)處理,線程本身返回至容器,異步線程處理完業(yè)務(wù)以后,可以直接生成響應(yīng)數(shù)據(jù),或者將請(qǐng)求繼續(xù)轉(zhuǎn)發(fā)給其它 Servlet。

          Service層

          service層用于處理具體的業(yè)務(wù),大部分功能需求都是在這里完成的。service層一般是使用單例模式(prototype),很少會(huì)保存狀態(tài),而且可以被controller復(fù)用。

          service層的代碼組織,對(duì)代碼的可讀性、性能影響都比較大。我們常說(shuō)的設(shè)計(jì)模式,大多數(shù)都是針對(duì)于service層來(lái)說(shuō)的。

          這里要著重提到的一點(diǎn),就是分布式事務(wù)。

          如上圖,四個(gè)操作分散在三個(gè)不同的資源中。要想達(dá)到一致性,需要三個(gè)不同的資源進(jìn)行統(tǒng)一協(xié)調(diào)。它們底層的協(xié)議,以及實(shí)現(xiàn)方式,都是不一樣的。那就無(wú)法通過(guò)Spring提供的Transaction注解來(lái)解決,需要借助外部的組件來(lái)完成。

          很多人都體驗(yàn)過(guò),加入了一些保證一致性的代碼,一壓測(cè),性能掉的驚掉下巴。分布式事務(wù)是性能殺手,因?yàn)樗褂妙~外的步驟去保證一致性,常用的方法有:兩階段提交方案、TCC、本地消息表、MQ事務(wù)消息、分布式事務(wù)中間件等。

          如上圖,分布式事務(wù)要在改造成本、性能、實(shí)效等方面進(jìn)行綜合考慮。有一個(gè)介于分布式事務(wù)和非事務(wù)之間的名詞,叫做柔性事務(wù)。柔性事務(wù)的理念是將業(yè)務(wù)邏輯和互斥操作,從資源層上移至業(yè)務(wù)層面。

          關(guān)于傳統(tǒng)事務(wù)和柔性事務(wù),我們來(lái)簡(jiǎn)單比較一下。

          ACID

          關(guān)系數(shù)據(jù)庫(kù), 最大的特點(diǎn)就是事務(wù)處理, 即滿足ACID。

          • 原子性(Atomicity):事務(wù)中的操作要么都做,要么都不做。
          • 一致性(Consistency):系統(tǒng)必須始終處在強(qiáng)一致?tīng)顟B(tài)下。
          • 隔離性(Isolation):一個(gè)事務(wù)的執(zhí)行不能被其他事務(wù)所干擾。
          • 持續(xù)性(Durability):一個(gè)已提交的事務(wù)對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變是永久性的。

          BASE

          BASE方法通過(guò)犧牲一致性和孤立性來(lái)提高可用性和系統(tǒng)性能。

          BASE為Basically Available, Soft-state, Eventually consistent三者的縮寫(xiě),其中BASE分別代表:

          • 基本可用(Basically Available):系統(tǒng)能夠基本運(yùn)行、一直提供服務(wù)。
          • 軟狀態(tài)(Soft-state):系統(tǒng)不要求一直保持強(qiáng)一致?tīng)顟B(tài)。
          • 最終一致性(Eventual consistency):系統(tǒng)需要在某一時(shí)刻后達(dá)到一致性要求。

          互聯(lián)網(wǎng)業(yè)務(wù),推薦使用補(bǔ)償事務(wù),完成最終一致性。比如,通過(guò)一系列的定時(shí)任務(wù),完成對(duì)數(shù)據(jù)的修復(fù)。具體可以參照下面的文章。

          常用的 分布式事務(wù) 都有哪些?我該用哪個(gè)?

          Dao層

          經(jīng)過(guò)合理的數(shù)據(jù)緩存,我們都會(huì)盡量避免請(qǐng)求穿透到Dao層。除非你對(duì)ORM本身提供的緩存特性特別的熟悉,否則,都推薦你使用更加通用的方式去緩存數(shù)據(jù)。

          Dao層,主要在于對(duì)ORM框架的使用上。比如,在JPA中,如果加了一對(duì)多或者多對(duì)多的映射關(guān)系,而又沒(méi)有開(kāi)啟懶加載,級(jí)聯(lián)查詢的時(shí)候就容易造成深層次的檢索,造成了內(nèi)存開(kāi)銷(xiāo)大、執(zhí)行緩慢的后果。

          在一些數(shù)據(jù)量比較大的業(yè)務(wù)中,多采用分庫(kù)分表的方式。在這些分庫(kù)分表組件中,很多簡(jiǎn)單的查詢語(yǔ)句,都會(huì)被重新解析后分散到各個(gè)節(jié)點(diǎn)進(jìn)行運(yùn)算,最后進(jìn)行結(jié)果合并。

          舉個(gè)例子,select count(*) from a這句簡(jiǎn)單的count語(yǔ)句,就可能將請(qǐng)求路由到十幾張表中去運(yùn)算,最后在協(xié)調(diào)節(jié)點(diǎn)進(jìn)行統(tǒng)計(jì),執(zhí)行效率是可想而知的。目前,分庫(kù)分表中間件,比較有代表性的是驅(qū)動(dòng)層的ShardingJdbc和代理層的MyCat,它們都有這樣的問(wèn)題。這些組件提供給使用者的視圖是一致的,但我們?cè)诰幋a的時(shí)候,一定要注意這些區(qū)別。

          End

          下面我們來(lái)總結(jié)一下。

          我們簡(jiǎn)單看了一下SpringBoot常見(jiàn)的優(yōu)化思路。我們介紹了三個(gè)新的性能分析工具。一個(gè)是監(jiān)控系統(tǒng)Prometheus,可以看到一些具體的指標(biāo)大小;一個(gè)是火焰圖,可以看到具體的代碼熱點(diǎn);一個(gè)是Skywalking,可以分析分布式環(huán)境中的調(diào)用鏈。在對(duì)性能有疑惑的時(shí)候,我們都會(huì)采用類(lèi)似于神農(nóng)氏嘗百草的方式,綜合各種測(cè)評(píng)工具的結(jié)果進(jìn)行分析。

          SpringBoot自身的Web容器是Tomcat,那我們就可以通過(guò)對(duì)Tomcat的調(diào)優(yōu)來(lái)獲取性能提升。當(dāng)然,對(duì)于服務(wù)上層的負(fù)載均衡Nginx,我們也提供了一系列的優(yōu)化思路。

          最后,我們看了在經(jīng)典的MVC架構(gòu)下,Controller、Service、Dao的一些優(yōu)化方向,并著重看了Service層的分布式事務(wù)問(wèn)題。

          SpringBoot作為一個(gè)廣泛應(yīng)用的服務(wù)框架,在性能優(yōu)化方面已經(jīng)做了很多工作,選用了很多高速組件。比如,數(shù)據(jù)庫(kù)連接池默認(rèn)使用hikaricp,Redis緩存框架默認(rèn)使用lettuce,本地緩存提供caffeine等。對(duì)于一個(gè)普通的于數(shù)據(jù)庫(kù)交互的Web服務(wù)來(lái)說(shuō),緩存是最主要的優(yōu)化手。

          完!

          程序汪資料鏈接

          程序汪接的7個(gè)私活都在這里,經(jīng)驗(yàn)整理

          Java項(xiàng)目分享 最新整理全集,找項(xiàng)目不累啦 06版

          堪稱神級(jí)的Spring Boot手冊(cè),從基礎(chǔ)入門(mén)到實(shí)戰(zhàn)進(jìn)階

          臥槽!字節(jié)跳動(dòng)《算法中文手冊(cè)》火了,完整版 PDF 開(kāi)放下載!

          臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開(kāi)放下載!

          字節(jié)跳動(dòng)總結(jié)的設(shè)計(jì)模式 PDF 火了,完整版開(kāi)放下載!


          歡迎添加程序汪個(gè)人微信 itwang009? 進(jìn)粉絲群或圍觀朋友圈


          瀏覽 43
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本黄色一级大片免费 | 三级片无码麻豆视频 | www.婷婷综合 | 久久久久免费视频 | 国产成人无码永久免费 |