버전 관리.
제품 라인을 만듭니다.
버전 관리는 핵심 제품을 기반으로하는 다양한 제품을 제공하는 관행입니다. 버전 관리는 새로운 이익과 성장을 두 가지 방식으로 생성 할 수 있습니다. 첫째, 고객에게 고유 한 요구 사항을 제공 할 수있는 새로운 제품을 제공하십시오. 따뜻한 기후의 운전자 (시동을 걸기 위해 크랭크 암을 많이 필요로하지 않음)는 Sears의 79.99 달러 DieHard South 자동차 배터리를 구입할 수 있습니다. 시어스는 더 강력한 배터리 (더 높은 크랭크 암페어)를 필요로하는 극한 지역의 운전자를 위해 $ 179.99 플래티넘 버전을 제공합니다. 둘째, 우수하고 우수하며 최상의 제품을 제공하면 고객이 지불 할 금액을 선택할 수 있습니다 (소비자의 선택에 따라 실제 가치가 드러납니다). McDonald 's는 $ 1 McDoubles와 $ 3.59 Quarter Pounders를 판매합니다. 대부분이 두 샌드위치의 재료는 비슷합니다. 가격에 민감한 고객은 McDoubles를 구입하지만 예산이 많은 고객은 프리미엄 가격의 Quarter Pounders를 선택합니다.
버전이 지정된 제품을 만드는 것은 세 가지 기본 방법을 통해 구현할 수 있습니다.
프리미엄 (높은 품질, 보장 된 액세스, 빠른, 우선 순위 서비스, 낮은 deductibles / 더 나은 범위). 철저히 지켜야합니다 (품질이 떨어지고, 제한이 많으며, 비수기, 개인 상표, 번들되지 않음, 공제액이 더 높고 혜택이 낮습니다). 독창적 인 고객 요구 사항 (패키지 크기, 확장 및 향상된 보증, 월별 클럽, 번들링, 플랫폼, 다양한 사용법)을 충족하십시오.
버전 관리는 새로운 고객을 유치하고 (고유 한 요구 사항 포함), 다른 고객에 대한 다양한 수익 마진을 도출합니다. 엄격한 제품은 할인을 제공하는 반면 보험료는 최고급 버전에서 파생됩니다. 이러한 고수익 프리미엄 버전 및 신규 고객 (고유 한 요구 사항과 저렴한 버전을 구매하는 고객)의 이익이 모든 회사의 가격 횡재 캠페인의 핵심 구성 요소입니다.
마이크로 서비스 - 버전 전략.
버전 관리의 문제점을 식별하고 가능한 솔루션을 탐색합니다.
ОјServices 힙합입니다! 그들은 잠시 동안 머물렀다. 그러나 매우 강력한 모듈 방식의 느슨하게 결합 된 접근 방식은 프로젝트를보다 쉽게 유지 관리 할 수있는 경향이 있지만 또한 문제를 야기합니다. 이 블로그 게시물에서 나는이 문제 중 하나에 뛰어 들고 싶습니다. 버전 관리.
이 블로그 게시물은 예제 코드와 함께 제공됩니다.
업데이트 : Spring Boot에서이 어댑터 패턴을 좀더 심층적으로 구현했습니다. 여기에 대한 모든 것을 읽어보십시오!
소개.
몇 년 전 나는 유일한 서비스 (ish) 접근 방식을 사용하는 프로젝트에 참여했습니다. 코드 작성이 잘되어 있고 버전 관리 작업에 대한 완전한 기쁨이 고려되었지만 서비스는 고려되지 않았습니다. 서비스 간 직렬화를 위해 Netty 및 Kryo에 기반한 가정용 RPC 계층이 사용되었습니다. 빠르고 사용하기 쉽지만 큰 문제가있었습니다. 직렬화 된 클래스가 완전히 똑같은 구조가 아니고 동일한 (또는 적어도 호환되는) Kryo 버전을 사용하지 않으면 서비스는 서로 통신 할 수 없습니다.
서비스 간 버전 협상이 없었기 때문에 유일한 해결책이있었습니다. 전체 생태계를 하나의 큰 단일체로 배치합니다. 그리고 이것은 마이크와 많이 관련이 있습니다. 진정한 서비스 아키텍처 대신 모듈 식 단일체 (monolith)인데, 여기에는 서비스 아키텍처의 이점 (민첩성 : 서비스 중 하나를 교환 할 수 있어야 함)이없는 반면 유일한 서비스 아키텍처의 단점을 가지고 있습니다 (주로 트랜잭션 업데이트 기능을 잃어 버립니다).
따라서 서비스 아키텍처를 선택하는 경우 사용하고 전략을 결정해야합니다.
데이터 형식.
데이터 형식으로 시작하겠습니다. 소개의 예제에서 우리는 크기가 작고 빠르지 만 이전 버전과 호환되도록 만드는 것이 어렵거나 거의 불가능하다는 점에서 큰 문제가있는 바이너리 형식을 사용했습니다. 이것은 실수였습니다. 이 예제에서 하위 호환성 (Protobuf, Smile)을 허용하는 바이너리 형식이 있지만 JSON 만 사용합니다. 새로운 프로토콜 버전이 필드를 추가하는 경우 오래된 프로토콜에 의해 잘 처리되어야합니다.
프로토콜 버전.
나는 프로토콜 버전을 언급했는데 이것은 당신이 결정할 필요가있는 두 번째 중요한 구성 요소이다 : 프로토콜 버전 번호 매기기. 새로운 버전이 호환성을 깨뜨릴 때마다 하나의 정수를 사용하여 실제로 충돌 할 수 있습니다. 프로토콜이 항상 버전 내에서 이전 버전과 호환되므로 & lt; 메이저 & gt;. & lt; 마이너 & gt; 버전 관리 체계.
또한 클라이언트가 항상 예상 한 프로토콜 버전을 서비스에 알려야합니다. 클라이언트가 특정 버전을 원한다는 암묵적인 가정을하는 대신 클라이언트가 실제로 오류를 던져야하는 경우가 아니라면 개발자에게 항상 버전을 포함해야한다는 것을 알려줍니다. 또한이 정보는 오래된 버전을 비추천으로 인한 영향을 평가하기 위해 얼마나 많은 클라이언트가 아직 최신 버전인지 알기 위해 필요합니다.
전략.
이제 데이터 형식과 프로토콜 버전 관리 전략을 결정 했으므로이를 적용하고 전략을 결정할 수 있습니다. 예를 들어 웹 상점을위한 매우 간단한 "주문"API를 사용합니다. ID를 기반으로 한 아이템을 가진 주문을 반환하는 단일 GET 호출 만 있습니다.
호환되지 않는 버전 1과 2의 두 가지 버전이 있습니다. 버전 1 응답 :
보시다시피이 응답에서 몇 가지 실수를 범했습니다. customerLastName 필드의 철자가 틀리며 orderTotal의 센트가 주문 항목 가격과 10 진수로 일치하지 않습니다. 그래서 우리는 형식 버전 2에서이를 수정했습니다 :
훨씬 낫다! 또한; 완전히 호환되지 않습니다!
라우팅 기반 버전 관리.
외부 적으로 노출 된 API의 경우 비교적 잘 작동하는 전략은 단순히 경로 URL에 버전을 포함시키는 것입니다. 그래서 우리 고객의 첫 번째 버전은 / v1 / order / 1을 얻을 것이고 새로운 API를 기반으로하는 새로운 버전은 GET / v2 / order / 1을 요청할 것입니다. 그러면 이전 버전의 API가 포트 8001에서 실행되고 새 버전은 포트 8002에서 실행됩니다.
Nginx와 같은 역방향 프록시는 요청을 (/ vN 비트를 제거하여) 해당 서비스로 간단히 라우트합니다.
이것은 비교적 간단하게 구현할 수있는 전략이지만 (뒤늦은 지켜 볼지라도) 몇 가지 단점이 있습니다. 첫 번째 버전은 분명히 이전 버전의 서비스를 병렬로 유지할 수 있어야합니다. Order 서버의 새 버전은 예를 들어 이전 버전이 더 이상 데이터에 액세스 할 수 없도록 데이터베이스를 변경할 수 있습니다.
또 다른 문제점은 외부에서 노출 된 서비스에 대해서는 잘 작동하지만 여기에서는 '서비스'에 대해 이야기하고 있다는 것입니다. 역방향 프록시를 통과해야하는 많은 서비스가 서로 통신합니다.
보기 기반 버전 관리.
또 다른 접근법은 하위 호환성을 처리하는 로직을 코드로 이동시키는 것입니다. 제 의견으로는 이것이 합리적입니다. 수정을 한 개발자는 하위 호환성이 가장 좋은 방법을 알고 싶어하는 경향이 있습니다.
이 예제 코드는 내부적으로 JSON 직렬화에 Jackson을 사용하는 Spring Boot (개념적으로 모든 언어와 프레임 워크를 사용할 수 있음)를 기반으로합니다. 잭슨은 '전망'이라는 개념을 가지고 있습니다. 이러한 종류의보기에 대한 일반적인 시나리오는 권한이없는 사용자에 대한 사용자 세부 정보의 일부만을 표시하고 권한이 부여 된 / 친구의 사용자에 대한 자세한 정보를 표시하려는 것입니다. 예를 들어 Facebook 프로필의 전자 메일을 친한 친구에게만 제공하는 것과 비슷합니다.
Jackson View에 대한 자세한 정보는 여기에 있습니다.
예제 프로젝트에서는 두 가지 접근법을 보여주는 컨트롤러를 만들었습니다. 먼저 생성자를 살펴 보겠습니다.
보시다시피 우리는 하나의 Order로 '데이터베이스'를 채우고 뷰 및 어댑터 맵을 구성합니다. 링크 된 기사에서 설명하는 '보기'는 모든 클래스입니다. 기본 문자열 상속을 사용할 수 있도록 일반 문자열 대신 클래스를 사용합니다.
어댑터는 주문을 MappingJacksonValue 객체에 매핑하는 간단한 함수입니다.
getProtocolVersion 유틸리티 메소드에 대해서도 설명하겠습니다.
두 가지 버전 관리 방법에서 사용되는이 방법은 클라이언트가 제공 한 헤더에서 프로토콜 버전을 얻는 방법입니다. 제공되지 않으면 400 상태 코드로 응답합니다.
컨트롤러 소스는 비교적 간단합니다.
컨트롤러에서 "Order"객체를 반환하지 않고 Jackson MappingJacksonValue 래퍼를 대신 사용한다는 사실을 눈치 챘을 것입니다. 이렇게하면 버전에 따라 다른보기를 제공 할 수있는 유연성이 생깁니다.
getOrderView 메서드는 단순히 버전을 기반으로 뷰 클래스를 찾거나 기본 Version2 뷰를 반환합니다.
이제 뷰를 설정하고 하위 호환성을 처리하는 방법을 살펴 보겠습니다. 이 클래스는 표준 Jackson serializer 인 OrderSerializer 클래스에서 처리됩니다.
표준 뷰 기능을 사용하는 대신 시리얼 라이저를 사용했던 이유는 다른 이름으로 속성을 잘 직렬화 할 수 없기 때문입니다.
보시다시피 view는 serializerProvider. getActiveView () 메소드를 통해 serializer에 제공되며이를 기반으로 호환성 논리를 수행 할 수 있습니다. 단점은 많은 수작업을해야한다는 것입니다.
어댑터 기반 버전 관리.
필자의 의견으로는 좀 더 우아한 방법은 어댑터 디자인 패턴을 적용하는 것입니다. 우리는 유일한 서비스의 느슨하게 결합 된 특성을 이용하고 프로토콜 버전에 따라 완전히 다른 객체를 반환합니다. 이 방법에서는 클라이언트가 제공 한 X-Protocol-Version 헤더도 사용합니다.
두 번째 경로를 살펴 보겠습니다.
컨트롤러는 발견 된 Order를 매핑 메소드를 검색하는 adapt 메소드에 넘깁니다 (예를 들어 Java 8!). 그리고이를 주문에 적용합니다. 버전 1의 생성자에서 보았 듯이 OrderV1 객체에서 Order를 래핑하고 Jackson은 그 결과를 출력으로 직렬화합니다. 다음과 같이 보입니다.
이는 일반적인 어댑터입니다. 즉, 데이터 자체가 포함되어 있지 않습니다. 새 버전을 이전 인터페이스에 '적용'합니다. 이 방식으로 모든 중요한 로직은 여전히 Order 클래스에 있고 '어댑터 로직'만이 어댑터에 포함됩니다.
이 솔루션에 대해 좋아하는 점은 명확하고 읽기 쉽기 때문에 많은 코드가 필요하지 않습니다 (버전간에 '클래스가 끊어지는 클래스에 어댑터가 필요함을 명심하십시오.) 또한 적응하기 쉽습니다 ( heh)를 다른 직렬화 방법에 적용합니다. 이 메커니즘은 예를 들어 Protobuf 직렬화 및 / 또는 예를 들어 Kafka 대기열과 통신하는 서비스에 쉽게 적용될 수 있습니다.
결론.
나는이 블로그 게시물에 몇 가지 접근법을 보여 주었고 이것들 각각은 프로와 사기극을 가지고있다. 그러나 중요한 교훈은 버전 관리가 일종의 사후 검토뿐만 아니라 아키텍처의 일부가되어야한다는 것입니다. 외부 API의 경우에는 이미 중요하지만 상호 서비스의 정밀한 메쉬를 사용하면 서비스 아키텍처에서 볼 수 있습니다. 전략을 결정해야합니다.
그리고 어떤 전략을 선택 하든지간에 예상 프로토콜 버전이 무엇인지 명확하게 (URL에서 카프카 주제 또는 헤더의 이름으로) 반드시 전달해야합니다. 그것 없이는 라우트가 불가능하며, 어댑터를 작성하는 것은 불가능하며 어떤 버전이 '외부에 있는지'알기조차 불가능합니다.
나는이 글을 즐겁게 쓰기를 기뻐했다. 이 예를 가지고 놀고 의견이나 질문이 있으면 알려주십시오!
저작권 및 사본; Niels Dommerholt 2017 - JBake와 구운 - 템플릿 깨끗한 블로그.
No comments:
Post a Comment