Dubbo 소스 코드 분석 1 : RPC 프레임 워크를 직접 작성하는 방법은 무엇입니까?

여기에 사진 설명 삽입

소개

모 놀리 식 프로젝트를 개발할 때 모든 사람이 유사한 코드를 작성 했어야합니다. 즉, 서비스 제공자와 서비스 호출자가 하나의 서비스에 있습니다.

public interface HelloService {
    
    
    public String sayHello(String content);
}
public class HelloServiceImpl implements HelloService {
    
    

    @Override
    public String sayHello(String content) {
    
    
        return "hello, " + content;
    }
}
public class Test {
    
    

    public static void main(String[] args) {
    
    
        HelloService helloService = new HelloServiceImpl();
        String msg = helloService.sayHello("world");
        // hello world
        System.out.println(msg);
    }
}

그러나 단일 서비스의 많은 단점으로 인해 많은 회사가 이제 관련없는 기능을 다른 서비스로 분할했습니다.

로컬 서비스와 같은 원격 서비스를 호출하는 방법은 무엇입니까? 이때 RPC 프레임 워크 (Remote Procedure Call)에 대해 언급해야합니다. 그는 네트워크 통신, 직렬화 및 기타 작업의 실현을 보호하고 원격 서비스 호출을 로컬 서비스 호출만큼 편리하게 만들었습니다.

잘 알려진 RPC 프레임 워크에는 Spring Cloud, Alibaba의 Dubbo, Facebook의 Thrift, Google grpc 등이 있습니다.

RPC 호출 프로세스

여기에 사진 설명 삽입
RPC 호출 프로세스는 다음과 같습니다.

  1. 호출자가 요청을 보낸 후 프록시 클래스에서 호출 할 메서드 및 매개 변수가 네트워크 전송이 가능한 메시지 본문으로 어셈블됩니다.
  2. 발신자가 메시지 본문을 공급자에게 보냅니다.
  3. 공급자는 메시지를 디코딩하고 호출 매개 변수를 가져옵니다.
  4. 공급자는 리플렉션에서 해당 메서드를 실행하고 결과를 반환합니다.

아래에서 우리는 rpc 프레임 워크가 어떻게 구현되는지 분석합니까? 확장 할 수있는 것.
모든 사람이보다 생생하게 이해할 수 있도록 간단한 것부터 어려운 rpc 프레임 워크까지 github 프로젝트를 작성했습니다. welcome star

https://github.com/erlieStar/simple-rpc

프록시 클래스 생성

앞서 말했듯이 호출자가 메서드를 실행 한 후 실제로 프록시 클래스의 메서드를 실행하고 프록시 클래스는 직렬화, 인코딩 및 디코딩 작업을 도와줍니다. 그렇다면 프록시 클래스를 생성하는 방법은 무엇입니까?

주류 관행을 살펴 보겠습니다.

Facebook의 Thrift와 Google의 grpc는 모두 스키마 파일을 정의한 다음 프로그램을 실행하여 클라이언트 프록시 클래스 및 인터페이스를 생성하는 데 도움을줍니다. 호출자는 생성 된 프록시 클래스를 직접 사용하여 요청하고 공급자는 생성 된 인터페이스를 상속 할 수 있습니다.

이 방법의 가장 큰 장점은 여러 언어로 통신 할 수 있다는 것입니다. 즉 , 스키마 파일은 Java 프로그램이나 Python 프로그램을 생성 할 수 있습니다. 호출자는 Java 프로그램이고 공급자는 Python 프로그램이므로 정상적으로 통신 할 수 있습니다. 그리고 바이너리 프로토콜이며 통신 효율이 상대적으로 높습니다 .

Java에서 프록시 클래스를 생성하는 방법에는 여러 가지가 있습니다.

  1. JDK 동적 프록시 (InvocationHandler 인터페이스 구현)
  2. 바이트 코드 조작 라이브러리 (예 : cglib, Javassist)

Dubbo에서 프록시 클래스를 생성하는 방법에는 jdk 동적 프록시와 Javassist 두 가지가 있습니다. 기본값은 javassist입니다. 이유는? 물론 javassist가 더 효율적입니다.

실험 계획안

합의가 필요한 이유는 무엇입니까? Spring Cloud는 Http 프로토콜을 통해 통신하는데 Dubbo는 어떤 프로토콜을 통해 통신합니까?

계약이 필요한 이유는 무엇입니까?

데이터가 이진 형식으로 네트워크에서 전송되기 때문에 RPC 요청 데이터는 전체적으로 공급자에게 전송되지 않고 여러 데이터 패킷으로 분할되어 전송 될 수 있습니다. 공급자는 데이터를 어떻게 식별합니까?

예를 들어 텍스트 ABCDEF의 경우 공급자가 차례로받는 데이터는 ABC DEF 또는 AB CD EF 일 수 있습니다. 공급자는이 데이터로 무엇을해야합니까?

간단합니다. 규칙을 정하세요. 이 규칙에는 여러 종류가있을 수 있습니다. 여기에 3 가지 예가 있습니다.

  1. 고정 길이 프로토콜 , 프로토콜 내용의 길이가 고정되어 있으며, 50 바이트를 읽으면 디코딩 작업이 시작되며 Netty의 FixedLengthFrameDecoder를 참조 할 수 있습니다.
  2. 특수 종결 자는 메시지 끝에 구분 기호를 정의합니다. \ n이면 데이터를 읽었 음을 의미합니다. 읽지 않으면 계속 읽습니다. Netty의 DelimiterBasedFrameDecoder를 참조 할 수 있습니다.
  3. 가변 길이 프로토콜 (프로토콜 헤더 + 프로토콜 본문) , 고정 길이는 메시지 본문의 길이를 나타내는 데 사용되며 나머지 콘텐츠는 메시지 본문입니다. 원하는 경우 프로토콜 헤더에 일반적으로 사용되는 속성도 추가됩니다. Http 프로토콜의 헤더는 컨텐츠 유형, 컨텐츠 길이 등과 같은 프로토콜 헤더입니다. Netty의 DelimiterBasedFrameDecoder를 참조 할 수 있습니다.

Dubbo는 사용자 정의 프로토콜을 통해 통신 하며 프로토콜 헤더 형식은 다음과 같으며
여기에 사진 설명 삽입
각 비트의 의미는 다음과 같습니다 .
여기에 사진 설명 삽입

Dubbo가 기존 Http 프로토콜 대신 프로토콜을 사용자 지정해야하는 이유는 무엇입니까?

주된 이유는 사용자 정의 프로토콜이 성능을 향상시킬 수 있다는 것입니다.

  1. Http 프로토콜의 요청 패킷은 상대적으로 크고 쓸모없는 내용이 많이 포함되어 있습니다. 맞춤형 프로토콜은 많은 콘텐츠를 간소화 할 수 있습니다.
  2. HTTP 프로토콜은 상태 비 저장이며 매번 연결을 다시 설정해야하며 응답이 완료된 후 연결이 닫힙니다.

계약을 사용자 정의하는 방법은 무엇입니까?

직렬화

프로토콜 헤더의 내용은 비트로 표시되며 프로토콜 본문은 응용 프로그램의 개체로 캡슐화됩니다. 예를 들어 Dubbo는 요청을 Request로, 응답을 Response로 캡슐화합니다.
여기에 사진 설명 삽입

앞서 네트워크에서 전송되는 데이터는 바이너리 데이터 여야하지만 호출자의 입력 매개 변수와 공급자의 반환 값은 모두 객체이므로 직렬화 및 역 직렬화 프로세스가 필요하다고 말했습니다.

직렬화하는 방법에는 여러 가지가 있습니다.

  1. JDK 네이티브 직렬화
  2. JSON
  3. Protobuf
  4. Kryo
  5. 헤 시안 2
  6. MessagePack

직렬화 방법을 선택할 때 주로 다음 요소를 고려합니다.

  1. 유효성
  2. 공간 오버 헤드
  3. 다양성 및 호환성
  4. 안전

통신

다음과 같은 네 가지 일반적인 IO 모델이 있습니다.

  1. 동기식 차단 IO (Blocking IO)
  2. 동기식 비 차단 IO (비 차단 IO)
  3. IO 멀티플렉싱 (IO 멀티플렉싱)
  4. 비동기 IO (비동기 IO)

이 4 가지 IO 모델에 대해서는 별도로 설명하지 않겠습니다. 다음 문서를 참조하십시오.

Java NIO의 기본 원칙 인 10 분 안에 이해

RPC는 일반적으로 높은 동시성 시나리오에서 사용되기 때문에 IO 멀티플렉싱 모델을 선택합니다. Netty의 IO 멀티플렉싱은 Reactor 개발 모델을 기반으로 구현됩니다.이 개발 모델이 후속 기사에서 어떻게되어 있는지 분석 할 것입니다. 높은 동시성 지원

기재

등록 센터의 역할은 전화 번호부와 유사합니다 . 서비스 이름과 특정 서비스 주소 사이의 매핑 관계가 저장되며, 서비스와 통신하고자 할 때 서비스 이름을 기준으로 서비스 주소 만 찾으면됩니다.

더 중요한 것은이 전화 번호부는 동적 입니다. 서비스 주소가 변경되면 전화 번호부의 주소가 변경되고 서비스를 사용할 수없는 경우 전화 번호부의 주소가 사라집니다.

이 동적 전화 번호부는 등록 센터입니다.

Zookeeper, Redis, Nocas 등과 같이 레지스트리를 구현하는 방법에는 여러 가지가 있습니다.

Zookeeper로 등록 센터를 구현하는 방법을 소개합니다.

Zookeeper에는 영구 노드와 임시 노드 의 두 가지 유형의 노드가 있습니다.

zookeeper에 서비스를 등록 할 때 임시 노드 를 사용하므로 서비스 연결이 끊어지면 노드를 삭제할 수 있습니다.

노드 유형 설명
영구 노드 노드를 영구 노드로 생성하면 데이터는 항상 사육사 서버에 저장됩니다. 노드를 생성 한 클라이언트와 서버 간의 세션이 닫혀 있어도 노드는 삭제되지 않습니다.
영구 시퀀스 노드 영구 노드를 기반으로 노드 순서가 추가됩니다.
임시 노드 노드를 임시 노드로 생성하면 데이터가 항상 사육사 서버에 저장되는 것은 아니며 임시 노드를 생성 한 클라이언트 세션이 닫히면 해당 사육사 서버에서 노드가 삭제됩니다.
임시 시퀀스 노드 임시 노드를 기준으로 노드 순서가 추가됩니다.

등록 센터가 끊겼을 때 어떻게 의사 소통을하나요?

사육사가 전화를 끊으면 자동으로 다른 사육사로 전환됩니다. dubbo는 매핑 관계의 사본을 로컬에 저장하기 때문에 전화를 끊더라도 상관 없습니다.이 매핑 관계는지도 또는 파일에 저장할 수 있습니다.

새 서비스가 레지스트리에 등록되면 로컬 캐시가 업데이트됩니까?

모니터링에 등록하면 당연히 업데이트됩니다. 모니터링되는 노드 또는 하위 노드가 변경되면 해당 콘텐츠가 모니터링 클라이언트로 푸시되고 로컬 캐시를 업데이트 할 수 있습니다.

Zookeeper의 이벤트는 다음과 같습니다. 이 모니터링을 분산 관찰자 모드로 이해할 수 있습니다 .
여기에 사진 설명 삽입

부하 분산 전략

동일한 서비스에 대해 하나의 노드 만 배포하는 것은 불가능하며, 호출 할 때마다 호출을 시작할 노드를 선택해야하는데 이는로드 밸런싱 전략을 포함합니다.

일반적인 부하 분산 전략은 다음과 같습니다.

  1. 무작위
  2. 투표
  3. 일관된 해시

요약

물론 성숙한 RPC 프레임 워크는 라우팅 전략, 비정상 재시도, 모니터링, 비동기 호출 등과 같은 주요 프로세스와 관련이없는 많은 사항을 고려해야하므로 더 이상 소개하지 않겠습니다.

참조 블로그

[1] https://blog.csdn.net/zzti_erlie/article/details/82292083
[2] https://www.cnblogs.com/LBSer/p/4853234.html
协议
[3] https : // dubbo. apache.org/zh-cn/blog/dubbo-protocol.html

추천

출처blog.csdn.net/zzti_erlie/article/details/110188704