Autor: Wang Yuxuan (github: DMwangnima), Kitex Committer
1. Fundo
Kitex é uma estrutura RPC de microsserviço Go lançada pela equipe de estrutura de serviço de infraestrutura da ByteDance. Ele oferece suporte a protocolos de mensagens como Thrift, Kitex Protobuf e gRPC e é caracterizado por alto desempenho e forte escalabilidade. Depois de ser oficialmente de código aberto em setembro de 2021, foi implementado com sucesso em muitas empresas externas, trazendo-lhes benefícios reais em termos de custo, desempenho e estabilidade.
No processo de utilização dos serviços de transformação Kitex, muitos usuários corporativos precisam do Kitex para se comunicar com os serviços implementados pela estrutura Dubbo existente. Isso coincide com o objetivo da comunidade CloudWeGo de expandir ativamente o ecossistema . nasceu.
Com a ajuda entusiástica de estudantes da comunidade, o codec-dubbo é atualmente capaz de interoperar entre Kitex e Dubbo-Java, Kitex e Dubbo-Go, e oferece suporte aos usuários do Dubbo na migração para o Kitex.
Este artigo tomará como exemplo a transformação bem-sucedida do serviço da Founder Securities usando Kitex e codec-dubbo, explicará as principais funções do codec-dubbo usadas no processo de transformação e analisará brevemente os detalhes da implementação.
2. Casos de implementação empresarial
Os serviços originais da Founder Securities são escritos em estruturas Java e Dubbo. Ambos são estáveis e foram verificados em um grande número de cenários, atendendo às suas necessidades de produção e desenvolvimento. Tomando como exemplo a página de detalhes de estoque individual de Xiaofang, que tem um grande número de solicitações, a interface QPS durante períodos de pico é de 3 a 4k e 16 máquinas virtuais de 16 Core 64G são usadas para hospedagem.
Com o surgimento da arquitetura nativa da nuvem, Go tornou-se gradualmente uma opção tecnológica importante para a construção de serviços corporativos devido às suas vantagens no uso de memória e eficiência de execução, bem como à sua adaptabilidade natural à arquitetura nativa da nuvem. Para reduzir melhor os custos e aumentar a eficiência, após considerar exaustivamente fatores como custo, desempenho e estabilidade, eles decidiram mudar de Java para Go para novos aplicativos, introduziram Kitex, Hertz e outros projetos CloudWeGo para desenvolvimento e reconstrução de serviços, e migraram todo o ambiente Kubernetes.
Durante o processo de reconstrução, o codec-dubbo contou com uma experiência de usuário próxima ao Kitex + Thrift nativo e um bom suporte para os conceitos Dubbo para reduzir o custo de uso e compreensão. Serviço Kitex Chame suavemente o serviço Dubbo original.
Atualmente, o serviço Kitex usando codec-dubbo foi lançado com sucesso e está funcionando de forma estável há dois meses. Tomando como exemplo a página de detalhes de estoque individual da Xiaofang, Kitex e Hertz hospedam cerca de metade das interfaces nesta página. Quando o QPS permanece inalterado, apenas 12 pods 4G de 4 núcleos precisam ser fornecidos, o que reduz significativamente o uso de recursos.
3. Recursos funcionais do codec-dubbo
Codec de protocolo Dubbo
O serviço Dubbo usa principalmente o protocolo Dubbo para comunicação. Para oferecer suporte à interoperabilidade Kitex <-> Dubbo, precisamos implementar o protocolo Dubbo no Kitex. Graças à excelente escalabilidade do Kitex, o codec-dubbo implementa DubboCodec, o codec principal, baseado na interface Codec fornecida pelo Kitex. Você só precisa injetar DubboCodec durante a inicialização para usar o protocolo Dubbo.
Mapeamento e expansão de tipo
mapeamento de tipo
Dubbo usa principalmente o protocolo de serialização Hessian2 para codificar e decodificar a carga útil. Sua maior característica é o tipo de serialização autodescritiva, ou seja, não depende de esquema externo ou definições de interface. O processo de serialização depende do mapeamento entre tipos de linguagem de programação e tipos Hessian2, tomando como exemplo a conversão de tipos Go em tipos Java:
Após análise, descobrimos que o sistema de tipo básico de Hessian2 basicamente se sobrepõe ao Thrift. Para garantir que a experiência de uso do Kitex + codec-dubbo seja basicamente a mesma do Kitex + Thrift, geramos o código de andaime Kitex Dubbo-Hessian2 baseado no Thrift IDL. Neste momento, o processo de conversão de tipo é o seguinte:
Consulte o mapeamento de tipo dubbo-go-hessian2 oficial do Dubbo , codec-dubbo fornece o seguinte mapeamento de tipo (apenas parte do mapeamento está incluída aqui. Para obter mais precauções, consulte o Leiame do codec-dubbo):
De acordo com o mapeamento de tipo fornecido pelo codec-dubbo, podemos facilmente converter a definição da interface Dubbo em Thrift IDL, usar a ferramenta de linha de comando Kitex para gerar o código de andaime do projeto e, finalmente, injetar DubboCodec para completar a comunicação Kitex -> Dubbo. Tomemos a seguinte definição de interface Dubbo como exemplo:
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;
}
}
O arquivo api.thrift correspondente é o seguinte. Deve-se observar que as definições de estrutura nele precisam ser anotadas com JavaClassName, que corresponde ao pacote + nome da classe na definição da interface Dubbo.
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)
}
Use a ferramenta de linha de comando Kitex e especifique o protocolo como Hessian2:
kitex -module demo-client -protocol Hessian2 ./api.thrift
Em seguida, inicialize DubboCodec e injete-o no Kitex. Use o código gerado para escrever o seguinte código do cliente para implementar a chamada 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)
O processo do lado do servidor Kitex + codec-dubbo é basicamente semelhante ao processo do lado do cliente. Para exemplos específicos, consulte a página inicial do projeto.
Expansão de tipo
O recurso livre de esquema do Hessian2 torna a implementação do Dubbo "muito flexível" e pode usar qualquer tipo. Para se adaptar à flexibilidade de uso de tipo do Dubbo Hessian2, o codec-dubbo suporta expansão de tipo, que inclui principalmente mapeamento personalizado e expansão de tipo comum Java.
Mapeamento personalizado
Os tipos básicos de Java possuem tipos de wrapper correspondentes, como boolean
e java.lang.Boolean
. O mapeamento padrão bool
de tipos Go para java.lang.Boolean
tipos Java no mapeamento de tipos não cobre o uso boolean
de . Para unificar a experiência do usuário e permitir que eles usem apenas bool
o tipo no lado Kitex, podemos adicionar anotações após a definição do método Thrift hessian.argsType="boolean"
, usar a função de reflexão IDL do thriftgo para gerar meta informações IDL antecipadamente e injetá-las no codec- dubbo, e então ele pode ser executado para java.lang.Boolean
reescrever dinamicamente o tipo de mapeamento padrão para boolean
. A definição específica de Thrift é a seguinte:
service EchoService {
bool EchoBoolean(1: bool req) (hessian.argsType="boolean")
}
Semelhante a boolean
e java.lang.Boolean
, outros tipos básicos de Java e tipos de wrapper também podem ser personalizados desta forma. Neste momento, o mapeamento de tipo completo fornecido pelo codec-dubbo é o seguinte:
expansão de tipo comum java
Devido às limitações dos tipos Thrift, não podemos usar diretamente os tipos comuns fornecidos nas bibliotecas de classes Java. Para este fim, codec-dubbo mantém tipos Java que o Thrift não suporta (por exemplo , ) e o java.thrift correspondente no pacote codec-dubbo/java , ao mesmo tempo, com a ajuda da função idl-ref fornecida. por thriftgo, podemos usar Thrift diretamente. Esses tipos são referenciados em IDL e o código correspondente é gerado. O java.thrift atual se parece com isto:java.lang.Object
java.util.Date
struct Object {} (JavaClassName="java.lang.Object")
struct Date {} (JavaClassName="java.util.Date")
struct Exception {} (JavaClassName="java.lang.Exception")
Para habilitar esses tipos, precisamos importá-los no Thrift IDL include "java.thrift"
e adicionar parâmetros ao gerar o código usando a ferramenta de linha de comando Kitex -hessian2 java_extension
para extrair o pacote de expansão.
A ferramenta de linha de comando Kitex baixará automaticamente o java.thrift. Você também pode baixá-lo manualmente e colocá-lo no diretório raiz do projeto. Exemplo de Thrift IDL referenciando tipos em java.thrift:
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)
}
Sobrecarga de método
Go não oferece suporte nativo à sobrecarga de métodos. Você só pode obter efeitos semelhantes aos da sobrecarga definindo vários métodos. Para mapear vários métodos em Go para métodos sobrecarregados em Java, semelhante à seção de mapeamento personalizado, adicionamos a tag JavaMethodName após a definição do método em Thrift e usamos a função de reflexão IDL de thriftgo para mapear Go dinamicamente em tempo de execução. o nome do método é reescrito no método sobrecarregado em Java especificado por JavaMethodName.
Veja EchoMethod no lado Java como exemplo:
String EchoMethod(Boolean req);
String EchoMethod(Integer req);
String EchoMethod(int req);
String EchoMethod(Boolean req1, Integer req2);
Escrevemos a seguinte definição de Thrift para completar o mapeamento de métodos sobrecarregados entre Go e Java. Observe JavaMethodName
que e hessian.argsType
pode ser usado ao mesmo tempo:
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")
}
Manipulação de exceção
codec-dubbo mapeia exceções em Java para erros em Go. Esses erros implementam uniformemente a seguinte interface:
type Throwabler interface {
Error() string
JavaClassName() string
GetStackTrace() []StackTraceElement
}
Com base nas práticas de tratamento de exceções oficialmente recomendadas pela Dubbo e nas necessidades atuais dos usuários corporativos, dividimos as exceções em exceções comuns e exceções personalizadas, levando em consideração as necessidades básicas e de escalabilidade dos usuários.
Exceções comuns
codec-dubbo fornece exceções Java comuns no pacote pkg/hessian2/exception , atualmente suportando java.lang.Exception.
Exceções comuns não requerem suporte da ferramenta de linha de comando Kitex e podem ser citadas diretamente. A seguir estão exemplos de exceções extraídas pelo cliente e exceções retornadas pelo servidor.
Exceção de extração de cliente
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)
}
}
Lado do servidor retorna exceção
func (s *GreetServiceImpl) Greet(ctx context.Context, req *hello.GreetRequest) (resp *hello.GreetResponse, err error) {
return nil, hessian2_exception.NewException("Your detailed message")
}
Exceção personalizada
Exceções personalizadas em Java geralmente herdam uma exceção básica. Aqui, tomando como CustomizedException
exemplo , CustomizedException
ela herda java.lang.Exception
:
public class CustomizedException extends Exception {
private final String customizedMessage;
public CustomizedException(String customizedMessage) {
super();
this.customizedMessage = customizedMessage;
}
}
Graças ao suporte do thriftgo para geração de estruturas aninhadas, para definir exceções correspondentes no lado Kitex, escrevemos a seguinte definição no 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")
Preste atenção exception
às anotações de campo thrift.nested="true"
, que permitem que o thriftgo gere estruturas aninhadas para obter um efeito semelhante à herança.
Assim como as extensões de tipo comum Java, você precisa adicionar parâmetros ao usar a ferramenta de andaime kitex para gerar código -hessian2 java_extension
para extrair o pacote de expansão.
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"`
}
O método de uso é consistente com exceções comuns e não será repetido aqui.
Registro e descoberta de serviço
Dubbo fornece modelos de descoberta e registro de serviço em nível de interface e em nível de aplicativo. Com base nas necessidades atuais do ambiente de produção dos usuários corporativos, optamos por priorizar o modelo de nível de interface baseado no zookeeper: Dubbo Registry-zookeeper.
Diferente do modelo de nível de aplicativo com o qual estamos familiarizados, o modelo de nível de interface precisa manter o relacionamento de mapeamento do nome da interface => serviço (diferente dos microsserviços, mais próximo do manipulador, um nome de interface será mapeado para vários serviços e). esses serviços podem existir no mesmo em um processo.
Considerando que o modelo de serviço de nível de interface do Dubbo é bastante diferente do modelo de serviço do Kitex, e o registro-zookeeper do Dubbo deve ser vinculado ao codec-dubbo para uso, não é considerado modificar o registro-zookeeper original no kitex-contrib para fazer o registro do dubbo -zookeeper Um módulo sub-go do codec-dubbo é desenvolvido e mantido uniformemente.
Levando em consideração o modelo de serviço de nível de interface Dubbo, a API Kitex e a experiência do usuário, fornecemos os seguintes níveis de configuração:
- As funções WithServers e WithRegistryGroup em Registry/options.go e resolver/options.go fornecem configuração em nível de registro, especificando respectivamente o endereço do tratador e o grupo ao qual esses tratadores pertencem. Os usuários usam essas funções para gerar Kitex
registry.Registry
ediscovery.Resolver
instâncias. - A configuração de nível de serviço é passada
client.WithTag
com e , registries/common.go fornece chaves de tag, que correspondem aos metadados de serviço no Dubbo um para um.server.WithRegistryInfo
exemplo de resolvedor
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 调用
exemplo de registro
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
Resumir
Kitex suporta o protocolo Dubbo, que é um grande passo para CloudWeGo ajudar a integrar a ecologia nativa da nuvem multilíngue. Ele resolve os problemas de muitos usuários corporativos na conversão de Java para Go e na coexistência entre Java e Go. tente acessá-lo; se você encontrar algum problema durante o uso, se tiver alguma dúvida, você pode ingressar em nosso grupo de usuários Feishu ou nos dar feedback no Github.
Os recursos piratas de "Celebrating More Than Years 2" foram carregados no npm, fazendo com que o npmmirror tivesse que suspender o serviço unpkg da Microsoft na China fez as malas coletivamente e foi para os Estados Unidos, envolvendo centenas de pessoas . biblioteca de visualização front-end e o conhecido projeto de código aberto ECharts do Baidu - "indo para o mar" para apoiar os golpistas de peixes usaram o TeamViewer para transferir 3,98 milhões! O que os fornecedores de desktop remoto devem fazer? Zhou Hongyi: Não resta muito tempo para o Google. Recomenda-se que todos os produtos sejam de código aberto. Um ex-funcionário de uma conhecida empresa de código aberto deu a notícia: Após ser desafiado por seus subordinados, o líder técnico ficou furioso e. demitiu a funcionária grávida. O Google mostrou como executar o ChromeOS em uma máquina virtual Android. Por favor, me dê alguns conselhos: qual é o papel do time.sleep(6) aqui? A Microsoft responde aos rumores de que a equipe de IA da China está "fazendo as malas para os Estados Unidos" Comentários do People's Daily Online sobre a cobrança semelhante a matryoshka do software de escritório: Somente resolvendo ativamente "conjuntos" podemos ter um futuro