Kitex는 Dubbo 프로토콜을 지원하여 다국어 클라우드 네이티브 생태계의 통합을 촉진합니다.

작성자: Wang Yuxuan(github: DMwangnima), Kitex 커미터

1. 배경

Kitex는 ByteDance 인프라 서비스 프레임워크 팀이 출시한 Go 마이크로서비스 RPC 프레임워크로 Thrift, Kitex Protobuf, gRPC 등의 메시지 프로토콜을 지원하며 고성능과 강력한 확장성이 특징입니다. 2021년 9월 공식적으로 오픈 소스로 공개된 후 많은 외부 회사에서 성공적으로 구현되어 실질적인 비용, 성능 및 안정성 이점을 제공했습니다.

Kitex 변환 서비스를 사용하는 과정에서 많은 기업 사용자는 기존 Dubbo 프레임워크에서 구현된 서비스와 통신하기 위해 Kitex를 필요로 합니다. 이는 생태계를 적극적으로 확장하려는 CloudWeGo 커뮤니티의 목표와 일치합니다. 따라서 Dubbo 상호 운용성 프로젝트 codec-dubbo 는 탄생하게 되었습니다.

커뮤니티 학생들의 열정적인 도움으로 codec-dubbo는 현재 Kitex와 Dubbo-Java, Kitex 및 Dubbo-Go 간의 상호 운용이 가능하며 Dubbo 사용자가 Kitex로 마이그레이션할 수 있도록 지원합니다.

본 글에서는 Kitex와 codec-dubbo를 활용한 Founder Securities의 성공적인 서비스 전환을 예로 들어, 전환 과정에서 사용되는 codec-dubbo의 주요 기능을 설명하고, 구현 세부 사항을 간략하게 분석합니다.

2. 기업 구현 사례

Founder Securities의 원래 서비스는 Java 및 Dubbo 프레임워크로 작성되었으며 둘 다 안정적이며 수많은 시나리오에서 검증되어 생산 및 개발 요구 사항을 충족합니다. 요청 수가 많은 Xiaofang의 개별 재고 세부 정보 페이지를 예로 들면, 피크 기간 동안의 인터페이스 QPS는 3~4k이고 16 16 Core 64G 가상 머신이 호스팅에 사용됩니다.

클라우드 네이티브 아키텍처의 부상과 함께 Go는 메모리 사용 및 실행 효율성의 장점은 물론 클라우드 네이티브에 대한 자연스러운 적응성으로 인해 점차 엔터프라이즈 서비스 구축을 위한 중요한 기술 옵션이 되었습니다. 비용을 더 절감하고 효율성을 높이기 위해 비용, 성능, 안정성 등의 요소를 종합적으로 고려한 후 새로운 애플리케이션을 Java에서 Go로 전환하기로 결정하고 서비스 개발 및 재구축을 위해 Kitex, Hertz 및 기타 CloudWeGo 프로젝트를 도입하고 마이그레이션했습니다. 전체 Kubernetes 환경.

재구성 과정에서 codec-dubbo는 기본 Kitex + Thrift에 가까운 사용자 경험과 Dubbo 개념에 대한 우수한 지원을 활용하여 사용 및 이해 비용을 절감했으며 Kitex <-> Dubbo의 상호 운용성 문제를 성공적으로 해결하는 데 도움을 주었습니다. Kitex 서비스 원래 Dubbo 서비스를 원활하게 호출합니다.

현재 codec-dubbo를 이용한 Kitex 서비스가 성공적으로 런칭되어 2개월째 안정적으로 운영되고 있습니다. Xiaofang 개별 재고 세부 정보 페이지를 예로 들면 Kitex와 Hertz는 이 페이지의 인터페이스 중 약 절반을 호스팅하며 QPS가 변경되지 않은 경우 12개의 4 코어 4G 포드만 제공하면 되며, 이는 리소스 사용량을 크게 줄여줍니다.

3. 코덱-더보 기능적 특징

Dubbo 프로토콜 코덱

Dubbo 서비스는 주로 Dubbo 프로토콜을 사용하여 통신합니다. Kitex <-> Dubbo 상호 운용성을 지원하려면 Kitex에서 Dubbo 프로토콜을 구현해야 합니다. Kitex의 뛰어난 확장성 덕분에 codec-dubbo는 Kitex에서 제공하는 코덱 인터페이스를 기반으로 핵심 코덱인 DubboCodec을 구현합니다. Dubbo 프로토콜을 사용하려면 초기화 중에 DubboCodec만 주입하면 됩니다.

유형 매핑 및 확장

유형 매핑

Dubbo는 주로 Hessian2 직렬화 프로토콜을 사용하여 페이로드를 인코딩하고 디코딩합니다. 가장 큰 특징은 자체 설명 직렬화 유형입니다. 즉, 외부 스키마나 인터페이스 정의에 의존하지 않습니다. 직렬화 프로세스는 프로그래밍 언어 유형과 Hessian2 유형 간의 매핑에 의존합니다. 예를 들어 Go 유형을 Java 유형으로 변환합니다.

분석 결과, Hessian2의 기본 타입 시스템이 Thrift와 기본적으로 겹치는 것을 발견했습니다. Kitex + codec-dubbo의 사용 환경이 기본적으로 Kitex + Thrift와 동일하도록 하기 위해 Thrift IDL을 기반으로 Kitex Dubbo-Hessian2 스캐폴딩 코드를 생성합니다. 이때 유형 변환 프로세스는 다음과 같습니다.

Dubbo의 공식 dubbo-go-hessian2 유형 매핑을 참조하세요 . codec-dubbo는 다음 유형 매핑을 제공합니다(여기에는 매핑의 일부만 포함되어 있습니다. 자세한 주의 사항은 codec-dubbo Readme를 참조하세요).

codec-dubbo에서 제공하는 유형 매핑에 따라 Dubbo 인터페이스 정의를 Thrift IDL로 쉽게 변환하고 Kitex 명령줄 도구를 사용하여 프로젝트 스캐폴딩 코드를 생성한 다음 마지막으로 DubboCodec을 삽입하여 Kitex -> Dubbo 통신을 완료할 수 있습니다. 다음 Dubbo 인터페이스 정의를 예로 들어 보겠습니다.

package org.cloudwego.kitex.samples.api;

public interface GreetProvider {
    GreetResponse Greet(GreetRequest req) throws Exception;
}

public class GreetRequest implements Serializable {
    String req;

    public GreetRequest(String req) {
        this.req = req;
    }
}

public class GreetResponse implements Serializable {
    String resp;

    public GreetResponse(String resp) {
        this.resp = resp;
    }
}

해당 api.thrift 파일은 다음과 같습니다. 해당 파일의 구조 정의에는 Dubbo 인터페이스 정의의 패키지 + 클래스 이름에 해당하는 JavaClassName으로 주석을 달아야 합니다.

struct GreetRequest {
    1: required string req,
} (JavaClassName="org.cloudwego.kitex.samples.api.GreetRequest")

struct GreetResponse {
    1: required string resp,
} (JavaClassName="org.cloudwego.kitex.samples.api.GreetResponse")

service GreetService {
    GreetResponse Greet(1: GreetRequest req)
}

Kitex 명령줄 도구를 사용하고 프로토콜을 Hessian2로 지정합니다.

kitex -module demo-client -protocol Hessian2 ./api.thrift

그런 다음 DubboCodec을 초기화하고 Kitex에 삽입합니다. 생성된 코드를 사용하여 Kitex -> Dubbo 호출을 구현하는 다음 클라이언트 코드를 작성합니다.

javaClass := "org.cloudwego.kitex.samples.api.GreetProvider"
cli, err := greetservice.NewClient("helloworld",
    // 指定想要访问的服务端地址,也支持 ZooKeeper 服务发现
    client.WithHostPorts("127.0.0.1:21000"),
    // 配置 DubboCodec
    client.WithCodec(
        // 指定想要调用的 Dubbo Interface
        dubbo.NewDubboCodec(dubbo.WithJavaClassName(javaClass))
    ),
)
if err != nil {
   panic(err)
}
    
resp, err := cli.Greet(context.Background(),
    &hello.GreetRequest{Req: "world"})
if err != nil {
    klog.Error(err)
    return
}
klog.Infof("resp: %s", resp.Resp)

Kitex + codec-dubbo 서버 측 프로세스는 기본적으로 클라이언트 측 프로세스와 유사합니다. 구체적인 예는 프로젝트 홈페이지를 참조하세요.

유형 확장

Hessian2의 스키마 없는 기능은 Dubbo의 구현을 "너무 유연하게" 만들고 모든 유형을 사용할 수 있게 만듭니다. Dubbo Hessian2의 유형 사용 유연성에 적응하기 위해 codec-dubbo는 주로 사용자 정의 매핑과 Java 공통 유형 확장을 포함하는 유형 확장을 지원합니다.

맞춤 매핑

Java의 기본 유형에는 boolean및 와 같은 해당 래퍼 유형이 있습니다 java.lang.Boolean. 유형 매핑에서 boolGo 유형과 Java 유형의 기본 매핑에는 . 사용자 경험을 통일하고 Kitex 측의 유형 만 사용할 수 있도록 하기 위해 Thrift 메소드 정의 뒤에 주석을 추가하고 thriftgo의 IDL 반사 기능을 사용하여 IDL 메타 정보를 미리 생성하여 코덱에 주입할 수 있습니다. dubbo를 실행한 다음 실행할 수 있습니다. 기본 매핑 유형을 . 구체적인 Thrift 정의는 다음과 같습니다.java.lang.Booleanbooleanboolhessian.argsType="boolean"java.lang.Booleanboolean

service EchoService {
   bool EchoBoolean(1: bool req) (hessian.argsType="boolean")
}

boolean및 와 마찬가지로 java.lang.Boolean다른 Java 기본 유형 및 래퍼 유형도 이러한 방식으로 사용자 정의할 수 있습니다. 이때 codec-dubbo에서 제공하는 전체 유형 매핑은 다음과 같습니다.

자바 공통 유형 확장

Thrift 유형의 제한으로 인해 Java 클래스 라이브러리에서 제공되는 일반 유형을 직접 사용할 수 없습니다. 이를 위해 codec-dubbo는 Thrift가 지원하지 않는 Java 유형(예 : )과 codec-dubbo/java 패키지의 해당 java.thrift를 동시에 제공되는 idl-ref 함수의 도움으로 유지합니다. thriftgo를 사용하면 Thrift를 직접 사용할 수 있습니다. 이러한 유형은 IDL에서 참조되고 해당 코드가 생성됩니다. 현재 java.thrift는 다음과 같습니다:java.lang.Objectjava.util.Date

struct Object {} (JavaClassName="java.lang.Object")

struct Date {} (JavaClassName="java.util.Date")

struct Exception {} (JavaClassName="java.lang.Exception")

이러한 유형을 활성화하려면 Thrift IDL에서 해당 유형을 가져와서 확장 패키지를 가져오기 위해 include "java.thrift"Kitex 명령줄 도구를 사용하여 코드를 생성할 때 매개변수를 추가해야 합니다.-hessian2 java_extension

Kitex 명령줄 도구는 java.thrift를 자동으로 다운로드합니다. 수동으로 다운로드하여 프로젝트의 루트 디렉터리에 넣을 수도 있습니다. java.thrift의 유형을 참조하는 Thrift IDL 예제:

include "java.thrift"

service EchoService {
    // java.lang.Object
    i64 EchoString2ObjectMap(1: map<string, java.Object> req)
    // java.util.Date
    i64 EchoDate(1: java.Date req)
}

메소드 오버로딩

Go는 기본적으로 메서드 오버로드를 지원하지 않습니다. 여러 메서드를 정의해야만 오버로딩과 같은 효과를 얻을 수 있습니다. 사용자 정의 매핑 섹션과 유사하게 Go의 여러 메서드를 Java의 오버로드된 메서드에 매핑하기 위해 Thrift의 메서드 정의 뒤에 JavaMethodName 태그를 추가하고 thriftgo의 IDL 반사 기능을 사용하여 런타임에 Go를 동적으로 매핑합니다. 메소드 이름은 JavaMethodName으로 지정된 Java의 오버로드된 메소드로 다시 작성됩니다.

Java 측의 EchoMethod를 예로 들어 보겠습니다.

String EchoMethod(Boolean req);
String EchoMethod(Integer req);
String EchoMethod(int req);
String EchoMethod(Boolean req1, Integer req2);

Go와 Java 간의 오버로드된 메서드 매핑 을 완료하기 위해 다음 Thrift 정의를 작성합니다 JavaMethodName.hessian.argsType

service EchoService {
    string EchoMethodA(1: bool req) (JavaMethodName="EchoMethod")
    string EchoMethodB(1: i32 req) (JavaMethodName="EchoMethod")
    string EchoMethodC(1: i32 req) (JavaMethodName="EchoMethod", hessian.argsType="int")
    string EchoMethodD(1: bool req1, 2: i32 req2) (JavaMethodName="EchoMethod")
 }

예외 처리

codec-dubbo는 Java의 예외를 Go의 오류에 매핑합니다. 이러한 오류는 다음 인터페이스를 균일하게 구현합니다.

type Throwabler interface {
    Error() string
    JavaClassName() string
    GetStackTrace() []StackTraceElement
}

Dubbo가 공식적으로 권장하는 예외 처리 방식과 기업 사용자의 현재 요구 사항을 기반으로 사용자의 기본 요구 사항과 확장성 요구 사항을 고려하여 예외를 일반 예외와 사용자 지정 예외로 나눕니다.

일반적인 예외

codec-dubbo는 현재 java.lang.Exception을 지원하는 pkg/hessian2/Exception 패키지 에서 일반적인 Java 예외를 제공합니다 .

일반적인 예외에는 Kitex 명령줄 도구의 지원이 필요하지 않으며 직접 인용할 수 있습니다. 다음은 클라이언트에서 추출한 예외와 서버에서 반환된 예외의 예입니다.

클라이언트 추출 예외

resp, err := cli.Greet(context.Background(),
    &hello.GreetRequest{Req: "world"})
if err != nil {
    // FromError 返回 Throwabler
    exceptionRaw, ok := hessian2_exception.FromError(err)
    if !ok {
        // 视作常规错误处理        
    } else {
        // 若不关心 exceptionRaw 的具体类型,直接调用 Throwabler 提供的方法即可
        klog.Errorf("get %s type Exception", exceptionRaw.JavaClassName())                
        // 若想获得 exceptionRaw 的具体类型,需要进行类型转换,但前提是已知该具体类型
        exception := exceptionRaw.(*hessian2_exception.Exception)
    }
}

서버 측 반환 예외

func (s *GreetServiceImpl) Greet(ctx context.Context, req *hello.GreetRequest) (resp *hello.GreetResponse, err error) {
    return nil, hessian2_exception.NewException("Your detailed message")
}

사용자 정의 예외

Java의 사용자 정의 예외는 종종 기본 예외를 상속합니다. CustomizedExceptionCustomizedException다음을 상속합니다 java.lang.Exception.

public class CustomizedException extends Exception {
    private final String customizedMessage;
    
    public CustomizedException(String customizedMessage) {
        super();
        this.customizedMessage = customizedMessage;
    }
}

중첩 구조 생성에 대한 thriftgo의 지원 덕분에 Kitex 측에서 해당 예외를 정의하기 위해 Thrift에서 다음 정의를 작성합니다.

exception CustomizedException {
    // thrift.nested=“true” 注解让 thriftgo 生成嵌套结构体
    1: required java.Exception exception (thrift.nested="true")
    2: required string customizedMessage
}(JavaClassName="org.cloudwego.kitex.samples.api.CustomizedException")

thriftgo가 중첩된 구조를 생성하여 상속과 유사한 효과를 얻을 수 있도록 하는 exception필드 주석에 주의하세요 .thrift.nested="true"

-hessian2 java_extensionJava 공통 유형 확장과 마찬가지로 kitex 스캐폴딩 도구를 사용하여 확장 패키지를 가져오는 코드를 생성할 때 매개변수를 추가해야 합니다 .

type EchoCustomizedException struct {
        java.Exception `thrift:"exception,1,required" frugal:"1,required,java.Exception" json:"exception"`

        CustomizedMessage string `thrift:"customizedMessage,2,required" frugal:"2,required,string" json:"customizedMessage"`
}

사용 방법은 일반적인 예외와 일치하므로 여기서는 반복하지 않습니다.

서비스 등록 및 검색

Dubbo는 기업 사용자의 현재 생산 환경 요구 사항을 기반으로 인터페이스 수준 및 애플리케이션 수준 서비스 등록 및 검색 모델을 모두 제공하며 Zookeeper: Dubbo Registry-Zookeeper를 기반으로 하는 인터페이스 수준 모델의 우선 순위를 선택합니다.

우리에게 익숙한 애플리케이션 수준 모델과 달리 인터페이스 수준 모델은 인터페이스 이름 => 서비스의 매핑 관계를 유지해야 합니다(마이크로서비스와 다르며 핸들러에 더 가깝습니다). 이러한 서비스는 프로세스 내에서 동일하게 존재할 수 있습니다.

Dubbo의 인터페이스 수준 서비스 모델이 Kitex의 서비스 모델과 상당히 다르며, Dubbo Registry-zookeeper를 codec-dubbo에 바인딩하여 사용해야 한다는 점을 고려하면, dubbo 레지스트리를 만들기 위해 kitex-contrib에서 원래의 Registry-zookeeper를 수정하는 것은 고려되지 않습니다. -zookeeper codec-dubbo의 sub-go 모듈이 개발되어 균일하게 유지됩니다.

Dubbo 인터페이스 수준 서비스 모델, Kitex API 및 사용자 경험을 고려하여 다음과 같은 구성 수준을 제공합니다.

  1. Registry/options.goResolver/options.go 의 WithServers 및 WithRegistryGroup 함수는 각각 사육사 주소와 이러한 사육사가 속한 그룹을 지정하는 레지스트리 수준 구성을 제공합니다. 사용자는 이러한 기능을 사용하여 Kitex registry.Registry및 인스턴스를 생성합니다 discovery.Resolver.
  2. 서비스 수준 구성은 registries/common.goclient.WithTag 를 통해 server.WithRegistryInfo전달되며 Dubbo의 서비스 메타데이터에 일대일로 대응하는 태그 키를 제공합니다.

리졸버 예

intfName := "org.cloudwego.kitex.samples.api.GreetProvider"
res, err := resolver.NewZookeeperResolver(
    // 指定 zookeeper 服务器的地址,可指定多个,请至少指定一个 
    resolver.WithServers("127.0.0.1:2181"),
)
if err != nil {
    panic(err)
}
cli, err := greetservice.NewClient("helloworld",
    // 配置 ZookeeperResolver
    client.WithResolver(res),
    // 指定想要调用的 dubbo Interface
    client.WithTag(registries.DubboServiceInterfaceKey, intfName),
)
if err != nil {
    panic(err)
}
// 使用 cli 进行 RPC 调用

레지스트리 예시

intfName := "org.cloudwego.kitex.samples.api.GreetProvider"
reg, err := registry.NewZookeeperRegistry(
    // 指定 zookeeper 服务器的地址,可指定多个,请至少指定一个 
    registry.WithServers("127.0.0.1:2181"),
)
if err != nil {
    panic(err)
}

svr := greetservice.NewServer(new(GreetServiceImpl),
    server.WithRegistry(reg),
    // 配置dubbo URL元数据
    server.WithRegistryInfo(&kitex_registry.Info{
        Tags: map[string]string{
            registries.DubboServiceInterfaceKey: intfName,
            // application请与dubbo所设置的ApplicationConfig保持一致,此处仅为示例
            registries.DubboServiceApplicationKey: "application-name",
        }
    }),
)
// 启动 svr

요약하다

Kitex는 CloudWeGo가 다국어 클라우드 네이티브 생태계를 통합하는 데 큰 진전을 이루는 Dubbo 프로토콜을 지원합니다. 이는 Java를 Go로 변환하고 Java와 Go가 공존하는 데 있어 많은 기업 사용자의 문제점을 해결합니다. 사용 중에 문제가 발생하면, 질문이 있는 경우 Feishu 사용자 그룹에 가입하거나 Github에 피드백을 제공할 수 있습니다.

"Celebrateing More Than Years 2"의 불법 복제 리소스가 npm에 업로드되어 npmmirror가 unpkg 서비스 를 중단해야 했습니다. Microsoft의 중국 AI 팀은 수백 명의 사람들을 모아 미국으로 떠났습니다. 프론트엔드 시각화 라이브러리와 Baidu의 유명한 오픈 소스 프로젝트 ECharts - Fish 사기꾼을 지원하기 위한 "going to the sea"는 TeamViewer를 사용하여 398만 개를 전송했습니다! 원격 데스크톱 공급업체는 무엇을 해야 합니까? Zhou Hongyi: Google은 시간이 얼마 남지 않았습니다. 모든 제품을 오픈소스로 만드는 것이 좋습니다. 한 유명 오픈소스 회사의 전직 직원이 소식을 전했습니다. 부하 직원의 도전을 받은 후 기술 리더는 분노했습니다. Google은 Android 가상 머신에서 ChromeOS를 실행하는 방법을 보여주었습니다. 여기서 time.sleep(6)은 어떤 역할을 합니까? 마이크로소프트, 중국 AI 팀이 "미국을 위해 준비 중"이라는 루머에 대응 사무용 소프트웨어의 마트료시카 같은 충전에 대한 인민일보 온라인 논평: "세트"를 적극적으로 해결해야만 미래를 가질 수 있다
{{o.이름}}
{{이름}}

추천

출처my.oschina.net/u/4843764/blog/11044578