gRPC 中使用 Channelz

gRPC 中使用 Channelz

gRPC 提供了 Channelz 用于对外提供服务的数据,用于调试、监控等;根据服务的角色不同,可以提供的数据有:

  • 服务端: Servers, Server, ServerSockets, Socket
  • 客户端: TopChannels, Channel, Subchannel

Channelz 服务定义

参考 Channelz 的设计 gRPC Channelz 以及服务定义 channelz.proto,提供了以下方法:

service Channelz {
// 返回所有的根 Channel(即应用直接创建的 Channel)
  rpc GetTopChannels(GetTopChannelsRequest) returns (GetTopChannelsResponse);
  // 根据 Channel ID 返回单个的 Channel 详情,包括 Subchannel,如果没有则返回 NOT_FOUND
  rpc GetChannel(GetChannelRequest) returns (GetChannelResponse);
  // 根据 Subchannel ID 返回 Subchannel 详情
  rpc GetSubchannel(GetSubchannelRequest) returns (GetSubchannelResponse);
  // 返回所有存在的 Server
  rpc GetServers(GetServersRequest) returns (GetServersResponse);
  // 根据 Server ID 返回 Server 详情
  rpc GetServer(GetServerRequest) returns (GetServerResponse);
  // 根据 Server ID 返回 Server 所有的 Socket
  rpc GetServerSockets(GetServerSocketsRequest) returns (GetServerSocketsResponse);
  // 根据 Socket ID 返回 Socket 详情
  rpc GetSocket(GetSocketRequest) returns (GetSocketResponse);
}

使用

相关项目参考 github.com/helloworlde/grpc-java-sample

添加依赖

  • build.gradle.kts

Channelz 服务在 grpc-services 包中,需要添加该依赖

dependencies {
    implementation("io.grpc:grpc-netty:${grpcVersion}")
    implementation("io.grpc:grpc-protobuf:${grpcVersion}")
    implementation("io.grpc:grpc-stub:${grpcVersion}")
    implementation("io.grpc:grpc-services:${grpcVersion}")
}

Server 端

Server 端添加 Channelz 服务非常简单,只需要将 Channelz 的服务添加到 Server 中即可

  • Server

可以通过 ChannelzService.newInstance(100) 直接构建 Channelz 服务实例,参数是获取数据时分页的大小

@Slf4j
public class ChannelzServer {

    @SneakyThrows
    public static void main(String[] args) {

        // 构建 Server
        Server server = NettyServerBuilder.forAddress(new InetSocketAddress(9090))
                                          // 添加服务
                                          .addService(new HelloServiceImpl())
                                          // 添加 Channelz 服务
+                                         .addService(ChannelzService.newInstance(100))
                                          // 添加反射服务,用于 grpcurl 等工具调试
+                                         .addService(ProtoReflectionService.newInstance())
                                          .build();

        // 启动 Server
        server.start();
        log.info("服务端启动成功");

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                server.awaitTermination(10, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }));

        // 保持运行
        server.awaitTermination();
    }
}

Client 端

Client 端不能直接开启,需要单独启动一个 Server,用于提供数据,与 Server 端一样,不过只提供 Channelz 的服务

@Slf4j
public class ChannelzClient {

    @SneakyThrows
    public static void main(String[] args) throws InterruptedException {

        // 构建并启动 Channelz 服务
+       Server server = NettyServerBuilder.forPort(9091)
+                                         // 添加 Channelz 服务
+                                         .addService(ChannelzService.newInstance(100))
+                                         // 添加反射服务,用于 grpcurl 等工具调试
+                                         .addService(ProtoReflectionService.newInstance())
+                                         .build()
+                                         .start();

        // 构建 Channel
        ManagedChannel channel = ManagedChannelBuilder.forAddress("127.0.0.1", 9090)
                                                      .usePlaintext()
                                                      .build();

        // 使用 Channel 构建 BlockingStub
        HelloServiceGrpc.HelloServiceBlockingStub blockingStub = HelloServiceGrpc.newBlockingStub(channel);

        // 发送多个请求,用于观察数据变化
        for (int i = 0; i < 10000; i++) {
            // 构建消息
            HelloMessage message = HelloMessage.newBuilder()
                                               .setMessage("Channelz " + i)
                                               .build();

            // 发送消息,并返回响应
            HelloResponse helloResponse = blockingStub.sayHello(message);
            log.info(helloResponse.getMessage());
            Thread.sleep(1000);
        }

        // 等待终止
+       server.awaitTermination();
    }
}

测试

分别启动 Server 端和 Client 端,发送请求;使用其他工具观察

grpc-zpages

gRPC 官方提供了 grpc-zpages 工具,可用通过 Web 查看指定服务的信息,不过该工具已经很久没有维护,使用较为复杂,具体可以参考 A short introduction to Channelz

channelzcli

是一个 CLI 工具,可以通过命令行实现获取 Channelz 的信息,对 Channelz 的数据做了一定的处理,较为友好,具体使用参考 channelzcli

安装
go get -u github.com/kazegusuri/channelzcli
使用
  • 获取 Channel 信息
channelzcli list channel -k --addr localhost:9091

ID	Name                                                                            	State	Channel	SubChannel	Calls	Success	Fail	LastCall
4	ManagedChannelImpl{logId=4, target=127.0.0.1:9090}                              	READY	0      	1         	4355  	4355  	0     	716ms
  • 描述 Server 信息
channelzcli describe server 2 -k --addr localhost:9090

ID: 	2
Name:	ServerImpl{logId=2, transportServers=[NettyServer{logId=1, address=0.0.0.0/0.0.0.0:9090}]}
Calls:
  Started:        	14486
  Succeeded:      	14476
  Failed:         	9
  LastCallStarted:	2021-01-06 08:51:35.608 +0000 UTC
  • 列出 Channel 信息
channelzcli tree channel -k --addr localhost:9091

127.0.0.1:9090 (ID:4) [READY]
  [Calls] Started:4627, Succeeded:4627, Failed:0, Last:638ms
  [Subchannels]
    |-- [[[/127.0.0.1:9090]/{}]] (ID:6) [READY]
          [Calls]: Started:4627, Succeeded:4627, Failed:0, Last:638ms
          [Socket] ID:7, Name:CallTracingTransport{delegate=CallCredentialsApplyingTransport{delegate=NettyClientTransport{logId=7, remoteAddress=/127.0.0.1:9090, channel=[id: 0x83c4d921, L:/127.0.0.1:52321 - R:/127.0.0.1:9090]}}}, RemoteName:, Local:[127.0.0.1]:52321 Remote:[127.0.0.1]:9090```

grpcurl

是一个 CLI 工具,可以像 CURL 一样对 gRPC 服务发起请求,但是需要服务添加反射服务,详情参考 grpcurl

安装
brew install grpcurl
使用
  • 获取 Server 列表
grpcurl -plaintext localhost:9090 grpc.channelz.v1.Channelz/GetServers

{
  "server": [
    {
      "ref": {
        "server_id": "2",
        "name": "ServerImpl{logId=2, transportServers=[NettyServer{logId=1, address=0.0.0.0/0.0.0.0:9090}]}"
      },
      "data": {
        "calls_started": "13438",
        "calls_succeeded": "13432",
        "calls_failed": "4",
        "last_call_started_timestamp": "2021-01-06T08:34:17.763Z"
      },
      "listen_socket": [
        {
          "socket_id": "3",
          "name": "ListenSocket{logId=3, channel=[id: 0xc703c330, L:/0:0:0:0:0:0:0:0:9090]}"
        }
      ]
    }
  ],
  "end": true
}
  • 获取 Channel 列表
grpcurl -plaintext localhost:9091 grpc.channelz.v1.Channelz/GetTopChannels

{
  "channel": [
    {
      "ref": {
        "channel_id": "4",
        "name": "ManagedChannelImpl{logId=4, target=127.0.0.1:9090}"
      },
      "data": {
        "state": {
          "state": "READY"
        },
        "target": "127.0.0.1:9090",
        "calls_started": "45",
        "calls_succeeded": "45",
        "last_call_started_timestamp": "2021-01-06T07:38:45.217Z"
      },
      "subchannel_ref": [
        {
          "subchannel_id": "6",
          "name": "InternalSubchannel{logId=6, addressGroups=[[[/127.0.0.1:9090]/{}]]}"
        }
      ]
    }
  ],
  "end": true
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java使用gRPC进行简单的使用包括以下几个步骤: 1. 定义.proto文件:首先,你需要定义一个.proto文件,用于描述你的服务和消息格式。这个文件可以使用Protocol Buffers语言编写。例如,你可以定义一个简单的服务和消息格式如下: ```protobuf syntax = "proto3"; package com.example; service GreetingService { rpc SayHello(HelloRequest) returns (HelloResponse); } message HelloRequest { string name = 1; } message HelloResponse { string message = 1; } ``` 2. 生成Java代码:一旦你定义了.proto文件,你可以使用gRPC的编译器将其生成为Java代码。你可以使用以下命令生成代码: ```bash protoc --java_out=<output_directory> <proto_file>.proto ``` 这将生成与.proto文件相匹配的Java类。 3. 实现服务逻辑:在服务器端,你需要编写一个类来实现你定义的服务接口。例如,对于上面的例子,你可以编写一个GreetingServiceImpl类来实现GreetingService接口: ```java package com.example; import io.grpc.stub.StreamObserver; public class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase { @Override public void sayHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) { String greeting = "Hello, " + request.getName() + "!"; HelloResponse response = HelloResponse.newBuilder().setMessage(greeting).build(); responseObserver.onNext(response); responseObserver.onCompleted(); } } ``` 4. 启动服务器:你需要编写一个服务器类来启动gRPC服务器并注册你的服务实现类。例如: ```java package com.example; import io.grpc.Server; import io.grpc.ServerBuilder; import java.io.IOException; public class ServerMain { public static void main(String[] args) throws IOException, InterruptedException { Server server = ServerBuilder.forPort(50051) .addService(new GreetingServiceImpl()) .build(); server.start(); System.out.println("Server started"); server.awaitTermination(); } } ``` 5. 创建客户端:最后,你可以编写一个客户端类来调用你的服务。例如: ```java package com.example; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; public class ClientMain { public static void main(String[] args) { ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051) .usePlaintext() .build(); GreetingServiceGrpc.GreetingServiceBlockingStub stub = GreetingServiceGrpc.newBlockingStub(channel); HelloRequest request = HelloRequest.newBuilder().setName("Alice").build(); HelloResponse response = stub.sayHello(request); System.out.println(response.getMessage()); channel.shutdown(); } } ``` 这样,你就可以使用gRPC进行简单通信了。在实际使用,你可能还需要处理错误、流式传输等更复杂的场景。你可以参考gRPC的官方文档和示例来深入了解更多用法和细节。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值