【.com快译】URL短链接生成器是一种根据冗长的URL创建短链接的服务。通常,短链接只有原始URL长度的三分之一,甚至四分之一。因此它们更易于键入、呈现和推送。用户只需点击短链接即可自动重定向至原网址。目前,tiny.cc、bitly.com和cutt.ly都提供在线URL缩短服务。当然,您也可以自己为应用系统设计开发网址缩短服务。下面,我就和大家一起探讨一下具体的实现过程。首先,让我们探讨与之相关的功能和非功能需求。功能需求:保存用户输入的长URL,并据此生成相应的短链接。允许用户选择到期日期,以便生成的短链接在该日期后自动失效。方便用户点击短链接后重定向到原来的长链接。作为一种选择,允许用户创建服务帐户并使生成的短链接仅对该帐户有效。或者,允许用户自己创建短链接。或者,允许用户标记最常访问的链接。非功能性要求:构建服务具有持续的可用性和可访问性。重定向不应超过2秒。URL如何转换URL短链接生成器中最重要的是转换算法。不同的转换方法通常会产生不同的输出,它们各有优缺点。假设我们需要一个最大长度为7个字符的短链接。然后我们可以使用MD5或SHA-2等哈希函数对原始URL进行哈希处理。由于散列的结果将超过7个字符,因此我们只取前7个字符。但是,由于前7个字符可能已经被其他短链接使用,可能会造成冲突,所以我们需要依次截取后面的7个字符,直到找到一个被使用的短链接。生成短链接的第二种方法是使用UUID。UUID被复制的概率几乎为零,因此可以完全忽略冲突的可能性。由于UUID是由36个字符组成,可能还是会遇到上面的问题,所以我们应该截取前7个字符,然后查看组合是否已经被占用。第三种方法是将数字从Base10转换为Base62。Base是可用于表示特定数字的字符数。10进制是我们日常生活中使用的数字,即:[0-9],而62进制是:[0-9][az][AZ]。这意味着以10为底数的四位数字将与以62为底数的数字相同,但有两个字符。所以在URL转换中,使用最大长度为7个字符的Base62可以让我们为短链接提供62^7个唯一值。Base62的转换机制我使用下面的算法将一个10进制数转换为62进制数:while(number>0)remainder=number%62number=number/62attachremaindertostartofresultcollection据此,我们只需要映射结果集为基数为62的字符[0,1,2,...,a,b,c...,A,B,C,...]。下面,我将通过采用将1000从Base10转换为Base62的示例。1stiteration:number=1000remainder=1000%62=8number=1000/62=16resultlist=[8]2nditeration:number=16remainder=16%62=16number=16/62=0resultlist=[16,8]Thereisnomoreiterationsssincenumber=0after2nditeration[16,8]映射到Base62后变成g8,即1000base10=g8base62。Base62转Base10的过程也很简单,即:i=0while(igetAndRedirect(@PathVariableStringrgetshortUrl){Original(Original=urlshortU);returnResponseEntity.status(HttpStatus.FOUND).location(URI.create(url)).build();}其中,POST方法会使用UrlLongRequest作为请求体。这是一个具有longURL和expiresDate属性的类。GET方法将使用一个短URL作为路径变量来获取并重定向到原始URL。在controller的上层,会注入urlService作为后续解释的依赖。UrlService.java不仅包含了很多逻辑,还为controller提供了服务。ConvertToShortUrl仅由控制器的POST方法使用。它只是在数据库中创建一条新记录并获取一个id,因此它可以转换为Base62短链接并返回给控制器。控制器使用GetOriginalUrl方法首先将字符串转换为Base10类型的ID。然后它通过该ID从数据库中获取记录。当然,如果记录不存在,则会抛出异常。最后,它将原始URL返回给控制器。下面,我将讨论Swagger文档、应用程序dockerization(容器化)、缓存和MySQL的计划事件。在开发过程中对Swagger的用户界面进行文档化无疑会使API更易于理解和使用。在这个项目中,我使用SwaggerUI来记录API。SwaggerUI允许任何人在没有任何实现逻辑的情况下可视化API资源并与之交互。不仅可以自动生成,还具有可视化的文档,方便后台的实现和客户端的使用。我通过执行以下步骤在我的项目中引入了SwaggerUI。首先,我在pom.xml文件中添加了一个Maven依赖:XMLio.springfoxspringfox-swagger22.9.2/dependency>io.springfoxspringfox-swagger-ui2.9.2添加Maven依赖后,我们就可以了添加Swagger的相关配置。在“Configuration”文件夹中,我新建了一个类——SwaggerConfig.java,请参考下面的代码片段。Java@Configuration@EnableSwagger2publicclassSwaggerConfig{@BeanpublicDocktapiDocket(){returnnewDocket(DocumentationType.SWAGGER_2).apiInfo(metadata()).select().apis(RequestHandlerSelectors.basePackage("com.amarin")).builddata();}private(ApiInfometa){returnnewApiInfoBuilder().title("UrlshortenerAPI").description("APIreferencefordevelopers").version("1.0").build();}}在类的顶部,我添加了以下注释:@Configuration表示一个类声明了一个或多个@Beans方法,可以被Spring容器处理,在运行时为这些bean生成相应的定义和服务请求。@EnableSwagger2指示应启用Swagger支持。接下来,我添加了Docketbean。它提供的主要API配置都有各种合理的默认值和便捷的配置方式。这里的apiInfo()方法除了可以接受默认值外,还可以接受ApiInfo对象,这样我们就可以配置所有需要的API信息。为了让代码更简洁,我们可以创建一个私有方法——metadata()来配置并返回ApiInfo对象,并将该方法作为apiInfo()方法的参数传递。同时,apis()方法还允许我们过滤那些记录在案的包。完成SwaggerUI的配置后,我们就可以开始编写API文档了。在UrlController内的每个端点上,我们可以使用@ApiOperation添加描述性注释。当然,你也可以根据需要使用其他类型的注解。我们还可以记录DTO并使用@ApiModelProperty添加各种允许值和描述。缓存根据维基百科的定义,缓存是一种存储数据的软件和硬件组件,可用于更快地处理后续对同一数据的请求。缓存中存储的数据通常是早期计算的结果,或者是已存储在别处的数据的副本。目前,最常用的缓存类型是内存缓存。它能够将缓存数据存储到RAM中。当请求的数据与缓存一致时,就从RAM中获取,而不是从数据库中获取。因此,我们避免了频繁调用后端的开销。由于可以将URL缩短器视为读取多于写入的请求应用程序,因此它是使用缓存的理想应用程序。要在SpringBoot应用程序中启用缓存,我们只需将@EnableCaching注解添加到UrlShortenerApiApplication类。接下来,在controller中,我们需要在GET方法上设置@Cachable注解,将方法调用的结果自动存储到缓存中。在@Cachable注解中,我设置了缓存名称的value参数和缓存键的key参数。鉴于缓存键的唯一性,我使用了“shortUrl”并将Sync参数设置为true以确保只有一个线程正在构建缓存值。到目前为止,当我们第一次加载带有短链接的URL时,结果将保存在缓存中。稍后,任何想要调用同一个短链接的端点将从缓存中而不是从数据库中检索结果。DockerizationDockerization是将应用程序及其依赖项打包到Docker容器中的过程。一旦配置了Docker容器,我们就可以轻松地在任何支持Docker的服务器或主机上运行应用程序。因此,我们首先需要创建一个包含所有命令的Dockerfile文本文件,这样用户就可以通过调用命令行来挂载某个镜像。DockerfileFROMopenjdk:13-jdk-alpineCOPY./target/url-shortener-api-0.0.1-SNAPSHOT.jar/usr/src/app/url-shortener-api-0.0.1-SNAPSHOT.jarEXPOSE8080ENTRYPOINT["java","-jar","/usr/src/app/url-shortener-api-0.0.1-SNAPSHOT.jar"]FROM:表示需要构建的基础镜像。我正在使用Java的免费开源版本-OpenJDKv13。您还可以在共享Docker镜像平台-Dockerhub(https://hub.docker.com/)上找到其他类型的基础镜像。COPY:该命令将文件从本地文件系统复制到指定路径的容器文件系统。在这里,我将目标文件夹中的JAR文件复制到容器中的/usr/src/app文件夹中(稍后我将解释如何创建JAR文件)。EXPOSE:负责通知Docker容器在运行时监听指定的网络端口。它的默认协议是TCP,你也可以使用UDP。ENTRYPOINT:负责配置可执行容器。在这里,我使用命令“java-jar.jar”来指定Docker如何运行.jar文件类型的应用程序。为了在项目中创建.jar文件以便Dockerfile中的COPY命令起作用,我使用Maven创建可执行文件.jar。如果你的pom.xml缺少Maven,请通过以下方式添加:XMLorg.springframework.bootspring-boot-maven-plugin然后,我运行命令:mvncleanpackage来构建Docker映像。接下来,在Dockerfile文件夹中,我运行了命令:dockerbuild-turl-shortener:latest。其中-t可以用来标记一个镜像,实现版本控制。在这里,最新的存储库URL缩短器。我们可以使用命令“dockerimages”来创建镜像。屏幕上显示的结果是:最后,我还需要在docker容器中构建一个MySQL服务器镜像,方便数据库容器与应用容器的隔离。为此,我在Docker容器中运行了以下命令:$dockerrun--nameshortener-eMYSQL_ROOT_PASSWORD=my-secret-pw-d-p3306:3306mysql:8您可以在Dockerhub上查看相关文档。为了在容器中运行数据库,我通过配置将现有应用程序连接到MySQL服务器。即:在application.properties中设置spring.datasource.url来连接shortener容器。然后,我使用如下命令运行构建好的Docker镜像容器:dockerrun-d--nameurl-shortener-api-p8080:8080--linkshortenerurl-shortener-d表示Docker容器在终端后台运行。--name设置容器的名称。-phost-port:docker-port:是将本地端口映射到容器中的端口。在这个例子中,我在容器内部暴露了8080端口,并在本地映射到8080。--link:用于链接应用容器和数据库容器,实现容器间的相互发现和安全传输。url-shortener:表示要运行的Docker镜像的名称。此时,我们可以在浏览器中访问http://localhost:8080/swagger-ui.html。通过在DockerHub上发布镜像,任何计算机和服务器都可以轻松运行该应用程序。当然,为了提升Docker的体验,我们需要注意多阶段构建和docker-compose。多阶段构建使用多阶段构建,您将能够在Dockerfile中使用多个FROM语句。每条FROM指令都可以使用不同的基础,每条指令都可以开始构建的新阶段。您可以有选择地将单个工件从一个阶段复制到另一个阶段,并去除最终图像中不需要的内容。多阶段构建帮助我们避免每次更改代码时都必须手动重建.jar文件。因此,我们可以定义一个构建阶段来执行Maven包命令。另一个阶段将第一次构建的结果直接复制到Docker容器的文件系统中。您可以通过链接查看完整的Dockerfile-https://github.com/AnteMarin/UrlShortener-API/blob/develop/Dockerfile。Docker-composeCompose是一个用于定义和运行多容器Docker应用程序的工具。借助Compose,您可以使用YAML文件配置应用程序的服务,然后使用单个命令从配置中创建和启动所有服务。使用docker-compose我们能够将应用程序和数据库打包到一个配置文件中以同时运行所有内容。因此,我们避免了每次运行MySQL容器并将其链接到应用程序容器的麻烦。从Docker-compose.yml文件的具体配置内容可以看出:首先,我们通过设置镜像mysqlv8.0和MySQL服务器的凭证来配置MySQL容器。接下来,我们通过设置构建参数来配置应用程序容器。毕竟我们需要的是镜像,而不是用MySQL来拉取。另外,我们还需要通过设置让应用容器依赖于MySQL容器。最后,我们可以使用命令“docker-composeup”来运行整个项目。MySQL计划事件(ScheduledEvent)当涉及到短链接的过期设置时,我们可以让用户自定义或者保持默认值。为此,我们可以在数据库中设置一个预定事件。通过每x分钟运行一次事件,只要过期时间小于当前时间,数据库就会自动删除一行,就这么简单。这对于在数据库中保存少量数据非常有用。但是,这种方法有两个值得注意的问题:首先,该事件只是从数据库中删除记录,而不是从缓存中删除数据。如前所述,如果缓存可以找到匹配的数据,则不会检查数据库。因此,即使数据库中删除了一个短链接,我们仍然可以从缓存中获取它。其次,在示例脚本中,我将事件设置为每2分钟运行一次。如果数据库的记录发生较大变化,可能前一个事件没有在其预定的时间间隔内执行,而后一个事件已经被触发,就会出现多个事件实例同时执行的混乱情况。总结通过上面的例子和讨论,我向你展示了如何使用Java和SpringBoot创建一个URL短链接生成器API。这是一个非常常见的面试问题,您可以创建自己的改进版本,或者从上述GitHub克隆项目的存储库并创建自己的前端。原标题:URLShortenerCompleteTutorial,作者:AnteMarin