Java 마이크로서비스 번역 및 적용 : 실용 가이드 . 가이드의 이전 부분:
- 마이크로서비스 기본 및 아키텍처
- 마이크로서비스 배포 및 테스트 .
Java 마이크로서비스를 탄력적으로 만드는 방법은 무엇입니까?
마이크로서비스를 생성할 때 기본적으로 동기 HTTP 호출 또는 비동기 메시징에 대한 JVM 메서드 호출을 교환한다는 점을 기억하세요. 메소드 호출은 대부분 완료가 보장되지만(예기치 않은 JVM 종료 제외) 네트워크 호출은 기본적으로 신뢰할 수 없습니다. 작동할 수도 있지만 네트워크 과부하, 새로운 방화벽 규칙 구현 등 다양한 이유로 작동하지 않을 수 있습니다. 이것이 어떻게 달라지는지 알아보기 위해 BillingService 예제를 살펴보겠습니다.HTTP/REST 탄력성 패턴
고객이 회사 웹사이트에서 전자책을 구입할 수 있다고 가정해 보겠습니다. 이를 위해 온라인 상점을 호출하여 실제 PDF 송장을 생성할 수 있는 청구 마이크로서비스를 구현했습니다. 지금은 HTTP를 통해 이 호출을 동기적으로 수행하겠습니다(물론 PDF 생성이 사용자 관점에서 즉각적으로 이루어질 필요는 없으므로 이 서비스를 비동기적으로 호출하는 것이 더 합리적이기는 하지만). 다음에서도 동일한 예를 사용하겠습니다. 섹션을 살펴보고 차이점을 살펴보세요.)@Service
class BillingService {
@Autowired
private HttpClient client;
public void bill(User user, Plan plan) {
Invoice invoice = createInvoice(user, plan);
httpClient.send(invoiceRequest(user.getEmail(), invoice), responseHandler());
// ...
}
}
요약하면 이 HTTP 호출의 가능한 세 가지 결과는 다음과 같습니다.
- 확인: 통화가 완료되었으며 계정이 성공적으로 생성되었습니다.
- DELAY: 통화가 완료되었지만 완료하는 데 너무 오랜 시간이 걸렸습니다.
- 오류. 호출이 실패했거나 호환되지 않는 요청을 보냈거나 시스템이 작동하지 않을 수 있습니다.
메시징 탄력성 패턴
비동기 통신에 대해 자세히 살펴보겠습니다. 메시징에 Spring과 RabbitMQ를 사용한다고 가정하면 BillingService 프로그램은 이제 다음과 같이 보일 수 있습니다. 계정을 생성하기 위해 이제 새 메시지를 기다리는 여러 작업자가 있는 RabbitMQ 메시지 브로커에 메시지를 보냅니다. 이러한 작업자는 PDF 송장을 생성하여 해당 사용자에게 보냅니다.@Service
class BillingService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void bill(User user, Plan plan) {
Invoice invoice = createInvoice(user, plan);
// преобразует счет, например, в json и использует его How тело messages
rabbitTemplate.convertAndSend(exchange, routingkey, invoice);
// ...
}
}
동기식 HTTP 연결에서와 같이 더 이상 즉시 OK 또는 ERROR 응답을 받을 수 없으므로 잠재적인 오류는 이제 약간 다르게 보입니다. 대신, 잘못될 수 있는 세 가지 잠재적인 시나리오가 있을 수 있으며, 이로 인해 다음과 같은 질문이 제기될 수 있습니다.
- 내 메시지가 직원에게 전달되어 사용되었나요? 아니면 잃어버린 걸까요? (사용자는 청구서를 받지 않습니다).
- 내 메시지가 한 번만 전달되었나요? 아니면 두 번 이상 배송되고 한 번만 처리됩니까? (사용자는 여러 개의 청구서를 받게 됩니다).
- 구성: "교환에 올바른 라우팅 키/이름을 사용했습니까?"에서 "내 메시지 브로커가 올바르게 구성 및 유지 관리되고 있습니까? 아니면 대기열이 가득 찼습니까?"까지입니다. (사용자는 청구서를 받지 않습니다).
- ActiveMQ와 같은 JMS 구현을 사용하는 경우 2단계(XA) 커밋을 보장하기 위해 속도를 교환할 수 있습니다.
- RabbitMQ를 사용하는 경우 먼저 이 튜토리얼을 읽은 다음 일반적인 확인, 내결함성 및 메시지 신뢰성에 대해 신중하게 생각하십시오.
- 아마도 Active 또는 RabbitMQ 서버 구성, 특히 클러스터링 및 Docker와 결합하는 데 능숙한 사람이 있을 것입니다(누가? ;)).
Java 마이크로서비스를 위한 최고의 솔루션은 어떤 프레임워크입니까?
한편으로는 Spring Boot 와 같이 매우 널리 사용되는 옵션을 설치할 수 있습니다 . .jar 파일을 매우 쉽게 생성할 수 있고 Tomcat 또는 Jetty와 같은 내장 웹 서버가 함께 제공되며 어디에서나 빠르게 실행할 수 있습니다. 마이크로서비스 애플리케이션 구축에 이상적입니다. 최근에는 반응형 프로그래밍에서 부분적으로 영감을 받은 몇 가지 특수 마이크로서비스 프레임워크인 Kubernetes 또는 GraalVM이 등장했습니다. 다음은 몇 가지 흥미로운 경쟁자입니다: Quarkus , Micronaut , Vert.x , Helidon . 결국에는 스스로 선택해야 하지만 완전히 표준이 아닐 수 있는 몇 가지 권장 사항을 제공할 수 있습니다. Spring Boot를 제외하고 모든 마이크로서비스 프레임워크는 일반적으로 거의 즉각적인 시작으로 믿을 수 없을 만큼 빠른 것으로 판매됩니다. , 낮은 메모리 사용량, 무한한 확장성. 마케팅 자료에는 일반적으로 거대한 Spring Boot 옆에 또는 서로 플랫폼을 과시하는 인상적인 그래픽이 포함되어 있습니다. 이론적으로 이는 로드하는 데 몇 분이 걸리는 레거시 프로젝트를 지원하는 개발자의 신경을 덜어줍니다. 또는 50ms 이내에 현재 필요한 만큼의 마이크로컨테이너를 시작/중지하려는 클라우드에서 작업하는 개발자입니다. 그러나 문제는 이러한 (인공적인) 베어 메탈 시작 시간과 재배포 시간이 프로젝트의 전반적인 성공에 거의 기여하지 않는다는 것입니다. 최소한 강력한 프레임워크 인프라, 강력한 문서, 커뮤니티 및 강력한 개발자 기술보다 훨씬 적은 영향을 미칩니다. 따라서 다음과 같이 보는 것이 더 좋습니다. 지금까지의 경우:- ORM을 만연하게 실행하여 간단한 워크플로에 대해 수백 개의 쿼리를 생성합니다.
- 적당히 복잡한 단일체를 실행하려면 끝없는 기가바이트가 필요합니다.
- 코드가 너무 많고 복잡성이 너무 높아서(지금은 Hibernate와 같이 잠재적으로 느린 시작에 대해 이야기하는 것이 아닙니다) 애플리케이션을 로드하는 데 몇 분이 걸립니다.
동기식 Java REST 호출에 가장 적합한 라이브러리는 무엇입니까?
낮은 수준의 기술 측면에서는 Java의 기본 HttpClient (Java 11 이후), Apache의 HttpClient 또는 OkHttp 와 같은 HTTP 클라이언트 라이브러리 중 하나로 끝날 가능성이 높습니다 . 좋은 오래된 JAX-RS 클라이언트 에서 최신 WebSocket 클라이언트에 이르기 까지 다른 옵션이 있기 때문에 여기서 "아마도"라고 말합니다 . 어쨌든 추세는 HTTP 호출을 직접 조작하는 것에서 벗어나 HTTP 클라이언트를 생성하는 것입니다. 이렇게 하려면 OpenFeign 프로젝트 와 해당 문서를 추가 읽기의 출발점으로 살펴봐야 합니다 .비동기식 Java 메시징을 위한 최고의 브로커는 무엇입니까?
아마도 인기 있는 ActiveMQ(Classic 또는 Artemis) , RabbitMQ 또는 Kafka를 접하게 될 것입니다 .- ActiveMQ와 RabbitMQ는 전통적인 완전한 메시지 브로커입니다. 여기에는 "스마트 브로커"와 "멍청한 사용자"의 상호 작용이 포함됩니다.
- 역사적으로 ActiveMQ는 RabbitMQ/Docker/TestContainer 설정으로 완화할 수 있는 간편한 인라인(테스트용)이라는 이점을 누려왔습니다.
- Kafka는 전통적인 "스마트" 브로커라고 할 수 없습니다. 대신, 현명한 소비자가 처리해야 하는 상대적으로 "멍청한" 메시지 저장소(로그 파일)입니다.
마이크로서비스를 테스트하는 데 어떤 라이브러리를 사용할 수 있나요?
스택에 따라 다릅니다. Spring 생태계가 배포된 경우 프레임워크의 특정 도구를 사용하는 것이 현명할 것입니다 . JavaEE가 Arquillian 과 같은 경우 . 특히 로컬 개발 또는 통합 테스트를 위해 Oracle 데이터베이스를 쉽고 빠르게 설정하는 데 도움이 되는 Docker와 정말 좋은 Testcontainers 라이브러리를 살펴보는 것이 좋습니다 . 전체 HTTP 서버의 모의 테스트를 보려면 Wiremock을 확인하세요 . 비동기 메시징을 테스트하려면 ActiveMQ 또는 RabbitMQ를 구현한 다음 Awaitility DSL을 사용하여 테스트를 작성해 보세요 . 또한 Junit , AssertJ 용 TestNG 및 Mockito 등 일반적인 도구가 모두 사용됩니다 . 이 목록은 전체 목록이 아닙니다. 여기에서 마음에 드는 도구를 찾지 못했다면 댓글 섹션에 게시해 주세요.모든 Java 마이크로서비스에 대한 로깅을 활성화하는 방법은 무엇입니까?
마이크로서비스의 경우 로깅은 흥미롭고 다소 복잡한 주제입니다. less 또는 grep 명령으로 조작할 수 있는 하나의 로그 파일 대신 이제 n개의 로그 파일이 있고 너무 흩어지지 않기를 원합니다. 벌목 생태계의 특징은 이 기사 (영문)에 잘 설명되어 있습니다. 반드시 읽어보고 마이크로서비스 관점에서 중앙 집중식 로깅 섹션을 주의 깊게 읽어보세요 . 실제로는 다양한 접근 방식을 접하게 됩니다. 시스템 관리자는 여러 서버의 로그 파일을 수집하고 단일 로그 파일로 결합하는 특정 스크립트를 작성하고 다운로드를 위해 FTP 서버에 저장합니다. 병렬 SSH 세션에서 cat/grep/unig/sort 조합을 실행합니다. 이것이 바로 Amazon AWS가 수행하는 작업이며 관리자에게 알릴 수 있습니다. Graylog 또는 ELK Stack(Elasticsearch, Logstash, Kibana) 과 같은 도구를 사용하세요.내 마이크로서비스는 어떻게 서로를 찾나요?
지금까지 우리는 마이크로서비스가 서로에 대해 알고 해당 IPS를 알고 있다고 가정했습니다. 정적 구성에 대해 이야기해 보겠습니다. 따라서 우리의 뱅킹 모놀리스 [ip = 192.168.200.1]는 속성 파일에 하드코딩된 위험 서버 [ip = 192.168.200.2]와 통신해야 한다는 것을 알고 있습니다. 그러나 다음과 같이 보다 동적으로 만들 수 있습니다.- 마이크로서비스에 application.properties 파일을 배포하는 대신 모든 마이크로서비스가 해당 구성을 가져오는 클라우드 기반 구성 서버를 사용합니다.
- 서비스 인스턴스는 동적으로 위치를 변경할 수 있으므로 서비스가 어디에 있는지, 해당 IP가 무엇인지, 라우팅 방법을 알고 있는 서비스를 살펴보는 것이 좋습니다.
- 이제 모든 것이 역동적이므로 리더 자동 선출과 같은 새로운 문제가 발생합니다. 예를 들어 특정 작업을 두 번 처리하지 않도록 작업하는 마스터는 누구입니까? 리더가 실패하면 누가 그 리더를 대신합니까? 교체는 어떤 기준으로 이루어지나요?
Java 마이크로서비스를 사용하여 권한 부여 및 인증을 구성하는 방법은 무엇입니까?
이 주제는 또한 별도의 이야기에 합당합니다. 다시 말하지만 옵션은 사용자 지정 보안 프레임워크를 사용하는 하드코딩된 기본 HTTPS 인증부터 자체 인증 서버를 사용하여 Oauth2 설치를 실행하는 것까지 다양합니다.모든 환경이 동일하게 보이도록 하려면 어떻게 해야 합니까?
마이크로서비스가 없는 배포에 적용되는 내용은 마이크로서비스가 포함된 배포에도 적용됩니다. Docker/Testcontainers와 스크립팅/Ansible을 조합해 보세요.질문 없음: YAML에 대해 간단히 설명합니다.
잠시 라이브러리 및 관련 문제에서 벗어나 Yaml을 간단히 살펴보겠습니다. 이 파일 형식은 사실상 "구성을 코드로 작성"하기 위한 형식으로 사용됩니다. Ansible과 같은 간단한 도구와 Kubernetes와 같은 거대 도구에서도 사용됩니다. YAML 들여쓰기의 어려움을 경험하려면 간단한 Ansible 파일을 작성해보고 예상대로 작동하기 전에 파일을 얼마나 편집해야 하는지 확인하세요. 그리고 이는 모든 주요 IDE에서 지원되는 형식에도 불구하고 그렇습니다! 그런 다음 다시 돌아와서 이 가이드를 끝까지 읽어보세요.Yaml:
- is:
- so
- great
분산 트랜잭션은 어떻습니까? 성능 시험? 다른 주제?
언젠가는 매뉴얼의 향후 버전에 나올 수도 있습니다. 지금은 그게 전부입니다. 우리와 함께 있어주세요!마이크로서비스의 개념적 문제
Java의 마이크로서비스의 특정 문제 외에도 모든 마이크로서비스 프로젝트에 나타나는 다른 문제가 있다고 가정해 보겠습니다. 이는 주로 조직, 팀 및 관리와 관련이 있습니다.프런트엔드와 백엔드 불일치
프런트엔드와 백엔드 불일치는 많은 마이크로서비스 프로젝트에서 매우 일반적인 문제입니다. 무슨 뜻이에요? 좋은 오래된 단일체에서는 웹 인터페이스 개발자가 데이터를 얻기 위한 하나의 특정 소스를 가졌습니다. 마이크로서비스 프로젝트에서 프런트엔드 개발자는 갑자기 데이터를 얻을 수 있는 n개의 소스를 갖게 됩니다. Java로 일종의 IoT(사물 인터넷) 마이크로서비스 프로젝트를 만들고 있다고 상상해 보세요. 유럽 전역에서 측지 기계와 산업용 용광로를 관리한다고 가정해 보겠습니다. 그리고 이 오븐은 온도 등의 정보를 정기적으로 업데이트합니다. 조만간 "퍼니스 검색" 마이크로서비스를 사용하여 관리 UI에서 오븐을 찾고 싶을 수도 있습니다. 백엔드 상대방이 도메인 기반 설계 또는 마이크로서비스 법칙을 얼마나 엄격하게 적용하는지에 따라 "오븐 찾기" 마이크로서비스는 오븐 ID만 반환할 수 있으며 유형, 모델 또는 위치와 같은 기타 데이터는 반환하지 않을 수 있습니다. 이를 위해 프런트엔드 개발자는 첫 번째 마이크로서비스에서 받은 ID를 사용하여 "퍼니스 데이터 가져오기" 마이크로서비스에서 (페이징 구현에 따라) 하나 또는 n개의 추가 호출을 수행해야 합니다. 이것은 실제(!) 프로젝트에서 가져온 간단한 예일 뿐이지만 다음과 같은 문제를 보여줍니다. 슈퍼마켓이 매우 인기를 얻었습니다. 그 이유는 야채, 레모네이드, 냉동 피자, 화장지를 사기 위해 10곳의 다른 장소에 갈 필요가 없기 때문입니다. 대신 한 곳으로 이동하는 것이 더 쉽고 빠릅니다. 프런트엔드 및 마이크로서비스 개발자도 마찬가지입니다.경영진의 기대
경영진은 이제 (중요한) 프로젝트를 위해 무한한 수의 개발자를 고용해야 한다는 잘못된 인식을 갖고 있습니다. 개발자는 이제 각자 자신의 마이크로서비스에서 서로 완전히 독립적으로 작업할 수 있기 때문입니다. 마지막 단계(출시 직전)에는 약간의 통합 작업만 필요합니다. 사실 이 접근법은 매우 문제가 많습니다. 다음 단락에서 우리는 그 이유를 설명하려고 노력할 것입니다."작은 조각"은 "더 나은 조각"과 같지 않습니다.
20개의 부분으로 나누어진 코드가 반드시 하나의 전체 부분보다 품질이 더 높다고 가정하는 것은 큰 실수입니다. 순전히 기술적인 관점에서 품질을 고려하더라도, 우리의 개별 서비스는 지원되지 않는 코드 계층을 탐색하면서 데이터베이스에서 사용자를 선택하기 위해 여전히 400개의 Hibernate 쿼리를 실행할 수 있습니다. 다시 한 번 Simon Brown의 명언으로 돌아가겠습니다. 모놀리스를 제대로 구축하지 못하면 적절한 마이크로서비스를 구축하기 어려울 것입니다. 마이크로서비스 프로젝트의 내결함성에 대해 이야기하는 것은 매우 늦은 경우가 많습니다. 너무 많아서 실제 프로젝트에서 마이크로서비스가 어떻게 작동하는지 보는 것이 때로는 두려울 때도 있습니다. 그 이유는 Java 개발자가 항상 내결함성, 네트워킹 및 기타 관련 주제를 적절한 수준에서 연구할 준비가 되어 있지 않기 때문입니다. "부분" 자체는 더 작지만 "기술적 부분"은 더 큽니다. 마이크로서비스 팀이 데이터베이스 시스템에 로그인하기 위한 기술적인 마이크로서비스를 작성하도록 요청받았다고 가정해 보세요.@Controller
class LoginController {
// ...
@PostMapping("/login")
public boolean login(String username, String password) {
User user = userDao.findByUserName(username);
if (user == null) {
// обработка варианта с несуществующим пользователем
return false;
}
if (!user.getPassword().equals(hashed(password))) {
// обработка неверного пароля
return false;
}
// 'Ю-ху, залогинorсь!';
// установите cookies, делайте, что угодно
return true;
}
}
이제 귀하의 팀은 로그인 서비스를 작성하는 대신 실제적이고 실질적인 비즈니스 요구 사항 없이 정말 유용한 UserStateChanged 마이크로서비스를 작성하는 것이 더 낫다고 판단하고 비즈니스 담당자를 설득할 수도 있습니다. 그리고 현재 일부 사람들은 Java를 공룡처럼 취급하므로 UserStateChanged 마이크로서비스를 세련된 Erlang으로 작성해 보겠습니다. 그리고 어딘가에 레드-블랙 트리를 사용해 봅시다. Steve Yegge가 Google에 지원하려면 레드-블랙 트리를 철저히 알아야 한다고 썼기 때문입니다. 통합, 유지 관리 및 전반적인 디자인 관점에서 이는 단일 단일체 내부에 스파게티 코드 레이어를 작성하는 것만큼 나쁩니다. 인위적이고 평범한 예인가요? 이것은 사실이다. 그러나 이것은 실제로 일어날 수 있습니다.
조각 수가 적고 이해도가 떨어짐
그런 다음 시스템 전체, 프로세스 및 작업 흐름을 이해하는 것에 대한 질문이 자연스럽게 떠오르지만 동시에 개발자로서 귀하는 격리된 마이크로서비스 [95: login-101: updateUserProfile] 작업에만 책임이 있습니다. 이전 단락과 조화를 이루지만 조직, 신뢰 및 의사소통 수준에 따라 마이크로서비스 체인에 우발적인 고장이 발생하는 경우 많은 혼란, 어깨 으쓱, 비난이 발생할 수 있습니다. 그리고 일어난 일에 대해 전적인 책임을 질 사람은 없습니다. 그리고 그것은 전혀 부정직의 문제가 아닙니다. 실제로 서로 다른 부분을 연결하고 프로젝트의 전체 그림에서 해당 부분의 위치를 이해하는 것은 매우 어렵습니다.커뮤니케이션 및 서비스
회사의 규모에 따라 의사소통 수준과 서비스 수준이 크게 다릅니다. 그러나 일반적인 관계는 분명합니다. 많을수록 문제가 더 커집니다.- 마이크로서비스 #47은 누가 운영하나요?
- 호환되지 않는 새 버전의 마이크로서비스를 배포했습니까? 이 내용은 어디에 문서화되어 있나요?
- 새로운 기능을 요청하려면 누구에게 문의해야 합니까?
- Erlang을 아는 유일한 사람이 회사를 떠난 후, Erlang의 마이크로서비스를 누가 지원할 것인가?
- 우리의 모든 마이크로서비스 팀은 다양한 프로그래밍 언어뿐만 아니라 다양한 시간대에서도 작업합니다! 이 모든 것을 어떻게 올바르게 조정합니까?
GO TO FULL VERSION