По-видимому, написание службы сокращения 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.Controllerpublic 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
|
@Autowiredprivate 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 и соседей . |