SpringBoot projesi: RedisTemplate hafif mesaj kuyruğu uygular

arka fon

Şirket projesinin bir gereksinimi vardır. Ön uç excel dosyalarını yükler, arka uç verileri okur, verileri işler ve hata verilerini döndürür. En basit yol, işlemeyi senkronize etmektir. Dosyaları yükledikten sonra, istemcinin yanıt beklemesi engellendi, ancak kullanıcı deneyimi şüphesiz zayıf, veri işliyor Çok zaman alabilir, kimse beklemeye istekli olmayabilir çünkü proje ActiveMQ ve diğer mesaj kuyruğu ara yazılımlarını kullanmadı ve redis'in lpush ve rpop'u hafif bir mesaj kuyruğu uygulaması olarak çok uygundur, bu yüzden fonksiyon geliştirmeyi tamamlamak için kullanın

1. Bu makale bilgi noktalarını içerir

  • Excel dosyası okuma ve yazma - Ali easyexcel sdk
  • Dosya yükleme ve indirme - Tencent Cloud Object Storage
  • Uzaktan servis çağrısı - restTemplate
  • Üretici, tüketici - redisTemplate leftPush ve rightPop işlemleri
  • Data-Executors iş parçacığı havuzunun zaman uyumsuz işlenmesi
  • Ağ dosya akışını oku - HttpClient
  • Kullanıcı kimliği kimlik doğrulaması elde etmek için özel açıklamalar - JWT belirteç kimlik doğrulaması, engelleyici @ LoginRequired annotation ile işaretlenen istek girişini durdurur

Tabii ki Java uygulaması

Dahil olan birçok bilgi noktası vardır ve her bilgi noktası, öğrenme ve analiz için bir konu olarak kullanılabilir. Bu makale, tam farkındalığı sunacak ve daha sonra öğrenimi arkadaşlarla paylaşacak ve paylaşacaktır.

İkincisi, proje dizin yapısı

Not: DAO veritabanı katmanı, bu makalenin odak noktası yerine başka bir modüle yerleştirilmiştir.

Üç, ana maven bağımlılığı

1. kolay excel

< easyexcel-latestVersion > 1.1.2-beta4 < / easyexcel-latestVersion > < bağımlılık > < Grup kimliği > com.alibaba < /Grup kimliği > < artifactId > Easyexcel < / artifactId > < versiyon > $ {easyexcel-latestVersion} < / version > < /bağımlılık >

2. JWT

< bağımlılık > < Grup kimliği > io.jsonwebtoken < /Grup kimliği > < artifactId > jjwt < / artifactId > < versiyon > 0.7.0 < / version > < /bağımlılık >

3. redis

< bağımlılık > < Grup kimliği > org.springframework.boot < /Grup kimliği > < artifactId > Spring-boot-starter-redis < / artifactId > < versiyon > 1.3.5. YAYIN < / version > < /bağımlılık >

4. Tencent çünkü

< bağımlılık > < Grup kimliği > com.qcloud < /Grup kimliği > < artifactId > cos_api < / artifactId > < versiyon > 5.4.5 < / version > < /bağımlılık >

Dördüncü olarak, süreç

  • Kullanıcı yükleme dosyası
  • Dosyaları Tencent'e depolayın çünkü
  • Yüklenen dosya kimliğini kaydedin ve kaydı veritabanına yükleyin
  • redis bir içe aktarma mesajı oluşturur, yani dosya kimliğini redis'e kaydedin
  • Talep bittiğinde "İşleniyor" durumuna dönün
  • redis tüketim haberleri
  • Cos dosyalarını okuyun, verileri eşzamansız olarak işleyin
  • Hata verilerini, kullanıcıların indirmesi için excel biçiminde cos'a yükleyin ve işleme durumunu "işlem tamamlandı" olarak güncelleyin
  • İstemci, sorgu işleme durumunu yoklar ve hata dosyalarını indirebilir
  • Son

Beş, etkiyi elde et

1. Dosyaları yükleyin

2. Veritabanı içe aktarma kayıtları

3. İçe aktarılan veriler

4. Yanlış dosyayı indirin

5. Yanlış veri istemi

6. Sorgu içe aktarma kayıtları

Altı, kod uygulaması

1. Excel kontrol katmanını içe aktarın

@Oturum açmak gereklidir @RequestMapping (value = "doImport", yöntem = RequestMethod.POST) publicJsonResponsedoImport (@RequestParam ("file") MultipartFilefile, HttpServletRequestrequest) { PLUseruser = getUser (istek); returnorderImportService.doImport (dosya, kullanıcı.getId ()); }

2. Hizmet katmanı

@Override publicJsonResponsedoImport (MultipartFilefile, IntegeruserId) { eğer (null == dosya || file.isEmpty ()) { thrownewServiceException ("Dosya boş olamaz"); } Stringfilename = file.getOriginalFilename (); eğer (! checkFileSuffix (dosya adı)) { thrownewServiceException ("Şu anda yalnızca xlsx biçiminde excel'i destekliyor"); } //Dosyaları saklama StringfileId = saveToOss (dosya); if (StringUtils.isBlank (fileId)) { thrownewServiceException ("Dosya yükleme başarısız oldu, lütfen daha sonra tekrar deneyin"); } // Kaydı veritabanına kaydedin saveRecordToDB (kullanıcı kimliği, dosya kimliği, dosya adı); // Bir sipariş içe aktarma mesajı oluşturun redisProducer.produce (RedisKey.orderImportKey, fileId); returnJsonResponse.ok ("Alma başarılı, işleniyor ..."); } / ** * Dosya formatını doğrulayın * @ paramfileName *@dönüş * / privatestaticbooleancheckFileSuffix (StringfileName) { if (StringUtils.isBlank (dosyaAdı) || dosyaAdı.lastIndexOf (".") < = 0) { yanlış dönüş; } intpointIndex = fileName.lastIndexOf ("."); Stringsuffix = dosyaAdı.substring (pointIndex, dosyaAdı.length ()). ToLowerCase (); eğer (". xlsx" .equals (sonek)) { dönüş; } yanlış dönüş; } / ** * Dosyaları Tencent OSS'de saklayın * @ paramfile *@dönüş * / privateStringsaveToOss (MultipartFilefile) { InputStreamins = boş; Deneyin{ ins = file.getInputStream (); } catch (IOExceptione) { e.printStackTrace (); } StringfileId; Deneyin{ StringoriginalFilename = file.getOriginalFilename (); Filef = newFile (orijinalFilename); inputStreamToFile (ins, f); FileSystemResourceresource = newFileSystemResource (f); MultiValueMap < Dize, Nesne > param = newLinkedMultiValueMap < > (); param.add ("dosya", kaynak); ResponseResultresponseResult = restTemplate.postForObject (txOssUploadUrl, param, ResponseResult.class); fileId = (Dize) responseResult.getData (); } catch (Exceptione) { fileId = boş; } returnfileId; }

3. Redis üreticisi

@Hizmet publicclassRedisProducerImplimplementsRedisProducer { @Autowired privateRedisTemplateredisTemplate; @Override publicJsonResponseproduce (Stringkey, Stringmsg) { Harita < Dize, Dize > map = Maps.newHashMap (); map.put ("fileId", msg); redisTemplate.opsForList (). leftPush (anahtar, harita); returnJsonResponse.ok (); } }

4. Redis tüketicileri

@Hizmet publicclassRedisConsumer { @Autowired publicRedisTemplateredisTemplate; @Value ("$ {txOssFileUrl}") privateStringtxOssFileUrl; @Value ("$ {txOssUploadUrl}") privateStringtxOssUploadUrl; @Filmdenkare publicvoidinit () { processOrderImport (); } / ** * İşlem siparişi içe aktarımı * / privatevoidprocessOrderImport () { ExecutorServiceexecutorService = Executors.newCachedThreadPool (); executorService.execute (() - > { while (true) { Objectobject = redisTemplate.opsForList (). RightPop (RedisKey.orderImportKey, 1, TimeUnit.SECONDS); if (null == nesne) { devam et; } Stringmsg = JSON.toJSONString (nesne); executorService.execute (newOrderImportTask (msg, txOssFileUrl, txOssUploadUrl)); } }); } }

5. Görev dizisi sınıfını işleme

publicclassOrderImportTaskimplementsRunnable { publicOrderImportTask (Stringmsg, StringtxOssFileUrl, StringtxOssUploadUrl) { this.msg = msg; this.txOssFileUrl = txOssFileUrl; this.txOssUploadUrl = txOssUploadUrl; } } / ** * Fasulyeyi enjekte edin * / privatevoidautowireBean () { this.restTemplate = BeanContext.getApplicationContext (). getBean (RestTemplate.class); this.transactionTemplate = BeanContext.getApplicationContext (). getBean (TransactionTemplate.class); this.orderImportService = BeanContext.getApplicationContext (). getBean (OrderImportService.class); } @Override publicvoidrun () { // Fasulye enjekte edin autowireBean (); JSONObjectjsonObject = JSON.parseObject (msg); StringfileId = jsonObject.getString ("fileId"); MultiValueMap < Dize, Nesne > param = newLinkedMultiValueMap < > (); param.add ("id", fileId); ResponseResultresponseResult = restTemplate.postForObject (txOssFileUrl, param, ResponseResult.class); StringfileUrl = (Dize) responseResult.getData (); if (StringUtils.isBlank (fileUrl)) { dönüş; } InputStreaminputStream = HttpClientUtil.readFileFromURL (fileUrl); Liste < Nesne > list = ExcelUtil.read (inputStream); süreç (liste, dosya kimliği); } / ** * Dosyaları oss'e yükleyin * @ paramfile *@dönüş * / privateStringsaveToOss (Filefile) { StringfileId; Deneyin{ FileSystemResourceresource = newFileSystemResource (dosya); MultiValueMap < Dize, Nesne > param = newLinkedMultiValueMap < > (); param.add ("dosya", kaynak); ResponseResultresponseResult = restTemplate.postForObject (txOssUploadUrl, param, ResponseResult.class); fileId = (Dize) responseResult.getData (); } catch (Exceptione) { fileId = boş; } returnfileId; }

Not: Verileri işlemek için iş mantığı kodunu göndermeye gerek yoktur

6. Dosyaları cos'a yükleyin

@RequestMapping ("/ txOssUpload") @Kafadergisi publicResponseResulttxOssUpload (@RequestParam ("file") MultipartFilefile) throwsUnsupportedEncodingException { eğer (null == dosya || file.isEmpty ()) { returnResponseResult.fail ("Dosya boş olamaz"); } StringoriginalFilename = file.getOriginalFilename (); originalFilename = MimeUtility.decodeText (originalFilename); // Çince bozuk problemi çözün StringcontentType = getContentType (orijinalFilename); Stringkey; InputStreamins = boş; Filef = boş; Deneyin{ ins = file.getInputStream (); f = newFile (orijinalFilename); inputStreamToFile (ins, f); key = iFileStorageClient.txOssUpload (newFileInputStream (f), originalFilename, contentType); } catch (Exceptione) { returnResponseResult.fail (e.getMessage ()); }en sonunda{ eğer (null! = ins) { Deneyin{ ins.close (); } catch (IOExceptione) { e.printStackTrace (); } } if (f.exists ()) {// Geçici dosyaları silin f.delete (); } } returnResponseResult.ok (anahtar); } publicstaticvoidinputStreamToFile (InputStreamins, Filefile) { Deneyin{ OutputStreamos = newFileOutputStream (dosya); intbytesRead = 0; bytebuffer = yeni bayt; while ((bytesRead = ins.read (arabellek, 0,8192))! = - 1) { os.write (arabellek, 0, bytesRead); } os.close (); ins.close (); } catch (Exceptione) { e.printStackTrace (); } } publicStringtxOssUpload (FileInputStreaminputStream, Stringkey, StringcontentType) { anahtar = Uuid.getUuid () + "-" + anahtar; OSSUtil.txOssUpload (inputStream, anahtar, contentType); Deneyin{ eğer (null! = inputStream) { inputStream.close (); } } catch (IOExceptione) { e.printStackTrace (); } Geri dönüş tuşu; } publicstaticvoidtxOssUpload (FileInputStreaminputStream, Stringkey, StringcontentType) { ObjectMetadataobjectMetadata = newObjectMetadata (); Deneyin{ intlength = inputStream.available (); objectMetadata.setContentLength (uzunluk); } catch (Exceptione) { logger.info (e.getMessage ()); } objectMetadata.setContentType (contentType); cosclient.putObject (txbucketName, key, inputStream, objectMetadata); }

7. Dosyaları indirin

/ ** * Tencent Bulut Dosyası İndir * @ paramresponse * @ paramid *@dönüş * / @RequestMapping ("/ txOssDownload") publicObjecttxOssDownload (HttpServletResponseresponse, Stringid) { COSObjectInputStreamcosObjectInputStream = iFileStorageClient.txOssDownload (id, yanıt); StringcontentType = getContentType (id); FileUtil.txOssDownload (yanıt, contentType, cosObjectInputStream, id); returnnull; } publicstaticvoidtxOssDownload (HttpServletResponseresponse, StringcontentType, InputStreamfileStream, StringfileName) { FileOutputStreamfos = boş; response.reset (); OutputStreamos = boş; Deneyin{ response.setContentType (contentType + "; charset = utf-8"); eğer (! contentType.equals (PlConstans.FileContentType.image)) { Deneyin{ response.setHeader ("Content-Disposition", "attachment; filename =" + newString (fileName.getBytes ("UTF-8"), "ISO8859-1")); } catch (UnsupportedEncodingExceptione) { response.setHeader ("İçerik-Düzenleme", "ek; dosyaadı =" + dosyaAdı); logger.error ("kodlama dosyası başarısız", e); } } os = response.getOutputStream (); byteb = yeni bayt; intlen; while ((len = fileStream.read (b)) > 0) { os.write (b, 0, len); os.flush (); Deneyin{ eğer (fos! = null) { fos.write (b, 0, len); fos.flush (); } } catch (Exceptione) { logger.error (e.getMessage ()); } } } catch (IOExceptione) { IOUtils.closeQuietly (fos); fos = boş; }en sonunda{ IOUtils.closeQuietly (os); IOUtils.closeQuietly (fileStream); eğer (fos! = null) { IOUtils.closeQuietly (fos); } } }

8. Ağ dosya akışını okuyun

/ ** * Ağ dosya akışını okuyun * @ paramurl *@dönüş * / publicstaticInputStreamreadFileFromURL (Stringurl) { if (StringUtils.isBlank (url)) { returnnull; } HttpClienthttpClient = newDefaultHttpClient (); HttpGetmethodGet = newHttpGet (url); Deneyin{ HttpResponseresponse = httpClient.execute (methodGet); eğer (response.getStatusLine (). getStatusCode () == 200) { HttpEntityentity = response.getEntity (); returnentity.getContent (); } } catch (Exceptione) { e.printStackTrace (); } returnnull; }

9, ExcelUtil

/ ** * Excel'i okuyun * @ paraminputStream dosyası giriş akışı * @ iade listesi koleksiyonu * / publicstaticList < Nesne > oku (InputStreaminputStream) { returnEasyExcelFactory.read (inputStream, newSheet (1,1)); } / ** * Excel yaz * @ paramdatalist verileri * @ paramclazz * @ paramsaveFilePath dosyası kaydetme yolu * @ throwsIOException * / publicstaticvoidwrite (Liste < ? extendsBaseRowModel > veri, Sınıf < ? extendsBaseRowModel > clazz, StringsaveFilePath) throwsIOException { FiletempFile = newFile (saveFilePath); OutputStreamout = newFileOutputStream (tempFile); ExcelWriterwriter = EasyExcelFactory.getWriter (çıkış); Sheetsheet = newSheet (1,3, clazz, "Sheet1", null); writer.write (veri, sayfa); writer.finish (); out.close (); }

Not: Bu noktada, tüm süreç tamamlanmıştır ve diğer bilgi noktası kodları da referans için aşağıda yayınlanmıştır.

Yedi, diğer

1. @LoginRequired ek açıklama

/ ** * Giriş doğrulaması gerektiren Kontrolör yönteminde bu açıklamayı kullanın * / @Target ({ElementType.METHOD}) @Retention (RetentionPolicy.RUNTIME) public @ interfaceLoginRequired { }

2. MyControllerAdvice

@Hayalhanemersin publicclassMyControllerAdvice { @Kafadergisi @ExceptionHandler (TokenValidationException.class) publicJsonResponsetokenValidationExceptionHandler () { returnJsonResponse.loginInvalid (); } @Kafadergisi @ExceptionHandler (ServiceException.class) publicJsonResponseserviceExceptionHandler (ServiceExceptionse) { returnJsonResponse.fail (se.getMsg ()); } @Kafadergisi @ExceptionHandler (Exception.class) publicJsonResponseexceptionHandler (Exceptione) { e.printStackTrace (); returnJsonResponse.fail (e.getMessage ()); } }

3. AuthenticationInterceptor

publicclassAuthenticationInterceptorimplementsHandlerInterceptor { privatestaticfinalStringCURRENT_USER = "kullanıcı"; @Autowired privateUserServiceuserService; @Override publicbooleanpreHandle (HttpServletRequestrequest, HttpServletResponseresponse, Objecthandler) { // Yönteme doğrudan eşlenmemişse if (! (handlerinstanceofHandlerMethod)) { dönüş; } HandlerMethodhandlerMethod = (HandlerMethod) işleyici; Methodmethod = handlerMethod.getMethod (); // Arayüzde @LoginRequired ek açıklaması olup olmadığını belirleyin, varsa oturum açmanız gerekir LoginRequiredmethodAnnotation = method.getAnnotation (LoginRequired.class); if (methodAnnotation! = null) { // Jetonu doğrula IntegeruserId = JwtUtil.verifyToken (istek); PLUserplUser = userService.selectByPrimaryKey (userId); if (null == plUser) { thrownewRuntimeException ("Kullanıcı mevcut değil, lütfen tekrar oturum açın"); } request.setAttribute (CURRENT_USER, plUser); dönüş; } dönüş; } @Override publicvoidpostHandle (HttpServletRequesthttpServletRequest, HttpServletResponsehttpServletResponse, Objecto, ModelAndViewmodelAndView) throwsException { } @Override publicvoidafterCompletion (HttpServletRequesthttpServletRequest, HttpServletResponsehttpServletResponse, Objecto, Exceptione) throwsException { } }

4. JwtUtil

publicstaticfinallongEXPIRATION_TIME = 2592_000_000L; // Geçerlilik süresi 30 gündür publicstaticfinalStringSECRET = "pl_token_secret"; publicstaticfinalStringHEADER = "belirteç"; publicstaticfinalStringUSER_ID = "kullanıcı kimliği"; / ** * Kullanıcı kimliğine göre belirteç oluşturun * @ paramuserId *@dönüş * / publicstaticStringgenerateToken (StringuserId) { HashMap < Dize, Nesne > map = newHashMap < > (); map.put (USER_ID, userId); Stringjwt = Jwts.builder () .setClaims (harita) .setExpiration (newDate (System.currentTimeMillis () + EXPIRATION_TIME)) .signWith (SignatureAlgorithm.HS512, SECRET) .kompakt(); returnjwt; } / ** * Jetonu doğrula * @ paramrequest * @ geri dönen userId tarafından doğrulandı * / publicstaticIntegerverifyToken (HttpServletRequestrequest) { Stringtoken = request.getHeader (HEADER); eğer (jeton! = null) { Deneyin{ Harita < Dize, Nesne > body = Jwts.parser () .setSigningKey (SECRET) .parseClaimsJws (jeton) .getBody (); for (Map.Entryentry: body.entrySet ()) { Objectkey = entry.getKey (); Objectvalue = entry.getValue (); eğer (key.toString (). equals (USER_ID)) { returnInteger.valueOf (value.toString ()); // userId } } returnnull; } catch (Exceptione) { logger.error (e.getMessage ()); thrownewTokenValidationException ("yetkisiz"); } }Başka{ thrownewTokenValidationException ("missingtoken"); } }
RocketMQ işlem mekanizmasının uygulama süreci, mesaj göndermede neden sıfır kayıp elde edebiliyor?
önceki
Birden fazla deneyin paralel yinelemesini nasıl elde edebilirsiniz, Alimama'nın A / B testi uygulaması hakkında konuşun
Sonraki
On milyarlarca trafik taşıyan yüksek performanslı bir mimari nasıl tasarlanır?
Ant Financial'ın 100 milyon düzeyinde eşzamanlılık altında mobil uçtan uca ağ erişim mimarisinin analizi
Zookeeper tarafından dağıtılmış kilit ve Zookeeper'a dayalı liderlik seçimi hakkında derinlemesine anlayış
Kandırılmaktan bunalıma giren sarhoş bir adam yüz dolarlık banknot dağıtır ve intihar etmek ister.
Tayland'da okumak için sahip olmanız gereken yetenekleri biliyor musunuz?
Lisansüstü eğitim için Tayland'a gitmek ister misiniz, bunların hepsini biliyor musunuz?
iyi haberler! Tayland'da hastaneden taburcu edilen iki önemli hasta: Tayland, bulaşıcı hastalıkları önleme ve kontrol etme kabiliyetiyle dünyada altıncı sırada
Bahşiş verme bilgisi ile ilgili olarak, Tayland seyahati için ipucu açmanın doğru yolu
Tmall 618 Tayland Reklamı: Tanrı "Sıcak" dizisini tersine çeviriyor
Tayland'da "Tayland'ı Görüntüleyin" Ulaşımı
İpuçları: Seyahat sırasında güvenlik için "tatil" yapmayın
Yurtdışında okurken oynamayı unutmayın, dil artık bir engel değil
To Top