최근에 Ratpack으로 작업을 시작했는데 꽤 마음에 들어요. 저는 주로 처음부터 빠른 프로젝트를 수행했습니다. 하지만 기존 Spring Boot 애플리케이션에서 기존 MVC 부분을 대체하기 위해 사용하고 싶습니다. 모든 것이 이미 고려되어 있기 때문에 이것은 실제로 쉽게 할 수 있습니다. 스프링 모듈.
이 블로그를 팔로우하신다면 다음과 같은 이전 포스팅을 기억하실 것입니다. 저장, 색인화 및 검색 파일이 있는 카우치베이스 및 스프링 부트. 저는 관련 코드 를 예로 들어보겠습니다. 이 아이디어는 Spring MVC를 Ratpack으로 대체하고 기존의 동기식 블로킹 서비스를 비동기식 및 비블로킹으로 만드는 것입니다. 결과 코드는 다음에서 사용할 수 있습니다. github 도 마찬가지입니다.
올바른 종속성 추가
저는 Gradle을 사용하고 있습니다. Ratpack과 매우 잘 통합되어 있습니다. 모듈을 추가하려면 다음을 호출하여 올바른 종속성을 추가하기만 하면 됩니다. ratpack.dependency("myFavoriteModule")
. 따라서 우리의 경우 Spring Boot에 대한 지원을 추가하려면 다음을 추가해야 합니다. ratpack.dependency("spring-boot")
. 안타깝게도 Ratpack에서 자동으로 관리하는 버전은 1.4.0.M3 미만이며, 이 버전은 Couchbase 자동 구성을 제공하는 버전입니다. 그래서 이번에는 수동으로 의존성을 추가해야 합니다.
1 2 3 4 5 6 7 8 9 10 11 12 |
종속성 { 컴파일 쥐팩.종속성("guice"), 쥐팩.종속성("rx"), 쥐팩.종속성("핸들바"), "com.couchbase.client:java-client:2.3.1", "org.springframework.boot:spring-boot-autoconfigure:1.4.0.M3", "io.ratpack:ratpack-spring-boot:1.3.3", "org.slf4j:slf4j-simple:1.7.12", "org.codehaus.plexus:plexus-utils:3.0.21", "커먼즈-코덱:커먼즈-코덱:1.10" } |
여기서 볼 수 있는 것은 ratpack.dependency("spring-boot")
를 추가하는 바로 가기입니다. org.springframework.boot:spring-boot-autoconfigure:1.4.0.M3
그리고 io.ratpack:ratpack-spring-boot:1.3.3
. 이 모듈이 제공하는 것은 Ratpack 서버를 Spring 애플리케이션에 통합하는 기능입니다. Ratpack 컨텍스트에서 Spring @Beans를 검색하고 핸들러를 Spring 구성으로 선언할 수 있습니다.
Ratpack 구성 선언
Spring Boot의 한 가지 마음에 드는 점은 자동 구성입니다. Couchbase SDK가 클래스 경로에 있는지, 그리고 속성 spring.couchbase.bootstrap-hosts
가 선언됩니다. 그 순간 기본 버킷에 대해 Spring 빈이 인스턴스화됩니다. 그리고 이 버킷 인스턴스는 빈으로 또는 Ratpack의 컨텍스트에서 사용할 수 있습니다. 따라서 Ratpack 레이어에서 Couchbase에 대한 바인딩을 선언할 필요가 없습니다.
일반적으로 Ratpack에서 가장 먼저 하는 일은 서버를 시작하고 구성 및 핸들러를 정의하는 것입니다. 여기에는 이미 Spring Boot 애플리케이션이 실행 중입니다. 구성으로 주석이 달린 모든 클래스는 자동으로 선택되어 애플리케이션 구성에 추가됩니다. 해당 구성을 선언하는 첫 번째 단계는 다음을 구현하는 클래스를 생성하는 것입니다. 쥐팩 서버 커스터마이저 를 생성하고 @Confguration으로 주석을 달면 됩니다. 이를 통해 핸들러, 바인딩 및 서버 구성 목록을 정의할 수 있습니다. 다음 예제에서는 몇 가지 서버 속성을 등록하고 여러 클래스를 Ratpack의 컨텍스트에 바인딩하고 있습니다. 'server.maxContentLength' 속성은 업로드할 수 있는 파일의 최대 크기입니다.
1 2 3 4 5 6 7 8 9 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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
패키지 org.카우치베이스.devex; 가져오기 자바.활용.ArrayList; 가져오기 자바.활용.컬렉션; 가져오기 자바.활용.목록; 가져오기 자바.활용.지도; 가져오기 org.카우치베이스.devex.도메인.저장된 파일 렌더러; 가져오기 org.카우치베이스.devex.서비스.검색 서비스; 가져오기 org.스프링 프레임워크.컨텍스트.주석.구성; 가져오기 com.구글.공통.수집.ImmutableMap; 가져오기 쥐팩.양식.양식; 가져오기 쥐팩.func.액션; 가져오기 쥐팩.guice.바인딩 사양; 가져오기 쥐팩.핸들바.핸들바 모듈; 가져오기 쥐팩.핸들바.템플릿; 가져오기 쥐팩.처리.체인; 가져오기 쥐팩.rx.RxRatpack; 가져오기 쥐팩.서버.BaseDir; 가져오기 쥐팩.서버.서버 구성 빌더; 가져오기 쥐팩.봄.구성.쥐팩 서버 커스터마이저; 가져오기 rx.관찰 가능; @구성 public 클래스 Ratpack 구성 구현 쥐팩 서버 커스터마이저 { @오버라이드 public 목록<액션> getHandlers() { 목록<액션> 핸들러 = new ArrayList<액션>(); 핸들러.추가(fileApi()); 반환 핸들러; } @오버라이드 public 액션 getServerConfig() { 반환 구성 -> 구성.baseDir(BaseDir.찾기()) .소품(ImmutableMap.의("server.maxContentLength", "100000000", "app.name", "스토어 파일 검색")); } @오버라이드 public 액션 getBindings() { 반환 bindingConfig -> bindingConfig.모듈(핸들바 모듈.클래스).바인드(파일 핸들러.클래스) .바인드(저장된 파일 렌더러.클래스).바인드(오류 처리기 임플란트.클래스).바인드(클라이언트 핸들러 임플리케이션.클래스); } 비공개 액션 fileApi() { 반환 체인 -> 체인.접두사("file", 파일 핸들러.클래스).post("전체 텍스트", ctx -> { ctx.parse(양식.클래스).다음(양식 -> { 문자열 쿼리 문자열 = 양식.get("쿼리 문자열"); 검색 서비스 검색 서비스 = ctx.get(검색 서비스.클래스); 관찰 가능<지도<문자열, 객체="">> 파일 = 검색 서비스.검색전체파일(쿼리 문자열); RxRatpack.약속(파일).다음(응답 -> ctx .렌더링(템플릿.핸들바템플릿("uploadForm", "text/html", m -> m.put("파일", 응답)))); }); }).post("n1ql", ctx -> { ctx.parse(양식.클래스).다음(양식 -> { 문자열 쿼리 문자열 = 양식.get("쿼리 문자열"); 검색 서비스 검색 서비스 = ctx.get(검색 서비스.클래스); 관찰 가능<지도<문자열, 객체="">> 파일 = 검색 서비스.검색N1QL파일(쿼리 문자열); RxRatpack.약속(파일).다음(응답 -> ctx .렌더링(템플릿.핸들바템플릿("uploadForm", "text/html", m -> m.put("파일", 응답)))); }); }); } } </지도<문자열,></지도<문자열,></액션</액션</액션 |
애플리케이션 템플릿 시스템은 핸들바에 의존하므로 핸들바 모듈이 필요합니다. FileHandler는 '/file' API에 대한 모든 호출을 처리하고, StoredFileRenderer는 StoredFile이 올바르게 렌더링되는지 확인합니다. 마지막 두 바인딩은 오류 관리를 위한 것입니다.
여기서 가장 중요한 것은 fileAPI
메서드에서 핸들러를 선언합니다. 핸들러는 사용자가 특정 URL을 클릭할 때 어떤 일이 일어나는지 정의합니다. 여기서는 모든 '/file/*' 호출을 FileHandler 클래스에 연결합니다. 또한 '/fulltext' 및 '/n1ql'의 POST에 대한 동작도 정의합니다.
Ratpack은 프로미스를 사용합니다. 따라서 POST 요청에서 오는 Form을 구문 분석하면 프로미스를 얻게 됩니다. 각 POST에서 볼 수 있는 것은 SearchService가 Ratpack의 컨텍스트에서 가져온다는 것입니다. 구성에서 바인딩되지 않은 경우에도 마찬가지입니다. 이는 통합의 일부로 컨텍스트에서 Spring 빈을 사용할 수 있기 때문입니다.
다음 단계는 옵저버블을 반환하는 검색 서비스를 호출하는 것입니다. 관찰 가능 항목에 대한 래퍼를 제공하는 Ratpack의 rx-java 모듈을 사용할 수 있습니다. 이 모듈은 이것을 프로미스로 래핑합니다. 그런 다음 간단히 응답을 렌더링하면 됩니다.
이 시점에서 우리는 모든 Spring MVC를 제거했습니다. 컨트롤러. 보시다시피 제 서비스는 관찰 가능을 반환합니다. 이전 애플리케이션에서는 그렇지 않았습니다.
Ratpack용 서비스 마이그레이션
제 서비스 대부분은 Couchbase에 의존하고 있습니다. SDK는 RxJava를 기반으로 하므로 대부분의 서비스를 비동기식 논블로킹 방식으로 변환하고 관찰 가능으로 반환하는 것이 매우 쉽습니다.
RxJava 사용
이것은 매우 간단한 예입니다. 결과를 지도 목록에 매핑하는 N1QL 쿼리입니다. 첫 두 줄은 대부분 쿼리를 정의하는 것이므로 전혀 변경되지 않습니다. 두 번째 버전에서는 동기식 버킷을 사용할 때 매핑이 더 자연스럽게 느껴지는 것을 볼 수 있습니다.
1 2 3 4 5 6 7 8 9 10 |
public 목록<지도<문자열, 객체="">> 검색N1QL파일(문자열 where절) { N1qlQuery 쿼리 = N1qlQuery.simple( "SELECT binaryStoreLocation, binaryStoreDigest FROM `default` WHERE type= 'file' " + where절); 쿼리.매개변수().일관성(스캔 일관성.STATEMENT_PLUS); N1qlQueryResult res = 버킷.쿼리(쿼리); 목록<지도<문자열, 객체="">> 파일 이름 = res.모든 행().스트림().지도(행 -> 행.값().toMap()) .수집(수집가.toList()); 반환 파일 이름; } </지도<문자열,></지도<문자열,> |
된다
1 2 3 4 5 6 7 |
public 관찰 가능<지도<문자열, 객체="">> 검색N1QL파일(문자열 where절) { N1qlQuery 쿼리 = N1qlQuery.simple( "SELECT binaryStoreLocation, binaryStoreDigest FROM `default` WHERE type= 'file' " + where절); 쿼리.매개변수().일관성(스캔 일관성.STATEMENT_PLUS); 반환 버킷.비동기().쿼리(쿼리).플랫맵(AsyncN1qlQueryResult::행).지도(r -> r.값().toMap()); } </지도<문자열,> |
레거시 코드 차단은 어떻게 하나요?
제 서비스 중 일부는 오래된 차단 코드에 의존하고 있습니다. 이러한 코드를 차단하지 않게 만드는 마법 같은 방법은 없지만, 프로미스로 쉽게 감쌀 수 있습니다. 이렇게 하면 핸들러에서 쉽게 사용할 수 있습니다. 블로킹 호출을 래핑하는 것은 매우 간단합니다. 함수를 'Blocking.get()'으로 래핑하기만 하면 됩니다. 다음은 매우 간단한 예시입니다:
1 2 3 4 |
public 문자열 getSha1Digest(입력 스트림 는) { 반환 다이제스트 유틸리티.sha1Hex(는); } |
가
1 2 3 4 |
public 약속 getSha1Digest(입력 스트림 는) { 반환 차단.get(() -> 다이제스트 유틸리티.sha1Hex(는)); } |
결론
이제 Spring Boot 애플리케이션에 Ratpack을 적용하는 데 필요한 거의 모든 것을 알게 되었습니다. 빠진 것이 있다고 생각되면 다음 주소로 연락해 주세요. 트위터 또는 아래 댓글로 문의하세요.