Redis böyle mi kullanılır? Bakalım kimin potu

Öncelikle sorundan bahsedelim: Intranet korumalı alan ortamı API'si 1 haftadır uygulamalarda takılı kaldı ve tüm API'ler yanıt vermiyor.

Başlangıçta, test ortamın yavaş yanıt vermesinden şikayet ettiğinde, uygulamayı yeniden başlattık ve uygulama normale döndü, bu nedenle herhangi bir işlem yapılmadı. Ancak daha sonra sorun gittikçe daha sık ortaya çıktı ve gittikçe daha fazla meslektaş şikayet etmeye başladı, bu yüzden kodda bir sorun olabileceğini hissettiler ve sorun gidermeye başladılar.

İlk olarak, geliştirilen yerel ide'nin herhangi bir sorun bulmadığını fark ettim.Uygulama sıkıştığında veritabanı ve redis normaldi ve özel bir hata kaydı yoktu. Sandbox ortamında bunun bir makine sorunu olduğundan şüphelenmeye başladım (test ortamının kendisi çok kırılgan! _!)

Yani sunucudaki ssh aşağıdaki komutu çalıştırır.

üst

Şu anda, makinenin hala normal olduğunu, ancak hala kalbimde olduğunu buldum, bu yüzden jvm yığını bilgilerine bakmayı planlıyorum

Önce sorunlu uygulamada daha fazla kaynak tüketen iş parçacığına bakın

Top -H -p 12798'i yürüt

Göreceli olarak yoğun kaynak gerektiren ilk 3 iletiyi bulun

jstack görünümü yığın bellek

jstack 12798 | Grep 12799'un Onaltılık 31ff'si

Herhangi bir sorun görmedim, bu yüzden üst ve alt 10 satıra bir göz attım.

Bazı iş parçacıklarının kilitli durumda olduğuna bakın. Ancak, göz ardı edilen işle ilgili bir kod yoktur. Şu anda hiçbir ipucu yok. Bunu düşün. Sıkışan makineyi terk etmeye karar verdi

Kaza mahallini korumak için, önce sorun işleminin tüm yığın belleğini boşaltın, ardından hata ayıklama modunda test ortamı uygulamasını yeniden başlatın ve sorun yeniden göründüğünde sorunlu makinede doğrudan hata ayıklayın.

Sorun ertesi gün yeniden ortaya çıktı, bu yüzden işletim ve bakım nginx'e sorunlu uygulamayı iletmesi ve tomcat hatalarını uzaktan ayıklaması için bildirimde bulundum.

Rastgele bir arayüz buldum ve kesme noktası arayüz girişindeydi. Trajedi başladı ve hiçbir şey olmadı! API, hizmet yanıtını bekler ve kesme noktasına girmez. Şu anda biraz kafam karıştı, bir süre sakinleştim, girişten önce aop yere bir kesme noktası yerleştirdim ve tekrar hata ayıkladım.Bu sefer kesme noktasına girdim. F8 N zamanlarından sonra, redis komutu çalıştırıldığında sahibinin takılı kaldığını gördüm. Takip etmeye devam edin ve sonunda jedis'in bulunduğu bir yerde bir sorun buldu:

/ ** * Redis bağlantısı olarak kullanılacak bir Jedis örneği döndürür. Örnek yeni oluşturulabilir veya bir * havuz. * * @ returnJedis örneği, {@linkRedisConnection} içine sarmaya hazır. * / protectedJedis fetchJedisConnector () { Deneyin{ if (usePool pool! = null) { returnpool.getResource (); } Jedis jedis = newJedis (getShardInfo ()); // başlatmaya zorla (Jedis sorunu # 82'ye bakın) jedis.connect (); returnjedis; } catch (Exceptionex) { thrownewRedisConnectionFailureException ("Jedis bağlantısı alınamıyor", örn.); } }

Yukarıdaki pool.getResource () işleminden sonra, iş parçacığı beklemeye başlar

publicT getResource () { Deneyin{ returninternalPool.borrowObject (); } catch (Exceptione) { thrownewJedisConnectionException ("Havuzdan bir kaynak alınamadı", e); } }

return internalPool.borrowObject (); bu kod, kiralanmış bir kod ve ardından

publicTborrowObject (longborrowMaxWaitMillis) throwsException { this.assertOpen (); AbandonedConfig ac = this.abandonedConfig; eğer (ac! = null ac.getRemoveAbandonedOnBorrow () this.getNumIdle () < 2this.getNumActive () > this.getMaxTotal () -3) { this.removeAbandoned (ac); } PooledObject < T > p = boş; booleanblockWhenExhausted = this.getBlockWhenExhausted (); longwaitTime = 0L; while (p == null) { booleancreate = yanlış; if (blockWhenExhausted) { p = (PooledObject) this.idleObjects.pollFirst (); eğer (p == null) { create = true; p = this.create (); } eğer (p == null) { eğer (borrowMaxWaitMillis < 0L) { p = (PooledObject) this.idleObjects.takeFirst (); }Başka{ waitTime = System.currentTimeMillis (); p = (PooledObject) this.idleObjects.pollFirst (borrowMaxWaitMillis, TimeUnit.MILLISECONDS); waitTime = System.currentTimeMillis () - waitTime; } } eğer (p == null) { thrownewNoSuchElementException ("Boş nesne için bekleme süresi"); }

Bir kod parçası var

eğer (p == null) { eğer (borrowMaxWaitMillis < 0L) { p = (PooledObject) this.idleObjects.takeFirst (); }Başka{ waitTime = System.currentTimeMillis (); p = (PooledObject) this.idleObjects.pollFirst (borrowMaxWaitMillis, TimeUnit.MILLISECONDS); waitTime = System.currentTimeMillis () - waitTime; } }

borrowMaxWaitMillis < 0 her zaman çalıştırılacak ve sonra döngüye devam edecek ve bu değerin yapılandırılmadığından şüphelenmeye başlayacaktır.

Redis havuzu yapılandırmasını buldum ve MaxWaitMillis'in gerçekten yapılandırılmadığını buldum.Yapılandırmadan sonra, else kodu da sorunu çözmeyen bir İstisna.

F8'e devam edin

publicEtakeFirst () InterruptedException { this.lock.lock (); Nesne var2; Deneyin{ Nesne x; while ((x = this.unlinkFirst ()) == null) { this.notEmpty.await (); } var2 = x; }en sonunda{ this.lock.unlock (); } returnvar2; }

Buraya geldiğimde, "kilit" kelimesini buldum ve tüm istek apilerinin engellendiğinden şüphelenmeye başladım.

Bu yüzden arthas'ı tekrar ssh sunucusuna yükleyin (Arthas, Alibaba'nın açık kaynaklı Java teşhis aracıdır)

İş parçacığı komutunu çalıştır

Pek çok http-nio iş parçacığı bekleme durumu buldum, http-nio-8083-exec-bu iş parçacığı aslında http isteğinden çıkan tomcat iş parçacığı

Yığın hafızasını görüntülemek için bir iş parçacığı bulmaktan çekinmeyin

iplik -428

Bu, redis bağlantı kodunun neden olduğu api'nin dönmeye devam ettiği doğrulanabilir.

Bu hafıza kodunu yorumlama Tüm iş parçacıkları @ 53e5504e'nin kilidi açmasını bekliyor. Bu nedenle jstack, 53e5504e'yi global olarak aradı, ancak nesnenin bulunduğu iş parçacığını bulamadı.

O zamandan beri. Sorunun nedeni redis bağlantı edinimi sorunu olarak belirlenebilir. Ancak bağlantının elde edilmemesine neden olan şey hala belirsiz

Arthas'ın thread -b'yi tekrar çalıştırın (thread -b, şu anda hangi thread'ın diğer konuları engellediğini bulun)

sonuç yok. Bu düşündüğümden farklı, engellenmiş bir iş parçacığı bulabilmeli, bu yüzden bu komutun belgelerini okudum ve aşağıdaki cümleyi buldum

Eh, biz ikincisiyiz. . . .

Fikirleri yeniden sıralayın. Bağlantının zaman aşımını 2 saniyeye ayarlamak için bu sefer redis havuzu yapılandırmasını değiştirin ve ardından sorunun tekrar etmesini bekleyin ve normal olduğunda uygulamanın ne yaptığını gözlemleyin.

Yapılandırma ekleyin

JedisConnectionFactory jedisConnectionFactory = newJedisConnectionFactory (); ....... JedisPoolConfig config = newJedisPoolConfig (); config.setMaxWaitMillis (2000); ....... jedisConnectionFactory.afterPropertiesSet ();

Servisi yeniden başlatın ve bekleyin. . . .

Bir gün daha geçti ve yine oldu.

ssh sunucusu, tomcat erişim günlüğünü kontrol edin ve çok sayıda api isteğinin 500 göründüğünü tespit edin.

org.springframework.data.redis.RedisConnectionFailureException: CannotgetJedisconnection; nestedexceptionisredis.clients.jedis.exceptions.JedisConnectionException: Couldnotgetaresourcefr omthepool atorg.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector (JedisConnectionFactory.java:140) atorg.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection (JedisConnectionFactory.java:229) atorg.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection (JedisConnectionFactory.java:57) atorg.springframework.data.redis.core.RedisConnectionUtils.doGetConnection (RedisConnectionUtils.java:128) atorg.springframework.data.redis.core.RedisConnectionUtils.getConnection (RedisConnectionUtils.java:91) atorg.springframework.data.redis.core.RedisConnectionUtils.getConnection (RedisConnectionUtils.java:78) atorg.springframework.data.redis.core.RedisTemplate.execute (RedisTemplate.java:177) atorg.springframework.data.redis.core.RedisTemplate.execute (RedisTemplate.java:152) atorg.springframework.data.redis.core.AbstractOperations.execute (AbstractOperations.java:85) atorg.springframework.data.redis.core.DefaultHashOperations.get (DefaultHashOperations.java:48)

Kaynağın göründüğü ilk 500 yeri buldum ve aşağıdaki kodu buldum

....... İmleç c = stringRedisTemplate.getConnectionFactory (). GetConnection (). Scan (seçenekler); while (c.hasNext ()) { ..... ,, }

Bu kodu analiz edin

StringRedisTemplate.getConnectionFactory (). GetConnection () havuzda redisConnection'ı aldıktan sonra, takip işlemi yoktur, yani redis bağlantı havuzundaki bağlantı, işletme işlenmiş olmasına rağmen kiraya verildikten sonra serbest bırakılmaz veya bağlantı havuzuna geri dönmez.

redisConnection boşta, ancak havuzdaki yeniden bağlantı durumu boşta durumuna dönmedi

Normal olmalı

O zamandan beri sorun bulundu.

sonuç olarak

spring stringRedisTemplate, redis'in normal işlemlerini kapsüller, ancak Scan SetNx gibi komutları desteklemez.Şu anda, bazı özel Komutlar için jedis Bağlantısı almanız gerekir.

StringRedisTemplate.getConnectionFactory (). GetConnection () kullanılması önerilmez.

Kullanabiliriz

stringRedisTemplate.execute (newRedisCallback < İmleç > () { @Override publicCursordoInRedis (RedisConnection bağlantısı) throwsDataAccessException { returnconnection.scan (seçenekler); } })

Yürütmek için

Veya bağlantıyı kullandıktan sonra şunu kullanın:

RedisConnectionUtils.releaseConnection (bağlantı, fabrika);

Bağlantıyı kesmek için.

Aynı zamanda, redis'de keys komutunun kullanılması tavsiye edilmez.Redis havuzu konfigürasyonu doğru şekilde konfigüre edilmelidir, aksi takdirde hata günlüğü, hata raporu olmayacak ve konumlandırma oldukça zordur.

Programcı bilgi teknolojisi hakkında daha fazla bilgi edinmek ve zengin mimari bilgileri almak için beni takip edin

Alibaba'nın son röportaj paylaşımı: Java sanal makinesi + veritabanı + Spring + multithreading + mikro hizmetler
önceki
Bu ne tür bir peri belgesi: Spring Boot'un tüm gerçek işlemlerinin panoramik bir görünümü
Sonraki
Kod okunabilirliği veya basitliği için hangisi önemlidir? Aksi takdirde değiştirme yöntemine bakın
En son programcı maaşı Mart ayında açıklanıyor, hangi aşamadasınız?
Bir programcı hayalini gerçekleştirir ve yarım yıldan fazla bir süredir röportajlara hazırlık deneyimini paylaşır
Birinci sınıf öğrencileri Google için Ali ve Toutiao'dan vazgeçti ve sonunda yıllık maaşı bir milyon olan bir teklif kazandı.
Yu'e Bao'nun eve olan ilgisinin yanı sıra Yu'e Bao ile bir video röportaj bile aldım.
Programcılar, iş aramak için akademik niteliklerini tahrif ediyorlar ve sonunda bir teklif alıyorlar. İK bunu öğrendikten sonra, çevrimiçi yardım isteme şansı vermek ister misiniz?
Hala TCP zaman aşımını ve yeniden iletimini anlamıyor musunuz? Bu uzun makaleyi okumaktan çok yararlanacaksınız
Uluslararası öğrenciler, 500.000 sağlık kiti ve 11 milyon maske yolda
Yeni pnömoni salgını altında İngiliz medyasının Çin'i sahtekarlıkla nasıl lekelediğini görün
Langfang Havaalanı Ekonomik Bölgesi'ndeki belediye kamu hizmetinin inşaatı üç yıl içinde tamamen başlatıldı
15 Haziran'dan önce, Hebei 1 şehrindeki bu 34 doğal nokta yangın önleme nedeniyle kapatıldı
Profesör Li Baoguo'ya çevrimiçi kurban! Onu hatırlayan mum ışığı asla sönmez
To Top