In daily life, I can see all kinds of strange short links. Every time I click on the jump, I will feel amazed. How does this short link lead the user to the right page?
Short chain principle
The principle of short chains is that short strings are long, so how does a short string become a long string of links? By some magical encryption algorithm? No, we can easily achieve this seemingly magical short and long by relying on the key/value mapping.
Using a picture, you can see the whole process of accessing the short chain very clearly.
First, we have a long link, and the short chain service usually outputs a URL with only one level of directory, which we can then distribute.
Then it comes to the user side. After the user clicks the short link, the first destination is not the target page, but the short link service.
The short-chain service intercepts the pathname on the link and treats it as a key to look up the corresponding value in the mapping.
If the corresponding value cannot be found, it indicates that the short chain does not exist or is invalid. If the query is successful, a short-chain access is performed by the short-chain service directly to 302 to the target link in value.
The specific implementation
Materials: Fast-nest scaffolding, Redis
The entire implementation is split into three parts:
① Receive long links
@Post('/createUrl')
async createUrl(
@Body('url') url: string.@Body('type') type: string.) {
const shortUrl = await this.shorturlService.createUrl(url, type);
return {
shortUrl,
};
}
Copy the code
Create a createUrl interface in the service, receive the URL and type field, pass it into the shorturlService, wait for the short link to be generated and output.
(2) generate the shortKey
async createUrl(url: string.type: string = 'normal') {
const urlKey = await this.handleUrlKey();
const dataStr = JSON.stringify({
url,
type
});
await this.client.set(urlKey, dataStr, type= = ='permanent' ? -1 : 300);
return `${Config.defaultHost}/${urlKey}`;
}
private asynchandleUrlKey(count? :number) :Promise<string> {
const _count = count || 1;
const maxCount = Config.maxRetryTimes;
if (_count >= maxCount) throw new HttpException('Number of retries exceeded, please regenerate the link', HttpStatus.INTERNAL_SERVER_ERROR);
const urlKey: string = Math.random().toString(36).slice(-4);
const _url = await this.client.get(urlKey);
if (_url) {
return await this.handleUrlKey(_count + 1);
}
return urlKey;
}
Copy the code
First fetch a 4-bit random string through math.random ().toString(36).slice(-4), which will be used as the pathname for the short chain.
Before mapping, we need to make a unique judgment on it. Although it is unlikely to happen, we still need to prevent problems such as short chain coverage. The solution of this service is retry generation. If the short chain value is unfortunately repeated, it will enter the retry branch, and the service will have a built-in number of retries. If the number of retries exceeds the configured word count, the conversion will return a failure.
In addition to the URL, the createUrl method also accepts a Type field, which refers to the properties of special short chains. We have three modes of short chain:
- Normal – A normal short link that will expire within a specified time
- Once – A one-time short link that will expire within a specified period of time and automatically expire after being accessed
- Permanent – Long-term short link, does not automatically fail, can only be manually deleted
After the urlKey is generated, it will be converted into a string with type and stored in Redis, and the short link will be printed.
③ Receive the short link and complete the target redirect
@Get('/:key')
@Redirect(Config.defaultIndex, 302)
async getUrl(
@Param('key') key: string.) {
if (key) {
const url = await this.shorturlService.getUrl(key);
return {
url
}
}
}
// this.shorturlService.getUrl
async getUrl(k: string) {
const dataStr = await this.client.get(k);
if(! dataStr)return;
const { url, type } = JSON.parse(dataStr);
if (type= = ='once') {
await this.client.del(k);
}
return url;
}
Copy the code
User side will GET a similar link of http://localhost:8000/s/ku6a, click on the following rather then give a short link service sends a GET request.
After receiving the request, the service gets the value of the key field in the link, which is the string ku6a, and uses it to find the mapping in Redis.
The redirection decorator redirects the request to the default destination link. The redirection decorator returns the request to the default destination link. The redirection decorator redirects the request to the default destination link.
If the relevant value is successfully found in Redis, the URL and type fields will be read. If the type is once, it means that this is a one-time link, which will actively trigger the deletion method and eventually return the target link.
Additional functionality
Use the logging system to output reports
When using short links, there is a large probability that relevant data statistics will be required. How to carry out data statistics without using databases?
In this service, we can scan the landing log file to complete the report of the day’s short chain access.
When generating short links, add the urlID field for statistical differentiation and actively output logs as follows:
async createUrl(url: string.type: string = 'normal') {
const urlKey = await this.handleUrlKey();
const urlID = UUID.genV4().toString();
const dataStr = JSON.stringify({
urlID,
url,
type
});
this.myLogger.log(`createUrl**${urlID}`.'createUrl'.false);
await this.client.set(urlKey, dataStr, type= = ='permanent' ? -1 : 300);
return `${Config.defaultHost}/${urlKey}`;
}
Copy the code
Then, when the user clicks the short link, the urlID field of the short link is obtained, and the log is actively output as follows:
async getUrl(k: string) {
const dataStr = await this.client.get(k);
if(! dataStr)return;
const { url, type, urlID } = JSON.parse(dataStr);
if (type= = ='once') {
await this.client.del(k);
}
this.myLogger.log(`getUrl**${urlID}`.'getUrl'.false);
return url;
}
Copy the code
We will then be able to get something like this in the logs directory of the service:
2021-04-25 22:31:03.306 INFO [11999] [-] createUrl** 3f577625-474A-4e30-9933-e469ce3b0dcf 2021-04-25 22:31:38.323 INFO [11999] [-] getUrl** 3f577625-474A-4e30-9933-e469ce3b0dcF 2021-04-25 22:31:39.399 INFO [11999] [-] GetUrl ** 3f577625-474A-4e30-9933-e469ce3b0dcF 2021-04-25 22:31:40.281 INFO [11999] [-] GetUrl ** 3f577625-474A-4e30-9933-e469ce3b0dcF 2021-04-25 22:31:40.997 INFO [11999] [-] GetUrl ** 3f577625-474A-4e30-9933-e469ce3b0dcF 2021-04-25 22:31:41.977 INFO [11999] [-] GetUrl ** 3f577625-474A-4e30-9933-e469ce3b0dcF 2021-04-25 22:31:42.870 INFO [11999] [-] GetUrl ** 3f577625-474A-4e30-9933-e469ce3b0dcF 2021-04-25 22:31:43.716 INFO [11999] [-] GetUrl ** 3f577625-474A-4e30-9933-e469ce3b0dcF 2021-04-25 22:31:44.614 INFO [11999] [-] getUrl**3f577625-474a-4e30-9933-e469ce3b0dcfCopy the code
CreateUrl = getUrl; createUrl = getUrl; getUrl = getUrl; getUrl = getUrl;
use
According to the above process, the author wrote a relatively simple short chain service, we can use out of the box.
Shorturl (Star⭐️⭐️)
Specific startup mode
Make sure you have redis available first, otherwise the service cannot start smoothly.
git clone https://github.com/mykurisu/shorturl.git
cd shorturl
npm install
npm start
Copy the code
Available configuration modification
The configuration related to the short chain is clustered in the root directory config.ts.
serverConfig: {
port: 8000,},redis: {
port: 6379.host: '0.0.0.0'.db: 0,},cacheType: 'redis'.defaultHost: 'http://localhost:8000/s'.defaultIndex: 'http://localhost:8000/defaultIndex'.Copy the code
configuration | The default value | Configuration utility |
---|---|---|
serverConfig.port | 8000 | Service startup port |
redis.port | 6379 | Redis port |
redis.host | 0.0.0.0 | Redis service address |
redis.db | 0 | Redis specific repository table |
cacheType | redis | Short chain storage mode, accept memory/ Redis |
maxRetryTimes | 5 | Maximum number of retries for generating a short link |
defaultHost | http://localhost:8000/s | Short link prefix |
defaultIndex | http://localhost:8000/defaultIndex | Redirect address after short link failure |
Built-in interface
Interface routing | Request way | The interface parameters | Interface USES |
---|---|---|---|
/s/createUrl | POST | url: string, type? : string | Short links generate interfaces |
/s/deleteUrl | POST | k: string | Delete the short link interface |
/s/:key | GET | none | Target link acquisition |
expand
① Storage degrade policy
Shorturl has a local storage scheme, which means we can listen to the status of Redis and temporarily store data in memory if the connection is disconnected in order to degrade the service. Of course, we can also use memory directly to store the short chain content, which can be changed in the config.ts configuration.
② It is not just short link service
Shorturl is already a micro storage service and can be redeveloped to export more modules to support a wider variety of services.
summary
The whole short link service is actually very simple, the trouble is to build the service, which is the first step. The author also struggled in the initial step for countless times, and finally accumulated such a scaffold as fast-nest, hoping to help students in the same situation.
Also, attach the source code for this article – Shorturl (welcome everyone Star⭐️⭐️)