По-видимому, написание службы сокращения URL-адресов — это новый « Привет, мир! ”В мире Интернета вещей / микросервиса / эры. Все началось с сервиса сокращения URL в 45 строках Scala — аккуратный кусок Scala, приправленный Spray и Redis для хранения. За этим быстро последовал сервис сокращения URL в 35 строках Clojure и даже URL Shortener в 43 строках Haskell . Так что мой внутренний анти-хипстер спросил: как долго это будет на Яве? Но не ради простой Java, ради бога. Spring Boot с Spring Data Redis — хорошая отправная точка. Все, что нам нужно, это простой контроллер, обрабатывающий GET и POST:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
import com.google.common.hash.Hashing; import org.apache.commons.validator.routines.UrlValidator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.*; import org.springframework.web.bind.annotation.*; import javax.servlet.http.*; import java.nio.charset.StandardCharsets; @org .springframework.boot.autoconfigure.EnableAutoConfiguration @org .springframework.stereotype.Controller public class UrlShortener { public static void main(String[] args) { SpringApplication.run(UrlShortener. class , args); } @Autowired private StringRedisTemplate redis; @RequestMapping (value = "/{id}" , method = RequestMethod.GET) public void redirect( @PathVariable String id, HttpServletResponse resp) throws Exception { final String url = redis.opsForValue().get(id); if (url != null ) resp.sendRedirect(url); else resp.sendError(HttpServletResponse.SC_NOT_FOUND); } @RequestMapping (method = RequestMethod.POST) public ResponseEntity<String> save(HttpServletRequest req) { final String queryParams = (req.getQueryString() != null ) ? "?" + req.getQueryString() : "" ; final String url = (req.getRequestURI() + queryParams).substring( 1 ); final UrlValidator urlValidator = new UrlValidator( new String[]{ "http" , "https" }); if (urlValidator.isValid(url)) { final String id = Hashing.murmur3_32().hashString(url, StandardCharsets.UTF_8).toString(); redis.opsForValue().set(id, url); } else return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } } |
Код хорошо нагляден и функционально эквивалентен версии в Scala. Я не пытался сжать его слишком сильно, чтобы счетчик строк был как можно короче, приведенный выше код довольно типичен, с некоторыми подробностями:
- Я обычно не использую символы подстановки
- Я не использую полностью определенные имена классов (я хочу сохранить одну строку
import
, я признаю) - Я окружаю
if
/else
блоки скобками - Я почти никогда не использую полевую инъекцию, самый уродливый брат в инверсии семейства контроля. Вместо этого я бы выбрал конструктор, чтобы разрешить тестирование с использованием мошеннического Redis
1
2
3
4
5
6
|
@Autowired private final StringRedisTemplate redis; public UrlShortener(StringRedisTemplate redis) { this .redis = redis; } |
Больше всего я боролся за то, чтобы получить оригинальный полный URL. В основном мне нужно было все после .com
или порта. Никакого кровавого пути (ни сервлетов, ни Spring MVC), отсюда и неудобное getQueryString()
. Вы можете использовать сервис следующим образом — создать более короткий URL:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
$ curl -vX POST localhost:8080 /https : //www .google.pl /search ?q=tomasz+nurkiewicz > POST /https : //www .google.pl /search ?q=tomasz+nurkiewicz HTTP /1 .1 > User-Agent: curl /7 .30.0 > Host: localhost:8080 > Accept: */* > < HTTP /1 .1 200 OK < Server: Apache-Coyote /1 .1 < Content-Type: text /plain ;charset=ISO-8859-1 < Content-Length: 28 < Date: Sat, 23 Aug 2014 20:47:40 GMT < http: //mydomain .com /50784f51 |
Перенаправление через более короткий URL:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
$ curl - v localhost:8080 /50784f51 > GET /50784f51 HTTP /1 .1 > User-Agent: curl /7 .30.0 > Host: localhost:8080 > Accept: */* > < HTTP /1 .1 302 Found < Server: Apache-Coyote /1 .1 < Location: https: //www .google.pl /search ?q=tomasz+nurkiewicz < Content-Length: 0 < Date: Sat, 23 Aug 2014 20:48:00 GMT < |
Для полноты, вот файл сборки в Gradle (также будет работать maven), пропущенный во всех предыдущих решениях:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
buildscript { repositories { mavenLocal() mavenCentral() } dependencies { classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.1.5.RELEASE' } } apply plugin: 'java' apply plugin: 'spring-boot' sourceCompatibility = '1.8' repositories { mavenLocal() mavenCentral() } dependencies { compile "org.springframework.boot:spring-boot-starter-web:1.1.5.RELEASE" compile "org.springframework.boot:spring-boot-starter-redis:1.1.5.RELEASE" compile 'com.google.guava:guava:17.0' compile 'org.apache.commons:commons-lang3:3.3.2' compile 'commons-validator:commons-validator:1.4.0' compile 'org.apache.tomcat.embed:tomcat-embed-el:8.0.9' compile "org.aspectj:aspectjrt:1.8.1" runtime "cglib:cglib-nodep:3.1" } tasks.withType(GroovyCompile) { groovyOptions.optimizationOptions.indy = true } task wrapper(type: Wrapper) { gradleVersion = '2.0' } |
На самом деле также 42 строки … Вот и все приложение, без XML, без дескрипторов, без установки.
Я не отношусь к этому упражнению как к фиктивному коду для кратчайшего, наиболее запутанного рабочего кода. Веб-сервис сокращения URL с бэкэндом Redis — интересная демонстрация синтаксиса и возможностей данного языка и экосистемы. Гораздо интереснее, чем куча алгоритмических проблем, например, в коде Розетты . Также это хороший минимальный шаблон для написания REST-сервиса.
Одна важная особенность оригинальной реализации Scala , о которой как-то молча забыли во всех реализациях, включая эту, заключается в том, что она не блокируется. Доступ как к HTTP, так и к Redis основан на событиях ( реактивный , хорошо, я так сказал), поэтому я полагаю, что он может одновременно обрабатывать десятки тысяч клиентов. Этого нельзя достичь с помощью блокирующих контроллеров, поддерживаемых Tomcat. Но все же вы должны признать, что такой сервис, написанный на Java (даже не на Java 8!), Удивительно лаконичен, прост в использовании и понятен — ни одно из других решений не является настолько читабельным (это, конечно, субъективно).
В ожидании других!
Ссылка: | Служба сокращения URL-адресов в 42 строках кода в … Java (?!) Spring Boot + Redis от нашего партнера по JCG Томаша Нуркевича из блога Java и соседей . |