Merge pull request #1926 from grpc/master

Merge master into 1.4.x (i.e. backport channelz to 1.4.x)
This commit is contained in:
Michael Lumish 2021-10-08 13:15:02 -07:00 committed by GitHub
commit c4fc1dbe8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
76 changed files with 4241 additions and 167 deletions

View File

@ -4,7 +4,7 @@ This guide is for troubleshooting the `grpc-js` library for Node.js
## Enabling extra logging and tracing
Extra logging can be very useful for diagnosing problems. `grpc-js` supporst
Extra logging can be very useful for diagnosing problems. `grpc-js` supports
the `GRPC_VERBOSITY` and `GRPC_TRACE` environment variables that can be used to increase the amount of information
that gets printed to stderr.

View File

@ -185,14 +185,7 @@ export class EdsLoadBalancer implements LoadBalancer {
private concurrentRequests: number = 0;
constructor(private readonly channelControlHelper: ChannelControlHelper) {
this.childBalancer = new ChildLoadBalancerHandler({
createSubchannel: (subchannelAddress, subchannelArgs) =>
this.channelControlHelper.createSubchannel(
subchannelAddress,
subchannelArgs
),
requestReresolution: () =>
this.channelControlHelper.requestReresolution(),
this.childBalancer = new ChildLoadBalancerHandler(experimental.createChildChannelControlHelper(this.channelControlHelper, {
updateState: (connectivityState, originalPicker) => {
if (this.latestEdsUpdate === null) {
return;
@ -243,7 +236,7 @@ export class EdsLoadBalancer implements LoadBalancer {
};
this.channelControlHelper.updateState(connectivityState, edsPicker);
},
});
}));
this.watcher = {
onValidUpdate: (update) => {
trace('Received EDS update for ' + this.edsServiceName + ': ' + JSON.stringify(update, undefined, 2));

View File

@ -174,20 +174,14 @@ export class LrsLoadBalancer implements LoadBalancer {
private localityStatsReporter: XdsClusterLocalityStats | null = null;
constructor(private channelControlHelper: ChannelControlHelper) {
this.childBalancer = new ChildLoadBalancerHandler({
createSubchannel: (subchannelAddress, subchannelArgs) =>
channelControlHelper.createSubchannel(
subchannelAddress,
subchannelArgs
),
requestReresolution: () => channelControlHelper.requestReresolution(),
this.childBalancer = new ChildLoadBalancerHandler(experimental.createChildChannelControlHelper(channelControlHelper, {
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
if (this.localityStatsReporter !== null) {
picker = new LoadReportingPicker(picker, this.localityStatsReporter);
}
channelControlHelper.updateState(connectivityState, picker);
},
});
}));
}
updateAddressList(

View File

@ -139,23 +139,11 @@ export class PriorityLoadBalancer implements LoadBalancer {
private failoverTimer: NodeJS.Timer | null = null;
private deactivationTimer: NodeJS.Timer | null = null;
constructor(private parent: PriorityLoadBalancer, private name: string) {
this.childBalancer = new ChildLoadBalancerHandler({
createSubchannel: (
subchannelAddress: SubchannelAddress,
subchannelArgs: ChannelOptions
) => {
return this.parent.channelControlHelper.createSubchannel(
subchannelAddress,
subchannelArgs
);
},
this.childBalancer = new ChildLoadBalancerHandler(experimental.createChildChannelControlHelper(this.parent.channelControlHelper, {
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
this.updateState(connectivityState, picker);
},
requestReresolution: () => {
this.parent.channelControlHelper.requestReresolution();
},
});
}));
this.picker = new QueuePicker(this.childBalancer);
}

View File

@ -168,17 +168,11 @@ export class WeightedTargetLoadBalancer implements LoadBalancer {
private weight: number = 0;
constructor(private parent: WeightedTargetLoadBalancer, private name: string) {
this.childBalancer = new ChildLoadBalancerHandler({
createSubchannel: (subchannelAddress, subchannelOptions) => {
return this.parent.channelControlHelper.createSubchannel(subchannelAddress, subchannelOptions);
},
updateState: (connectivityState, picker) => {
this.childBalancer = new ChildLoadBalancerHandler(experimental.createChildChannelControlHelper(this.parent.channelControlHelper, {
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
this.updateState(connectivityState, picker);
},
requestReresolution: () => {
this.parent.channelControlHelper.requestReresolution();
}
});
}));
this.picker = new QueuePicker(this.childBalancer);
}

View File

@ -131,17 +131,11 @@ class XdsClusterManager implements LoadBalancer {
private childBalancer: ChildLoadBalancerHandler;
constructor(private parent: XdsClusterManager, private name: string) {
this.childBalancer = new ChildLoadBalancerHandler({
createSubchannel: (subchannelAddress, subchannelOptions) => {
return this.parent.channelControlHelper.createSubchannel(subchannelAddress, subchannelOptions);
},
updateState: (connectivityState, picker) => {
this.childBalancer = new ChildLoadBalancerHandler(experimental.createChildChannelControlHelper(this.parent.channelControlHelper, {
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
this.updateState(connectivityState, picker);
},
requestReresolution: () => {
this.parent.channelControlHelper.requestReresolution();
}
});
}));
this.picker = new QueuePicker(this.childBalancer);
}

View File

@ -15,7 +15,6 @@
"types": "build/src/index.d.ts",
"license": "Apache-2.0",
"devDependencies": {
"@grpc/proto-loader": "^0.5.5",
"@types/gulp": "^4.0.6",
"@types/gulp-mocha": "0.0.32",
"@types/lodash": "^4.14.108",
@ -54,9 +53,11 @@
"check": "gts check src/**/*.ts",
"fix": "gts fix src/*.ts",
"pretest": "npm run compile",
"posttest": "npm run check && madge -c ./build/src"
"posttest": "npm run check && madge -c ./build/src",
"generate-types": "proto-loader-gen-types --keepCase --longs String --enums String --defaults --oneofs --includeComments --includeDirs proto/ -O src/generated/ --grpcLib ../index channelz.proto"
},
"dependencies": {
"@grpc/proto-loader": "^0.6.4",
"@types/node": ">=12.12.47"
},
"files": [

View File

@ -0,0 +1,564 @@
// Copyright 2018 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This file defines an interface for exporting monitoring information
// out of gRPC servers. See the full design at
// https://github.com/grpc/proposal/blob/master/A14-channelz.md
//
// The canonical version of this proto can be found at
// https://github.com/grpc/grpc-proto/blob/master/grpc/channelz/v1/channelz.proto
syntax = "proto3";
package grpc.channelz.v1;
import "google/protobuf/any.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
option go_package = "google.golang.org/grpc/channelz/grpc_channelz_v1";
option java_multiple_files = true;
option java_package = "io.grpc.channelz.v1";
option java_outer_classname = "ChannelzProto";
// Channel is a logical grouping of channels, subchannels, and sockets.
message Channel {
// The identifier for this channel. This should bet set.
ChannelRef ref = 1;
// Data specific to this channel.
ChannelData data = 2;
// At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
// There are no ordering guarantees on the order of channel refs.
// There may not be cycles in the ref graph.
// A channel ref may be present in more than one channel or subchannel.
repeated ChannelRef channel_ref = 3;
// At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
// There are no ordering guarantees on the order of subchannel refs.
// There may not be cycles in the ref graph.
// A sub channel ref may be present in more than one channel or subchannel.
repeated SubchannelRef subchannel_ref = 4;
// There are no ordering guarantees on the order of sockets.
repeated SocketRef socket_ref = 5;
}
// Subchannel is a logical grouping of channels, subchannels, and sockets.
// A subchannel is load balanced over by it's ancestor
message Subchannel {
// The identifier for this channel.
SubchannelRef ref = 1;
// Data specific to this channel.
ChannelData data = 2;
// At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
// There are no ordering guarantees on the order of channel refs.
// There may not be cycles in the ref graph.
// A channel ref may be present in more than one channel or subchannel.
repeated ChannelRef channel_ref = 3;
// At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
// There are no ordering guarantees on the order of subchannel refs.
// There may not be cycles in the ref graph.
// A sub channel ref may be present in more than one channel or subchannel.
repeated SubchannelRef subchannel_ref = 4;
// There are no ordering guarantees on the order of sockets.
repeated SocketRef socket_ref = 5;
}
// These come from the specified states in this document:
// https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md
message ChannelConnectivityState {
enum State {
UNKNOWN = 0;
IDLE = 1;
CONNECTING = 2;
READY = 3;
TRANSIENT_FAILURE = 4;
SHUTDOWN = 5;
}
State state = 1;
}
// Channel data is data related to a specific Channel or Subchannel.
message ChannelData {
// The connectivity state of the channel or subchannel. Implementations
// should always set this.
ChannelConnectivityState state = 1;
// The target this channel originally tried to connect to. May be absent
string target = 2;
// A trace of recent events on the channel. May be absent.
ChannelTrace trace = 3;
// The number of calls started on the channel
int64 calls_started = 4;
// The number of calls that have completed with an OK status
int64 calls_succeeded = 5;
// The number of calls that have completed with a non-OK status
int64 calls_failed = 6;
// The last time a call was started on the channel.
google.protobuf.Timestamp last_call_started_timestamp = 7;
}
// A trace event is an interesting thing that happened to a channel or
// subchannel, such as creation, address resolution, subchannel creation, etc.
message ChannelTraceEvent {
// High level description of the event.
string description = 1;
// The supported severity levels of trace events.
enum Severity {
CT_UNKNOWN = 0;
CT_INFO = 1;
CT_WARNING = 2;
CT_ERROR = 3;
}
// the severity of the trace event
Severity severity = 2;
// When this event occurred.
google.protobuf.Timestamp timestamp = 3;
// ref of referenced channel or subchannel.
// Optional, only present if this event refers to a child object. For example,
// this field would be filled if this trace event was for a subchannel being
// created.
oneof child_ref {
ChannelRef channel_ref = 4;
SubchannelRef subchannel_ref = 5;
}
}
// ChannelTrace represents the recent events that have occurred on the channel.
message ChannelTrace {
// Number of events ever logged in this tracing object. This can differ from
// events.size() because events can be overwritten or garbage collected by
// implementations.
int64 num_events_logged = 1;
// Time that this channel was created.
google.protobuf.Timestamp creation_timestamp = 2;
// List of events that have occurred on this channel.
repeated ChannelTraceEvent events = 3;
}
// ChannelRef is a reference to a Channel.
message ChannelRef {
// The globally unique id for this channel. Must be a positive number.
int64 channel_id = 1;
// An optional name associated with the channel.
string name = 2;
// Intentionally don't use field numbers from other refs.
reserved 3, 4, 5, 6, 7, 8;
}
// SubchannelRef is a reference to a Subchannel.
message SubchannelRef {
// The globally unique id for this subchannel. Must be a positive number.
int64 subchannel_id = 7;
// An optional name associated with the subchannel.
string name = 8;
// Intentionally don't use field numbers from other refs.
reserved 1, 2, 3, 4, 5, 6;
}
// SocketRef is a reference to a Socket.
message SocketRef {
// The globally unique id for this socket. Must be a positive number.
int64 socket_id = 3;
// An optional name associated with the socket.
string name = 4;
// Intentionally don't use field numbers from other refs.
reserved 1, 2, 5, 6, 7, 8;
}
// ServerRef is a reference to a Server.
message ServerRef {
// A globally unique identifier for this server. Must be a positive number.
int64 server_id = 5;
// An optional name associated with the server.
string name = 6;
// Intentionally don't use field numbers from other refs.
reserved 1, 2, 3, 4, 7, 8;
}
// Server represents a single server. There may be multiple servers in a single
// program.
message Server {
// The identifier for a Server. This should be set.
ServerRef ref = 1;
// The associated data of the Server.
ServerData data = 2;
// The sockets that the server is listening on. There are no ordering
// guarantees. This may be absent.
repeated SocketRef listen_socket = 3;
}
// ServerData is data for a specific Server.
message ServerData {
// A trace of recent events on the server. May be absent.
ChannelTrace trace = 1;
// The number of incoming calls started on the server
int64 calls_started = 2;
// The number of incoming calls that have completed with an OK status
int64 calls_succeeded = 3;
// The number of incoming calls that have a completed with a non-OK status
int64 calls_failed = 4;
// The last time a call was started on the server.
google.protobuf.Timestamp last_call_started_timestamp = 5;
}
// Information about an actual connection. Pronounced "sock-ay".
message Socket {
// The identifier for the Socket.
SocketRef ref = 1;
// Data specific to this Socket.
SocketData data = 2;
// The locally bound address.
Address local = 3;
// The remote bound address. May be absent.
Address remote = 4;
// Security details for this socket. May be absent if not available, or
// there is no security on the socket.
Security security = 5;
// Optional, represents the name of the remote endpoint, if different than
// the original target name.
string remote_name = 6;
}
// SocketData is data associated for a specific Socket. The fields present
// are specific to the implementation, so there may be minor differences in
// the semantics. (e.g. flow control windows)
message SocketData {
// The number of streams that have been started.
int64 streams_started = 1;
// The number of streams that have ended successfully:
// On client side, received frame with eos bit set;
// On server side, sent frame with eos bit set.
int64 streams_succeeded = 2;
// The number of streams that have ended unsuccessfully:
// On client side, ended without receiving frame with eos bit set;
// On server side, ended without sending frame with eos bit set.
int64 streams_failed = 3;
// The number of grpc messages successfully sent on this socket.
int64 messages_sent = 4;
// The number of grpc messages received on this socket.
int64 messages_received = 5;
// The number of keep alives sent. This is typically implemented with HTTP/2
// ping messages.
int64 keep_alives_sent = 6;
// The last time a stream was created by this endpoint. Usually unset for
// servers.
google.protobuf.Timestamp last_local_stream_created_timestamp = 7;
// The last time a stream was created by the remote endpoint. Usually unset
// for clients.
google.protobuf.Timestamp last_remote_stream_created_timestamp = 8;
// The last time a message was sent by this endpoint.
google.protobuf.Timestamp last_message_sent_timestamp = 9;
// The last time a message was received by this endpoint.
google.protobuf.Timestamp last_message_received_timestamp = 10;
// The amount of window, granted to the local endpoint by the remote endpoint.
// This may be slightly out of date due to network latency. This does NOT
// include stream level or TCP level flow control info.
google.protobuf.Int64Value local_flow_control_window = 11;
// The amount of window, granted to the remote endpoint by the local endpoint.
// This may be slightly out of date due to network latency. This does NOT
// include stream level or TCP level flow control info.
google.protobuf.Int64Value remote_flow_control_window = 12;
// Socket options set on this socket. May be absent if 'summary' is set
// on GetSocketRequest.
repeated SocketOption option = 13;
}
// Address represents the address used to create the socket.
message Address {
message TcpIpAddress {
// Either the IPv4 or IPv6 address in bytes. Will be either 4 bytes or 16
// bytes in length.
bytes ip_address = 1;
// 0-64k, or -1 if not appropriate.
int32 port = 2;
}
// A Unix Domain Socket address.
message UdsAddress {
string filename = 1;
}
// An address type not included above.
message OtherAddress {
// The human readable version of the value. This value should be set.
string name = 1;
// The actual address message.
google.protobuf.Any value = 2;
}
oneof address {
TcpIpAddress tcpip_address = 1;
UdsAddress uds_address = 2;
OtherAddress other_address = 3;
}
}
// Security represents details about how secure the socket is.
message Security {
message Tls {
oneof cipher_suite {
// The cipher suite name in the RFC 4346 format:
// https://tools.ietf.org/html/rfc4346#appendix-C
string standard_name = 1;
// Some other way to describe the cipher suite if
// the RFC 4346 name is not available.
string other_name = 2;
}
// the certificate used by this endpoint.
bytes local_certificate = 3;
// the certificate used by the remote endpoint.
bytes remote_certificate = 4;
}
message OtherSecurity {
// The human readable version of the value.
string name = 1;
// The actual security details message.
google.protobuf.Any value = 2;
}
oneof model {
Tls tls = 1;
OtherSecurity other = 2;
}
}
// SocketOption represents socket options for a socket. Specifically, these
// are the options returned by getsockopt().
message SocketOption {
// The full name of the socket option. Typically this will be the upper case
// name, such as "SO_REUSEPORT".
string name = 1;
// The human readable value of this socket option. At least one of value or
// additional will be set.
string value = 2;
// Additional data associated with the socket option. At least one of value
// or additional will be set.
google.protobuf.Any additional = 3;
}
// For use with SocketOption's additional field. This is primarily used for
// SO_RCVTIMEO and SO_SNDTIMEO
message SocketOptionTimeout {
google.protobuf.Duration duration = 1;
}
// For use with SocketOption's additional field. This is primarily used for
// SO_LINGER.
message SocketOptionLinger {
// active maps to `struct linger.l_onoff`
bool active = 1;
// duration maps to `struct linger.l_linger`
google.protobuf.Duration duration = 2;
}
// For use with SocketOption's additional field. Tcp info for
// SOL_TCP and TCP_INFO.
message SocketOptionTcpInfo {
uint32 tcpi_state = 1;
uint32 tcpi_ca_state = 2;
uint32 tcpi_retransmits = 3;
uint32 tcpi_probes = 4;
uint32 tcpi_backoff = 5;
uint32 tcpi_options = 6;
uint32 tcpi_snd_wscale = 7;
uint32 tcpi_rcv_wscale = 8;
uint32 tcpi_rto = 9;
uint32 tcpi_ato = 10;
uint32 tcpi_snd_mss = 11;
uint32 tcpi_rcv_mss = 12;
uint32 tcpi_unacked = 13;
uint32 tcpi_sacked = 14;
uint32 tcpi_lost = 15;
uint32 tcpi_retrans = 16;
uint32 tcpi_fackets = 17;
uint32 tcpi_last_data_sent = 18;
uint32 tcpi_last_ack_sent = 19;
uint32 tcpi_last_data_recv = 20;
uint32 tcpi_last_ack_recv = 21;
uint32 tcpi_pmtu = 22;
uint32 tcpi_rcv_ssthresh = 23;
uint32 tcpi_rtt = 24;
uint32 tcpi_rttvar = 25;
uint32 tcpi_snd_ssthresh = 26;
uint32 tcpi_snd_cwnd = 27;
uint32 tcpi_advmss = 28;
uint32 tcpi_reordering = 29;
}
// Channelz is a service exposed by gRPC servers that provides detailed debug
// information.
service Channelz {
// Gets all root channels (i.e. channels the application has directly
// created). This does not include subchannels nor non-top level channels.
rpc GetTopChannels(GetTopChannelsRequest) returns (GetTopChannelsResponse);
// Gets all servers that exist in the process.
rpc GetServers(GetServersRequest) returns (GetServersResponse);
// Returns a single Server, or else a NOT_FOUND code.
rpc GetServer(GetServerRequest) returns (GetServerResponse);
// Gets all server sockets that exist in the process.
rpc GetServerSockets(GetServerSocketsRequest) returns (GetServerSocketsResponse);
// Returns a single Channel, or else a NOT_FOUND code.
rpc GetChannel(GetChannelRequest) returns (GetChannelResponse);
// Returns a single Subchannel, or else a NOT_FOUND code.
rpc GetSubchannel(GetSubchannelRequest) returns (GetSubchannelResponse);
// Returns a single Socket or else a NOT_FOUND code.
rpc GetSocket(GetSocketRequest) returns (GetSocketResponse);
}
message GetTopChannelsRequest {
// start_channel_id indicates that only channels at or above this id should be
// included in the results.
// To request the first page, this should be set to 0. To request
// subsequent pages, the client generates this value by adding 1 to
// the highest seen result ID.
int64 start_channel_id = 1;
// If non-zero, the server will return a page of results containing
// at most this many items. If zero, the server will choose a
// reasonable page size. Must never be negative.
int64 max_results = 2;
}
message GetTopChannelsResponse {
// list of channels that the connection detail service knows about. Sorted in
// ascending channel_id order.
// Must contain at least 1 result, otherwise 'end' must be true.
repeated Channel channel = 1;
// If set, indicates that the list of channels is the final list. Requesting
// more channels can only return more if they are created after this RPC
// completes.
bool end = 2;
}
message GetServersRequest {
// start_server_id indicates that only servers at or above this id should be
// included in the results.
// To request the first page, this must be set to 0. To request
// subsequent pages, the client generates this value by adding 1 to
// the highest seen result ID.
int64 start_server_id = 1;
// If non-zero, the server will return a page of results containing
// at most this many items. If zero, the server will choose a
// reasonable page size. Must never be negative.
int64 max_results = 2;
}
message GetServersResponse {
// list of servers that the connection detail service knows about. Sorted in
// ascending server_id order.
// Must contain at least 1 result, otherwise 'end' must be true.
repeated Server server = 1;
// If set, indicates that the list of servers is the final list. Requesting
// more servers will only return more if they are created after this RPC
// completes.
bool end = 2;
}
message GetServerRequest {
// server_id is the identifier of the specific server to get.
int64 server_id = 1;
}
message GetServerResponse {
// The Server that corresponds to the requested server_id. This field
// should be set.
Server server = 1;
}
message GetServerSocketsRequest {
int64 server_id = 1;
// start_socket_id indicates that only sockets at or above this id should be
// included in the results.
// To request the first page, this must be set to 0. To request
// subsequent pages, the client generates this value by adding 1 to
// the highest seen result ID.
int64 start_socket_id = 2;
// If non-zero, the server will return a page of results containing
// at most this many items. If zero, the server will choose a
// reasonable page size. Must never be negative.
int64 max_results = 3;
}
message GetServerSocketsResponse {
// list of socket refs that the connection detail service knows about. Sorted in
// ascending socket_id order.
// Must contain at least 1 result, otherwise 'end' must be true.
repeated SocketRef socket_ref = 1;
// If set, indicates that the list of sockets is the final list. Requesting
// more sockets will only return more if they are created after this RPC
// completes.
bool end = 2;
}
message GetChannelRequest {
// channel_id is the identifier of the specific channel to get.
int64 channel_id = 1;
}
message GetChannelResponse {
// The Channel that corresponds to the requested channel_id. This field
// should be set.
Channel channel = 1;
}
message GetSubchannelRequest {
// subchannel_id is the identifier of the specific subchannel to get.
int64 subchannel_id = 1;
}
message GetSubchannelResponse {
// The Subchannel that corresponds to the requested subchannel_id. This
// field should be set.
Subchannel subchannel = 1;
}
message GetSocketRequest {
// socket_id is the identifier of the specific socket to get.
int64 socket_id = 1;
// If true, the response will contain only high level information
// that is inexpensive to obtain. Fields thay may be omitted are
// documented.
bool summary = 2;
}
message GetSocketResponse {
// The Socket that corresponds to the requested socket_id. This field
// should be set.
Socket socket = 1;
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2021 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { ServiceDefinition } from "./make-client";
import { Server, UntypedServiceImplementation } from "./server";
interface GetServiceDefinition {
(): ServiceDefinition;
}
interface GetHandlers {
(): UntypedServiceImplementation;
}
const registeredAdminServices: {getServiceDefinition: GetServiceDefinition, getHandlers: GetHandlers}[] = [];
export function registerAdminService(getServiceDefinition: GetServiceDefinition, getHandlers: GetHandlers) {
registeredAdminServices.push({getServiceDefinition, getHandlers});
}
export function addAdminServicesToServer(server: Server): void {
for (const {getServiceDefinition, getHandlers} of registeredAdminServices) {
server.addService(getServiceDefinition(), getHandlers());
}
}

View File

@ -25,7 +25,7 @@ import { FilterStackFactory, FilterStack } from './filter-stack';
import { Metadata } from './metadata';
import { StreamDecoder } from './stream-decoder';
import { ChannelImplementation } from './channel';
import { Subchannel } from './subchannel';
import { SubchannelCallStatsTracker, Subchannel } from './subchannel';
import * as logging from './logging';
import { LogVerbosity } from './constants';
import { ServerSurfaceCall } from './server-call';
@ -249,6 +249,11 @@ export class Http2CallStream implements Call {
private configDeadline: Deadline = Infinity;
private statusWatchers: ((status: StatusObject) => void)[] = [];
private streamEndWatchers: ((success: boolean) => void)[] = [];
private callStatsTracker: SubchannelCallStatsTracker | null = null;
constructor(
private readonly methodName: string,
private readonly channel: ChannelImplementation,
@ -283,6 +288,7 @@ export class Http2CallStream implements Call {
const filteredStatus = this.filterStack.receiveTrailers(
this.finalStatus!
);
this.statusWatchers.forEach(watcher => watcher(filteredStatus));
/* We delay the actual action of bubbling up the status to insulate the
* cleanup code in this class from any errors that may be thrown in the
* upper layers as a result of bubbling up the status. In particular,
@ -426,6 +432,7 @@ export class Http2CallStream implements Call {
}
private handleTrailers(headers: http2.IncomingHttpHeaders) {
this.streamEndWatchers.forEach(watcher => watcher(true));
let headersString = '';
for (const header of Object.keys(headers)) {
headersString += '\t\t' + header + ': ' + headers[header] + '\n';
@ -463,10 +470,16 @@ export class Http2CallStream implements Call {
this.endCall(status);
}
private writeMessageToStream(message: Buffer, callback: WriteCallback) {
this.callStatsTracker?.addMessageSent();
this.http2Stream!.write(message, callback);
}
attachHttp2Stream(
stream: http2.ClientHttp2Stream,
subchannel: Subchannel,
extraFilters: Filter[]
extraFilters: Filter[],
callStatsTracker: SubchannelCallStatsTracker
): void {
this.filterStack.push(extraFilters);
if (this.finalStatus !== null) {
@ -477,6 +490,7 @@ export class Http2CallStream implements Call {
);
this.http2Stream = stream;
this.subchannel = subchannel;
this.callStatsTracker = callStatsTracker;
subchannel.addDisconnectListener(this.disconnectListener);
subchannel.callRef();
stream.on('response', (headers, flags) => {
@ -542,6 +556,7 @@ export class Http2CallStream implements Call {
for (const message of messages) {
this.trace('parsed message of length ' + message.length);
this.callStatsTracker!.addMessageReceived();
this.tryPush(message);
}
});
@ -645,6 +660,7 @@ export class Http2CallStream implements Call {
);
this.internalError = err;
}
this.streamEndWatchers.forEach(watcher => watcher(false));
});
if (!this.pendingRead) {
stream.pause();
@ -659,7 +675,7 @@ export class Http2CallStream implements Call {
' (deferred)'
);
try {
stream.write(this.pendingWrite, this.pendingWriteCallback);
this.writeMessageToStream(this.pendingWrite, this.pendingWriteCallback);
} catch (error) {
this.endCall({
code: Status.UNAVAILABLE,
@ -742,6 +758,14 @@ export class Http2CallStream implements Call {
this.configDeadline = configDeadline;
}
addStatusWatcher(watcher: (status: StatusObject) => void) {
this.statusWatchers.push(watcher);
}
addStreamEndWatcher(watcher: (success: boolean) => void) {
this.streamEndWatchers.push(watcher);
}
addFilters(extraFilters: Filter[]) {
this.filterStack.push(extraFilters);
}
@ -799,7 +823,7 @@ export class Http2CallStream implements Call {
} else {
this.trace('sending data chunk of length ' + message.message.length);
try {
this.http2Stream.write(message.message, cb);
this.writeMessageToStream(message.message, cb);
} catch (error) {
this.endCall({
code: Status.UNAVAILABLE,

View File

@ -49,6 +49,7 @@ import { SurfaceCall } from './call';
import { Filter } from './filter';
import { ConnectivityState } from './connectivity-state';
import { ChannelInfo, ChannelRef, ChannelzCallTracker, ChannelzChildrenTracker, ChannelzTrace, registerChannelzChannel, SubchannelRef, unregisterChannelzRef } from './channelz';
/**
* See https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_args
@ -104,6 +105,12 @@ export interface Channel {
deadline: Date | number,
callback: (error?: Error) => void
): void;
/**
* Get the channelz reference object for this channel. A request to the
* channelz service for the id in this object will provide information
* about this channel.
*/
getChannelzRef(): ChannelRef;
/**
* Create a call object. Call is an opaque type that is used by the Client
* class. This function is called by the gRPC library when starting a
@ -163,6 +170,14 @@ export class ChannelImplementation implements Channel {
*/
private callRefTimer: NodeJS.Timer;
private configSelector: ConfigSelector | null = null;
// Channelz info
private originalTarget: string;
private channelzRef: ChannelRef;
private channelzTrace: ChannelzTrace;
private callTracker = new ChannelzCallTracker();
private childrenTracker = new ChannelzChildrenTracker();
constructor(
target: string,
private readonly credentials: ChannelCredentials,
@ -181,6 +196,7 @@ export class ChannelImplementation implements Channel {
throw new TypeError('Channel options must be an object');
}
}
this.originalTarget = target;
const originalTargetUri = parseUri(target);
if (originalTargetUri === null) {
throw new Error(`Could not parse target name "${target}"`);
@ -197,6 +213,10 @@ export class ChannelImplementation implements Channel {
this.callRefTimer = setInterval(() => {}, MAX_TIMEOUT_TIME);
this.callRefTimer.unref?.();
this.channelzRef = registerChannelzChannel(target, () => this.getChannelzInfo());
this.channelzTrace = new ChannelzTrace();
this.channelzTrace.addTrace('CT_INFO', 'Channel created');
if (this.options['grpc.default_authority']) {
this.defaultAuthority = this.options['grpc.default_authority'] as string;
} else {
@ -216,12 +236,14 @@ export class ChannelImplementation implements Channel {
subchannelAddress: SubchannelAddress,
subchannelArgs: ChannelOptions
) => {
return this.subchannelPool.getOrCreateSubchannel(
const subchannel = this.subchannelPool.getOrCreateSubchannel(
this.target,
subchannelAddress,
Object.assign({}, this.options, subchannelArgs),
this.credentials
);
this.channelzTrace.addTrace('CT_INFO', 'Created subchannel or used existing subchannel', subchannel.getChannelzRef());
return subchannel;
},
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
this.currentPicker = picker;
@ -239,12 +261,19 @@ export class ChannelImplementation implements Channel {
'Resolving load balancer should never call requestReresolution'
);
},
addChannelzChild: (child: ChannelRef | SubchannelRef) => {
this.childrenTracker.refChild(child);
},
removeChannelzChild: (child: ChannelRef | SubchannelRef) => {
this.childrenTracker.unrefChild(child);
}
};
this.resolvingLoadBalancer = new ResolvingLoadBalancer(
this.target,
channelControlHelper,
options,
(configSelector) => {
this.channelzTrace.addTrace('CT_INFO', 'Address resolution succeeded');
this.configSelector = configSelector;
/* We process the queue asynchronously to ensure that the corresponding
* load balancer update has completed. */
@ -259,14 +288,9 @@ export class ChannelImplementation implements Channel {
});
},
(status) => {
this.channelzTrace.addTrace('CT_WARNING', 'Address resolution failed with code ' + status.code + ' and details "' + status.details + '"');
if (this.configSelectionQueue.length > 0) {
trace(
LogVerbosity.DEBUG,
'channel',
'Name resolution failed for target ' +
uriToString(this.target) +
' with calls queued for config selection'
);
this.trace('Name resolution failed with calls queued for config selection');
}
const localQueue = this.configSelectionQueue;
this.configSelectionQueue = [];
@ -287,14 +311,27 @@ export class ChannelImplementation implements Channel {
new MaxMessageSizeFilterFactory(this.options),
new CompressionFilterFactory(this),
]);
this.trace('Constructed channel');
}
private getChannelzInfo(): ChannelInfo {
return {
target: this.originalTarget,
state: this.connectivityState,
trace: this.channelzTrace,
callTracker: this.callTracker,
children: this.childrenTracker.getChildLists()
};
}
private trace(text: string, verbosityOverride?: LogVerbosity) {
trace(verbosityOverride ?? LogVerbosity.DEBUG, 'channel', '(' + this.channelzRef.id + ') ' + uriToString(this.target) + ' ' + text);
}
private callRefTimerRef() {
// If the hasRef function does not exist, always run the code
if (!this.callRefTimer.hasRef?.()) {
trace(
LogVerbosity.DEBUG,
'channel',
this.trace(
'callRefTimer.ref | configSelectionQueue.length=' +
this.configSelectionQueue.length +
' pickQueue.length=' +
@ -307,9 +344,7 @@ export class ChannelImplementation implements Channel {
private callRefTimerUnref() {
// If the hasRef function does not exist, always run the code
if (!this.callRefTimer.hasRef || this.callRefTimer.hasRef()) {
trace(
LogVerbosity.DEBUG,
'channel',
this.trace(
'callRefTimer.unref | configSelectionQueue.length=' +
this.configSelectionQueue.length +
' pickQueue.length=' +
@ -346,9 +381,7 @@ export class ChannelImplementation implements Channel {
metadata: callMetadata,
extraPickInfo: callConfig.pickInformation,
});
trace(
LogVerbosity.DEBUG,
'channel',
this.trace(
'Pick result: ' +
PickResultType[pickResult.pickResultType] +
' subchannel: ' +
@ -422,25 +455,23 @@ export class ChannelImplementation implements Channel {
* the stream because the correct behavior may be
* re-queueing instead, based on the logic in the rest of
* tryPick */
trace(
LogVerbosity.INFO,
'channel',
this.trace(
'Failed to start call on picked subchannel ' +
pickResult.subchannel!.getAddress() +
' with error ' +
(error as Error).message +
'. Retrying pick'
'. Retrying pick',
LogVerbosity.INFO
);
this.tryPick(callStream, callMetadata, callConfig, dynamicFilters);
} else {
trace(
LogVerbosity.INFO,
'channel',
this.trace(
'Failed to start call on picked subchanel ' +
pickResult.subchannel!.getAddress() +
' with error ' +
(error as Error).message +
'. Ending call'
'. Ending call',
LogVerbosity.INFO
);
callStream.cancelWithStatus(
Status.INTERNAL,
@ -453,14 +484,13 @@ export class ChannelImplementation implements Channel {
} else {
/* The logic for doing this here is the same as in the catch
* block above */
trace(
LogVerbosity.INFO,
'channel',
this.trace(
'Picked subchannel ' +
pickResult.subchannel!.getAddress() +
' has state ' +
ConnectivityState[subchannelState] +
' after metadata filters. Retrying pick'
' after metadata filters. Retrying pick',
LogVerbosity.INFO
);
this.tryPick(callStream, callMetadata, callConfig, dynamicFilters);
}
@ -516,12 +546,14 @@ export class ChannelImplementation implements Channel {
trace(
LogVerbosity.DEBUG,
'connectivity_state',
'(' + this.channelzRef.id + ') ' +
uriToString(this.target) +
' ' +
ConnectivityState[this.connectivityState] +
' -> ' +
ConnectivityState[newState]
);
this.channelzTrace.addTrace('CT_INFO', ConnectivityState[this.connectivityState] + ' -> ' + ConnectivityState[newState]);
this.connectivityState = newState;
const watchersCopy = this.connectivityStateWatchers.slice();
for (const watcherObject of watchersCopy) {
@ -606,6 +638,7 @@ export class ChannelImplementation implements Channel {
this.resolvingLoadBalancer.destroy();
this.updateState(ConnectivityState.SHUTDOWN);
clearInterval(this.callRefTimer);
unregisterChannelzRef(this.channelzRef);
this.subchannelPool.unrefUnusedSubchannels();
}
@ -657,6 +690,10 @@ export class ChannelImplementation implements Channel {
this.connectivityStateWatchers.push(watcherObject);
}
getChannelzRef() {
return this.channelzRef;
}
createCall(
method: string,
deadline: Deadline,
@ -676,11 +713,8 @@ export class ChannelImplementation implements Channel {
throw new Error('Channel has been shut down');
}
const callNumber = getNewCallNumber();
trace(
LogVerbosity.DEBUG,
'channel',
uriToString(this.target) +
' createCall [' +
this.trace(
'createCall [' +
callNumber +
'] method="' +
method +
@ -701,6 +735,14 @@ export class ChannelImplementation implements Channel {
this.credentials._getCallCredentials(),
callNumber
);
this.callTracker.addCallStarted();
stream.addStatusWatcher(status => {
if (status.code === Status.OK) {
this.callTracker.addCallSucceeded();
} else {
this.callTracker.addCallFailed();
}
});
return stream;
}
}

View File

@ -0,0 +1,727 @@
/*
* Copyright 2021 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { isIPv4, isIPv6 } from "net";
import { ConnectivityState } from "./connectivity-state";
import { Status } from "./constants";
import { Timestamp } from "./generated/google/protobuf/Timestamp";
import { Channel as ChannelMessage } from "./generated/grpc/channelz/v1/Channel";
import { ChannelConnectivityState__Output } from "./generated/grpc/channelz/v1/ChannelConnectivityState";
import { ChannelRef as ChannelRefMessage } from "./generated/grpc/channelz/v1/ChannelRef";
import { ChannelTrace } from "./generated/grpc/channelz/v1/ChannelTrace";
import { GetChannelRequest__Output } from "./generated/grpc/channelz/v1/GetChannelRequest";
import { GetChannelResponse } from "./generated/grpc/channelz/v1/GetChannelResponse";
import { sendUnaryData, ServerUnaryCall } from "./server-call";
import { ServerRef as ServerRefMessage } from "./generated/grpc/channelz/v1/ServerRef";
import { SocketRef as SocketRefMessage } from "./generated/grpc/channelz/v1/SocketRef";
import { isTcpSubchannelAddress, SubchannelAddress } from "./subchannel-address";
import { SubchannelRef as SubchannelRefMessage } from "./generated/grpc/channelz/v1/SubchannelRef";
import { GetServerRequest__Output } from "./generated/grpc/channelz/v1/GetServerRequest";
import { GetServerResponse } from "./generated/grpc/channelz/v1/GetServerResponse";
import { Server as ServerMessage } from "./generated/grpc/channelz/v1/Server";
import { GetServersRequest__Output } from "./generated/grpc/channelz/v1/GetServersRequest";
import { GetServersResponse } from "./generated/grpc/channelz/v1/GetServersResponse";
import { GetTopChannelsRequest__Output } from "./generated/grpc/channelz/v1/GetTopChannelsRequest";
import { GetTopChannelsResponse } from "./generated/grpc/channelz/v1/GetTopChannelsResponse";
import { GetSubchannelRequest__Output } from "./generated/grpc/channelz/v1/GetSubchannelRequest";
import { GetSubchannelResponse } from "./generated/grpc/channelz/v1/GetSubchannelResponse";
import { Subchannel as SubchannelMessage } from "./generated/grpc/channelz/v1/Subchannel";
import { GetSocketRequest__Output } from "./generated/grpc/channelz/v1/GetSocketRequest";
import { GetSocketResponse } from "./generated/grpc/channelz/v1/GetSocketResponse";
import { Socket as SocketMessage } from "./generated/grpc/channelz/v1/Socket";
import { Address } from "./generated/grpc/channelz/v1/Address";
import { Security } from "./generated/grpc/channelz/v1/Security";
import { GetServerSocketsRequest__Output } from "./generated/grpc/channelz/v1/GetServerSocketsRequest";
import { GetServerSocketsResponse } from "./generated/grpc/channelz/v1/GetServerSocketsResponse";
import { ChannelzDefinition, ChannelzHandlers } from "./generated/grpc/channelz/v1/Channelz";
import { ProtoGrpcType as ChannelzProtoGrpcType } from "./generated/channelz";
import type { loadSync } from '@grpc/proto-loader';
import { registerAdminService } from "./admin";
import { loadPackageDefinition } from "./make-client";
export type TraceSeverity = 'CT_UNKNOWN' | 'CT_INFO' | 'CT_WARNING' | 'CT_ERROR';
export interface ChannelRef {
kind: 'channel';
id: number;
name: string;
}
export interface SubchannelRef {
kind: 'subchannel';
id: number;
name: string;
}
export interface ServerRef {
kind: 'server';
id: number;
}
export interface SocketRef {
kind: 'socket';
id: number;
name: string;
}
function channelRefToMessage(ref: ChannelRef): ChannelRefMessage {
return {
channel_id: ref.id,
name: ref.name
};
}
function subchannelRefToMessage(ref: SubchannelRef): SubchannelRefMessage {
return {
subchannel_id: ref.id,
name: ref.name
}
}
function serverRefToMessage(ref: ServerRef): ServerRefMessage {
return {
server_id: ref.id
}
}
function socketRefToMessage(ref: SocketRef): SocketRefMessage {
return {
socket_id: ref.id,
name: ref.name
}
}
interface TraceEvent {
description: string;
severity: TraceSeverity;
timestamp: Date;
childChannel?: ChannelRef;
childSubchannel?: SubchannelRef;
}
export class ChannelzTrace {
events: TraceEvent[] = [];
creationTimestamp: Date;
eventsLogged: number = 0;
constructor() {
this.creationTimestamp = new Date();
}
addTrace(severity: TraceSeverity, description: string, child?: ChannelRef | SubchannelRef) {
const timestamp = new Date();
this.events.push({
description: description,
severity: severity,
timestamp: timestamp,
childChannel: child?.kind === 'channel' ? child : undefined,
childSubchannel: child?.kind === 'subchannel' ? child : undefined
});
this.eventsLogged += 1;
}
getTraceMessage(): ChannelTrace {
return {
creation_timestamp: dateToProtoTimestamp(this.creationTimestamp),
num_events_logged: this.eventsLogged,
events: this.events.map(event => {
return {
description: event.description,
severity: event.severity,
timestamp: dateToProtoTimestamp(event.timestamp),
channel_ref: event.childChannel ? channelRefToMessage(event.childChannel) : null,
subchannel_ref: event.childSubchannel ? subchannelRefToMessage(event.childSubchannel) : null
}
})
};
}
}
export class ChannelzChildrenTracker {
private channelChildren: Map<number, {ref: ChannelRef, count: number}> = new Map<number, {ref: ChannelRef, count: number}>();
private subchannelChildren: Map<number, {ref: SubchannelRef, count: number}> = new Map<number, {ref: SubchannelRef, count: number}>();
private socketChildren: Map<number, {ref: SocketRef, count: number}> = new Map<number, {ref: SocketRef, count: number}>();
refChild(child: ChannelRef | SubchannelRef | SocketRef) {
switch (child.kind) {
case 'channel': {
let trackedChild = this.channelChildren.get(child.id) ?? {ref: child, count: 0};
trackedChild.count += 1;
this.channelChildren.set(child.id, trackedChild);
break;
}
case 'subchannel':{
let trackedChild = this.subchannelChildren.get(child.id) ?? {ref: child, count: 0};
trackedChild.count += 1;
this.subchannelChildren.set(child.id, trackedChild);
break;
}
case 'socket':{
let trackedChild = this.socketChildren.get(child.id) ?? {ref: child, count: 0};
trackedChild.count += 1;
this.socketChildren.set(child.id, trackedChild);
break;
}
}
}
unrefChild(child: ChannelRef | SubchannelRef | SocketRef) {
switch (child.kind) {
case 'channel': {
let trackedChild = this.channelChildren.get(child.id);
if (trackedChild !== undefined) {
trackedChild.count -= 1;
if (trackedChild.count === 0) {
this.channelChildren.delete(child.id);
} else {
this.channelChildren.set(child.id, trackedChild);
}
}
break;
}
case 'subchannel': {
let trackedChild = this.subchannelChildren.get(child.id);
if (trackedChild !== undefined) {
trackedChild.count -= 1;
if (trackedChild.count === 0) {
this.subchannelChildren.delete(child.id);
} else {
this.subchannelChildren.set(child.id, trackedChild);
}
}
break;
}
case 'socket': {
let trackedChild = this.socketChildren.get(child.id);
if (trackedChild !== undefined) {
trackedChild.count -= 1;
if (trackedChild.count === 0) {
this.socketChildren.delete(child.id);
} else {
this.socketChildren.set(child.id, trackedChild);
}
}
break;
}
}
}
getChildLists(): ChannelzChildren {
const channels: ChannelRef[] = [];
for (const {ref} of this.channelChildren.values()) {
channels.push(ref);
}
const subchannels: SubchannelRef[] = [];
for (const {ref} of this.subchannelChildren.values()) {
subchannels.push(ref);
}
const sockets: SocketRef[] = [];
for (const {ref} of this.socketChildren.values()) {
sockets.push(ref);
}
return {channels, subchannels, sockets};
}
}
export class ChannelzCallTracker {
callsStarted: number = 0;
callsSucceeded: number = 0;
callsFailed: number = 0;
lastCallStartedTimestamp: Date | null = null;
addCallStarted() {
this.callsStarted += 1;
this.lastCallStartedTimestamp = new Date();
}
addCallSucceeded() {
this.callsSucceeded += 1;
}
addCallFailed() {
this.callsFailed += 1;
}
}
export interface ChannelzChildren {
channels: ChannelRef[];
subchannels: SubchannelRef[];
sockets: SocketRef[];
}
export interface ChannelInfo {
target: string;
state: ConnectivityState;
trace: ChannelzTrace;
callTracker: ChannelzCallTracker;
children: ChannelzChildren;
}
export interface SubchannelInfo extends ChannelInfo {}
export interface ServerInfo {
trace: ChannelzTrace;
callTracker: ChannelzCallTracker;
listenerChildren: ChannelzChildren;
sessionChildren: ChannelzChildren;
}
export interface TlsInfo {
cipherSuiteStandardName: string | null;
cipherSuiteOtherName: string | null;
localCertificate: Buffer | null;
remoteCertificate: Buffer | null;
}
export interface SocketInfo {
localAddress: SubchannelAddress;
remoteAddress: SubchannelAddress | null;
security: TlsInfo | null;
remoteName: string | null;
streamsStarted: number;
streamsSucceeded: number;
streamsFailed: number;
messagesSent: number;
messagesReceived: number;
keepAlivesSent: number;
lastLocalStreamCreatedTimestamp: Date | null;
lastRemoteStreamCreatedTimestamp: Date | null;
lastMessageSentTimestamp: Date | null;
lastMessageReceivedTimestamp: Date | null;
localFlowControlWindow: number | null;
remoteFlowControlWindow: number | null;
}
interface ChannelEntry {
ref: ChannelRef;
getInfo(): ChannelInfo;
}
interface SubchannelEntry {
ref: SubchannelRef;
getInfo(): SubchannelInfo;
}
interface ServerEntry {
ref: ServerRef;
getInfo(): ServerInfo;
}
interface SocketEntry {
ref: SocketRef;
getInfo(): SocketInfo;
}
let nextId = 1;
function getNextId(): number {
return nextId++;
}
const channels: (ChannelEntry | undefined)[] = [];
const subchannels: (SubchannelEntry | undefined)[] = [];
const servers: (ServerEntry | undefined)[] = [];
const sockets: (SocketEntry | undefined)[] = [];
export function registerChannelzChannel(name: string, getInfo: () => ChannelInfo): ChannelRef {
const id = getNextId();
const ref: ChannelRef = {id, name, kind: 'channel'};
channels[id] = { ref, getInfo };
return ref;
}
export function registerChannelzSubchannel(name: string, getInfo:() => SubchannelInfo): SubchannelRef {
const id = getNextId();
const ref: SubchannelRef = {id, name, kind: 'subchannel'};
subchannels[id] = { ref, getInfo };
return ref;
}
export function registerChannelzServer(getInfo: () => ServerInfo): ServerRef {
const id = getNextId();
const ref: ServerRef = {id, kind: 'server'};
servers[id] = { ref, getInfo };
return ref;
}
export function registerChannelzSocket(name: string, getInfo: () => SocketInfo): SocketRef {
const id = getNextId();
const ref: SocketRef = {id, name, kind: 'socket'};
sockets[id] = { ref, getInfo};
return ref;
}
export function unregisterChannelzRef(ref: ChannelRef | SubchannelRef | ServerRef | SocketRef) {
switch (ref.kind) {
case 'channel':
delete channels[ref.id];
return;
case 'subchannel':
delete subchannels[ref.id];
return;
case 'server':
delete servers[ref.id];
return;
case 'socket':
delete sockets[ref.id];
return;
}
}
export interface ChannelzClientView {
updateState(connectivityState: ConnectivityState): void;
addTrace(severity: TraceSeverity, description: string, child?: ChannelRef | SubchannelRef): void;
addCallStarted(): void;
addCallSucceeded(): void;
addCallFailed(): void;
addChild(child: ChannelRef | SubchannelRef): void;
removeChild(child: ChannelRef | SubchannelRef): void;
}
export interface ChannelzSubchannelView extends ChannelzClientView {
getRef(): SubchannelRef;
}
/**
* Converts an IPv4 or IPv6 address from string representation to binary
* representation
* @param ipAddress an IP address in standard IPv4 or IPv6 text format
* @returns
*/
function ipAddressStringToBuffer(ipAddress: string): Buffer | null {
if (isIPv4(ipAddress)) {
return Buffer.from(Uint8Array.from(ipAddress.split('.').map(segment => Number.parseInt(segment))));
} else if (isIPv6(ipAddress)) {
let leftSection: string;
let rightSection: string | null;
const doubleColonIndex = ipAddress.indexOf('::');
if (doubleColonIndex === -1) {
leftSection = ipAddress;
rightSection = null;
} else {
leftSection = ipAddress.substring(0, doubleColonIndex);
rightSection = ipAddress.substring(doubleColonIndex + 2);
}
const leftBuffer = Uint8Array.from(leftSection.split(':').map(segment => Number.parseInt(segment, 16)));
const rightBuffer = rightSection ? Uint8Array.from(rightSection.split(':').map(segment => Number.parseInt(segment, 16))) : new Uint8Array();
const middleBuffer = Buffer.alloc(16 - leftBuffer.length - rightBuffer.length, 0);
return Buffer.concat([leftBuffer, middleBuffer, rightBuffer]);
} else {
return null;
}
}
function connectivityStateToMessage(state: ConnectivityState): ChannelConnectivityState__Output {
switch (state) {
case ConnectivityState.CONNECTING:
return {
state: 'CONNECTING'
};
case ConnectivityState.IDLE:
return {
state: 'IDLE'
};
case ConnectivityState.READY:
return {
state: 'READY'
};
case ConnectivityState.SHUTDOWN:
return {
state: 'SHUTDOWN'
};
case ConnectivityState.TRANSIENT_FAILURE:
return {
state: 'TRANSIENT_FAILURE'
};
default:
return {
state: 'UNKNOWN'
};
}
}
function dateToProtoTimestamp(date?: Date | null): Timestamp | null {
if (!date) {
return null;
}
const millisSinceEpoch = date.getTime();
return {
seconds: (millisSinceEpoch / 1000) | 0,
nanos: (millisSinceEpoch % 1000) * 1_000_000
}
}
function getChannelMessage(channelEntry: ChannelEntry): ChannelMessage {
const resolvedInfo = channelEntry.getInfo();
return {
ref: channelRefToMessage(channelEntry.ref),
data: {
target: resolvedInfo.target,
state: connectivityStateToMessage(resolvedInfo.state),
calls_started: resolvedInfo.callTracker.callsStarted,
calls_succeeded: resolvedInfo.callTracker.callsSucceeded,
calls_failed: resolvedInfo.callTracker.callsFailed,
last_call_started_timestamp: dateToProtoTimestamp(resolvedInfo.callTracker.lastCallStartedTimestamp),
trace: resolvedInfo.trace.getTraceMessage()
},
channel_ref: resolvedInfo.children.channels.map(ref => channelRefToMessage(ref)),
subchannel_ref: resolvedInfo.children.subchannels.map(ref => subchannelRefToMessage(ref))
};
}
function GetChannel(call: ServerUnaryCall<GetChannelRequest__Output, GetChannelResponse>, callback: sendUnaryData<GetChannelResponse>): void {
const channelId = Number.parseInt(call.request.channel_id);
const channelEntry = channels[channelId];
if (channelEntry === undefined) {
callback({
'code': Status.NOT_FOUND,
'details': 'No channel data found for id ' + channelId
});
return;
}
callback(null, {channel: getChannelMessage(channelEntry)});
}
function GetTopChannels(call: ServerUnaryCall<GetTopChannelsRequest__Output, GetTopChannelsResponse>, callback: sendUnaryData<GetTopChannelsResponse>): void {
const maxResults = Number.parseInt(call.request.max_results);
const resultList: ChannelMessage[] = [];
let i = Number.parseInt(call.request.start_channel_id);
for (; i < channels.length; i++) {
const channelEntry = channels[i];
if (channelEntry === undefined) {
continue;
}
resultList.push(getChannelMessage(channelEntry));
if (resultList.length >= maxResults) {
break;
}
}
callback(null, {
channel: resultList,
end: i >= servers.length
});
}
function getServerMessage(serverEntry: ServerEntry): ServerMessage {
const resolvedInfo = serverEntry.getInfo();
return {
ref: serverRefToMessage(serverEntry.ref),
data: {
calls_started: resolvedInfo.callTracker.callsStarted,
calls_succeeded: resolvedInfo.callTracker.callsSucceeded,
calls_failed: resolvedInfo.callTracker.callsFailed,
last_call_started_timestamp: dateToProtoTimestamp(resolvedInfo.callTracker.lastCallStartedTimestamp),
trace: resolvedInfo.trace.getTraceMessage()
},
listen_socket: resolvedInfo.listenerChildren.sockets.map(ref => socketRefToMessage(ref))
};
}
function GetServer(call: ServerUnaryCall<GetServerRequest__Output, GetServerResponse>, callback: sendUnaryData<GetServerResponse>): void {
const serverId = Number.parseInt(call.request.server_id);
const serverEntry = servers[serverId];
if (serverEntry === undefined) {
callback({
'code': Status.NOT_FOUND,
'details': 'No server data found for id ' + serverId
});
return;
}
callback(null, {server: getServerMessage(serverEntry)});
}
function GetServers(call: ServerUnaryCall<GetServersRequest__Output, GetServersResponse>, callback: sendUnaryData<GetServersResponse>): void {
const maxResults = Number.parseInt(call.request.max_results);
const resultList: ServerMessage[] = [];
let i = Number.parseInt(call.request.start_server_id);
for (; i < servers.length; i++) {
const serverEntry = servers[i];
if (serverEntry === undefined) {
continue;
}
resultList.push(getServerMessage(serverEntry));
if (resultList.length >= maxResults) {
break;
}
}
callback(null, {
server: resultList,
end: i >= servers.length
});
}
function GetSubchannel(call: ServerUnaryCall<GetSubchannelRequest__Output, GetSubchannelResponse>, callback: sendUnaryData<GetSubchannelResponse>): void {
const subchannelId = Number.parseInt(call.request.subchannel_id);
const subchannelEntry = subchannels[subchannelId];
if (subchannelEntry === undefined) {
callback({
'code': Status.NOT_FOUND,
'details': 'No subchannel data found for id ' + subchannelId
});
return;
}
const resolvedInfo = subchannelEntry.getInfo();
const subchannelMessage: SubchannelMessage = {
ref: subchannelRefToMessage(subchannelEntry.ref),
data: {
target: resolvedInfo.target,
state: connectivityStateToMessage(resolvedInfo.state),
calls_started: resolvedInfo.callTracker.callsStarted,
calls_succeeded: resolvedInfo.callTracker.callsSucceeded,
calls_failed: resolvedInfo.callTracker.callsFailed,
last_call_started_timestamp: dateToProtoTimestamp(resolvedInfo.callTracker.lastCallStartedTimestamp),
trace: resolvedInfo.trace.getTraceMessage()
},
socket_ref: resolvedInfo.children.sockets.map(ref => socketRefToMessage(ref))
};
callback(null, {subchannel: subchannelMessage});
}
function subchannelAddressToAddressMessage(subchannelAddress: SubchannelAddress): Address {
if (isTcpSubchannelAddress(subchannelAddress)) {
return {
address: 'tcpip_address',
tcpip_address: {
ip_address: ipAddressStringToBuffer(subchannelAddress.host) ?? undefined,
port: subchannelAddress.port
}
};
} else {
return {
address: 'uds_address',
uds_address: {
filename: subchannelAddress.path
}
};
}
}
function GetSocket(call: ServerUnaryCall<GetSocketRequest__Output, GetSocketResponse>, callback: sendUnaryData<GetSocketResponse>): void {
const socketId = Number.parseInt(call.request.socket_id);
const socketEntry = sockets[socketId];
if (socketEntry === undefined) {
callback({
'code': Status.NOT_FOUND,
'details': 'No socket data found for id ' + socketId
});
return;
}
const resolvedInfo = socketEntry.getInfo();
const securityMessage: Security | null = resolvedInfo.security ? {
model: 'tls',
tls: {
cipher_suite: resolvedInfo.security.cipherSuiteStandardName ? 'standard_name' : 'other_name',
standard_name: resolvedInfo.security.cipherSuiteStandardName ?? undefined,
other_name: resolvedInfo.security.cipherSuiteOtherName ?? undefined,
local_certificate: resolvedInfo.security.localCertificate ?? undefined,
remote_certificate: resolvedInfo.security.remoteCertificate ?? undefined
}
} : null;
const socketMessage: SocketMessage = {
ref: socketRefToMessage(socketEntry.ref),
local: subchannelAddressToAddressMessage(resolvedInfo.localAddress),
remote: resolvedInfo.remoteAddress ? subchannelAddressToAddressMessage(resolvedInfo.remoteAddress) : null,
remote_name: resolvedInfo.remoteName ?? undefined,
security: securityMessage,
data: {
keep_alives_sent: resolvedInfo.keepAlivesSent,
streams_started: resolvedInfo.streamsStarted,
streams_succeeded: resolvedInfo.streamsSucceeded,
streams_failed: resolvedInfo.streamsFailed,
last_local_stream_created_timestamp: dateToProtoTimestamp(resolvedInfo.lastLocalStreamCreatedTimestamp),
last_remote_stream_created_timestamp: dateToProtoTimestamp(resolvedInfo.lastRemoteStreamCreatedTimestamp),
messages_received: resolvedInfo.messagesReceived,
messages_sent: resolvedInfo.messagesSent,
last_message_received_timestamp: dateToProtoTimestamp(resolvedInfo.lastMessageReceivedTimestamp),
last_message_sent_timestamp: dateToProtoTimestamp(resolvedInfo.lastMessageSentTimestamp),
local_flow_control_window: resolvedInfo.localFlowControlWindow ? { value: resolvedInfo.localFlowControlWindow } : null,
remote_flow_control_window: resolvedInfo.remoteFlowControlWindow ? { value: resolvedInfo.remoteFlowControlWindow } : null,
}
};
callback(null, {socket: socketMessage});
}
function GetServerSockets(call: ServerUnaryCall<GetServerSocketsRequest__Output, GetServerSocketsResponse>, callback: sendUnaryData<GetServerSocketsResponse>): void {
const serverId = Number.parseInt(call.request.server_id);
const serverEntry = servers[serverId];
if (serverEntry === undefined) {
callback({
'code': Status.NOT_FOUND,
'details': 'No server data found for id ' + serverId
});
return;
}
const startId = Number.parseInt(call.request.start_socket_id);
const maxResults = Number.parseInt(call.request.max_results);
const resolvedInfo = serverEntry.getInfo();
// If we wanted to include listener sockets in the result, this line would
// instead say
// const allSockets = resolvedInfo.listenerChildren.sockets.concat(resolvedInfo.sessionChildren.sockets).sort((ref1, ref2) => ref1.id - ref2.id);
const allSockets = resolvedInfo.sessionChildren.sockets.sort((ref1, ref2) => ref1.id - ref2.id);
const resultList: SocketRefMessage[] = [];
let i = 0;
for (; i < allSockets.length; i++) {
if (allSockets[i].id >= startId) {
resultList.push(socketRefToMessage(allSockets[i]));
if (resultList.length >= maxResults) {
break;
}
}
}
callback(null, {
socket_ref: resultList,
end: i >= allSockets.length
});
}
export function getChannelzHandlers(): ChannelzHandlers {
return {
GetChannel,
GetTopChannels,
GetServer,
GetServers,
GetSubchannel,
GetSocket,
GetServerSockets
};
}
let loadedChannelzDefinition: ChannelzDefinition | null = null;
export function getChannelzServiceDefinition(): ChannelzDefinition {
if (loadedChannelzDefinition) {
return loadedChannelzDefinition;
}
/* The purpose of this complexity is to avoid loading @grpc/proto-loader at
* runtime for users who will not use/enable channelz. */
const loaderLoadSync = require('@grpc/proto-loader').loadSync as typeof loadSync;
const loadedProto = loaderLoadSync('channelz.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: [
`${__dirname}/../../proto`
]
});
const channelzGrpcObject = loadPackageDefinition(loadedProto) as unknown as ChannelzProtoGrpcType;
loadedChannelzDefinition = channelzGrpcObject.grpc.channelz.v1.Channelz.service;
return loadedChannelzDefinition;
}
export function setup() {
registerAdminService(getChannelzServiceDefinition, getChannelzHandlers);
}

View File

@ -12,6 +12,7 @@ export {
LoadBalancer,
LoadBalancingConfig,
ChannelControlHelper,
createChildChannelControlHelper,
registerLoadBalancerType,
getFirstUsableConfig,
validateLoadBalancingConfig,
@ -32,3 +33,4 @@ export {
export { Call as CallStream } from './call-stream';
export { Filter, BaseFilter, FilterFactory } from './filter';
export { FilterStackFactory } from './filter-stack';
export { registerAdminService } from './admin';

View File

@ -0,0 +1,73 @@
import type * as grpc from '../index';
import type { MessageTypeDefinition } from '@grpc/proto-loader';
import type { ChannelzClient as _grpc_channelz_v1_ChannelzClient, ChannelzDefinition as _grpc_channelz_v1_ChannelzDefinition } from './grpc/channelz/v1/Channelz';
type SubtypeConstructor<Constructor extends new (...args: any) => any, Subtype> = {
new(...args: ConstructorParameters<Constructor>): Subtype;
};
export interface ProtoGrpcType {
google: {
protobuf: {
Any: MessageTypeDefinition
BoolValue: MessageTypeDefinition
BytesValue: MessageTypeDefinition
DoubleValue: MessageTypeDefinition
Duration: MessageTypeDefinition
FloatValue: MessageTypeDefinition
Int32Value: MessageTypeDefinition
Int64Value: MessageTypeDefinition
StringValue: MessageTypeDefinition
Timestamp: MessageTypeDefinition
UInt32Value: MessageTypeDefinition
UInt64Value: MessageTypeDefinition
}
}
grpc: {
channelz: {
v1: {
Address: MessageTypeDefinition
Channel: MessageTypeDefinition
ChannelConnectivityState: MessageTypeDefinition
ChannelData: MessageTypeDefinition
ChannelRef: MessageTypeDefinition
ChannelTrace: MessageTypeDefinition
ChannelTraceEvent: MessageTypeDefinition
/**
* Channelz is a service exposed by gRPC servers that provides detailed debug
* information.
*/
Channelz: SubtypeConstructor<typeof grpc.Client, _grpc_channelz_v1_ChannelzClient> & { service: _grpc_channelz_v1_ChannelzDefinition }
GetChannelRequest: MessageTypeDefinition
GetChannelResponse: MessageTypeDefinition
GetServerRequest: MessageTypeDefinition
GetServerResponse: MessageTypeDefinition
GetServerSocketsRequest: MessageTypeDefinition
GetServerSocketsResponse: MessageTypeDefinition
GetServersRequest: MessageTypeDefinition
GetServersResponse: MessageTypeDefinition
GetSocketRequest: MessageTypeDefinition
GetSocketResponse: MessageTypeDefinition
GetSubchannelRequest: MessageTypeDefinition
GetSubchannelResponse: MessageTypeDefinition
GetTopChannelsRequest: MessageTypeDefinition
GetTopChannelsResponse: MessageTypeDefinition
Security: MessageTypeDefinition
Server: MessageTypeDefinition
ServerData: MessageTypeDefinition
ServerRef: MessageTypeDefinition
Socket: MessageTypeDefinition
SocketData: MessageTypeDefinition
SocketOption: MessageTypeDefinition
SocketOptionLinger: MessageTypeDefinition
SocketOptionTcpInfo: MessageTypeDefinition
SocketOptionTimeout: MessageTypeDefinition
SocketRef: MessageTypeDefinition
Subchannel: MessageTypeDefinition
SubchannelRef: MessageTypeDefinition
}
}
}
}

View File

@ -0,0 +1,13 @@
// Original file: null
import type { AnyExtension } from '@grpc/proto-loader';
export type Any = AnyExtension | {
type_url: string;
value: Buffer | Uint8Array | string;
}
export interface Any__Output {
'type_url': (string);
'value': (Buffer);
}

View File

@ -0,0 +1,10 @@
// Original file: null
export interface BoolValue {
'value'?: (boolean);
}
export interface BoolValue__Output {
'value': (boolean);
}

View File

@ -0,0 +1,10 @@
// Original file: null
export interface BytesValue {
'value'?: (Buffer | Uint8Array | string);
}
export interface BytesValue__Output {
'value': (Buffer);
}

View File

@ -0,0 +1,10 @@
// Original file: null
export interface DoubleValue {
'value'?: (number | string);
}
export interface DoubleValue__Output {
'value': (number);
}

View File

@ -0,0 +1,13 @@
// Original file: null
import type { Long } from '@grpc/proto-loader';
export interface Duration {
'seconds'?: (number | string | Long);
'nanos'?: (number);
}
export interface Duration__Output {
'seconds': (string);
'nanos': (number);
}

View File

@ -0,0 +1,10 @@
// Original file: null
export interface FloatValue {
'value'?: (number | string);
}
export interface FloatValue__Output {
'value': (number);
}

View File

@ -0,0 +1,10 @@
// Original file: null
export interface Int32Value {
'value'?: (number);
}
export interface Int32Value__Output {
'value': (number);
}

View File

@ -0,0 +1,11 @@
// Original file: null
import type { Long } from '@grpc/proto-loader';
export interface Int64Value {
'value'?: (number | string | Long);
}
export interface Int64Value__Output {
'value': (string);
}

View File

@ -0,0 +1,10 @@
// Original file: null
export interface StringValue {
'value'?: (string);
}
export interface StringValue__Output {
'value': (string);
}

View File

@ -0,0 +1,13 @@
// Original file: null
import type { Long } from '@grpc/proto-loader';
export interface Timestamp {
'seconds'?: (number | string | Long);
'nanos'?: (number);
}
export interface Timestamp__Output {
'seconds': (string);
'nanos': (number);
}

View File

@ -0,0 +1,10 @@
// Original file: null
export interface UInt32Value {
'value'?: (number);
}
export interface UInt32Value__Output {
'value': (number);
}

View File

@ -0,0 +1,11 @@
// Original file: null
import type { Long } from '@grpc/proto-loader';
export interface UInt64Value {
'value'?: (number | string | Long);
}
export interface UInt64Value__Output {
'value': (string);
}

View File

@ -0,0 +1,89 @@
// Original file: proto/channelz.proto
import type { Any as _google_protobuf_Any, Any__Output as _google_protobuf_Any__Output } from '../../../google/protobuf/Any';
/**
* An address type not included above.
*/
export interface _grpc_channelz_v1_Address_OtherAddress {
/**
* The human readable version of the value. This value should be set.
*/
'name'?: (string);
/**
* The actual address message.
*/
'value'?: (_google_protobuf_Any | null);
}
/**
* An address type not included above.
*/
export interface _grpc_channelz_v1_Address_OtherAddress__Output {
/**
* The human readable version of the value. This value should be set.
*/
'name': (string);
/**
* The actual address message.
*/
'value': (_google_protobuf_Any__Output | null);
}
export interface _grpc_channelz_v1_Address_TcpIpAddress {
/**
* Either the IPv4 or IPv6 address in bytes. Will be either 4 bytes or 16
* bytes in length.
*/
'ip_address'?: (Buffer | Uint8Array | string);
/**
* 0-64k, or -1 if not appropriate.
*/
'port'?: (number);
}
export interface _grpc_channelz_v1_Address_TcpIpAddress__Output {
/**
* Either the IPv4 or IPv6 address in bytes. Will be either 4 bytes or 16
* bytes in length.
*/
'ip_address': (Buffer);
/**
* 0-64k, or -1 if not appropriate.
*/
'port': (number);
}
/**
* A Unix Domain Socket address.
*/
export interface _grpc_channelz_v1_Address_UdsAddress {
'filename'?: (string);
}
/**
* A Unix Domain Socket address.
*/
export interface _grpc_channelz_v1_Address_UdsAddress__Output {
'filename': (string);
}
/**
* Address represents the address used to create the socket.
*/
export interface Address {
'tcpip_address'?: (_grpc_channelz_v1_Address_TcpIpAddress | null);
'uds_address'?: (_grpc_channelz_v1_Address_UdsAddress | null);
'other_address'?: (_grpc_channelz_v1_Address_OtherAddress | null);
'address'?: "tcpip_address"|"uds_address"|"other_address";
}
/**
* Address represents the address used to create the socket.
*/
export interface Address__Output {
'tcpip_address'?: (_grpc_channelz_v1_Address_TcpIpAddress__Output | null);
'uds_address'?: (_grpc_channelz_v1_Address_UdsAddress__Output | null);
'other_address'?: (_grpc_channelz_v1_Address_OtherAddress__Output | null);
'address': "tcpip_address"|"uds_address"|"other_address";
}

View File

@ -0,0 +1,68 @@
// Original file: proto/channelz.proto
import type { ChannelRef as _grpc_channelz_v1_ChannelRef, ChannelRef__Output as _grpc_channelz_v1_ChannelRef__Output } from '../../../grpc/channelz/v1/ChannelRef';
import type { ChannelData as _grpc_channelz_v1_ChannelData, ChannelData__Output as _grpc_channelz_v1_ChannelData__Output } from '../../../grpc/channelz/v1/ChannelData';
import type { SubchannelRef as _grpc_channelz_v1_SubchannelRef, SubchannelRef__Output as _grpc_channelz_v1_SubchannelRef__Output } from '../../../grpc/channelz/v1/SubchannelRef';
import type { SocketRef as _grpc_channelz_v1_SocketRef, SocketRef__Output as _grpc_channelz_v1_SocketRef__Output } from '../../../grpc/channelz/v1/SocketRef';
/**
* Channel is a logical grouping of channels, subchannels, and sockets.
*/
export interface Channel {
/**
* The identifier for this channel. This should bet set.
*/
'ref'?: (_grpc_channelz_v1_ChannelRef | null);
/**
* Data specific to this channel.
*/
'data'?: (_grpc_channelz_v1_ChannelData | null);
/**
* There are no ordering guarantees on the order of channel refs.
* There may not be cycles in the ref graph.
* A channel ref may be present in more than one channel or subchannel.
*/
'channel_ref'?: (_grpc_channelz_v1_ChannelRef)[];
/**
* At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
* There are no ordering guarantees on the order of subchannel refs.
* There may not be cycles in the ref graph.
* A sub channel ref may be present in more than one channel or subchannel.
*/
'subchannel_ref'?: (_grpc_channelz_v1_SubchannelRef)[];
/**
* There are no ordering guarantees on the order of sockets.
*/
'socket_ref'?: (_grpc_channelz_v1_SocketRef)[];
}
/**
* Channel is a logical grouping of channels, subchannels, and sockets.
*/
export interface Channel__Output {
/**
* The identifier for this channel. This should bet set.
*/
'ref': (_grpc_channelz_v1_ChannelRef__Output | null);
/**
* Data specific to this channel.
*/
'data': (_grpc_channelz_v1_ChannelData__Output | null);
/**
* There are no ordering guarantees on the order of channel refs.
* There may not be cycles in the ref graph.
* A channel ref may be present in more than one channel or subchannel.
*/
'channel_ref': (_grpc_channelz_v1_ChannelRef__Output)[];
/**
* At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
* There are no ordering guarantees on the order of subchannel refs.
* There may not be cycles in the ref graph.
* A sub channel ref may be present in more than one channel or subchannel.
*/
'subchannel_ref': (_grpc_channelz_v1_SubchannelRef__Output)[];
/**
* There are no ordering guarantees on the order of sockets.
*/
'socket_ref': (_grpc_channelz_v1_SocketRef__Output)[];
}

View File

@ -0,0 +1,29 @@
// Original file: proto/channelz.proto
// Original file: proto/channelz.proto
export enum _grpc_channelz_v1_ChannelConnectivityState_State {
UNKNOWN = 0,
IDLE = 1,
CONNECTING = 2,
READY = 3,
TRANSIENT_FAILURE = 4,
SHUTDOWN = 5,
}
/**
* These come from the specified states in this document:
* https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md
*/
export interface ChannelConnectivityState {
'state'?: (_grpc_channelz_v1_ChannelConnectivityState_State | keyof typeof _grpc_channelz_v1_ChannelConnectivityState_State);
}
/**
* These come from the specified states in this document:
* https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md
*/
export interface ChannelConnectivityState__Output {
'state': (keyof typeof _grpc_channelz_v1_ChannelConnectivityState_State);
}

View File

@ -0,0 +1,76 @@
// Original file: proto/channelz.proto
import type { ChannelConnectivityState as _grpc_channelz_v1_ChannelConnectivityState, ChannelConnectivityState__Output as _grpc_channelz_v1_ChannelConnectivityState__Output } from '../../../grpc/channelz/v1/ChannelConnectivityState';
import type { ChannelTrace as _grpc_channelz_v1_ChannelTrace, ChannelTrace__Output as _grpc_channelz_v1_ChannelTrace__Output } from '../../../grpc/channelz/v1/ChannelTrace';
import type { Timestamp as _google_protobuf_Timestamp, Timestamp__Output as _google_protobuf_Timestamp__Output } from '../../../google/protobuf/Timestamp';
import type { Long } from '@grpc/proto-loader';
/**
* Channel data is data related to a specific Channel or Subchannel.
*/
export interface ChannelData {
/**
* The connectivity state of the channel or subchannel. Implementations
* should always set this.
*/
'state'?: (_grpc_channelz_v1_ChannelConnectivityState | null);
/**
* The target this channel originally tried to connect to. May be absent
*/
'target'?: (string);
/**
* A trace of recent events on the channel. May be absent.
*/
'trace'?: (_grpc_channelz_v1_ChannelTrace | null);
/**
* The number of calls started on the channel
*/
'calls_started'?: (number | string | Long);
/**
* The number of calls that have completed with an OK status
*/
'calls_succeeded'?: (number | string | Long);
/**
* The number of calls that have completed with a non-OK status
*/
'calls_failed'?: (number | string | Long);
/**
* The last time a call was started on the channel.
*/
'last_call_started_timestamp'?: (_google_protobuf_Timestamp | null);
}
/**
* Channel data is data related to a specific Channel or Subchannel.
*/
export interface ChannelData__Output {
/**
* The connectivity state of the channel or subchannel. Implementations
* should always set this.
*/
'state': (_grpc_channelz_v1_ChannelConnectivityState__Output | null);
/**
* The target this channel originally tried to connect to. May be absent
*/
'target': (string);
/**
* A trace of recent events on the channel. May be absent.
*/
'trace': (_grpc_channelz_v1_ChannelTrace__Output | null);
/**
* The number of calls started on the channel
*/
'calls_started': (string);
/**
* The number of calls that have completed with an OK status
*/
'calls_succeeded': (string);
/**
* The number of calls that have completed with a non-OK status
*/
'calls_failed': (string);
/**
* The last time a call was started on the channel.
*/
'last_call_started_timestamp': (_google_protobuf_Timestamp__Output | null);
}

View File

@ -0,0 +1,31 @@
// Original file: proto/channelz.proto
import type { Long } from '@grpc/proto-loader';
/**
* ChannelRef is a reference to a Channel.
*/
export interface ChannelRef {
/**
* The globally unique id for this channel. Must be a positive number.
*/
'channel_id'?: (number | string | Long);
/**
* An optional name associated with the channel.
*/
'name'?: (string);
}
/**
* ChannelRef is a reference to a Channel.
*/
export interface ChannelRef__Output {
/**
* The globally unique id for this channel. Must be a positive number.
*/
'channel_id': (string);
/**
* An optional name associated with the channel.
*/
'name': (string);
}

View File

@ -0,0 +1,45 @@
// Original file: proto/channelz.proto
import type { Timestamp as _google_protobuf_Timestamp, Timestamp__Output as _google_protobuf_Timestamp__Output } from '../../../google/protobuf/Timestamp';
import type { ChannelTraceEvent as _grpc_channelz_v1_ChannelTraceEvent, ChannelTraceEvent__Output as _grpc_channelz_v1_ChannelTraceEvent__Output } from '../../../grpc/channelz/v1/ChannelTraceEvent';
import type { Long } from '@grpc/proto-loader';
/**
* ChannelTrace represents the recent events that have occurred on the channel.
*/
export interface ChannelTrace {
/**
* Number of events ever logged in this tracing object. This can differ from
* events.size() because events can be overwritten or garbage collected by
* implementations.
*/
'num_events_logged'?: (number | string | Long);
/**
* Time that this channel was created.
*/
'creation_timestamp'?: (_google_protobuf_Timestamp | null);
/**
* List of events that have occurred on this channel.
*/
'events'?: (_grpc_channelz_v1_ChannelTraceEvent)[];
}
/**
* ChannelTrace represents the recent events that have occurred on the channel.
*/
export interface ChannelTrace__Output {
/**
* Number of events ever logged in this tracing object. This can differ from
* events.size() because events can be overwritten or garbage collected by
* implementations.
*/
'num_events_logged': (string);
/**
* Time that this channel was created.
*/
'creation_timestamp': (_google_protobuf_Timestamp__Output | null);
/**
* List of events that have occurred on this channel.
*/
'events': (_grpc_channelz_v1_ChannelTraceEvent__Output)[];
}

View File

@ -0,0 +1,73 @@
// Original file: proto/channelz.proto
import type { Timestamp as _google_protobuf_Timestamp, Timestamp__Output as _google_protobuf_Timestamp__Output } from '../../../google/protobuf/Timestamp';
import type { ChannelRef as _grpc_channelz_v1_ChannelRef, ChannelRef__Output as _grpc_channelz_v1_ChannelRef__Output } from '../../../grpc/channelz/v1/ChannelRef';
import type { SubchannelRef as _grpc_channelz_v1_SubchannelRef, SubchannelRef__Output as _grpc_channelz_v1_SubchannelRef__Output } from '../../../grpc/channelz/v1/SubchannelRef';
// Original file: proto/channelz.proto
/**
* The supported severity levels of trace events.
*/
export enum _grpc_channelz_v1_ChannelTraceEvent_Severity {
CT_UNKNOWN = 0,
CT_INFO = 1,
CT_WARNING = 2,
CT_ERROR = 3,
}
/**
* A trace event is an interesting thing that happened to a channel or
* subchannel, such as creation, address resolution, subchannel creation, etc.
*/
export interface ChannelTraceEvent {
/**
* High level description of the event.
*/
'description'?: (string);
/**
* the severity of the trace event
*/
'severity'?: (_grpc_channelz_v1_ChannelTraceEvent_Severity | keyof typeof _grpc_channelz_v1_ChannelTraceEvent_Severity);
/**
* When this event occurred.
*/
'timestamp'?: (_google_protobuf_Timestamp | null);
'channel_ref'?: (_grpc_channelz_v1_ChannelRef | null);
'subchannel_ref'?: (_grpc_channelz_v1_SubchannelRef | null);
/**
* ref of referenced channel or subchannel.
* Optional, only present if this event refers to a child object. For example,
* this field would be filled if this trace event was for a subchannel being
* created.
*/
'child_ref'?: "channel_ref"|"subchannel_ref";
}
/**
* A trace event is an interesting thing that happened to a channel or
* subchannel, such as creation, address resolution, subchannel creation, etc.
*/
export interface ChannelTraceEvent__Output {
/**
* High level description of the event.
*/
'description': (string);
/**
* the severity of the trace event
*/
'severity': (keyof typeof _grpc_channelz_v1_ChannelTraceEvent_Severity);
/**
* When this event occurred.
*/
'timestamp': (_google_protobuf_Timestamp__Output | null);
'channel_ref'?: (_grpc_channelz_v1_ChannelRef__Output | null);
'subchannel_ref'?: (_grpc_channelz_v1_SubchannelRef__Output | null);
/**
* ref of referenced channel or subchannel.
* Optional, only present if this event refers to a child object. For example,
* this field would be filled if this trace event was for a subchannel being
* created.
*/
'child_ref': "channel_ref"|"subchannel_ref";
}

View File

@ -0,0 +1,178 @@
// Original file: proto/channelz.proto
import type * as grpc from '../../../../index'
import type { MethodDefinition } from '@grpc/proto-loader'
import type { GetChannelRequest as _grpc_channelz_v1_GetChannelRequest, GetChannelRequest__Output as _grpc_channelz_v1_GetChannelRequest__Output } from '../../../grpc/channelz/v1/GetChannelRequest';
import type { GetChannelResponse as _grpc_channelz_v1_GetChannelResponse, GetChannelResponse__Output as _grpc_channelz_v1_GetChannelResponse__Output } from '../../../grpc/channelz/v1/GetChannelResponse';
import type { GetServerRequest as _grpc_channelz_v1_GetServerRequest, GetServerRequest__Output as _grpc_channelz_v1_GetServerRequest__Output } from '../../../grpc/channelz/v1/GetServerRequest';
import type { GetServerResponse as _grpc_channelz_v1_GetServerResponse, GetServerResponse__Output as _grpc_channelz_v1_GetServerResponse__Output } from '../../../grpc/channelz/v1/GetServerResponse';
import type { GetServerSocketsRequest as _grpc_channelz_v1_GetServerSocketsRequest, GetServerSocketsRequest__Output as _grpc_channelz_v1_GetServerSocketsRequest__Output } from '../../../grpc/channelz/v1/GetServerSocketsRequest';
import type { GetServerSocketsResponse as _grpc_channelz_v1_GetServerSocketsResponse, GetServerSocketsResponse__Output as _grpc_channelz_v1_GetServerSocketsResponse__Output } from '../../../grpc/channelz/v1/GetServerSocketsResponse';
import type { GetServersRequest as _grpc_channelz_v1_GetServersRequest, GetServersRequest__Output as _grpc_channelz_v1_GetServersRequest__Output } from '../../../grpc/channelz/v1/GetServersRequest';
import type { GetServersResponse as _grpc_channelz_v1_GetServersResponse, GetServersResponse__Output as _grpc_channelz_v1_GetServersResponse__Output } from '../../../grpc/channelz/v1/GetServersResponse';
import type { GetSocketRequest as _grpc_channelz_v1_GetSocketRequest, GetSocketRequest__Output as _grpc_channelz_v1_GetSocketRequest__Output } from '../../../grpc/channelz/v1/GetSocketRequest';
import type { GetSocketResponse as _grpc_channelz_v1_GetSocketResponse, GetSocketResponse__Output as _grpc_channelz_v1_GetSocketResponse__Output } from '../../../grpc/channelz/v1/GetSocketResponse';
import type { GetSubchannelRequest as _grpc_channelz_v1_GetSubchannelRequest, GetSubchannelRequest__Output as _grpc_channelz_v1_GetSubchannelRequest__Output } from '../../../grpc/channelz/v1/GetSubchannelRequest';
import type { GetSubchannelResponse as _grpc_channelz_v1_GetSubchannelResponse, GetSubchannelResponse__Output as _grpc_channelz_v1_GetSubchannelResponse__Output } from '../../../grpc/channelz/v1/GetSubchannelResponse';
import type { GetTopChannelsRequest as _grpc_channelz_v1_GetTopChannelsRequest, GetTopChannelsRequest__Output as _grpc_channelz_v1_GetTopChannelsRequest__Output } from '../../../grpc/channelz/v1/GetTopChannelsRequest';
import type { GetTopChannelsResponse as _grpc_channelz_v1_GetTopChannelsResponse, GetTopChannelsResponse__Output as _grpc_channelz_v1_GetTopChannelsResponse__Output } from '../../../grpc/channelz/v1/GetTopChannelsResponse';
/**
* Channelz is a service exposed by gRPC servers that provides detailed debug
* information.
*/
export interface ChannelzClient extends grpc.Client {
/**
* Returns a single Channel, or else a NOT_FOUND code.
*/
GetChannel(argument: _grpc_channelz_v1_GetChannelRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetChannelResponse__Output) => void): grpc.ClientUnaryCall;
GetChannel(argument: _grpc_channelz_v1_GetChannelRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetChannelResponse__Output) => void): grpc.ClientUnaryCall;
GetChannel(argument: _grpc_channelz_v1_GetChannelRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetChannelResponse__Output) => void): grpc.ClientUnaryCall;
GetChannel(argument: _grpc_channelz_v1_GetChannelRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetChannelResponse__Output) => void): grpc.ClientUnaryCall;
/**
* Returns a single Server, or else a NOT_FOUND code.
*/
GetServer(argument: _grpc_channelz_v1_GetServerRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerResponse__Output) => void): grpc.ClientUnaryCall;
GetServer(argument: _grpc_channelz_v1_GetServerRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerResponse__Output) => void): grpc.ClientUnaryCall;
GetServer(argument: _grpc_channelz_v1_GetServerRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerResponse__Output) => void): grpc.ClientUnaryCall;
GetServer(argument: _grpc_channelz_v1_GetServerRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerResponse__Output) => void): grpc.ClientUnaryCall;
/**
* Returns a single Server, or else a NOT_FOUND code.
*/
getServer(argument: _grpc_channelz_v1_GetServerRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerResponse__Output) => void): grpc.ClientUnaryCall;
getServer(argument: _grpc_channelz_v1_GetServerRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerResponse__Output) => void): grpc.ClientUnaryCall;
getServer(argument: _grpc_channelz_v1_GetServerRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerResponse__Output) => void): grpc.ClientUnaryCall;
getServer(argument: _grpc_channelz_v1_GetServerRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerResponse__Output) => void): grpc.ClientUnaryCall;
/**
* Gets all server sockets that exist in the process.
*/
GetServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerSocketsResponse__Output) => void): grpc.ClientUnaryCall;
GetServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerSocketsResponse__Output) => void): grpc.ClientUnaryCall;
GetServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerSocketsResponse__Output) => void): grpc.ClientUnaryCall;
GetServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerSocketsResponse__Output) => void): grpc.ClientUnaryCall;
/**
* Gets all server sockets that exist in the process.
*/
getServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerSocketsResponse__Output) => void): grpc.ClientUnaryCall;
getServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerSocketsResponse__Output) => void): grpc.ClientUnaryCall;
getServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerSocketsResponse__Output) => void): grpc.ClientUnaryCall;
getServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServerSocketsResponse__Output) => void): grpc.ClientUnaryCall;
/**
* Gets all servers that exist in the process.
*/
GetServers(argument: _grpc_channelz_v1_GetServersRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServersResponse__Output) => void): grpc.ClientUnaryCall;
GetServers(argument: _grpc_channelz_v1_GetServersRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServersResponse__Output) => void): grpc.ClientUnaryCall;
GetServers(argument: _grpc_channelz_v1_GetServersRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServersResponse__Output) => void): grpc.ClientUnaryCall;
GetServers(argument: _grpc_channelz_v1_GetServersRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServersResponse__Output) => void): grpc.ClientUnaryCall;
/**
* Gets all servers that exist in the process.
*/
getServers(argument: _grpc_channelz_v1_GetServersRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServersResponse__Output) => void): grpc.ClientUnaryCall;
getServers(argument: _grpc_channelz_v1_GetServersRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServersResponse__Output) => void): grpc.ClientUnaryCall;
getServers(argument: _grpc_channelz_v1_GetServersRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServersResponse__Output) => void): grpc.ClientUnaryCall;
getServers(argument: _grpc_channelz_v1_GetServersRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetServersResponse__Output) => void): grpc.ClientUnaryCall;
/**
* Returns a single Socket or else a NOT_FOUND code.
*/
GetSocket(argument: _grpc_channelz_v1_GetSocketRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSocketResponse__Output) => void): grpc.ClientUnaryCall;
GetSocket(argument: _grpc_channelz_v1_GetSocketRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSocketResponse__Output) => void): grpc.ClientUnaryCall;
GetSocket(argument: _grpc_channelz_v1_GetSocketRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSocketResponse__Output) => void): grpc.ClientUnaryCall;
GetSocket(argument: _grpc_channelz_v1_GetSocketRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSocketResponse__Output) => void): grpc.ClientUnaryCall;
/**
* Returns a single Socket or else a NOT_FOUND code.
*/
getSocket(argument: _grpc_channelz_v1_GetSocketRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSocketResponse__Output) => void): grpc.ClientUnaryCall;
getSocket(argument: _grpc_channelz_v1_GetSocketRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSocketResponse__Output) => void): grpc.ClientUnaryCall;
getSocket(argument: _grpc_channelz_v1_GetSocketRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSocketResponse__Output) => void): grpc.ClientUnaryCall;
getSocket(argument: _grpc_channelz_v1_GetSocketRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSocketResponse__Output) => void): grpc.ClientUnaryCall;
/**
* Returns a single Subchannel, or else a NOT_FOUND code.
*/
GetSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSubchannelResponse__Output) => void): grpc.ClientUnaryCall;
GetSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSubchannelResponse__Output) => void): grpc.ClientUnaryCall;
GetSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSubchannelResponse__Output) => void): grpc.ClientUnaryCall;
GetSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSubchannelResponse__Output) => void): grpc.ClientUnaryCall;
/**
* Returns a single Subchannel, or else a NOT_FOUND code.
*/
getSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSubchannelResponse__Output) => void): grpc.ClientUnaryCall;
getSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSubchannelResponse__Output) => void): grpc.ClientUnaryCall;
getSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSubchannelResponse__Output) => void): grpc.ClientUnaryCall;
getSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetSubchannelResponse__Output) => void): grpc.ClientUnaryCall;
/**
* Gets all root channels (i.e. channels the application has directly
* created). This does not include subchannels nor non-top level channels.
*/
GetTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetTopChannelsResponse__Output) => void): grpc.ClientUnaryCall;
GetTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetTopChannelsResponse__Output) => void): grpc.ClientUnaryCall;
GetTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetTopChannelsResponse__Output) => void): grpc.ClientUnaryCall;
GetTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetTopChannelsResponse__Output) => void): grpc.ClientUnaryCall;
/**
* Gets all root channels (i.e. channels the application has directly
* created). This does not include subchannels nor non-top level channels.
*/
getTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetTopChannelsResponse__Output) => void): grpc.ClientUnaryCall;
getTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, metadata: grpc.Metadata, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetTopChannelsResponse__Output) => void): grpc.ClientUnaryCall;
getTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, options: grpc.CallOptions, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetTopChannelsResponse__Output) => void): grpc.ClientUnaryCall;
getTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, callback: (error?: grpc.ServiceError, result?: _grpc_channelz_v1_GetTopChannelsResponse__Output) => void): grpc.ClientUnaryCall;
}
/**
* Channelz is a service exposed by gRPC servers that provides detailed debug
* information.
*/
export interface ChannelzHandlers extends grpc.UntypedServiceImplementation {
/**
* Returns a single Channel, or else a NOT_FOUND code.
*/
GetChannel: grpc.handleUnaryCall<_grpc_channelz_v1_GetChannelRequest__Output, _grpc_channelz_v1_GetChannelResponse>;
/**
* Returns a single Server, or else a NOT_FOUND code.
*/
GetServer: grpc.handleUnaryCall<_grpc_channelz_v1_GetServerRequest__Output, _grpc_channelz_v1_GetServerResponse>;
/**
* Gets all server sockets that exist in the process.
*/
GetServerSockets: grpc.handleUnaryCall<_grpc_channelz_v1_GetServerSocketsRequest__Output, _grpc_channelz_v1_GetServerSocketsResponse>;
/**
* Gets all servers that exist in the process.
*/
GetServers: grpc.handleUnaryCall<_grpc_channelz_v1_GetServersRequest__Output, _grpc_channelz_v1_GetServersResponse>;
/**
* Returns a single Socket or else a NOT_FOUND code.
*/
GetSocket: grpc.handleUnaryCall<_grpc_channelz_v1_GetSocketRequest__Output, _grpc_channelz_v1_GetSocketResponse>;
/**
* Returns a single Subchannel, or else a NOT_FOUND code.
*/
GetSubchannel: grpc.handleUnaryCall<_grpc_channelz_v1_GetSubchannelRequest__Output, _grpc_channelz_v1_GetSubchannelResponse>;
/**
* Gets all root channels (i.e. channels the application has directly
* created). This does not include subchannels nor non-top level channels.
*/
GetTopChannels: grpc.handleUnaryCall<_grpc_channelz_v1_GetTopChannelsRequest__Output, _grpc_channelz_v1_GetTopChannelsResponse>;
}
export interface ChannelzDefinition extends grpc.ServiceDefinition {
GetChannel: MethodDefinition<_grpc_channelz_v1_GetChannelRequest, _grpc_channelz_v1_GetChannelResponse, _grpc_channelz_v1_GetChannelRequest__Output, _grpc_channelz_v1_GetChannelResponse__Output>
GetServer: MethodDefinition<_grpc_channelz_v1_GetServerRequest, _grpc_channelz_v1_GetServerResponse, _grpc_channelz_v1_GetServerRequest__Output, _grpc_channelz_v1_GetServerResponse__Output>
GetServerSockets: MethodDefinition<_grpc_channelz_v1_GetServerSocketsRequest, _grpc_channelz_v1_GetServerSocketsResponse, _grpc_channelz_v1_GetServerSocketsRequest__Output, _grpc_channelz_v1_GetServerSocketsResponse__Output>
GetServers: MethodDefinition<_grpc_channelz_v1_GetServersRequest, _grpc_channelz_v1_GetServersResponse, _grpc_channelz_v1_GetServersRequest__Output, _grpc_channelz_v1_GetServersResponse__Output>
GetSocket: MethodDefinition<_grpc_channelz_v1_GetSocketRequest, _grpc_channelz_v1_GetSocketResponse, _grpc_channelz_v1_GetSocketRequest__Output, _grpc_channelz_v1_GetSocketResponse__Output>
GetSubchannel: MethodDefinition<_grpc_channelz_v1_GetSubchannelRequest, _grpc_channelz_v1_GetSubchannelResponse, _grpc_channelz_v1_GetSubchannelRequest__Output, _grpc_channelz_v1_GetSubchannelResponse__Output>
GetTopChannels: MethodDefinition<_grpc_channelz_v1_GetTopChannelsRequest, _grpc_channelz_v1_GetTopChannelsResponse, _grpc_channelz_v1_GetTopChannelsRequest__Output, _grpc_channelz_v1_GetTopChannelsResponse__Output>
}

View File

@ -0,0 +1,17 @@
// Original file: proto/channelz.proto
import type { Long } from '@grpc/proto-loader';
export interface GetChannelRequest {
/**
* channel_id is the identifier of the specific channel to get.
*/
'channel_id'?: (number | string | Long);
}
export interface GetChannelRequest__Output {
/**
* channel_id is the identifier of the specific channel to get.
*/
'channel_id': (string);
}

View File

@ -0,0 +1,19 @@
// Original file: proto/channelz.proto
import type { Channel as _grpc_channelz_v1_Channel, Channel__Output as _grpc_channelz_v1_Channel__Output } from '../../../grpc/channelz/v1/Channel';
export interface GetChannelResponse {
/**
* The Channel that corresponds to the requested channel_id. This field
* should be set.
*/
'channel'?: (_grpc_channelz_v1_Channel | null);
}
export interface GetChannelResponse__Output {
/**
* The Channel that corresponds to the requested channel_id. This field
* should be set.
*/
'channel': (_grpc_channelz_v1_Channel__Output | null);
}

View File

@ -0,0 +1,17 @@
// Original file: proto/channelz.proto
import type { Long } from '@grpc/proto-loader';
export interface GetServerRequest {
/**
* server_id is the identifier of the specific server to get.
*/
'server_id'?: (number | string | Long);
}
export interface GetServerRequest__Output {
/**
* server_id is the identifier of the specific server to get.
*/
'server_id': (string);
}

View File

@ -0,0 +1,19 @@
// Original file: proto/channelz.proto
import type { Server as _grpc_channelz_v1_Server, Server__Output as _grpc_channelz_v1_Server__Output } from '../../../grpc/channelz/v1/Server';
export interface GetServerResponse {
/**
* The Server that corresponds to the requested server_id. This field
* should be set.
*/
'server'?: (_grpc_channelz_v1_Server | null);
}
export interface GetServerResponse__Output {
/**
* The Server that corresponds to the requested server_id. This field
* should be set.
*/
'server': (_grpc_channelz_v1_Server__Output | null);
}

View File

@ -0,0 +1,39 @@
// Original file: proto/channelz.proto
import type { Long } from '@grpc/proto-loader';
export interface GetServerSocketsRequest {
'server_id'?: (number | string | Long);
/**
* start_socket_id indicates that only sockets at or above this id should be
* included in the results.
* To request the first page, this must be set to 0. To request
* subsequent pages, the client generates this value by adding 1 to
* the highest seen result ID.
*/
'start_socket_id'?: (number | string | Long);
/**
* If non-zero, the server will return a page of results containing
* at most this many items. If zero, the server will choose a
* reasonable page size. Must never be negative.
*/
'max_results'?: (number | string | Long);
}
export interface GetServerSocketsRequest__Output {
'server_id': (string);
/**
* start_socket_id indicates that only sockets at or above this id should be
* included in the results.
* To request the first page, this must be set to 0. To request
* subsequent pages, the client generates this value by adding 1 to
* the highest seen result ID.
*/
'start_socket_id': (string);
/**
* If non-zero, the server will return a page of results containing
* at most this many items. If zero, the server will choose a
* reasonable page size. Must never be negative.
*/
'max_results': (string);
}

View File

@ -0,0 +1,33 @@
// Original file: proto/channelz.proto
import type { SocketRef as _grpc_channelz_v1_SocketRef, SocketRef__Output as _grpc_channelz_v1_SocketRef__Output } from '../../../grpc/channelz/v1/SocketRef';
export interface GetServerSocketsResponse {
/**
* list of socket refs that the connection detail service knows about. Sorted in
* ascending socket_id order.
* Must contain at least 1 result, otherwise 'end' must be true.
*/
'socket_ref'?: (_grpc_channelz_v1_SocketRef)[];
/**
* If set, indicates that the list of sockets is the final list. Requesting
* more sockets will only return more if they are created after this RPC
* completes.
*/
'end'?: (boolean);
}
export interface GetServerSocketsResponse__Output {
/**
* list of socket refs that the connection detail service knows about. Sorted in
* ascending socket_id order.
* Must contain at least 1 result, otherwise 'end' must be true.
*/
'socket_ref': (_grpc_channelz_v1_SocketRef__Output)[];
/**
* If set, indicates that the list of sockets is the final list. Requesting
* more sockets will only return more if they are created after this RPC
* completes.
*/
'end': (boolean);
}

View File

@ -0,0 +1,37 @@
// Original file: proto/channelz.proto
import type { Long } from '@grpc/proto-loader';
export interface GetServersRequest {
/**
* start_server_id indicates that only servers at or above this id should be
* included in the results.
* To request the first page, this must be set to 0. To request
* subsequent pages, the client generates this value by adding 1 to
* the highest seen result ID.
*/
'start_server_id'?: (number | string | Long);
/**
* If non-zero, the server will return a page of results containing
* at most this many items. If zero, the server will choose a
* reasonable page size. Must never be negative.
*/
'max_results'?: (number | string | Long);
}
export interface GetServersRequest__Output {
/**
* start_server_id indicates that only servers at or above this id should be
* included in the results.
* To request the first page, this must be set to 0. To request
* subsequent pages, the client generates this value by adding 1 to
* the highest seen result ID.
*/
'start_server_id': (string);
/**
* If non-zero, the server will return a page of results containing
* at most this many items. If zero, the server will choose a
* reasonable page size. Must never be negative.
*/
'max_results': (string);
}

View File

@ -0,0 +1,33 @@
// Original file: proto/channelz.proto
import type { Server as _grpc_channelz_v1_Server, Server__Output as _grpc_channelz_v1_Server__Output } from '../../../grpc/channelz/v1/Server';
export interface GetServersResponse {
/**
* list of servers that the connection detail service knows about. Sorted in
* ascending server_id order.
* Must contain at least 1 result, otherwise 'end' must be true.
*/
'server'?: (_grpc_channelz_v1_Server)[];
/**
* If set, indicates that the list of servers is the final list. Requesting
* more servers will only return more if they are created after this RPC
* completes.
*/
'end'?: (boolean);
}
export interface GetServersResponse__Output {
/**
* list of servers that the connection detail service knows about. Sorted in
* ascending server_id order.
* Must contain at least 1 result, otherwise 'end' must be true.
*/
'server': (_grpc_channelz_v1_Server__Output)[];
/**
* If set, indicates that the list of servers is the final list. Requesting
* more servers will only return more if they are created after this RPC
* completes.
*/
'end': (boolean);
}

View File

@ -0,0 +1,29 @@
// Original file: proto/channelz.proto
import type { Long } from '@grpc/proto-loader';
export interface GetSocketRequest {
/**
* socket_id is the identifier of the specific socket to get.
*/
'socket_id'?: (number | string | Long);
/**
* If true, the response will contain only high level information
* that is inexpensive to obtain. Fields thay may be omitted are
* documented.
*/
'summary'?: (boolean);
}
export interface GetSocketRequest__Output {
/**
* socket_id is the identifier of the specific socket to get.
*/
'socket_id': (string);
/**
* If true, the response will contain only high level information
* that is inexpensive to obtain. Fields thay may be omitted are
* documented.
*/
'summary': (boolean);
}

View File

@ -0,0 +1,19 @@
// Original file: proto/channelz.proto
import type { Socket as _grpc_channelz_v1_Socket, Socket__Output as _grpc_channelz_v1_Socket__Output } from '../../../grpc/channelz/v1/Socket';
export interface GetSocketResponse {
/**
* The Socket that corresponds to the requested socket_id. This field
* should be set.
*/
'socket'?: (_grpc_channelz_v1_Socket | null);
}
export interface GetSocketResponse__Output {
/**
* The Socket that corresponds to the requested socket_id. This field
* should be set.
*/
'socket': (_grpc_channelz_v1_Socket__Output | null);
}

View File

@ -0,0 +1,17 @@
// Original file: proto/channelz.proto
import type { Long } from '@grpc/proto-loader';
export interface GetSubchannelRequest {
/**
* subchannel_id is the identifier of the specific subchannel to get.
*/
'subchannel_id'?: (number | string | Long);
}
export interface GetSubchannelRequest__Output {
/**
* subchannel_id is the identifier of the specific subchannel to get.
*/
'subchannel_id': (string);
}

View File

@ -0,0 +1,19 @@
// Original file: proto/channelz.proto
import type { Subchannel as _grpc_channelz_v1_Subchannel, Subchannel__Output as _grpc_channelz_v1_Subchannel__Output } from '../../../grpc/channelz/v1/Subchannel';
export interface GetSubchannelResponse {
/**
* The Subchannel that corresponds to the requested subchannel_id. This
* field should be set.
*/
'subchannel'?: (_grpc_channelz_v1_Subchannel | null);
}
export interface GetSubchannelResponse__Output {
/**
* The Subchannel that corresponds to the requested subchannel_id. This
* field should be set.
*/
'subchannel': (_grpc_channelz_v1_Subchannel__Output | null);
}

View File

@ -0,0 +1,37 @@
// Original file: proto/channelz.proto
import type { Long } from '@grpc/proto-loader';
export interface GetTopChannelsRequest {
/**
* start_channel_id indicates that only channels at or above this id should be
* included in the results.
* To request the first page, this should be set to 0. To request
* subsequent pages, the client generates this value by adding 1 to
* the highest seen result ID.
*/
'start_channel_id'?: (number | string | Long);
/**
* If non-zero, the server will return a page of results containing
* at most this many items. If zero, the server will choose a
* reasonable page size. Must never be negative.
*/
'max_results'?: (number | string | Long);
}
export interface GetTopChannelsRequest__Output {
/**
* start_channel_id indicates that only channels at or above this id should be
* included in the results.
* To request the first page, this should be set to 0. To request
* subsequent pages, the client generates this value by adding 1 to
* the highest seen result ID.
*/
'start_channel_id': (string);
/**
* If non-zero, the server will return a page of results containing
* at most this many items. If zero, the server will choose a
* reasonable page size. Must never be negative.
*/
'max_results': (string);
}

View File

@ -0,0 +1,33 @@
// Original file: proto/channelz.proto
import type { Channel as _grpc_channelz_v1_Channel, Channel__Output as _grpc_channelz_v1_Channel__Output } from '../../../grpc/channelz/v1/Channel';
export interface GetTopChannelsResponse {
/**
* list of channels that the connection detail service knows about. Sorted in
* ascending channel_id order.
* Must contain at least 1 result, otherwise 'end' must be true.
*/
'channel'?: (_grpc_channelz_v1_Channel)[];
/**
* If set, indicates that the list of channels is the final list. Requesting
* more channels can only return more if they are created after this RPC
* completes.
*/
'end'?: (boolean);
}
export interface GetTopChannelsResponse__Output {
/**
* list of channels that the connection detail service knows about. Sorted in
* ascending channel_id order.
* Must contain at least 1 result, otherwise 'end' must be true.
*/
'channel': (_grpc_channelz_v1_Channel__Output)[];
/**
* If set, indicates that the list of channels is the final list. Requesting
* more channels can only return more if they are created after this RPC
* completes.
*/
'end': (boolean);
}

View File

@ -0,0 +1,87 @@
// Original file: proto/channelz.proto
import type { Any as _google_protobuf_Any, Any__Output as _google_protobuf_Any__Output } from '../../../google/protobuf/Any';
export interface _grpc_channelz_v1_Security_OtherSecurity {
/**
* The human readable version of the value.
*/
'name'?: (string);
/**
* The actual security details message.
*/
'value'?: (_google_protobuf_Any | null);
}
export interface _grpc_channelz_v1_Security_OtherSecurity__Output {
/**
* The human readable version of the value.
*/
'name': (string);
/**
* The actual security details message.
*/
'value': (_google_protobuf_Any__Output | null);
}
export interface _grpc_channelz_v1_Security_Tls {
/**
* The cipher suite name in the RFC 4346 format:
* https://tools.ietf.org/html/rfc4346#appendix-C
*/
'standard_name'?: (string);
/**
* Some other way to describe the cipher suite if
* the RFC 4346 name is not available.
*/
'other_name'?: (string);
/**
* the certificate used by this endpoint.
*/
'local_certificate'?: (Buffer | Uint8Array | string);
/**
* the certificate used by the remote endpoint.
*/
'remote_certificate'?: (Buffer | Uint8Array | string);
'cipher_suite'?: "standard_name"|"other_name";
}
export interface _grpc_channelz_v1_Security_Tls__Output {
/**
* The cipher suite name in the RFC 4346 format:
* https://tools.ietf.org/html/rfc4346#appendix-C
*/
'standard_name'?: (string);
/**
* Some other way to describe the cipher suite if
* the RFC 4346 name is not available.
*/
'other_name'?: (string);
/**
* the certificate used by this endpoint.
*/
'local_certificate': (Buffer);
/**
* the certificate used by the remote endpoint.
*/
'remote_certificate': (Buffer);
'cipher_suite': "standard_name"|"other_name";
}
/**
* Security represents details about how secure the socket is.
*/
export interface Security {
'tls'?: (_grpc_channelz_v1_Security_Tls | null);
'other'?: (_grpc_channelz_v1_Security_OtherSecurity | null);
'model'?: "tls"|"other";
}
/**
* Security represents details about how secure the socket is.
*/
export interface Security__Output {
'tls'?: (_grpc_channelz_v1_Security_Tls__Output | null);
'other'?: (_grpc_channelz_v1_Security_OtherSecurity__Output | null);
'model': "tls"|"other";
}

View File

@ -0,0 +1,45 @@
// Original file: proto/channelz.proto
import type { ServerRef as _grpc_channelz_v1_ServerRef, ServerRef__Output as _grpc_channelz_v1_ServerRef__Output } from '../../../grpc/channelz/v1/ServerRef';
import type { ServerData as _grpc_channelz_v1_ServerData, ServerData__Output as _grpc_channelz_v1_ServerData__Output } from '../../../grpc/channelz/v1/ServerData';
import type { SocketRef as _grpc_channelz_v1_SocketRef, SocketRef__Output as _grpc_channelz_v1_SocketRef__Output } from '../../../grpc/channelz/v1/SocketRef';
/**
* Server represents a single server. There may be multiple servers in a single
* program.
*/
export interface Server {
/**
* The identifier for a Server. This should be set.
*/
'ref'?: (_grpc_channelz_v1_ServerRef | null);
/**
* The associated data of the Server.
*/
'data'?: (_grpc_channelz_v1_ServerData | null);
/**
* The sockets that the server is listening on. There are no ordering
* guarantees. This may be absent.
*/
'listen_socket'?: (_grpc_channelz_v1_SocketRef)[];
}
/**
* Server represents a single server. There may be multiple servers in a single
* program.
*/
export interface Server__Output {
/**
* The identifier for a Server. This should be set.
*/
'ref': (_grpc_channelz_v1_ServerRef__Output | null);
/**
* The associated data of the Server.
*/
'data': (_grpc_channelz_v1_ServerData__Output | null);
/**
* The sockets that the server is listening on. There are no ordering
* guarantees. This may be absent.
*/
'listen_socket': (_grpc_channelz_v1_SocketRef__Output)[];
}

View File

@ -0,0 +1,57 @@
// Original file: proto/channelz.proto
import type { ChannelTrace as _grpc_channelz_v1_ChannelTrace, ChannelTrace__Output as _grpc_channelz_v1_ChannelTrace__Output } from '../../../grpc/channelz/v1/ChannelTrace';
import type { Timestamp as _google_protobuf_Timestamp, Timestamp__Output as _google_protobuf_Timestamp__Output } from '../../../google/protobuf/Timestamp';
import type { Long } from '@grpc/proto-loader';
/**
* ServerData is data for a specific Server.
*/
export interface ServerData {
/**
* A trace of recent events on the server. May be absent.
*/
'trace'?: (_grpc_channelz_v1_ChannelTrace | null);
/**
* The number of incoming calls started on the server
*/
'calls_started'?: (number | string | Long);
/**
* The number of incoming calls that have completed with an OK status
*/
'calls_succeeded'?: (number | string | Long);
/**
* The number of incoming calls that have a completed with a non-OK status
*/
'calls_failed'?: (number | string | Long);
/**
* The last time a call was started on the server.
*/
'last_call_started_timestamp'?: (_google_protobuf_Timestamp | null);
}
/**
* ServerData is data for a specific Server.
*/
export interface ServerData__Output {
/**
* A trace of recent events on the server. May be absent.
*/
'trace': (_grpc_channelz_v1_ChannelTrace__Output | null);
/**
* The number of incoming calls started on the server
*/
'calls_started': (string);
/**
* The number of incoming calls that have completed with an OK status
*/
'calls_succeeded': (string);
/**
* The number of incoming calls that have a completed with a non-OK status
*/
'calls_failed': (string);
/**
* The last time a call was started on the server.
*/
'last_call_started_timestamp': (_google_protobuf_Timestamp__Output | null);
}

View File

@ -0,0 +1,31 @@
// Original file: proto/channelz.proto
import type { Long } from '@grpc/proto-loader';
/**
* ServerRef is a reference to a Server.
*/
export interface ServerRef {
/**
* A globally unique identifier for this server. Must be a positive number.
*/
'server_id'?: (number | string | Long);
/**
* An optional name associated with the server.
*/
'name'?: (string);
}
/**
* ServerRef is a reference to a Server.
*/
export interface ServerRef__Output {
/**
* A globally unique identifier for this server. Must be a positive number.
*/
'server_id': (string);
/**
* An optional name associated with the server.
*/
'name': (string);
}

View File

@ -0,0 +1,70 @@
// Original file: proto/channelz.proto
import type { SocketRef as _grpc_channelz_v1_SocketRef, SocketRef__Output as _grpc_channelz_v1_SocketRef__Output } from '../../../grpc/channelz/v1/SocketRef';
import type { SocketData as _grpc_channelz_v1_SocketData, SocketData__Output as _grpc_channelz_v1_SocketData__Output } from '../../../grpc/channelz/v1/SocketData';
import type { Address as _grpc_channelz_v1_Address, Address__Output as _grpc_channelz_v1_Address__Output } from '../../../grpc/channelz/v1/Address';
import type { Security as _grpc_channelz_v1_Security, Security__Output as _grpc_channelz_v1_Security__Output } from '../../../grpc/channelz/v1/Security';
/**
* Information about an actual connection. Pronounced "sock-ay".
*/
export interface Socket {
/**
* The identifier for the Socket.
*/
'ref'?: (_grpc_channelz_v1_SocketRef | null);
/**
* Data specific to this Socket.
*/
'data'?: (_grpc_channelz_v1_SocketData | null);
/**
* The locally bound address.
*/
'local'?: (_grpc_channelz_v1_Address | null);
/**
* The remote bound address. May be absent.
*/
'remote'?: (_grpc_channelz_v1_Address | null);
/**
* Security details for this socket. May be absent if not available, or
* there is no security on the socket.
*/
'security'?: (_grpc_channelz_v1_Security | null);
/**
* Optional, represents the name of the remote endpoint, if different than
* the original target name.
*/
'remote_name'?: (string);
}
/**
* Information about an actual connection. Pronounced "sock-ay".
*/
export interface Socket__Output {
/**
* The identifier for the Socket.
*/
'ref': (_grpc_channelz_v1_SocketRef__Output | null);
/**
* Data specific to this Socket.
*/
'data': (_grpc_channelz_v1_SocketData__Output | null);
/**
* The locally bound address.
*/
'local': (_grpc_channelz_v1_Address__Output | null);
/**
* The remote bound address. May be absent.
*/
'remote': (_grpc_channelz_v1_Address__Output | null);
/**
* Security details for this socket. May be absent if not available, or
* there is no security on the socket.
*/
'security': (_grpc_channelz_v1_Security__Output | null);
/**
* Optional, represents the name of the remote endpoint, if different than
* the original target name.
*/
'remote_name': (string);
}

View File

@ -0,0 +1,150 @@
// Original file: proto/channelz.proto
import type { Timestamp as _google_protobuf_Timestamp, Timestamp__Output as _google_protobuf_Timestamp__Output } from '../../../google/protobuf/Timestamp';
import type { Int64Value as _google_protobuf_Int64Value, Int64Value__Output as _google_protobuf_Int64Value__Output } from '../../../google/protobuf/Int64Value';
import type { SocketOption as _grpc_channelz_v1_SocketOption, SocketOption__Output as _grpc_channelz_v1_SocketOption__Output } from '../../../grpc/channelz/v1/SocketOption';
import type { Long } from '@grpc/proto-loader';
/**
* SocketData is data associated for a specific Socket. The fields present
* are specific to the implementation, so there may be minor differences in
* the semantics. (e.g. flow control windows)
*/
export interface SocketData {
/**
* The number of streams that have been started.
*/
'streams_started'?: (number | string | Long);
/**
* The number of streams that have ended successfully:
* On client side, received frame with eos bit set;
* On server side, sent frame with eos bit set.
*/
'streams_succeeded'?: (number | string | Long);
/**
* The number of streams that have ended unsuccessfully:
* On client side, ended without receiving frame with eos bit set;
* On server side, ended without sending frame with eos bit set.
*/
'streams_failed'?: (number | string | Long);
/**
* The number of grpc messages successfully sent on this socket.
*/
'messages_sent'?: (number | string | Long);
/**
* The number of grpc messages received on this socket.
*/
'messages_received'?: (number | string | Long);
/**
* The number of keep alives sent. This is typically implemented with HTTP/2
* ping messages.
*/
'keep_alives_sent'?: (number | string | Long);
/**
* The last time a stream was created by this endpoint. Usually unset for
* servers.
*/
'last_local_stream_created_timestamp'?: (_google_protobuf_Timestamp | null);
/**
* The last time a stream was created by the remote endpoint. Usually unset
* for clients.
*/
'last_remote_stream_created_timestamp'?: (_google_protobuf_Timestamp | null);
/**
* The last time a message was sent by this endpoint.
*/
'last_message_sent_timestamp'?: (_google_protobuf_Timestamp | null);
/**
* The last time a message was received by this endpoint.
*/
'last_message_received_timestamp'?: (_google_protobuf_Timestamp | null);
/**
* The amount of window, granted to the local endpoint by the remote endpoint.
* This may be slightly out of date due to network latency. This does NOT
* include stream level or TCP level flow control info.
*/
'local_flow_control_window'?: (_google_protobuf_Int64Value | null);
/**
* The amount of window, granted to the remote endpoint by the local endpoint.
* This may be slightly out of date due to network latency. This does NOT
* include stream level or TCP level flow control info.
*/
'remote_flow_control_window'?: (_google_protobuf_Int64Value | null);
/**
* Socket options set on this socket. May be absent if 'summary' is set
* on GetSocketRequest.
*/
'option'?: (_grpc_channelz_v1_SocketOption)[];
}
/**
* SocketData is data associated for a specific Socket. The fields present
* are specific to the implementation, so there may be minor differences in
* the semantics. (e.g. flow control windows)
*/
export interface SocketData__Output {
/**
* The number of streams that have been started.
*/
'streams_started': (string);
/**
* The number of streams that have ended successfully:
* On client side, received frame with eos bit set;
* On server side, sent frame with eos bit set.
*/
'streams_succeeded': (string);
/**
* The number of streams that have ended unsuccessfully:
* On client side, ended without receiving frame with eos bit set;
* On server side, ended without sending frame with eos bit set.
*/
'streams_failed': (string);
/**
* The number of grpc messages successfully sent on this socket.
*/
'messages_sent': (string);
/**
* The number of grpc messages received on this socket.
*/
'messages_received': (string);
/**
* The number of keep alives sent. This is typically implemented with HTTP/2
* ping messages.
*/
'keep_alives_sent': (string);
/**
* The last time a stream was created by this endpoint. Usually unset for
* servers.
*/
'last_local_stream_created_timestamp': (_google_protobuf_Timestamp__Output | null);
/**
* The last time a stream was created by the remote endpoint. Usually unset
* for clients.
*/
'last_remote_stream_created_timestamp': (_google_protobuf_Timestamp__Output | null);
/**
* The last time a message was sent by this endpoint.
*/
'last_message_sent_timestamp': (_google_protobuf_Timestamp__Output | null);
/**
* The last time a message was received by this endpoint.
*/
'last_message_received_timestamp': (_google_protobuf_Timestamp__Output | null);
/**
* The amount of window, granted to the local endpoint by the remote endpoint.
* This may be slightly out of date due to network latency. This does NOT
* include stream level or TCP level flow control info.
*/
'local_flow_control_window': (_google_protobuf_Int64Value__Output | null);
/**
* The amount of window, granted to the remote endpoint by the local endpoint.
* This may be slightly out of date due to network latency. This does NOT
* include stream level or TCP level flow control info.
*/
'remote_flow_control_window': (_google_protobuf_Int64Value__Output | null);
/**
* Socket options set on this socket. May be absent if 'summary' is set
* on GetSocketRequest.
*/
'option': (_grpc_channelz_v1_SocketOption__Output)[];
}

View File

@ -0,0 +1,47 @@
// Original file: proto/channelz.proto
import type { Any as _google_protobuf_Any, Any__Output as _google_protobuf_Any__Output } from '../../../google/protobuf/Any';
/**
* SocketOption represents socket options for a socket. Specifically, these
* are the options returned by getsockopt().
*/
export interface SocketOption {
/**
* The full name of the socket option. Typically this will be the upper case
* name, such as "SO_REUSEPORT".
*/
'name'?: (string);
/**
* The human readable value of this socket option. At least one of value or
* additional will be set.
*/
'value'?: (string);
/**
* Additional data associated with the socket option. At least one of value
* or additional will be set.
*/
'additional'?: (_google_protobuf_Any | null);
}
/**
* SocketOption represents socket options for a socket. Specifically, these
* are the options returned by getsockopt().
*/
export interface SocketOption__Output {
/**
* The full name of the socket option. Typically this will be the upper case
* name, such as "SO_REUSEPORT".
*/
'name': (string);
/**
* The human readable value of this socket option. At least one of value or
* additional will be set.
*/
'value': (string);
/**
* Additional data associated with the socket option. At least one of value
* or additional will be set.
*/
'additional': (_google_protobuf_Any__Output | null);
}

View File

@ -0,0 +1,33 @@
// Original file: proto/channelz.proto
import type { Duration as _google_protobuf_Duration, Duration__Output as _google_protobuf_Duration__Output } from '../../../google/protobuf/Duration';
/**
* For use with SocketOption's additional field. This is primarily used for
* SO_LINGER.
*/
export interface SocketOptionLinger {
/**
* active maps to `struct linger.l_onoff`
*/
'active'?: (boolean);
/**
* duration maps to `struct linger.l_linger`
*/
'duration'?: (_google_protobuf_Duration | null);
}
/**
* For use with SocketOption's additional field. This is primarily used for
* SO_LINGER.
*/
export interface SocketOptionLinger__Output {
/**
* active maps to `struct linger.l_onoff`
*/
'active': (boolean);
/**
* duration maps to `struct linger.l_linger`
*/
'duration': (_google_protobuf_Duration__Output | null);
}

View File

@ -0,0 +1,74 @@
// Original file: proto/channelz.proto
/**
* For use with SocketOption's additional field. Tcp info for
* SOL_TCP and TCP_INFO.
*/
export interface SocketOptionTcpInfo {
'tcpi_state'?: (number);
'tcpi_ca_state'?: (number);
'tcpi_retransmits'?: (number);
'tcpi_probes'?: (number);
'tcpi_backoff'?: (number);
'tcpi_options'?: (number);
'tcpi_snd_wscale'?: (number);
'tcpi_rcv_wscale'?: (number);
'tcpi_rto'?: (number);
'tcpi_ato'?: (number);
'tcpi_snd_mss'?: (number);
'tcpi_rcv_mss'?: (number);
'tcpi_unacked'?: (number);
'tcpi_sacked'?: (number);
'tcpi_lost'?: (number);
'tcpi_retrans'?: (number);
'tcpi_fackets'?: (number);
'tcpi_last_data_sent'?: (number);
'tcpi_last_ack_sent'?: (number);
'tcpi_last_data_recv'?: (number);
'tcpi_last_ack_recv'?: (number);
'tcpi_pmtu'?: (number);
'tcpi_rcv_ssthresh'?: (number);
'tcpi_rtt'?: (number);
'tcpi_rttvar'?: (number);
'tcpi_snd_ssthresh'?: (number);
'tcpi_snd_cwnd'?: (number);
'tcpi_advmss'?: (number);
'tcpi_reordering'?: (number);
}
/**
* For use with SocketOption's additional field. Tcp info for
* SOL_TCP and TCP_INFO.
*/
export interface SocketOptionTcpInfo__Output {
'tcpi_state': (number);
'tcpi_ca_state': (number);
'tcpi_retransmits': (number);
'tcpi_probes': (number);
'tcpi_backoff': (number);
'tcpi_options': (number);
'tcpi_snd_wscale': (number);
'tcpi_rcv_wscale': (number);
'tcpi_rto': (number);
'tcpi_ato': (number);
'tcpi_snd_mss': (number);
'tcpi_rcv_mss': (number);
'tcpi_unacked': (number);
'tcpi_sacked': (number);
'tcpi_lost': (number);
'tcpi_retrans': (number);
'tcpi_fackets': (number);
'tcpi_last_data_sent': (number);
'tcpi_last_ack_sent': (number);
'tcpi_last_data_recv': (number);
'tcpi_last_ack_recv': (number);
'tcpi_pmtu': (number);
'tcpi_rcv_ssthresh': (number);
'tcpi_rtt': (number);
'tcpi_rttvar': (number);
'tcpi_snd_ssthresh': (number);
'tcpi_snd_cwnd': (number);
'tcpi_advmss': (number);
'tcpi_reordering': (number);
}

View File

@ -0,0 +1,19 @@
// Original file: proto/channelz.proto
import type { Duration as _google_protobuf_Duration, Duration__Output as _google_protobuf_Duration__Output } from '../../../google/protobuf/Duration';
/**
* For use with SocketOption's additional field. This is primarily used for
* SO_RCVTIMEO and SO_SNDTIMEO
*/
export interface SocketOptionTimeout {
'duration'?: (_google_protobuf_Duration | null);
}
/**
* For use with SocketOption's additional field. This is primarily used for
* SO_RCVTIMEO and SO_SNDTIMEO
*/
export interface SocketOptionTimeout__Output {
'duration': (_google_protobuf_Duration__Output | null);
}

View File

@ -0,0 +1,31 @@
// Original file: proto/channelz.proto
import type { Long } from '@grpc/proto-loader';
/**
* SocketRef is a reference to a Socket.
*/
export interface SocketRef {
/**
* The globally unique id for this socket. Must be a positive number.
*/
'socket_id'?: (number | string | Long);
/**
* An optional name associated with the socket.
*/
'name'?: (string);
}
/**
* SocketRef is a reference to a Socket.
*/
export interface SocketRef__Output {
/**
* The globally unique id for this socket. Must be a positive number.
*/
'socket_id': (string);
/**
* An optional name associated with the socket.
*/
'name': (string);
}

View File

@ -0,0 +1,70 @@
// Original file: proto/channelz.proto
import type { SubchannelRef as _grpc_channelz_v1_SubchannelRef, SubchannelRef__Output as _grpc_channelz_v1_SubchannelRef__Output } from '../../../grpc/channelz/v1/SubchannelRef';
import type { ChannelData as _grpc_channelz_v1_ChannelData, ChannelData__Output as _grpc_channelz_v1_ChannelData__Output } from '../../../grpc/channelz/v1/ChannelData';
import type { ChannelRef as _grpc_channelz_v1_ChannelRef, ChannelRef__Output as _grpc_channelz_v1_ChannelRef__Output } from '../../../grpc/channelz/v1/ChannelRef';
import type { SocketRef as _grpc_channelz_v1_SocketRef, SocketRef__Output as _grpc_channelz_v1_SocketRef__Output } from '../../../grpc/channelz/v1/SocketRef';
/**
* Subchannel is a logical grouping of channels, subchannels, and sockets.
* A subchannel is load balanced over by it's ancestor
*/
export interface Subchannel {
/**
* The identifier for this channel.
*/
'ref'?: (_grpc_channelz_v1_SubchannelRef | null);
/**
* Data specific to this channel.
*/
'data'?: (_grpc_channelz_v1_ChannelData | null);
/**
* There are no ordering guarantees on the order of channel refs.
* There may not be cycles in the ref graph.
* A channel ref may be present in more than one channel or subchannel.
*/
'channel_ref'?: (_grpc_channelz_v1_ChannelRef)[];
/**
* At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
* There are no ordering guarantees on the order of subchannel refs.
* There may not be cycles in the ref graph.
* A sub channel ref may be present in more than one channel or subchannel.
*/
'subchannel_ref'?: (_grpc_channelz_v1_SubchannelRef)[];
/**
* There are no ordering guarantees on the order of sockets.
*/
'socket_ref'?: (_grpc_channelz_v1_SocketRef)[];
}
/**
* Subchannel is a logical grouping of channels, subchannels, and sockets.
* A subchannel is load balanced over by it's ancestor
*/
export interface Subchannel__Output {
/**
* The identifier for this channel.
*/
'ref': (_grpc_channelz_v1_SubchannelRef__Output | null);
/**
* Data specific to this channel.
*/
'data': (_grpc_channelz_v1_ChannelData__Output | null);
/**
* There are no ordering guarantees on the order of channel refs.
* There may not be cycles in the ref graph.
* A channel ref may be present in more than one channel or subchannel.
*/
'channel_ref': (_grpc_channelz_v1_ChannelRef__Output)[];
/**
* At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
* There are no ordering guarantees on the order of subchannel refs.
* There may not be cycles in the ref graph.
* A sub channel ref may be present in more than one channel or subchannel.
*/
'subchannel_ref': (_grpc_channelz_v1_SubchannelRef__Output)[];
/**
* There are no ordering guarantees on the order of sockets.
*/
'socket_ref': (_grpc_channelz_v1_SocketRef__Output)[];
}

View File

@ -0,0 +1,31 @@
// Original file: proto/channelz.proto
import type { Long } from '@grpc/proto-loader';
/**
* SubchannelRef is a reference to a Subchannel.
*/
export interface SubchannelRef {
/**
* The globally unique id for this subchannel. Must be a positive number.
*/
'subchannel_id'?: (number | string | Long);
/**
* An optional name associated with the subchannel.
*/
'name'?: (string);
}
/**
* SubchannelRef is a reference to a Subchannel.
*/
export interface SubchannelRef__Output {
/**
* The globally unique id for this subchannel. Must be a positive number.
*/
'subchannel_id': (string);
/**
* An optional name associated with the subchannel.
*/
'name': (string);
}

View File

@ -249,6 +249,13 @@ export { GrpcObject } from './make-client';
export { ChannelOptions } from './channel-options';
export {
getChannelzServiceDefinition,
getChannelzHandlers
} from './channelz';
export { addAdminServicesToServer } from './admin';
import * as experimental from './experimental';
export { experimental };
@ -257,6 +264,7 @@ import * as resolver_uds from './resolver-uds';
import * as resolver_ip from './resolver-ip';
import * as load_balancer_pick_first from './load-balancer-pick-first';
import * as load_balancer_round_robin from './load-balancer-round-robin';
import * as channelz from './channelz';
const clientVersion = require('../../package.json').version;
@ -267,4 +275,5 @@ const clientVersion = require('../../package.json').version;
resolver_ip.setup();
load_balancer_pick_first.setup();
load_balancer_round_robin.setup();
channelz.setup();
})();

View File

@ -26,6 +26,7 @@ import { SubchannelAddress } from './subchannel-address';
import { ChannelOptions } from './channel-options';
import { ConnectivityState } from './connectivity-state';
import { Picker } from './picker';
import { ChannelRef, SubchannelRef } from './channelz';
const TYPE_NAME = 'child_load_balancer_helper';
@ -67,6 +68,13 @@ export class ChildLoadBalancerHandler implements LoadBalancer {
setChild(newChild: LoadBalancer) {
this.child = newChild;
}
addChannelzChild(child: ChannelRef | SubchannelRef) {
this.parent.channelControlHelper.addChannelzChild(child);
}
removeChannelzChild(child: ChannelRef | SubchannelRef) {
this.parent.channelControlHelper.removeChannelzChild(child);
}
private calledByPendingChild(): boolean {
return this.child === this.parent.pendingChild;
}

View File

@ -229,6 +229,7 @@ export class PickFirstLoadBalancer implements LoadBalancer {
subchannel.removeConnectivityStateListener(
this.pickedSubchannelStateListener
);
this.channelControlHelper.removeChannelzChild(subchannel.getChannelzRef());
if (this.subchannels.length > 0) {
if (this.triedAllSubchannels) {
let newLBState: ConnectivityState;
@ -321,6 +322,7 @@ export class PickFirstLoadBalancer implements LoadBalancer {
this.updateState(ConnectivityState.READY, new PickFirstPicker(subchannel));
subchannel.addConnectivityStateListener(this.pickedSubchannelStateListener);
subchannel.ref();
this.channelControlHelper.addChannelzChild(subchannel.getChannelzRef());
this.resetSubchannelList();
clearTimeout(this.connectionDelayTimeout);
}
@ -339,6 +341,7 @@ export class PickFirstLoadBalancer implements LoadBalancer {
for (const subchannel of this.subchannels) {
subchannel.removeConnectivityStateListener(this.subchannelStateListener);
subchannel.unref();
this.channelControlHelper.removeChannelzChild(subchannel.getChannelzRef());
}
this.currentSubchannelIndex = 0;
this.subchannelStateCounts = {
@ -369,6 +372,7 @@ export class PickFirstLoadBalancer implements LoadBalancer {
);
for (const subchannel of this.subchannels) {
subchannel.ref();
this.channelControlHelper.addChannelzChild(subchannel.getChannelzRef());
}
for (const subchannel of this.subchannels) {
subchannel.addConnectivityStateListener(this.subchannelStateListener);
@ -449,6 +453,7 @@ export class PickFirstLoadBalancer implements LoadBalancer {
this.currentPick.removeConnectivityStateListener(
this.pickedSubchannelStateListener
);
this.channelControlHelper.removeChannelzChild(this.currentPick.getChannelzRef());
}
}

View File

@ -191,6 +191,7 @@ export class RoundRobinLoadBalancer implements LoadBalancer {
for (const subchannel of this.subchannels) {
subchannel.removeConnectivityStateListener(this.subchannelStateListener);
subchannel.unref();
this.channelControlHelper.removeChannelzChild(subchannel.getChannelzRef());
}
this.subchannelStateCounts = {
[ConnectivityState.CONNECTING]: 0,
@ -217,6 +218,7 @@ export class RoundRobinLoadBalancer implements LoadBalancer {
for (const subchannel of this.subchannels) {
subchannel.ref();
subchannel.addConnectivityStateListener(this.subchannelStateListener);
this.channelControlHelper.addChannelzChild(subchannel.getChannelzRef());
const subchannelState = subchannel.getConnectivityState();
this.subchannelStateCounts[subchannelState] += 1;
if (

View File

@ -20,6 +20,7 @@ import { Subchannel } from './subchannel';
import { SubchannelAddress } from './subchannel-address';
import { ConnectivityState } from './connectivity-state';
import { Picker } from './picker';
import { ChannelRef, SubchannelRef } from './channelz';
/**
* A collection of functions associated with a channel that a load balancer
@ -47,6 +48,26 @@ export interface ChannelControlHelper {
* Request new data from the resolver.
*/
requestReresolution(): void;
addChannelzChild(child: ChannelRef | SubchannelRef): void;
removeChannelzChild(child: ChannelRef | SubchannelRef): void;
}
/**
* Create a child ChannelControlHelper that overrides some methods of the
* parent while letting others pass through to the parent unmodified. This
* allows other code to create these children without needing to know about
* all of the methods to be passed through.
* @param parent
* @param overrides
*/
export function createChildChannelControlHelper(parent: ChannelControlHelper, overrides: Partial<ChannelControlHelper>): ChannelControlHelper {
return {
createSubchannel: overrides.createSubchannel?.bind(overrides) ?? parent.createSubchannel.bind(parent),
updateState: overrides.updateState?.bind(overrides) ?? parent.updateState.bind(parent),
requestReresolution: overrides.requestReresolution?.bind(overrides) ?? parent.requestReresolution.bind(parent),
addChannelzChild: overrides.addChannelzChild?.bind(overrides) ?? parent.addChannelzChild.bind(parent),
removeChannelzChild: overrides.removeChannelzChild?.bind(overrides) ?? parent.removeChannelzChild.bind(parent)
};
}
/**

View File

@ -172,6 +172,12 @@ export class ResolvingLoadBalancer implements LoadBalancer {
this.latestChildPicker = picker;
this.updateState(newState, picker);
},
addChannelzChild: channelControlHelper.addChannelzChild.bind(
channelControlHelper
),
removeChannelzChild: channelControlHelper.removeChannelzChild.bind(
channelControlHelper
)
});
this.innerResolver = createResolver(
target,

View File

@ -411,6 +411,8 @@ export class Http2ServerCallStream<
);
this.cancelled = true;
this.emit('cancelled', 'cancelled');
this.emit('streamEnd', false);
this.sendStatus({code: Status.CANCELLED, details: 'Cancelled by client', metadata: new Metadata()});
});
this.stream.on('drain', () => {
@ -513,6 +515,7 @@ export class Http2ServerCallStream<
resolve();
}
this.emit('receiveMessage');
resolve(this.deserializeMessage(requestBytes));
} catch (err) {
err.code = Status.INTERNAL;
@ -575,6 +578,8 @@ export class Http2ServerCallStream<
}
sendStatus(statusObj: StatusObject) {
this.emit('callEnd', statusObj.code);
this.emit('streamEnd', statusObj.code === Status.OK);
if (this.checkCancelled()) {
return;
}
@ -609,9 +614,6 @@ export class Http2ServerCallStream<
}
sendError(error: ServerErrorResponse | ServerStatusResponse) {
if (this.checkCancelled()) {
return;
}
const status: StatusObject = {
code: Status.UNKNOWN,
details: 'message' in error ? error.message : 'Unknown Error',
@ -653,6 +655,7 @@ export class Http2ServerCallStream<
}
this.sendMetadata();
this.emit('sendMessage');
return this.stream.write(chunk);
}
@ -688,6 +691,7 @@ export class Http2ServerCallStream<
});
return;
}
this.emit('receiveMessage');
this.pushOrBufferMessage(readable, message);
}
});

View File

@ -56,15 +56,14 @@ import {
TcpSubchannelAddress,
isTcpSubchannelAddress,
subchannelAddressToString,
stringToSubchannelAddress,
} from './subchannel-address';
import { parseUri } from './uri-parser';
import { ChannelzCallTracker, ChannelzChildrenTracker, ChannelzTrace, registerChannelzServer, registerChannelzSocket, ServerInfo, ServerRef, SocketInfo, SocketRef, TlsInfo, unregisterChannelzRef } from './channelz';
import { CipherNameAndProtocol, TLSSocket } from 'tls';
const TRACER_NAME = 'server';
function trace(text: string): void {
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text);
}
interface BindResult {
port: number;
count: number;
@ -125,21 +124,101 @@ function getDefaultHandler(handlerType: HandlerType, methodName: string) {
}
}
interface ChannelzSessionInfo {
ref: SocketRef;
streamTracker: ChannelzCallTracker;
messagesSent: number;
messagesReceived: number;
lastMessageSentTimestamp: Date | null;
lastMessageReceivedTimestamp: Date | null;
}
interface ChannelzListenerInfo {
ref: SocketRef;
}
export class Server {
private http2ServerList: (http2.Http2Server | http2.Http2SecureServer)[] = [];
private http2ServerList: { server: (http2.Http2Server | http2.Http2SecureServer), channelzRef: SocketRef }[] = [];
private handlers: Map<string, UntypedHandler> = new Map<
string,
UntypedHandler
>();
private sessions = new Set<http2.ServerHttp2Session>();
private sessions = new Map<http2.ServerHttp2Session, ChannelzSessionInfo>();
private started = false;
private options: ChannelOptions;
// Channelz Info
private channelzRef: ServerRef;
private channelzTrace = new ChannelzTrace();
private callTracker = new ChannelzCallTracker();
private listenerChildrenTracker = new ChannelzChildrenTracker();
private sessionChildrenTracker = new ChannelzChildrenTracker();
constructor(options?: ChannelOptions) {
this.options = options ?? {};
this.channelzRef = registerChannelzServer(() => this.getChannelzInfo());
this.channelzTrace.addTrace('CT_INFO', 'Server created');
this.trace('Server constructed');
}
private getChannelzInfo(): ServerInfo {
return {
trace: this.channelzTrace,
callTracker: this.callTracker,
listenerChildren: this.listenerChildrenTracker.getChildLists(),
sessionChildren: this.sessionChildrenTracker.getChildLists()
};
}
private getChannelzSessionInfoGetter(session: http2.ServerHttp2Session): () => SocketInfo {
return () => {
const sessionInfo = this.sessions.get(session)!;
const sessionSocket = session.socket;
const remoteAddress = sessionSocket.remoteAddress ? stringToSubchannelAddress(sessionSocket.remoteAddress, sessionSocket.remotePort) : null;
const localAddress = stringToSubchannelAddress(sessionSocket.localAddress, sessionSocket.localPort);
let tlsInfo: TlsInfo | null;
if (session.encrypted) {
const tlsSocket: TLSSocket = sessionSocket as TLSSocket;
const cipherInfo: CipherNameAndProtocol & {standardName?: string} = tlsSocket.getCipher();
const certificate = tlsSocket.getCertificate();
const peerCertificate = tlsSocket.getPeerCertificate();
tlsInfo = {
cipherSuiteStandardName: cipherInfo.standardName ?? null,
cipherSuiteOtherName: cipherInfo.standardName ? null : cipherInfo.name,
localCertificate: (certificate && 'raw' in certificate) ? certificate.raw : null,
remoteCertificate: (peerCertificate && 'raw' in peerCertificate) ? peerCertificate.raw : null
};
} else {
tlsInfo = null;
}
const socketInfo: SocketInfo = {
remoteAddress: remoteAddress,
localAddress: localAddress,
security: tlsInfo,
remoteName: null,
streamsStarted: sessionInfo.streamTracker.callsStarted,
streamsSucceeded: sessionInfo.streamTracker.callsSucceeded,
streamsFailed: sessionInfo.streamTracker.callsFailed,
messagesSent: sessionInfo.messagesSent,
messagesReceived: sessionInfo.messagesReceived,
keepAlivesSent: 0,
lastLocalStreamCreatedTimestamp: null,
lastRemoteStreamCreatedTimestamp: sessionInfo.streamTracker.lastCallStartedTimestamp,
lastMessageSentTimestamp: sessionInfo.lastMessageSentTimestamp,
lastMessageReceivedTimestamp: sessionInfo.lastMessageReceivedTimestamp,
localFlowControlWindow: session.state.localWindowSize ?? null,
remoteFlowControlWindow: session.state.remoteWindowSize ?? null
};
return socketInfo;
};
}
private trace(text: string): void {
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, '(' + this.channelzRef.id + ') ' + text);
}
addProtoService(): void {
throw new Error('Not implemented. Use addService() instead');
}
@ -299,7 +378,7 @@ export class Server {
}
return Promise.all(
addressList.map((address) => {
trace('Attempting to bind ' + subchannelAddressToString(address));
this.trace('Attempting to bind ' + subchannelAddressToString(address));
let addr: SubchannelAddress;
if (isTcpSubchannelAddress(address)) {
addr = {
@ -312,22 +391,50 @@ export class Server {
const http2Server = setupServer();
return new Promise<number | Error>((resolve, reject) => {
function onError(err: Error): void {
trace('Failed to bind ' + subchannelAddressToString(address) + ' with error ' + err.message);
const onError = (err: Error) => {
this.trace('Failed to bind ' + subchannelAddressToString(address) + ' with error ' + err.message);
resolve(err);
}
http2Server.once('error', onError);
http2Server.listen(addr, () => {
trace('Successfully bound ' + subchannelAddressToString(address));
this.http2ServerList.push(http2Server);
const boundAddress = http2Server.address()!;
let boundSubchannelAddress: SubchannelAddress;
if (typeof boundAddress === 'string') {
resolve(portNum);
boundSubchannelAddress = {
path: boundAddress
};
} else {
resolve(boundAddress.port);
boundSubchannelAddress = {
host: boundAddress.address,
port: boundAddress.port
}
}
const channelzRef = registerChannelzSocket(subchannelAddressToString(boundSubchannelAddress), () => {
return {
localAddress: boundSubchannelAddress,
remoteAddress: null,
security: null,
remoteName: null,
streamsStarted: 0,
streamsSucceeded: 0,
streamsFailed: 0,
messagesSent: 0,
messagesReceived: 0,
keepAlivesSent: 0,
lastLocalStreamCreatedTimestamp: null,
lastRemoteStreamCreatedTimestamp: null,
lastMessageSentTimestamp: null,
lastMessageReceivedTimestamp: null,
localFlowControlWindow: null,
remoteFlowControlWindow: null
};
});
this.listenerChildrenTracker.refChild(channelzRef);
this.http2ServerList.push({server: http2Server, channelzRef: channelzRef});
this.trace('Successfully bound ' + subchannelAddressToString(boundSubchannelAddress));
resolve('port' in boundSubchannelAddress ? boundSubchannelAddress.port : portNum);
http2Server.removeListener('error', onError);
});
});
@ -360,19 +467,46 @@ export class Server {
const address = addressList[0];
const http2Server = setupServer();
return new Promise<BindResult>((resolve, reject) => {
function onError(err: Error): void {
trace('Failed to bind ' + subchannelAddressToString(address) + ' with error ' + err.message);
const onError = (err: Error) => {
this.trace('Failed to bind ' + subchannelAddressToString(address) + ' with error ' + err.message);
resolve(bindWildcardPort(addressList.slice(1)));
}
http2Server.once('error', onError);
http2Server.listen(address, () => {
this.http2ServerList.push(http2Server);
const boundAddress = http2Server.address() as AddressInfo;
const boundSubchannelAddress: SubchannelAddress = {
host: boundAddress.address,
port: boundAddress.port
};
const channelzRef = registerChannelzSocket(subchannelAddressToString(boundSubchannelAddress), () => {
return {
localAddress: boundSubchannelAddress,
remoteAddress: null,
security: null,
remoteName: null,
streamsStarted: 0,
streamsSucceeded: 0,
streamsFailed: 0,
messagesSent: 0,
messagesReceived: 0,
keepAlivesSent: 0,
lastLocalStreamCreatedTimestamp: null,
lastRemoteStreamCreatedTimestamp: null,
lastMessageSentTimestamp: null,
lastMessageReceivedTimestamp: null,
localFlowControlWindow: null,
remoteFlowControlWindow: null
};
});
this.listenerChildrenTracker.refChild(channelzRef);
this.http2ServerList.push({server: http2Server, channelzRef: channelzRef});
this.trace('Successfully bound ' + subchannelAddressToString(boundSubchannelAddress));
resolve(
bindSpecificPort(
addressList.slice(1),
(http2Server.address() as AddressInfo).port,
boundAddress.port,
1
)
);
@ -443,9 +577,12 @@ export class Server {
forceShutdown(): void {
// Close the server if it is still running.
for (const http2Server of this.http2ServerList) {
for (const {server: http2Server, channelzRef: ref} of this.http2ServerList) {
if (http2Server.listening) {
http2Server.close();
http2Server.close(() => {
this.listenerChildrenTracker.unrefChild(ref);
unregisterChannelzRef(ref);
});
}
}
@ -453,13 +590,14 @@ export class Server {
// Always destroy any available sessions. It's possible that one or more
// tryShutdown() calls are in progress. Don't wait on them to finish.
this.sessions.forEach((session) => {
this.sessions.forEach((channelzInfo, session) => {
// Cast NGHTTP2_CANCEL to any because TypeScript doesn't seem to
// recognize destroy(code) as a valid signature.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
session.destroy(http2.constants.NGHTTP2_CANCEL as any);
});
this.sessions.clear();
unregisterChannelzRef(this.channelzRef);
}
register<RequestType, ResponseType>(
@ -491,7 +629,7 @@ export class Server {
if (
this.http2ServerList.length === 0 ||
this.http2ServerList.every(
(http2Server) => http2Server.listening !== true
({server: http2Server}) => http2Server.listening !== true
)
) {
throw new Error('server must be bound in order to start');
@ -500,39 +638,47 @@ export class Server {
if (this.started === true) {
throw new Error('server is already started');
}
this.channelzTrace.addTrace('CT_INFO', 'Starting');
this.started = true;
}
tryShutdown(callback: (error?: Error) => void): void {
const wrappedCallback = (error?: Error) => {
unregisterChannelzRef(this.channelzRef);
callback(error);
};
let pendingChecks = 0;
function maybeCallback(): void {
pendingChecks--;
if (pendingChecks === 0) {
callback();
wrappedCallback();
}
}
// Close the server if necessary.
this.started = false;
for (const http2Server of this.http2ServerList) {
for (const {server: http2Server, channelzRef: ref} of this.http2ServerList) {
if (http2Server.listening) {
pendingChecks++;
http2Server.close(maybeCallback);
http2Server.close(() => {
this.listenerChildrenTracker.unrefChild(ref);
unregisterChannelzRef(ref);
maybeCallback();
});
}
}
this.sessions.forEach((session) => {
this.sessions.forEach((channelzInfo, session) => {
if (!session.closed) {
pendingChecks += 1;
session.close(maybeCallback);
}
});
if (pendingChecks === 0) {
callback();
wrappedCallback();
}
}
@ -540,6 +686,10 @@ export class Server {
throw new Error('Not yet implemented');
}
getChannelzRef() {
return this.channelzRef;
}
private _setupHandlers(
http2Server: http2.Http2Server | http2.Http2SecureServer
): void {
@ -550,6 +700,9 @@ export class Server {
http2Server.on(
'stream',
(stream: http2.ServerHttp2Stream, headers: http2.IncomingHttpHeaders) => {
const channelzSessionInfo = this.sessions.get(stream.session as http2.ServerHttp2Session);
this.callTracker.addCallStarted();
channelzSessionInfo?.streamTracker.addCallStarted();
const contentType = headers[http2.constants.HTTP2_HEADER_CONTENT_TYPE];
if (
@ -563,9 +716,13 @@ export class Server {
},
{ endStream: true }
);
this.callTracker.addCallFailed();
channelzSessionInfo?.streamTracker.addCallFailed();
return;
}
let call: Http2ServerCallStream<any, any> | null = null;
try {
const path = headers[http2.constants.HTTP2_HEADER_PATH] as string;
const serverAddress = http2Server.address();
@ -578,7 +735,7 @@ export class Server {
serverAddress.address + ':' + serverAddress.port;
}
}
trace(
this.trace(
'Received call to method ' +
path +
' at address ' +
@ -587,7 +744,7 @@ export class Server {
const handler = this.handlers.get(path);
if (handler === undefined) {
trace(
this.trace(
'No handler registered for method ' +
path +
'. Sending UNIMPLEMENTED status.'
@ -595,7 +752,31 @@ export class Server {
throw getUnimplementedStatusResponse(path);
}
const call = new Http2ServerCallStream(stream, handler, this.options);
call = new Http2ServerCallStream(stream, handler, this.options);
call.once('callEnd', (code: Status) => {
if (code === Status.OK) {
this.callTracker.addCallSucceeded();
} else {
this.callTracker.addCallFailed();
}
});
if (channelzSessionInfo) {
call.once('streamEnd', (success: boolean) => {
if (success) {
channelzSessionInfo.streamTracker.addCallSucceeded();
} else {
channelzSessionInfo.streamTracker.addCallFailed();
}
});
call.on('sendMessage', () => {
channelzSessionInfo.messagesSent += 1;
channelzSessionInfo.lastMessageSentTimestamp = new Date();
});
call.on('receiveMessage', () => {
channelzSessionInfo.messagesReceived += 1;
channelzSessionInfo.lastMessageReceivedTimestamp = new Date();
});
}
const metadata: Metadata = call.receiveMetadata(headers) as Metadata;
switch (handler.type) {
case 'unary':
@ -626,7 +807,11 @@ export class Server {
throw new Error(`Unknown handler type: ${handler.type}`);
}
} catch (err) {
const call = new Http2ServerCallStream(stream, null!, this.options);
if (!call) {
call = new Http2ServerCallStream(stream, null!, this.options);
this.callTracker.addCallFailed();
channelzSessionInfo?.streamTracker.addCallFailed()
}
if (err.code === undefined) {
err.code = Status.INTERNAL;
@ -643,9 +828,25 @@ export class Server {
return;
}
this.sessions.add(session);
const channelzRef = registerChannelzSocket(session.socket.remoteAddress ?? 'unknown', this.getChannelzSessionInfoGetter(session));
const channelzSessionInfo: ChannelzSessionInfo = {
ref: channelzRef,
streamTracker: new ChannelzCallTracker(),
messagesSent: 0,
messagesReceived: 0,
lastMessageSentTimestamp: null,
lastMessageReceivedTimestamp: null
};
this.sessions.set(session, channelzSessionInfo);
const clientAddress = session.socket.remoteAddress;
this.channelzTrace.addTrace('CT_INFO', 'Connection established by client ' + clientAddress);
this.sessionChildrenTracker.refChild(channelzRef);
session.on('close', () => {
this.channelzTrace.addTrace('CT_INFO', 'Connection dropped by client ' + clientAddress);
this.sessionChildrenTracker.unrefChild(channelzRef);
unregisterChannelzRef(channelzRef);
this.sessions.delete(session);
});
});

View File

@ -15,6 +15,8 @@
*
*/
import { isIP } from "net";
export interface TcpSubchannelAddress {
port: number;
host: string;
@ -60,3 +62,18 @@ export function subchannelAddressToString(address: SubchannelAddress): string {
return address.path;
}
}
const DEFAULT_PORT = 443;
export function stringToSubchannelAddress(addressString: string, port?: number): SubchannelAddress {
if (isIP(addressString)) {
return {
host: addressString,
port: port ?? DEFAULT_PORT
};
} else {
return {
path: addressString
};
}
}

View File

@ -18,36 +18,30 @@
import * as http2 from 'http2';
import { ChannelCredentials } from './channel-credentials';
import { Metadata } from './metadata';
import { Http2CallStream } from './call-stream';
import { Call, Http2CallStream, WriteObject } from './call-stream';
import { ChannelOptions } from './channel-options';
import { PeerCertificate, checkServerIdentity } from 'tls';
import { PeerCertificate, checkServerIdentity, TLSSocket, CipherNameAndProtocol } from 'tls';
import { ConnectivityState } from './connectivity-state';
import { BackoffTimeout, BackoffOptions } from './backoff-timeout';
import { getDefaultAuthority } from './resolver';
import * as logging from './logging';
import { LogVerbosity } from './constants';
import { LogVerbosity, Status } from './constants';
import { getProxiedConnection, ProxyConnectionResult } from './http_proxy';
import * as net from 'net';
import { GrpcUri, parseUri, splitHostPort, uriToString } from './uri-parser';
import { ConnectionOptions } from 'tls';
import { FilterFactory, Filter } from './filter';
import { FilterFactory, Filter, BaseFilter } from './filter';
import {
stringToSubchannelAddress,
SubchannelAddress,
subchannelAddressToString,
} from './subchannel-address';
import { SubchannelRef, ChannelzTrace, ChannelzChildrenTracker, SubchannelInfo, registerChannelzSubchannel, ChannelzCallTracker, SocketInfo, SocketRef, unregisterChannelzRef, registerChannelzSocket, TlsInfo } from './channelz';
const clientVersion = require('../../package.json').version;
const TRACER_NAME = 'subchannel';
function trace(text: string): void {
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text);
}
function refTrace(text: string): void {
logging.trace(LogVerbosity.DEBUG, 'subchannel_refcount', text);
}
const MIN_CONNECT_TIMEOUT_MS = 20000;
const INITIAL_BACKOFF_MS = 1000;
const BACKOFF_MULTIPLIER = 1.6;
@ -66,6 +60,11 @@ export type ConnectivityStateListener = (
newState: ConnectivityState
) => void;
export interface SubchannelCallStatsTracker {
addMessageSent(): void;
addMessageReceived(): void;
}
const {
HTTP2_HEADER_AUTHORITY,
HTTP2_HEADER_CONTENT_TYPE,
@ -157,6 +156,26 @@ export class Subchannel {
*/
private subchannelAddressString: string;
// Channelz info
private channelzRef: SubchannelRef;
private channelzTrace: ChannelzTrace;
private callTracker = new ChannelzCallTracker();
private childrenTracker = new ChannelzChildrenTracker();
// Channelz socket info
private channelzSocketRef: SocketRef | null = null;
/**
* Name of the remote server, if it is not the same as the subchannel
* address, i.e. if connecting through an HTTP CONNECT proxy.
*/
private remoteName: string | null = null;
private streamTracker = new ChannelzCallTracker();
private keepalivesSent = 0;
private messagesSent = 0;
private messagesReceived = 0;
private lastMessageSentTimestamp: Date | null = null;
private lastMessageReceivedTimestamp: Date | null = null;
/**
* A class representing a connection to a single backend.
* @param channelTarget The target string for the channel as a whole
@ -206,6 +225,87 @@ export class Subchannel {
this.handleBackoffTimer();
}, backoffOptions);
this.subchannelAddressString = subchannelAddressToString(subchannelAddress);
this.channelzRef = registerChannelzSubchannel(this.subchannelAddressString, () => this.getChannelzInfo());
this.channelzTrace = new ChannelzTrace();
this.channelzTrace.addTrace('CT_INFO', 'Subchannel created');
this.trace('Subchannel constructed');
}
private getChannelzInfo(): SubchannelInfo {
return {
state: this.connectivityState,
trace: this.channelzTrace,
callTracker: this.callTracker,
children: this.childrenTracker.getChildLists(),
target: this.subchannelAddressString
};
}
private getChannelzSocketInfo(): SocketInfo | null {
if (this.session === null) {
return null;
}
const sessionSocket = this.session.socket;
const remoteAddress = sessionSocket.remoteAddress ? stringToSubchannelAddress(sessionSocket.remoteAddress, sessionSocket.remotePort) : null;
const localAddress = stringToSubchannelAddress(sessionSocket.localAddress, sessionSocket.localPort);
let tlsInfo: TlsInfo | null;
if (this.session.encrypted) {
const tlsSocket: TLSSocket = sessionSocket as TLSSocket;
const cipherInfo: CipherNameAndProtocol & {standardName?: string} = tlsSocket.getCipher();
const certificate = tlsSocket.getCertificate();
const peerCertificate = tlsSocket.getPeerCertificate();
tlsInfo = {
cipherSuiteStandardName: cipherInfo.standardName ?? null,
cipherSuiteOtherName: cipherInfo.standardName ? null : cipherInfo.name,
localCertificate: (certificate && 'raw' in certificate) ? certificate.raw : null,
remoteCertificate: (peerCertificate && 'raw' in peerCertificate) ? peerCertificate.raw : null
};
} else {
tlsInfo = null;
}
const socketInfo: SocketInfo = {
remoteAddress: remoteAddress,
localAddress: localAddress,
security: tlsInfo,
remoteName: this.remoteName,
streamsStarted: this.streamTracker.callsStarted,
streamsSucceeded: this.streamTracker.callsSucceeded,
streamsFailed: this.streamTracker.callsFailed,
messagesSent: this.messagesSent,
messagesReceived: this.messagesReceived,
keepAlivesSent: this.keepalivesSent,
lastLocalStreamCreatedTimestamp: this.streamTracker.lastCallStartedTimestamp,
lastRemoteStreamCreatedTimestamp: null,
lastMessageSentTimestamp: this.lastMessageSentTimestamp,
lastMessageReceivedTimestamp: this.lastMessageReceivedTimestamp,
localFlowControlWindow: this.session.state.localWindowSize ?? null,
remoteFlowControlWindow: this.session.state.remoteWindowSize ?? null
};
return socketInfo;
}
private resetChannelzSocketInfo() {
if (this.channelzSocketRef) {
unregisterChannelzRef(this.channelzSocketRef);
this.childrenTracker.unrefChild(this.channelzSocketRef);
this.channelzSocketRef = null;
}
this.remoteName = null;
this.streamTracker = new ChannelzCallTracker();
this.keepalivesSent = 0;
this.messagesSent = 0;
this.messagesReceived = 0;
this.lastMessageSentTimestamp = null;
this.lastMessageReceivedTimestamp = null;
}
private trace(text: string): void {
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, '(' + this.channelzRef.id + ') ' + this.subchannelAddressString + ' ' + text);
}
private refTrace(text: string): void {
logging.trace(LogVerbosity.DEBUG, 'subchannel_refcount', '(' + this.channelzRef.id + ') ' + this.subchannelAddressString + ' ' + text);
}
private handleBackoffTimer() {
@ -235,10 +335,12 @@ export class Subchannel {
}
private sendPing() {
this.keepalivesSent += 1;
logging.trace(
LogVerbosity.DEBUG,
'keepalive',
'Sending ping to ' + this.subchannelAddressString
'(' + this.channelzRef.id + ') ' + this.subchannelAddressString + ' ' +
'Sending ping'
);
this.keepaliveTimeoutId = setTimeout(() => {
this.transitionToState([ConnectivityState.READY], ConnectivityState.IDLE);
@ -267,9 +369,11 @@ export class Subchannel {
private createSession(proxyConnectionResult: ProxyConnectionResult) {
if (proxyConnectionResult.realTarget) {
trace(this.subchannelAddressString + ' creating HTTP/2 session through proxy to ' + proxyConnectionResult.realTarget);
this.remoteName = uriToString(proxyConnectionResult.realTarget);
this.trace('creating HTTP/2 session through proxy to ' + proxyConnectionResult.realTarget);
} else {
trace(this.subchannelAddressString + ' creating HTTP/2 session');
this.remoteName = null;
this.trace('creating HTTP/2 session');
}
const targetAuthority = getDefaultAuthority(
proxyConnectionResult.realTarget ?? this.channelTarget
@ -358,6 +462,8 @@ export class Subchannel {
connectionOptions
);
this.session = session;
this.channelzSocketRef = registerChannelzSocket(this.subchannelAddressString, () => this.getChannelzSocketInfo()!);
this.childrenTracker.refChild(this.channelzSocketRef);
session.unref();
/* For all of these events, check if the session at the time of the event
* is the same one currently attached to this subchannel, to ensure that
@ -373,7 +479,7 @@ export class Subchannel {
});
session.once('close', () => {
if (this.session === session) {
trace(this.subchannelAddressString + ' connection closed');
this.trace('connection closed');
this.transitionToState(
[ConnectivityState.CONNECTING],
ConnectivityState.TRANSIENT_FAILURE
@ -410,9 +516,8 @@ export class Subchannel {
} ms`
);
}
trace(
this.subchannelAddressString +
' connection closed by GOAWAY with code ' +
this.trace(
'connection closed by GOAWAY with code ' +
errorCode
);
this.transitionToState(
@ -425,12 +530,12 @@ export class Subchannel {
session.once('error', (error) => {
/* Do nothing here. Any error should also trigger a close event, which is
* where we want to handle that. */
trace(
this.subchannelAddressString +
' connection closed with error ' +
this.trace(
'connection closed with error ' +
(error as Error).message
);
});
registerChannelzSocket(this.subchannelAddressString, () => this.getChannelzSocketInfo()!);
}
private startConnectingInternal() {
@ -505,13 +610,12 @@ export class Subchannel {
if (oldStates.indexOf(this.connectivityState) === -1) {
return false;
}
trace(
this.subchannelAddressString +
' ' +
this.trace(
ConnectivityState[this.connectivityState] +
' -> ' +
ConnectivityState[newState]
);
this.channelzTrace.addTrace('CT_INFO', ConnectivityState[this.connectivityState] + ' -> ' + ConnectivityState[newState]);
const previousState = this.connectivityState;
this.connectivityState = newState;
switch (newState) {
@ -536,6 +640,7 @@ export class Subchannel {
this.session.close();
}
this.session = null;
this.resetChannelzSocketInfo();
this.stopKeepalivePings();
/* If the backoff timer has already ended by the time we get to the
* TRANSIENT_FAILURE state, we want to immediately transition out of
@ -551,6 +656,7 @@ export class Subchannel {
this.session.close();
}
this.session = null;
this.resetChannelzSocketInfo();
this.stopKeepalivePings();
break;
default:
@ -572,17 +678,18 @@ export class Subchannel {
/* If no calls, channels, or subchannel pools have any more references to
* this subchannel, we can be sure it will never be used again. */
if (this.callRefcount === 0 && this.refcount === 0) {
this.channelzTrace.addTrace('CT_INFO', 'Shutting down');
this.transitionToState(
[ConnectivityState.CONNECTING, ConnectivityState.READY],
ConnectivityState.TRANSIENT_FAILURE
);
unregisterChannelzRef(this.channelzRef);
}
}
callRef() {
refTrace(
this.subchannelAddressString +
' callRefcount ' +
this.refTrace(
'callRefcount ' +
this.callRefcount +
' -> ' +
(this.callRefcount + 1)
@ -600,9 +707,8 @@ export class Subchannel {
}
callUnref() {
refTrace(
this.subchannelAddressString +
' callRefcount ' +
this.refTrace(
'callRefcount ' +
this.callRefcount +
' -> ' +
(this.callRefcount - 1)
@ -621,9 +727,8 @@ export class Subchannel {
}
ref() {
refTrace(
this.subchannelAddressString +
' refcount ' +
this.refTrace(
'refcount ' +
this.refcount +
' -> ' +
(this.refcount + 1)
@ -632,9 +737,8 @@ export class Subchannel {
}
unref() {
refTrace(
this.subchannelAddressString +
' refcount ' +
this.refTrace(
'refcount ' +
this.refcount +
' -> ' +
(this.refcount - 1)
@ -696,11 +800,39 @@ export class Subchannel {
LogVerbosity.DEBUG,
'call_stream',
'Starting stream on subchannel ' +
'(' + this.channelzRef.id + ') ' +
this.subchannelAddressString +
' with headers\n' +
headersString
);
callStream.attachHttp2Stream(http2Stream, this, extraFilters);
this.callTracker.addCallStarted();
callStream.addStatusWatcher(status => {
if (status.code === Status.OK) {
this.callTracker.addCallSucceeded();
} else {
this.callTracker.addCallFailed();
}
});
const streamSession = this.session;
this.streamTracker.addCallStarted();
callStream.addStreamEndWatcher(success => {
if (streamSession === this.session) {
if (success) {
this.streamTracker.addCallSucceeded();
} else {
this.streamTracker.addCallFailed();
}
}
});
callStream.attachHttp2Stream(http2Stream, this, extraFilters, {
addMessageSent: () => {
this.messagesSent += 1;
this.lastMessageSentTimestamp = new Date();
},
addMessageReceived: () => {
this.messagesReceived += 1;
}
});
}
/**
@ -779,4 +911,8 @@ export class Subchannel {
getAddress(): string {
return this.subchannelAddressString;
}
getChannelzRef(): SubchannelRef {
return this.channelzRef;
}
}

View File

@ -0,0 +1,289 @@
/*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import * as assert from 'assert';
import * as protoLoader from '@grpc/proto-loader';
import * as grpc from '../src';
import { ProtoGrpcType } from '../src/generated/channelz'
import { ChannelzClient } from '../src/generated/grpc/channelz/v1/Channelz';
import { Channel__Output } from '../src/generated/grpc/channelz/v1/Channel';
import { Server__Output } from '../src/generated/grpc/channelz/v1/Server';
import { ServiceClient, ServiceClientConstructor } from '../src/make-client';
import { loadProtoFile } from './common';
const loadedChannelzProto = protoLoader.loadSync('channelz.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: [
`${__dirname}/../../proto`
]
});
const channelzGrpcObject = grpc.loadPackageDefinition(loadedChannelzProto) as unknown as ProtoGrpcType;
const TestServiceClient = loadProtoFile(`${__dirname}/fixtures/test_service.proto`).TestService as ServiceClientConstructor;
const testServiceImpl: grpc.UntypedServiceImplementation = {
unary(call: grpc.ServerUnaryCall<any, any>, callback: grpc.sendUnaryData<any>) {
if (call.request.error) {
setTimeout(() => {
callback({
code: grpc.status.INVALID_ARGUMENT,
details: call.request.message
});
}, call.request.errorAfter)
} else {
callback(null, {count: 1});
}
}
}
describe('Channelz', () => {
let channelzServer: grpc.Server;
let channelzClient: ChannelzClient;
let testServer: grpc.Server;
let testClient: ServiceClient;
before((done) => {
channelzServer = new grpc.Server();
channelzServer.addService(grpc.getChannelzServiceDefinition(), grpc.getChannelzHandlers());
channelzServer.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) {
done(error);
return;
}
channelzServer.start();
channelzClient = new channelzGrpcObject.grpc.channelz.v1.Channelz(`localhost:${port}`, grpc.credentials.createInsecure());
done();
});
});
after(() => {
channelzClient.close();
channelzServer.forceShutdown();
});
beforeEach((done) => {
testServer = new grpc.Server();
testServer.addService(TestServiceClient.service, testServiceImpl);
testServer.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) {
done(error);
return;
}
testServer.start();
testClient = new TestServiceClient(`localhost:${port}`, grpc.credentials.createInsecure());
done();
});
});
afterEach(() => {
testClient.close();
testServer.forceShutdown();
});
it('should see a newly created channel', (done) => {
// Test that the specific test client channel info can be retrieved
channelzClient.GetChannel({channel_id: testClient.getChannel().getChannelzRef().id}, (error, result) => {
assert.ifError(error);
assert(result);
assert(result.channel);
assert(result.channel.ref);
assert.strictEqual(+result.channel.ref.channel_id, testClient.getChannel().getChannelzRef().id);
// Test that the channel is in the list of top channels
channelzClient.getTopChannels({start_channel_id: testClient.getChannel().getChannelzRef().id, max_results:1}, (error, result) => {
assert.ifError(error);
assert(result);
assert.strictEqual(result.channel.length, 1);
assert(result.channel[0].ref);
assert.strictEqual(+result.channel[0].ref.channel_id, testClient.getChannel().getChannelzRef().id);
done();
});
});
});
it('should see a newly created server', (done) => {
// Test that the specific test server info can be retrieved
channelzClient.getServer({server_id: testServer.getChannelzRef().id}, (error, result) => {
assert.ifError(error);
assert(result);
assert(result.server);
assert(result.server.ref);
assert.strictEqual(+result.server.ref.server_id, testServer.getChannelzRef().id);
// Test that the server is in the list of servers
channelzClient.getServers({start_server_id: testServer.getChannelzRef().id, max_results: 1}, (error, result) => {
assert.ifError(error);
assert(result);
assert.strictEqual(result.server.length, 1);
assert(result.server[0].ref);
assert.strictEqual(+result.server[0].ref.server_id, testServer.getChannelzRef().id);
done();
});
});
});
it('should count successful calls', (done) => {
testClient.unary({}, (error: grpc.ServiceError, value: unknown) => {
assert.ifError(error);
// Channel data tests
channelzClient.GetChannel({channel_id: testClient.getChannel().getChannelzRef().id}, (error, channelResult) => {
assert.ifError(error);
assert(channelResult);
assert(channelResult.channel);
assert(channelResult.channel.ref);
assert(channelResult.channel.data);
assert.strictEqual(+channelResult.channel.data.calls_started, 1);
assert.strictEqual(+channelResult.channel.data.calls_succeeded, 1);
assert.strictEqual(+channelResult.channel.data.calls_failed, 0);
assert.strictEqual(channelResult.channel.subchannel_ref.length, 1);
channelzClient.getSubchannel({subchannel_id: channelResult.channel.subchannel_ref[0].subchannel_id}, (error, subchannelResult) => {
assert.ifError(error);
assert(subchannelResult);
assert(subchannelResult.subchannel);
assert(subchannelResult.subchannel.ref);
assert(subchannelResult.subchannel.data);
assert.strictEqual(subchannelResult.subchannel.ref.subchannel_id, channelResult.channel!.subchannel_ref[0].subchannel_id);
assert.strictEqual(+subchannelResult.subchannel.data.calls_started, 1);
assert.strictEqual(+subchannelResult.subchannel.data.calls_succeeded, 1);
assert.strictEqual(+subchannelResult.subchannel.data.calls_failed, 0);
assert.strictEqual(subchannelResult.subchannel.socket_ref.length, 1);
channelzClient.getSocket({socket_id: subchannelResult.subchannel.socket_ref[0].socket_id}, (error, socketResult) => {
assert.ifError(error);
assert(socketResult);
assert(socketResult.socket);
assert(socketResult.socket.ref);
assert(socketResult.socket.data);
assert.strictEqual(socketResult.socket.ref.socket_id, subchannelResult.subchannel!.socket_ref[0].socket_id);
assert.strictEqual(+socketResult.socket.data.streams_started, 1);
assert.strictEqual(+socketResult.socket.data.streams_succeeded, 1);
assert.strictEqual(+socketResult.socket.data.streams_failed, 0);
assert.strictEqual(+socketResult.socket.data.messages_received, 1);
assert.strictEqual(+socketResult.socket.data.messages_sent, 1);
// Server data tests
channelzClient.getServer({server_id: testServer.getChannelzRef().id}, (error, serverResult) => {
assert.ifError(error);
assert(serverResult);
assert(serverResult.server);
assert(serverResult.server.ref);
assert(serverResult.server.data);
assert.strictEqual(+serverResult.server.ref.server_id, testServer.getChannelzRef().id);
assert.strictEqual(+serverResult.server.data.calls_started, 1);
assert.strictEqual(+serverResult.server.data.calls_succeeded, 1);
assert.strictEqual(+serverResult.server.data.calls_failed, 0);
channelzClient.getServerSockets({server_id: testServer.getChannelzRef().id}, (error, socketsResult) => {
assert.ifError(error);
assert(socketsResult);
assert.strictEqual(socketsResult.socket_ref.length, 1);
channelzClient.getSocket({socket_id: socketsResult.socket_ref[0].socket_id}, (error, serverSocketResult) => {
assert.ifError(error);
assert(serverSocketResult);
assert(serverSocketResult.socket);
assert(serverSocketResult.socket.ref);
assert(serverSocketResult.socket.data);
assert.strictEqual(serverSocketResult.socket.ref.socket_id, socketsResult.socket_ref[0].socket_id);
assert.strictEqual(+serverSocketResult.socket.data.streams_started, 1);
assert.strictEqual(+serverSocketResult.socket.data.streams_succeeded, 1);
assert.strictEqual(+serverSocketResult.socket.data.streams_failed, 0);
assert.strictEqual(+serverSocketResult.socket.data.messages_received, 1);
assert.strictEqual(+serverSocketResult.socket.data.messages_sent, 1);
done();
});
});
});
});
});
});
});
});
it('should count failed calls', (done) => {
testClient.unary({error: true}, (error: grpc.ServiceError, value: unknown) => {
assert(error);
// Channel data tests
channelzClient.GetChannel({channel_id: testClient.getChannel().getChannelzRef().id}, (error, channelResult) => {
assert.ifError(error);
assert(channelResult);
assert(channelResult.channel);
assert(channelResult.channel.ref);
assert(channelResult.channel.data);
assert.strictEqual(+channelResult.channel.data.calls_started, 1);
assert.strictEqual(+channelResult.channel.data.calls_succeeded, 0);
assert.strictEqual(+channelResult.channel.data.calls_failed, 1);
assert.strictEqual(channelResult.channel.subchannel_ref.length, 1);
channelzClient.getSubchannel({subchannel_id: channelResult.channel.subchannel_ref[0].subchannel_id}, (error, subchannelResult) => {
assert.ifError(error);
assert(subchannelResult);
assert(subchannelResult.subchannel);
assert(subchannelResult.subchannel.ref);
assert(subchannelResult.subchannel.data);
assert.strictEqual(subchannelResult.subchannel.ref.subchannel_id, channelResult.channel!.subchannel_ref[0].subchannel_id);
assert.strictEqual(+subchannelResult.subchannel.data.calls_started, 1);
assert.strictEqual(+subchannelResult.subchannel.data.calls_succeeded, 0);
assert.strictEqual(+subchannelResult.subchannel.data.calls_failed, 1);
assert.strictEqual(subchannelResult.subchannel.socket_ref.length, 1);
channelzClient.getSocket({socket_id: subchannelResult.subchannel.socket_ref[0].socket_id}, (error, socketResult) => {
assert.ifError(error);
assert(socketResult);
assert(socketResult.socket);
assert(socketResult.socket.ref);
assert(socketResult.socket.data);
assert.strictEqual(socketResult.socket.ref.socket_id, subchannelResult.subchannel!.socket_ref[0].socket_id);
assert.strictEqual(+socketResult.socket.data.streams_started, 1);
assert.strictEqual(+socketResult.socket.data.streams_succeeded, 1);
assert.strictEqual(+socketResult.socket.data.streams_failed, 0);
assert.strictEqual(+socketResult.socket.data.messages_received, 0);
assert.strictEqual(+socketResult.socket.data.messages_sent, 1);
// Server data tests
channelzClient.getServer({server_id: testServer.getChannelzRef().id}, (error, serverResult) => {
assert.ifError(error);
assert(serverResult);
assert(serverResult.server);
assert(serverResult.server.ref);
assert(serverResult.server.data);
assert.strictEqual(+serverResult.server.ref.server_id, testServer.getChannelzRef().id);
assert.strictEqual(+serverResult.server.data.calls_started, 1);
assert.strictEqual(+serverResult.server.data.calls_succeeded, 0);
assert.strictEqual(+serverResult.server.data.calls_failed, 1);
channelzClient.getServerSockets({server_id: testServer.getChannelzRef().id}, (error, socketsResult) => {
assert.ifError(error);
assert(socketsResult);
assert.strictEqual(socketsResult.socket_ref.length, 1);
channelzClient.getSocket({socket_id: socketsResult.socket_ref[0].socket_id}, (error, serverSocketResult) => {
assert.ifError(error);
assert(serverSocketResult);
assert(serverSocketResult.socket);
assert(serverSocketResult.socket.ref);
assert(serverSocketResult.socket.data);
assert.strictEqual(serverSocketResult.socket.ref.socket_id, socketsResult.socket_ref[0].socket_id);
assert.strictEqual(+serverSocketResult.socket.data.streams_started, 1);
assert.strictEqual(+serverSocketResult.socket.data.streams_succeeded, 0);
assert.strictEqual(+serverSocketResult.socket.data.streams_failed, 1);
assert.strictEqual(+serverSocketResult.socket.data.messages_received, 1);
assert.strictEqual(+serverSocketResult.socket.data.messages_sent, 0);
done();
});
});
});
});
});
});
});
});
});

View File

@ -454,6 +454,38 @@ function generateEnumInterface(formatter: TextFormatter, enumType: Protobuf.Enum
formatter.writeLine('}');
}
/**
* This is a list of methods that are exist in the generic Client class in the
* gRPC libraries. TypeScript has a problem with methods in subclasses with the
* same names as methods in the superclass, but with mismatched APIs. So, we
* avoid generating methods with these names in the service client interfaces.
* We always generate two service client methods per service method: one camel
* cased, and one with the original casing. So we will still generate one
* service client method for any conflicting name.
*
* Technically, at runtime conflicting name in the service client method
* actually shadows the original method, but TypeScript does not have a good
* way to represent that. So this change is not 100% accurate, but it gets the
* generated code to compile.
*
* This is just a list of the methods in the Client class definitions in
* grpc@1.24.11 and @grpc/grpc-js@1.4.0.
*/
const CLIENT_RESERVED_METHOD_NAMES = new Set([
'close',
'getChannel',
'waitForReady',
'makeUnaryRequest',
'makeClientStreamRequest',
'makeServerStreamRequest',
'makeBidiStreamRequest',
'resolveCallInterceptors',
/* These methods are private, but TypeScript is not happy with overriding even
* private methods with mismatched APIs. */
'checkOptionalUnaryResponseArguments',
'checkMetadataAndOptions'
]);
function generateServiceClientInterface(formatter: TextFormatter, serviceType: Protobuf.Service, options: GeneratorOptions) {
if (options.includeComments) {
formatComment(formatter, serviceType.comment);
@ -463,6 +495,9 @@ function generateServiceClientInterface(formatter: TextFormatter, serviceType: P
for (const methodName of Object.keys(serviceType.methods).sort()) {
const method = serviceType.methods[methodName];
for (const name of [methodName, camelCase(methodName)]) {
if (CLIENT_RESERVED_METHOD_NAMES.has(name)) {
continue;
}
if (options.includeComments) {
formatComment(formatter, method.comment);
}

View File

@ -1,6 +1,6 @@
{
"name": "@grpc/proto-loader",
"version": "0.6.4",
"version": "0.6.5",
"author": "Google Inc.",
"contributors": [
{

View File

@ -0,0 +1,73 @@
/*
*
* Copyright 2021 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
'use strict';
require('../fixtures/js_js');
const interopClient = require('../interop/interop_client');
const interopServer = require('../interop/interop_server');
const serverGrpc = require('../any_grpc').server;
const hostOverride = 'foo.test.google.fr';
const testCases = [
'empty_unary',
'large_unary',
'client_streaming',
'server_streaming',
'ping_pong',
'empty_stream',
'cancel_after_begin',
'cancel_after_first_response',
'timeout_on_sleeping_server',
'custom_metadata',
'status_code_and_message',
'special_status_message',
'unimplemented_service',
'unimplemented_method'
];
function getRandomTest() {
return testCases[(Math.random() * testCases.length) | 0];
}
let testCompleteCount = 0;
interopServer.getServer('0', true, (error, result) => {
if (error) {
throw error;
}
const channelzServer = new serverGrpc.Server();
channelzServer.bindAsync('localhost:0', serverGrpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) {
throw error;
}
console.log(`Serving channelz at port ${port}`);
serverGrpc.addAdminServicesToServer(channelzServer);
channelzServer.start();
result.server.start();
setInterval(() => {
interopClient.runTest(`localhost:${result.port}`, hostOverride, getRandomTest(), true, true, () => {
testCompleteCount += 1;
if (testCompleteCount % 100 === 0) {
console.log(`Completed ${testCompleteCount} tests`);
}
});
}, 100);
});
})

View File

@ -200,7 +200,7 @@ function handleHalfDuplex(call) {
* Get a server object bound to the given port
* @param {string} port Port to which to bind
* @param {boolean} tls Indicates that the bound port should use TLS
* @param {function(Error, {{server: Server, port: number}})} callback Callback
* @param {function(Error, {server: Server, port: number})} callback Callback
* to call with result or error
* @param {object?} options Optional additional options to use when
* constructing the server