URL 短縮サービスの実装は複雑な作業ではなく、多くの場合、システム設計の面接の一部です。この投稿では、サービスを実装するプロセスについて説明します。 URL 短縮サービスは、非常に長い URL から短いリンクを作成するために使用されるサービスです。
通常、短いリンクは元の URL の 3 分の 1 または 4 分の 1 のサイズであるため、入力、提示、またはツイートが容易になります。短縮リンクをクリックすると、ユーザーは元の URL に自動的にリダイレクトされます。 tiny.cc、bitly.com、cutt.ly など、オンラインで利用できる URL 短縮サービスは多数あります。
実装する前に、何を行う必要があるかを機能要件と非機能要件の形で書き留めておくことをお勧めします。
最大長 7 の短いリンクが必要だとしましょう。URL 短縮サービスで最も重要なことは、変換アルゴリズムです。 URL 変換はいくつかの異なる方法で実装でき、それぞれの方法には長所と短所があります。
短いリンクを生成する 1 つの方法は、元の URL を何らかのハッシュ関数 (例: MD5またはSHA-2 ) でハッシュすることです。ハッシュ関数を使用する場合、異なる入力が異なる出力になることは確かです。ハッシュの結果は 7 文字よりも長いため、最初の 7 文字を取得する必要があります。ただし、この場合、最初の 7 文字が既に短いリンクとして使用されている可能性があるため、競合が発生する可能性があります。次に、使用されていない短いリンクが見つかるまで、次の 7 文字を取得します。
短いリンクを生成する 2 つ目の方法は、 UUIDを使用することです。 UUID が重複する確率はゼロではありませんが、無視できるほどゼロに近い値です。 UUID は 36 文字なので、上記と同じ問題が発生します。最初の 7 文字を取得して、その組み合わせが既に使用されているかどうかを確認する必要があります。
3 番目のオプションは、数値を基数 10 から基数 62 に変換することです。基数とは、特定の数値を表すために使用できる数字または文字の数です。 10 進数は日常生活で使用する [0-9] の数字で、62 進数は [0-9][az][AZ] です。これは、たとえば、10 進数で 4 桁の数値は、62 進数で 2 文字の同じ数値になることを意味します。
URL 変換で base 62 を使用し、最大長を 7 文字にすると、短いリンクに 62^7 の一意の値を使用できます。
では、base 62 変換はどのように機能するのでしょうか?
基数 10 の数値を基数 62 に変換します。次のアルゴリズムを使用します。
while(number > 0) remainder = number % 62 number = number / 62 attach remainder to start of result collection
その後、結果のコレクションの数値を 62 進数の Alphabet = [0,1,2,…,a,b,c…,A,B,C,…] にマッピングするだけです。
これが実際の例でどのように機能するかを見てみましょう。この例では、1000 を基数 10 から基数 62 に変換してみましょう。
1st iteration: number = 1000 remainder = 1000 % 62 = 8 number = 1000 / 62 = 16 result list = [8] 2nd iteration: number = 16 remainder = 16 % 62 = 16 number = 16 / 62 = 0 result list = [16,8] There is no more iterations since number = 0 after 2nd iteration
[16,8] を base 62 にマッピングすると g8 になります。これは、1000base10 = g8base62 であることを意味します。
基数 62 から基数 10 への変換も簡単です。
i = 0 while(i < inputString lenght) counter = i + 1 mapped = base62alphabet.indexOf(inputString[i]) // map character to number based on its index in alphabet result = result + mapped * 62^(inputString lenght - counter) i++
実際の例:
inputString = g8 inputString length = 2 i = 0 result = 0 1st iteration counter = 1 mapped = 16 // index of g in base62alphabet is 16 result = 0 + 16 * 62^1 = 992 2nd iteration counter = 2 mapped = 8 // index of 8 in base62alphabet is 8 result = 992 + 8 * 62^1 = 1000
注: ソリューション全体は私のGithubにあります。 Spring Boot と MySQL を使用してこのサービスを実装しました。
データベースの自動インクリメント機能を使用します。自動インクリメント番号は、base 62 変換に使用されます。自動インクリメント機能を持つ他のデータベースを使用できます。
まず、 Spring initializrにアクセスし、Spring Web と MySql Driver を選択します。その後、生成ボタンをクリックしてzipファイルをダウンロードします。ファイルを解凍し、お気に入りの IDE でプロジェクトを開きます。新しいプロジェクトを開始するたびに、いくつかのフォルダーを作成して、コードを論理的に分割するのが好きです。この場合の私のフォルダーは、コントローラー、エンティティー、サービス、リポジトリー、dto、および構成です。
エンティティ フォルダー内に、id、longUrl、createdDate、expiresDate の 4 つの属性を持つUrl.javaクラスを作成しましょう。
ショート リンク属性がないことに注意してください。短いリンクは保存されません。 GET リクエストがあるたびに、id 属性を base 10 から base 62 に変換します。このようにして、データベースのスペースを節約しています。
LongUrl 属性は、ユーザーが短いリンクにアクセスしたときにリダイレクトする URL です。作成日は、longUrl がいつ保存されたかを確認するためのものであり (重要ではありません)、ユーザーがしばらくしてから短いリンクを使用できないようにしたい場合は、expiresDate があります。
次に、サービス フォルダーにBaseService .javaを作成しましょう。 BaseService には、base 10 から base 62 に、またはその逆に変換するメソッドが含まれています。
private static final String allowedString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; private char[] allowedCharacters = allowedString.toCharArray(); private int base = allowedCharacters.length;
前に述べたように、base 62 変換を使用する場合は、base 62 アルファベットが必要です。この場合は、allowedCharacters と呼ばれます。また、許可された文字を変更したい場合に備えて、ベース変数の値は許可された文字の長さから計算されます。
encode メソッドは数値を入力として取り、短いリンクを返します。デコード メソッドは文字列 (短いリンク) を入力として取り、数値を返します。アルゴリズムは、上で説明したように実装する必要があります。
その後、リポジトリ フォルダー内にUrlRepository .javaファイルを作成します。これは JpaRepository の単なる拡張であり、「findById」、「save」などの多くのメソッドを提供します。他に何も追加する必要はありません。これに。
次に、コントローラ フォルダに UrlController.java ファイルを作成しましょう。コントローラーには、短いリンクを作成するための 1 つの POST メソッドと、元の URL にリダイレクトするための 1 つの GET メソッドが必要です。
@PostMapping("create-short") public String convertToShortUrl(@RequestBody UrlLongRequest request) { return urlService.convertToShortUrl(request); } @GetMapping(value = "{shortUrl}") public ResponseEntity<Void> getAndRedirect(@PathVariable String shortUrl) { var url = urlService.getOriginalUrl(shortUrl); return ResponseEntity.status(HttpStatus.FOUND) .location(URI.create(url)) .build(); }
POST メソッドには、リクエストボディとしてUrlLongRequestがあります。これは、longUrl および expiresDate 属性を持つ単なるクラスです。
GET メソッドは短い URL をパス変数として取り、取得して元の URL にリダイレクトします。コントローラーの上部に、UrlService が依存関係として挿入されます。これについては次に説明します。
UrlService .javaは、ほとんどのロジックが存在する場所であり、コントローラーによって使用されるサービスです。
ConvertToShortUrl は、コントローラーからの POST メソッドによって使用されます。データベースに新しいレコードを作成し、ID を取得するだけです。その後、ID は Base 62 ショート リンクに変換され、コントローラに返されます。
GetOriginalUrl は、コントローラーからの GET メソッドで使用されるメソッドです。最初に文字列を 10 進数に変換し、その結果が ID になります。次に、その ID を持つデータベースからレコードを取得し、存在しない場合は例外をスローします。その後、元の URL をコントローラーに返します。
このパートでは、 swaggerのドキュメント、アプリケーションの Docker 化、アプリケーション キャッシュ、MySql のスケジュールされたイベントについて説明します。
API を開発するたびに、なんらかの方法で文書化することをお勧めします。ドキュメントは、API の理解と使用を容易にします。このプロジェクトの API は、Swagger UI を使用して文書化されています。
Swagger UIを使用すると、実装ロジックを用意しなくても、誰でも API のリソースを視覚化して操作できます。
これは自動的に生成され、視覚的なドキュメントにより、バックエンドの実装とクライアント側での使用が容易になります。
プロジェクトに Swagger UI を含めるには、いくつかの手順を実行する必要があります。
まず、Maven の依存関係を pom.xml ファイルに追加する必要があります。
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>
ご参考までに、完全な pom.xml ファイルはこちらでご覧いただけます。 Maven 依存関係を追加したら、Swagger 構成を追加します。 config フォルダー内に、新しいクラスSwaggerConfig .javaを作成する必要があります。
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket apiDocket() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(metadata()) .select() .apis(RequestHandlerSelectors.basePackage("com.amarin")) .build(); } private ApiInfo metadata(){ return new ApiInfoBuilder() .title("Url shortener API") .description("API reference for developers") .version("1.0") .build(); } }
クラスの先頭に、いくつかの注釈を追加する必要があります。
@Configurationは、クラスが 1 つ以上の@Beansメソッドを宣言し、Spring コンテナーによって処理されて、実行時に Bean 定義とそれらの Bean のサービス要求を生成できることを示します。
@EnableSwagger2は、Swagger サポートを有効にする必要があることを示します。
次に、主要な API 構成に適切なデフォルトと構成のための便利なメソッドを提供する Docket Bean を追加する必要があります。
apiInfo()メソッドは、必要なすべての API 情報を構成できる ApiInfo オブジェクトを受け取ります。それ以外の場合は、いくつかのデフォルト値を使用します。コードを簡潔にするために、ApiInfo オブジェクトを構成して返すプライベート メソッドを作成し、そのメソッドをapiInfo()メソッドのパラメーターとして渡す必要があります。この場合は、 metadata()メソッドです。
apis()メソッドを使用すると、ドキュメント化されているパッケージをフィルタリングできます。
Swagger UI が構成され、API の文書化を開始できます。 UrlController内では、すべてのエンドポイントの上に@ApiOperationアノテーションを使用して説明を追加できます。必要に応じて、他の注釈を使用できます。
@ApiModelPropertyを使用してDTOを文書化することもできます。これにより、許可された値、説明などを追加できます。
ウィキペディアによると、 [キャッシュ](https://en.wikipedia.org/wiki/Cache_(computing)は、データを保存するハードウェアまたはソフトウェア コンポーネントであり、そのデータに対する将来のリクエストをより迅速に処理できるようにします。キャッシュは、以前の計算の結果であるか、別の場所に保存されているデータのコピーである可能性があります。
最も頻繁に使用されるタイプのキャッシュは、キャッシュされたデータを RAM に格納するインメモリ キャッシュです。データが要求されてキャッシュにある場合、データはデータベースではなく RAM から提供されます。このようにして、ユーザーがデータを要求したときにコストのかかるバックエンドを呼び出すことを回避します。
URL 短縮サービスは、書き込み要求よりも読み取り要求の方が多いアプリケーションの一種です。つまり、キャッシュを使用するのに理想的なアプリケーションです。
Spring Boot アプリケーションでキャッシュを有効にするには、 UrlShortenerApiApplicationクラスに@EnableCachingアノテーションを追加するだけです。
その後、 controllerで、 @Cachableアノテーションを GET メソッドの上に設定する必要があります。このアノテーションは、キャッシュと呼ばれるメソッドの結果を自動的に保存します。 @Cachable アノテーションでは、キャッシュの名前であるvalueパラメーターと、キャッシュのキーであるkeyパラメーターを設定します。
この場合、キャッシュ キーには「shortUrl」を使用します。これは一意であることが確実だからです。同期パラメーターは true に設定され、1 つのスレッドのみがキャッシュ値を構築するようにします。
それだけです – キャッシュが設定され、最初に短縮リンクを含む URL をロードすると、結果がキャッシュに保存され、同じ短縮リンクを使用したエンドポイントへの追加の呼び出しは、代わりにキャッシュから結果を取得します。データベースから。
Docker 化とは、アプリケーションとその依存関係を [Docker](https://en.wikipedia.org/wiki/Docker_(software) コンテナー) にパッケージ化するプロセスです。Docker コンテナーを構成すると、アプリケーションを任意の環境で簡単に実行できます。 Docker をサポートするサーバーまたはコンピューター。
まず、Dockerfile を作成する必要があります。
Dockerfileは、イメージを組み立てるためにユーザーがコマンド ラインで呼び出すことができるすべてのコマンドを含むテキスト ファイルです。
FROM openjdk:13-jdk-alpine COPY ./target/url-shortener-api-0.0.1-SNAPSHOT.jar /usr/src/app/url-shortener-api-0.0.1-SNAPSHOT.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","/usr/src/app/url-shortener-api-0.0.1-SNAPSHOT.jar"]
FROM – これは、ビルド ベースのベース イメージを設定する場所です。 Java の無料のオープンソース バージョンである OpenJDK v13 を使用します。 Docker イメージを共有する場所であるDocker ハブで、基本イメージの他のイメージを見つけることができます。
COPY – このコマンドは、ファイルをローカル ファイル システム (コンピューター) から、指定したパスにあるコンテナーのファイル システムにコピーします。 JAR ファイルをターゲット フォルダーからコンテナー内の /usr/src/app フォルダーにコピーします。 JARファイルの作成については後ほど説明します。
EXPOSE – コンテナーが実行時に指定されたネットワーク ポートをリッスンすることを Docker に通知する命令。デフォルトのプロトコルは TCP で、UDP を使用するかどうかを指定できます。
ENTRYPOINT – この命令により、実行可能ファイルとして実行されるコンテナーを構成できます。ここで、Docker がアプリケーションを使い果たす方法を指定する必要があります。
.jar ファイルからアプリケーションを実行するコマンドは次のとおりです。
java -jar <app_name>.jar
これらの 3 つの単語を配列に入れれば、それだけです。
Dockerfile ができたので、そこからイメージをビルドする必要があります。しかし、前に述べたように、最初にプロジェクトから .jar ファイルを作成して、Dockerfile の COPY コマンドが正しく機能するようにする必要があります。実行可能な .jar を作成するには、 mavenを使用します。
pom .xml内に Maven があることを確認する必要があります。 Maven が欠落している場合は、追加できます
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
その後、コマンドを実行するだけです
mvn clean package
それが完了したら、Docker イメージをビルドできます。このコマンドを実行できるように、Dockerfile と同じフォルダーにいることを確認する必要があります。
docker build -t url-shortener:latest .
-tは、画像にタグを付けるために使用されます。私たちの場合、これはリポジトリの名前が url-shortener になり、タグが最新になることを意味します。タグ付けは、イメージのバージョン管理に使用されます。そのコマンドが完了したら、コマンドでイメージを作成したことを確認できます
docker images
それは私たちにこのようなものを与えるでしょう
最後のステップとして、イメージを構築する必要があります。イメージと言うのは、Docker コンテナーで MySQL サーバーも実行するためです。データベース コンテナーは、アプリケーション コンテナーから分離されます。 DockerコンテナでMySQLサーバーを実行するには、単純に実行します
$ docker run --name shortener -e MYSQL_ROOT_PASSWORD=my-secret-pw -d -p 3306:3306 mysql:8
ドキュメントはDocker ハブで参照できます。
コンテナー内でデータベースを実行している場合、その MySQL サーバーに接続するようにアプリケーションを構成する必要があります。 application.properties内で spring.datasource.url を設定して、「shortener」コンテナーに接続します。
プロジェクトにいくつかの変更を加えたため、Maven を使用してプロジェクトを .jar ファイルにパックし、Dockerfile から Docker イメージを再度ビルドする必要があります。
Docker イメージができたので、コンテナーを実行する必要があります。コマンドでそれを行います
docker run -d --name url-shortener-api -p 8080:8080 --link shortener url-shortener
-dは、Docker コンテナーがターミナルのバックグラウンドで実行されることを意味します。 –nameを使用すると、コンテナの名前を設定できます
-p host-port:docker-port – これは、ローカル コンピューターのポートをコンテナー内のポートにマッピングするだけです。この場合、コンテナー内でポート 8080 を公開し、それをローカル ポート 8080 にマップすることにしました。
–linkこれにより、アプリケーション コンテナーとデータベース コンテナーをリンクして、コンテナーが相互に検出し、あるコンテナーに関する情報を別のコンテナーに安全に転送できるようにします。
このフラグは現在レガシーであり、近い将来削除されることを知っておくことが重要です。リンクの代わりに、2 つのコンテナー間の通信を容易にするネットワークを作成する必要があります。
url-shortener – 実行したい docker イメージの名前です。
これで完了です。ブラウザでhttp://localhost:8080/swagger-ui.html にアクセスします。
イメージを DockerHub に公開し、任意のコンピューターまたはサーバーでアプリケーションを簡単に実行できるようになりました。
Docker エクスペリエンスを向上させるために、さらに 2 つのことについて話したいと思います。 1 つはマルチステージ ビルドで、もう 1 つは docker-compose です。
マルチステージ ビルドでは、Dockerfile で複数のFROMステートメントを使用できます。各FROM命令は異なるベースを使用でき、それぞれがビルドの新しい段階を開始します。アーティファクトをあるステージから別のステージに選択的にコピーして、最終イメージに不要なものをすべて残すことができます。
マルチステージ ビルドは、コードに変更を加えるたびに手動で .jar ファイルを作成することを避けるのに適しています。マルチステージ ビルドでは、Maven パッケージ コマンドを実行するビルドの 1 つのステージを定義し、もう 1 つのステージで最初のビルドの結果を Docker コンテナーのファイル システムにコピーすることができます。
ここで完全な Dockerfile を確認できます。
Composeは、複数コンテナーの Docker アプリケーションを定義して実行するためのツールです。 Compose では、YAML ファイルを使用してアプリケーションのサービスを構成します。次に、1 つのコマンドで、構成からすべてのサービスを作成して開始します。
docker-compose を使用すると、アプリケーションとデータベースを単一の構成ファイルにパックし、すべてを一度に実行します。このようにして、毎回 MySQL コンテナーを実行してアプリケーション コンテナーにリンクすることを回避します。
Docker- compose .ymlはほとんど一目瞭然です。まず、イメージ mysql v8.0 と MySQL サーバーの資格情報を設定して、MySQL コンテナーを構成します。その後、ビルド パラメーターを設定してアプリケーション コンテナーを構成します。これは、MySQL の場合のようにイメージをプルするのではなく、イメージをビルドする必要があるためです。また、アプリケーション コンテナーが MySQL コンテナーに依存するように設定する必要があります。
これで、1 つのコマンドだけでプロジェクト全体を実行できます。
docker-compose up
この部分はオプションですが、誰かがこれを役に立つと思うかもしれません。ユーザー定義またはデフォルト値にすることができる短縮リンクの有効期限について話しました。この問題については、データベース内にスケジュールされたイベントを設定できます。このイベントは x 分ごとに実行され、有効期限が現在の時刻より前の行をデータベースから削除します。そのような単純な。これは、データベース内の少量のデータでうまく機能します。
ここで、このソリューションに関するいくつかの問題について警告する必要があります。
この投稿が、URL 短縮サービスの作成方法に関する一般的なアイデアを得るのに少し役立つことを願っています.このアイデアを採用して改善することができます。いくつかの新しい機能要件を書き留めて、それらを実装してみてください。質問がある場合は、この投稿の下に投稿できます。