SpringBoot實現(xiàn)短鏈接系統(tǒng)的使用示例
前言
短鏈接系統(tǒng)是一種將較長的URL(統(tǒng)一資源定位符)轉(zhuǎn)換為較短的URL的服務(wù)。這種服務(wù)通常被用于URL分享,因為較短的URL更加方便用戶復制和粘貼,也更容易在社交媒體和其他在線平臺分享。本文使用了SpringBoot開發(fā)了一個簡易的短鏈接轉(zhuǎn)換接口,和短鏈接重定向接口。
一、短鏈接系統(tǒng)入門??
1. 什么是短鏈接系統(tǒng)?
短鏈接系統(tǒng)是一種將較長的URL轉(zhuǎn)換成較短URL的服務(wù)。當用戶點擊短鏈接時,他們會被重定向到原始URL。短鏈接系統(tǒng)在社交媒體平臺(如微博)上特別有用,因為在這些平臺上,限制了可以發(fā)布的文字數(shù)量。使用短鏈接服務(wù)可以節(jié)省空間,使URL更短,更方便用戶輸入。
短鏈接有什么優(yōu)勢:
- 便捷分享: 短鏈接更短、更易分享,適用于社交媒體、短信、郵件等場景,提供更美觀的外觀。
- 提高用戶體驗: 短鏈接可以簡化用戶輸入,減少用戶訪問鏈接時的操作,提升用戶體驗
- 推廣和營銷: 短鏈接可以用于推廣和營銷活動,跟蹤廣告點擊和轉(zhuǎn)化率,幫助優(yōu)化營銷策略
2. 準備工作
(1)創(chuàng)建一個maven項目
(2)引入相關(guān)依賴
繼承spring boot parent項目
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.0</version> </parent>
引入spring boot maven插件
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
引入spring boot提供的starter
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
引入lombok插件
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
引入ORM框架JPA
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
引入Google開源的Java庫
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1.1-jre</version> </dependency>
增加application.yaml配置文件
server: port: 8888 spring: application: name: shorten-service datasource: url: jdbc:mysql://localhost:3306/shorten_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC username: root password: xxxx #修改成自己的密碼 jpa: hibernate: ddl-auto: create-drop properties: hibernate: show_sql: true format_sql: true
(3)創(chuàng)建啟動類
package org.shortenservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ShortenServiceApplication { public static void main(String[] args) { SpringApplication.run(ShortenServiceApplication.class, args); } }
(4)自定義RESTful結(jié)果封裝類
public class ResponseResult<T> { private String code; private String msg; private T data; }
(5)創(chuàng)建響應(yīng)工具類
package org.shortenservice.common; public class ResultUtils { private ResultUtils() {} public static <T> ResponseResult success(T data) { return build("200", "success", data); } public static ResponseResult success() { return build("200", "success", null); } public static boolean isSuccess(String code) { return "200".equals(code); } public static ResponseResult failure(String msg) { return build("500", msg, null); } public static ResponseResult failure(String code, String msg) { return build(code, msg, null); } public static <T> ResponseResult failure(String code, String msg, T data) { return build(code, msg, data); } public static <T> ResponseResult<T> build(String code, String msg, T data) { return new ResponseResult<>(code, msg, data); } }
二、核心功能實現(xiàn)??
1. 實現(xiàn)Base62編碼
package org.shortenservice.utils; public class Base62Utils { private static final String BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; private Base62Utils() { } public static String idToShortKey(long id) { StringBuilder stringBuilder = new StringBuilder(); while (id > 0) { stringBuilder.append(BASE62.charAt((int) (id % 62))); id = id / 62; } while (stringBuilder.length() < 6) { stringBuilder.append(0); } return stringBuilder.reverse().toString(); } public static long shortKeyToId(String shortKey) { long id = 0; for (int i = 0; i < shortKey.length(); i++) { id = id * 62 + BASE62.indexOf(shortKey.charAt(i)); } return id; } }
方法解釋
idToShortKey方法:
- 創(chuàng)建一個StringBuilder對象,用于存儲轉(zhuǎn)換后的字符串。
- 使用while循環(huán),當id大于0時,執(zhí)行循環(huán)體。在循環(huán)體中,首先計算id除以62的余數(shù),然后將余數(shù)對應(yīng)的BASE62字符添加到StringBuilder對象中。接著,將id除以62,更新id的值。
- 當id小于等于0時,跳出循環(huán)。此時,StringBuilder對象中的字符串長度可能小于6。為了確保字符串長度為6,使用另一個while循環(huán),在StringBuilder對象的開頭添加0,直到其長度達到6。
- 最后,將StringBuilder對象反轉(zhuǎn),并將其轉(zhuǎn)換為字符串返回。
shortKeyToId方法:
- 創(chuàng)建一個名為id的長整型變量,初始值為0。
- 使用for循環(huán)遍歷shortKey字符串中的每個字符。在循環(huán)體中,首先計算當前字符在BASE62字符串中的索引值,然后將id乘以62,再加上當前字符的索引值。將結(jié)果賦值給id。
- 當所有字符都遍歷完畢后,返回id作為最終結(jié)果。
2. 創(chuàng)建實體類
package org.shortenservice.model; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.annotations.CreationTimestamp; import java.time.Instant; @Entity @Table(name = "t_url_map", indexes = {@Index(columnList = "longUrl", unique = true), @Index(columnList = "expireTime", unique = false)}) @Data @Builder @AllArgsConstructor @NoArgsConstructor public class UrlMap { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String longUrl; private Instant expireTime; @CreationTimestamp private Instant creationTime; }
3. 創(chuàng)建Dao層
package com.shorten.dao; import com.shorten.model.UrlMap; import org.springframework.data.repository.CrudRepository; import java.time.Instant; import java.util.List; public interface UrlMapDao extends CrudRepository<UrlMap, Long> { UrlMap findFirstByLongUrl(String longUrl); List<UrlMap> findByExpireTimeBefore(Instant instant); }
4. 創(chuàng)建service層
package org.shortenservice.service; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.shortenservice.dao.UrlMapDao; import org.shortenservice.model.UrlMap; import org.shortenservice.utils.Base62Utils; import org.springframework.stereotype.Service; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Optional; @Service @Slf4j public class UrlMapService { @Resource UrlMapDao urlMapDao; public String encode(String longUrl) { UrlMap urlMap = urlMapDao.findFirstByLongUrl(longUrl); if (urlMap == null) { urlMap = urlMapDao.save(UrlMap.builder() .longUrl(longUrl) .expireTime(Instant.now().plus(30, ChronoUnit.DAYS)) .build()); log.info("create urlMap:{}", urlMap); } return Base62Utils.idToShortKey(urlMap.getId()); } public Optional<String> decode(String shortKey) { long id = Base62Utils.shortKeyToId(shortKey); return urlMapDao.findById(id).map(UrlMap::getLongUrl); } }
5. 編寫測試接口
package org.shortenservice.controller; import jakarta.annotation.Resource; import org.shortenservice.common.ResponseResult; import org.shortenservice.common.ResultUtils; import org.shortenservice.service.UrlMapService; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.view.RedirectView; import java.util.Map; @RestController public class UrlMapController { private static final String DOMAIN = "http://127.0.0.1:8888/"; @Resource private UrlMapService urlMapService; /*** * 長鏈接轉(zhuǎn)短鏈接 * @param longUrl 長鏈接 * @return ResponseResult */ @PostMapping("/shorten") public ResponseResult<Map> shorten(@RequestParam("longUrl") String longUrl) { String encode = urlMapService.encode(longUrl); return ResultUtils.success(Map.of("shortKey", encode, "shortUrl", DOMAIN + encode)); } /*** * 短鏈接重定向 * @param shortKey 短鏈接 * @return RedirectView */ @GetMapping("/{shortKey}") public RedirectView redirect(@PathVariable("shortKey") String shortKey) { return urlMapService.decode(shortKey).map(RedirectView::new) .orElse(new RedirectView("/sorry")); } @GetMapping("/sorry") public String sorry() { return "抱歉,未找到頁面!"; } }
6. 使用curl測試
#將長鏈接轉(zhuǎn)為短鏈接 curl -XPOST "localhost:8888/shorten?longUrl=https://i.csdn.net/#/user-center/profile?spm=1011.2415.3001.5111" #訪問短鏈接重定向到目標網(wǎng)站 curl -i "http://127.0.0.1:8888/000003"
數(shù)據(jù)庫中也存儲了相關(guān)數(shù)據(jù)
三、系統(tǒng)優(yōu)化??
1. 緩存簡介
什么是緩存
緩存(Caching)是一種用于提高數(shù)據(jù)處理速度的技術(shù),涉及到了計算機硬件、操作系統(tǒng)、應(yīng)用程序等多個領(lǐng)域。緩存的主要原理是將經(jīng)常使用或最近使用的數(shù)據(jù)存儲在快速訪問的存儲設(shè)備中,這樣在需要這些數(shù)據(jù)時,就可以更快地獲取到它們,從而提高系統(tǒng)的整體性能。
在計算機科學中,緩存通常指的是存儲臨時數(shù)據(jù)的地方,這些數(shù)據(jù)可能是來自于計算過程中的結(jié)果,也可能是來自于磁盤、網(wǎng)絡(luò)等慢速存儲設(shè)備的數(shù)據(jù)副本。緩存中的數(shù)據(jù)通常是根據(jù)一定的算法進行管理和替換的,以確保緩存中的數(shù)據(jù)是最需要或最常用的。
引入的緩存用于解決哪些問題
- 高并發(fā)訪問: 在高并發(fā)情況下,數(shù)據(jù)源可能會受到過大的負載,通過緩存可以減輕數(shù)據(jù)源的壓力,提高系統(tǒng)的性能和響應(yīng)速度。
- 頻繁訪問: 對于頻繁被訪問的數(shù)據(jù),通過緩存可以減少重復的數(shù)據(jù)讀取,提高效率。
- 數(shù)據(jù)計算: 對于一些需要復雜計算的數(shù)據(jù),將計算結(jié)果緩存起來可以節(jié)省計算時間和資源。
- 數(shù)據(jù)共享: 緩存可以在不同的組件、模塊或服務(wù)之間共享數(shù)據(jù),提高數(shù)據(jù)的可用性和共享性。
- 離線訪問: 緩存可以在斷網(wǎng)或無法連接數(shù)據(jù)源的情況下,仍然提供某些數(shù)據(jù)的訪問能力。
本地緩存 VS 分布式緩存
分布式緩存:
概念: 分布式緩存是一種將緩存數(shù)據(jù)分布在多個服務(wù)器節(jié)點上的緩存系統(tǒng),用于存儲和管理大量的數(shù)據(jù)。
優(yōu)點:
- 可擴展性: 分布式緩存可以通過增加節(jié)點來實現(xiàn)水平擴展,以應(yīng)對大規(guī)模的數(shù)據(jù)和高并發(fā)訪問。
- 高可用性: 分布式緩存通常采用復制和備份機制,確保即使有節(jié)點故障,仍然能夠提供可靠的緩存服務(wù)。
- 跨節(jié)點共享: 多個應(yīng)用實例可以共享同一分布式緩存,提高數(shù)據(jù)共享和協(xié)作能力。
- 靈活的存儲后端: 分布式緩存可以支持多種后端存儲,如內(nèi)存、磁盤、數(shù)據(jù)庫等。
缺點:
- 復雜性: 部署、配置和管理分布式緩存系統(tǒng)可能較為復雜,需要考慮分布式系統(tǒng)的一些挑戰(zhàn),如一致性、網(wǎng)絡(luò)延遲等。
- 性能開銷: 分布式緩存通常需要在網(wǎng)絡(luò)上進行數(shù)據(jù)傳輸,可能引入一些性能開銷。
本地緩存:
概念: 本地緩存是將緩存數(shù)據(jù)存儲在應(yīng)用程序的本地內(nèi)存中,用于臨時保存常用的數(shù)據(jù)。
優(yōu)點:
- 簡單性: 本地緩存相對較簡單,不需要搭建額外的分布式緩存系統(tǒng)。
- 低延遲: 由于數(shù)據(jù)存儲在本地內(nèi)存中,本地緩存通常具有低延遲的讀取速度。
- 少量數(shù)據(jù): 本地緩存適用于存儲相對較小的數(shù)據(jù)量,不需要進行分布式存儲和管理。
缺點:
- 有限的擴展性: 本地緩存只能在單個應(yīng)用實例內(nèi)使用,無法滿足多實例和分布式應(yīng)用的需求。
- 數(shù)據(jù)一致性: 不同應(yīng)用實例的本地緩存可能存在數(shù)據(jù)不一致的問題,需要額外的機制來解決。
2. 引入Guava
更新service層的代碼
package org.shortenservice.service; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.shortenservice.dao.UrlMapDao; import org.shortenservice.model.UrlMap; import org.shortenservice.utils.Base62Utils; import org.springframework.stereotype.Service; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Optional; @Service @Slf4j public class UrlMapService { @Resource UrlMapDao urlMapDao; @Resource LoadingCache<String, String> loadingCache; @PostConstruct public void init() { CacheLoader<String, String> cacheLoader = new CacheLoader<String, String>() { @Override public String load(String s) throws Exception { long id = Base62Utils.shortKeyToId(s); log.info("load cache: {}", s); return urlMapDao.findById(id).map(UrlMap::getLongUrl).orElse(null); } }; loadingCache = CacheBuilder.newBuilder() .maximumSize(1000000) // 設(shè)置最大緩存大小 .build(cacheLoader); } public String encode(String longUrl) { UrlMap urlMap = urlMapDao.findFirstByLongUrl(longUrl); if (urlMap == null) { urlMap = urlMapDao.save(UrlMap.builder() .longUrl(longUrl) .expireTime(Instant.now().plus(30, ChronoUnit.DAYS)) .build()); log.info("create urlMap:{}", urlMap); } return Base62Utils.idToShortKey(urlMap.getId()); } public Optional<String> decode(String shortKey) { return Optional.ofNullable(loadingCache.getUnchecked(shortKey)); } }
提示:由于短鏈接系統(tǒng)通常需要處理大量的用戶請求和數(shù)據(jù),因此需要具有高效和可擴展性。同時,由于短鏈接可能涉及到用戶隱私和安全問題,短鏈接系統(tǒng)也需要符合相關(guān)的數(shù)據(jù)保護和安全標準。
到此這篇關(guān)于SpringBoot實現(xiàn)短鏈接系統(tǒng)的使用示例的文章就介紹到這了,更多相關(guān)SpringBoot 短鏈接內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis Interceptor 攔截器的實現(xiàn)
這篇文章主要介紹了Mybatis Interceptor 攔截器的實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12Java基礎(chǔ)篇_有關(guān)接口和抽象類的幾道練習題(分享)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)篇_有關(guān)接口和抽象類的幾道練習題(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06Mybatis如何自動生成數(shù)據(jù)庫表的實體類
這篇文章主要介紹了Mybatis自動生成數(shù)據(jù)庫表的實體類的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06