|
@@ -4,7 +4,11 @@ import { ReportStatus, ColorCode, Message, MessageLog, ConnectionAttribute, Conn
|
|
import { Status } from '@grpc/grpc-js/build/src/constants';
|
|
import { Status } from '@grpc/grpc-js/build/src/constants';
|
|
import { v4 as uuidv4 } from 'uuid'
|
|
import { v4 as uuidv4 } from 'uuid'
|
|
import { message_proto } from './protos/server.proto'
|
|
import { message_proto } from './protos/server.proto'
|
|
|
|
+import { ServerWritableStreamImpl } from '@grpc/grpc-js/build/src/server-call';
|
|
export class GrpcServiceMethod {
|
|
export class GrpcServiceMethod {
|
|
|
|
+ private server: grpc.Server | any
|
|
|
|
+ private messageToBeSendOver: Message | any
|
|
|
|
+ private callRequestsFromRemote: ServerWritableStreamImpl<any, ResponseType>[] = []
|
|
|
|
|
|
public async create(request: ConnectionRequest, connectionAttribute: ConnectionAttribute): Promise<any> {
|
|
public async create(request: ConnectionRequest, connectionAttribute: ConnectionAttribute): Promise<any> {
|
|
// Assuming currently only one client
|
|
// Assuming currently only one client
|
|
@@ -12,36 +16,44 @@ export class GrpcServiceMethod {
|
|
this.createGrpcInstance(request.client.targetServer, { instanceType: 'client' }, connectionAttribute)
|
|
this.createGrpcInstance(request.client.targetServer, { instanceType: 'client' }, connectionAttribute)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // For testing only
|
|
|
|
+ public async shutDownServer(): Promise<string> {
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
+ console.log(`Shutting down servers...`)
|
|
|
|
+ if (this.server) {
|
|
|
|
+ this.callRequestsFromRemote[0].destroy()
|
|
|
|
+ this.callRequestsFromRemote[0].end()
|
|
|
|
+ this.server.forceShutdown()
|
|
|
|
+ let message: string = `Server shut down successfully!`
|
|
|
|
+ resolve(message)
|
|
|
|
+ }
|
|
|
|
+ if (!this.server) {
|
|
|
|
+ let errorMsg: string = `There's no active server here`
|
|
|
|
+ reject(errorMsg)
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
private async generateAdditionalAttributes(connectionAttribute: ConnectionAttribute, clientInfo?: any, localInfo?: any) {
|
|
private async generateAdditionalAttributes(connectionAttribute: ConnectionAttribute, clientInfo?: any, localInfo?: any) {
|
|
if (clientInfo) {
|
|
if (clientInfo) {
|
|
- connectionAttribute.inComing.ChannelID = clientInfo.channelID
|
|
|
|
|
|
+ connectionAttribute.inComing.StreamID = clientInfo.channelID
|
|
connectionAttribute.inComing.PublisherID = clientInfo.publisherID
|
|
connectionAttribute.inComing.PublisherID = clientInfo.publisherID
|
|
connectionAttribute.inComing.SubscriberID = clientInfo.subscriberID
|
|
connectionAttribute.inComing.SubscriberID = clientInfo.subscriberID
|
|
- // let report: any = {
|
|
|
|
- // message: 'Remote Server Communication Established',
|
|
|
|
- // channelID: clientInfo.channelID
|
|
|
|
- // }
|
|
|
|
- // connectionAttribute.connectionStatus.next(report)
|
|
|
|
}
|
|
}
|
|
if (localInfo) {
|
|
if (localInfo) {
|
|
- connectionAttribute.outGoing.ChannelID = localInfo.channelID
|
|
|
|
|
|
+ connectionAttribute.outGoing.StreamID = localInfo.channelID
|
|
connectionAttribute.outGoing.PublisherID = localInfo.publisherID
|
|
connectionAttribute.outGoing.PublisherID = localInfo.publisherID
|
|
connectionAttribute.outGoing.SubscriberID = localInfo.subscriberID
|
|
connectionAttribute.outGoing.SubscriberID = localInfo.subscriberID
|
|
- // let report: any = {
|
|
|
|
- // message: 'Local Server Communication Established',
|
|
|
|
- // channelID: localInfo.channelID
|
|
|
|
- // }
|
|
|
|
- // connectionAttribute.connectionStatus.next(report)
|
|
|
|
}
|
|
}
|
|
- if (connectionAttribute.outGoing.ChannelID && connectionAttribute.inComing.ChannelID) {
|
|
|
|
- connectionAttribute.ConnectionID.local = connectionAttribute.outGoing.ChannelID + connectionAttribute.inComing.ChannelID
|
|
|
|
- connectionAttribute.ConnectionID.remote = connectionAttribute.inComing.ChannelID + connectionAttribute.outGoing.ChannelID
|
|
|
|
|
|
+ if (connectionAttribute.outGoing.StreamID && connectionAttribute.inComing.StreamID) {
|
|
|
|
+ connectionAttribute.ConnectionID.local = connectionAttribute.outGoing.StreamID + connectionAttribute.inComing.StreamID
|
|
|
|
+ connectionAttribute.ConnectionID.remote = connectionAttribute.inComing.StreamID + connectionAttribute.outGoing.StreamID
|
|
let report: ReportStatus = {
|
|
let report: ReportStatus = {
|
|
code: ColorCode.GREEN,
|
|
code: ColorCode.GREEN,
|
|
message: `ConnectionID acquired. Informing Restranmission to release Messages...`,
|
|
message: `ConnectionID acquired. Informing Restranmission to release Messages...`,
|
|
}
|
|
}
|
|
connectionAttribute.connectionStatus.next(report)
|
|
connectionAttribute.connectionStatus.next(report)
|
|
- console.log(connectionAttribute)
|
|
|
|
|
|
+ // console.log(connectionAttribute)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -54,7 +66,6 @@ export class GrpcServiceMethod {
|
|
let statusControl: Subject<ReportStatus> = connectionAttribute.connectionStatus
|
|
let statusControl: Subject<ReportStatus> = connectionAttribute.connectionStatus
|
|
let consecutiveResolutions = 0;
|
|
let consecutiveResolutions = 0;
|
|
let lastResolutionTime = Date.now();
|
|
let lastResolutionTime = Date.now();
|
|
- let alreadyHealthCheck: boolean = false
|
|
|
|
let yellowErrorEmission: boolean = false
|
|
let yellowErrorEmission: boolean = false
|
|
let redErrorEmission: boolean = false
|
|
let redErrorEmission: boolean = false
|
|
|
|
|
|
@@ -67,7 +78,7 @@ export class GrpcServiceMethod {
|
|
})
|
|
})
|
|
}
|
|
}
|
|
if (grpcType.instanceType == 'client') {
|
|
if (grpcType.instanceType == 'client') {
|
|
- this.createServerStreamingClient(serverUrl, alreadyHealthCheck, connectionAttribute).then(() => {
|
|
|
|
|
|
+ this.createServerStreamingClient(serverUrl, connectionAttribute).then(() => {
|
|
resolve('recreate')
|
|
resolve('recreate')
|
|
})
|
|
})
|
|
}
|
|
}
|
|
@@ -76,26 +87,16 @@ export class GrpcServiceMethod {
|
|
// If connection resolves (indicating failure), increment the count
|
|
// If connection resolves (indicating failure), increment the count
|
|
consecutiveResolutions++;
|
|
consecutiveResolutions++;
|
|
// console.log(`Reconnection Attempt: ${consecutiveResolutions}`)
|
|
// console.log(`Reconnection Attempt: ${consecutiveResolutions}`)
|
|
- alreadyHealthCheck = true
|
|
|
|
if (redErrorEmission == false) {
|
|
if (redErrorEmission == false) {
|
|
redErrorEmission = true
|
|
redErrorEmission = true
|
|
// console.error(`Connection failed ${consecutiveResolutions} times. Stopping connection attempts.`);
|
|
// console.error(`Connection failed ${consecutiveResolutions} times. Stopping connection attempts.`);
|
|
let error: ReportStatus = {
|
|
let error: ReportStatus = {
|
|
- code: ColorCode.RED,
|
|
|
|
|
|
+ code: ColorCode.YELLOW,
|
|
message: 'Server is not responding. Proceed to buffer.',
|
|
message: 'Server is not responding. Proceed to buffer.',
|
|
}
|
|
}
|
|
statusControl.next(error)
|
|
statusControl.next(error)
|
|
}
|
|
}
|
|
- // Comment it out if Client wishes to use YELLOW for memory buffer instead of persistent storage buffer
|
|
|
|
- // if (consecutiveResolutions < parseInt(process.env.ReconnectionAttempt as string) && yellowErrorEmission == false) {
|
|
|
|
- // yellowErrorEmission = true
|
|
|
|
- // let error: ReportStatus = {
|
|
|
|
- // code: ColorCode.YELLOW,
|
|
|
|
- // // message: `Reconnection Attempt: ${consecutiveResolutions}. Server has yet to respond`
|
|
|
|
- // message: `Attempting reconnection... Server has yet to respond`,
|
|
|
|
- // }
|
|
|
|
- // statusControl.next(error);
|
|
|
|
- // }
|
|
|
|
|
|
+
|
|
} catch (error) {
|
|
} catch (error) {
|
|
// Connection did not resolve, reset the count
|
|
// Connection did not resolve, reset the count
|
|
consecutiveResolutions = 0;
|
|
consecutiveResolutions = 0;
|
|
@@ -108,7 +109,6 @@ export class GrpcServiceMethod {
|
|
consecutiveResolutions = 0;
|
|
consecutiveResolutions = 0;
|
|
yellowErrorEmission = false
|
|
yellowErrorEmission = false
|
|
redErrorEmission = false
|
|
redErrorEmission = false
|
|
- alreadyHealthCheck = false
|
|
|
|
}
|
|
}
|
|
// Update the last resolution time
|
|
// Update the last resolution time
|
|
lastResolutionTime = currentTime;
|
|
lastResolutionTime = currentTime;
|
|
@@ -116,6 +116,7 @@ export class GrpcServiceMethod {
|
|
// timeout generate message to trigger this reconnection
|
|
// timeout generate message to trigger this reconnection
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
// Create Server Instance to stream all application Outgoing messages
|
|
// Create Server Instance to stream all application Outgoing messages
|
|
public async createServerStreamingServer(
|
|
public async createServerStreamingServer(
|
|
serverUrl: string,
|
|
serverUrl: string,
|
|
@@ -123,11 +124,13 @@ export class GrpcServiceMethod {
|
|
): Promise<any> { // '0.0.0.0:3001'
|
|
): Promise<any> { // '0.0.0.0:3001'
|
|
return new Promise((resolve, reject) => {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
try {
|
|
- // https://github.com/grpc/proposal/blob/master/L5-node-client-interceptors.md
|
|
|
|
- let server: grpc.Server = new grpc.Server();
|
|
|
|
- server.addService(message_proto.Message.service, {
|
|
|
|
|
|
+ if (!this.server) {
|
|
|
|
+ this.server = new grpc.Server();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.server.addService(message_proto.Message.service, {
|
|
HandleMessage: (call) => {
|
|
HandleMessage: (call) => {
|
|
- // Assign channel uuid
|
|
|
|
|
|
+ this.callRequestsFromRemote.push(call)
|
|
let clientInfo = JSON.parse(call.request.message)
|
|
let clientInfo = JSON.parse(call.request.message)
|
|
this.generateAdditionalAttributes(connectionAttribute, clientInfo)
|
|
this.generateAdditionalAttributes(connectionAttribute, clientInfo)
|
|
|
|
|
|
@@ -135,7 +138,8 @@ export class GrpcServiceMethod {
|
|
if (connectionAttribute.outGoing.MessageToBePublished) {
|
|
if (connectionAttribute.outGoing.MessageToBePublished) {
|
|
let subscription: Subscription = connectionAttribute.outGoing.MessageToBePublished.subscribe({
|
|
let subscription: Subscription = connectionAttribute.outGoing.MessageToBePublished.subscribe({
|
|
next: (response: Message) => {
|
|
next: (response: Message) => {
|
|
- console.log(`Sending ${(response.message as MessageLog).appData.msgId}`)
|
|
|
|
|
|
+ console.log(`Sending from GRPC server: ${(response.message as MessageLog).appData.msgId} `)
|
|
|
|
+ this.messageToBeSendOver = response
|
|
let message = {
|
|
let message = {
|
|
id: response.id,
|
|
id: response.id,
|
|
message: JSON.stringify(response.message)
|
|
message: JSON.stringify(response.message)
|
|
@@ -154,6 +158,7 @@ export class GrpcServiceMethod {
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
+
|
|
},
|
|
},
|
|
|
|
|
|
Check: (_, callback) => {
|
|
Check: (_, callback) => {
|
|
@@ -163,9 +168,9 @@ export class GrpcServiceMethod {
|
|
},
|
|
},
|
|
});
|
|
});
|
|
// Bind and start the server
|
|
// Bind and start the server
|
|
- server.bindAsync(serverUrl, grpc.ServerCredentials.createInsecure(), () => {
|
|
|
|
|
|
+ this.server.bindAsync(serverUrl, grpc.ServerCredentials.createInsecure(), () => {
|
|
console.log(`gRPC server is running on ${serverUrl}`);
|
|
console.log(`gRPC server is running on ${serverUrl}`);
|
|
- server.start();
|
|
|
|
|
|
+ this.server.start();
|
|
});
|
|
});
|
|
}
|
|
}
|
|
catch (error) {
|
|
catch (error) {
|
|
@@ -178,12 +183,10 @@ export class GrpcServiceMethod {
|
|
// Send a request over to the other server to open a channel for this server to emit/stream messages over
|
|
// Send a request over to the other server to open a channel for this server to emit/stream messages over
|
|
public async createServerStreamingClient(
|
|
public async createServerStreamingClient(
|
|
server: string,
|
|
server: string,
|
|
- alreadyHealthCheck: boolean,
|
|
|
|
connectionAttribute: ConnectionAttribute
|
|
connectionAttribute: ConnectionAttribute
|
|
): Promise<string> {
|
|
): Promise<string> {
|
|
return new Promise(async (resolve, reject) => {
|
|
return new Promise(async (resolve, reject) => {
|
|
const client = new message_proto.Message(server, grpc.credentials.createInsecure());
|
|
const client = new message_proto.Message(server, grpc.credentials.createInsecure());
|
|
- // perform check to see if server is alive, if not terminate this grpc instant and create again
|
|
|
|
|
|
|
|
let outGoingInfo: any = {
|
|
let outGoingInfo: any = {
|
|
channelID: uuidv4(),
|
|
channelID: uuidv4(),
|
|
@@ -202,8 +205,9 @@ export class GrpcServiceMethod {
|
|
// RPC completed successfully
|
|
// RPC completed successfully
|
|
} if (status == grpc.status.UNAVAILABLE) {
|
|
} if (status == grpc.status.UNAVAILABLE) {
|
|
let report: ReportStatus = {
|
|
let report: ReportStatus = {
|
|
- code: ColorCode.RED,
|
|
|
|
|
|
+ code: ColorCode.YELLOW,
|
|
message: `Server doesn't seem to be alive. Error returned.`,
|
|
message: `Server doesn't seem to be alive. Error returned.`,
|
|
|
|
+ payload: this.messageToBeSendOver ?? `There's no message at the moment...`
|
|
}
|
|
}
|
|
connectionAttribute.connectionStatus.next(report)
|
|
connectionAttribute.connectionStatus.next(report)
|
|
resolve('No connection established. Server is not responding..')
|
|
resolve('No connection established. Server is not responding..')
|
|
@@ -218,7 +222,7 @@ export class GrpcServiceMethod {
|
|
if (connectionAttribute.inComing.MessageToBeReceived) {
|
|
if (connectionAttribute.inComing.MessageToBeReceived) {
|
|
connectionAttribute.inComing.MessageToBeReceived.next(response)
|
|
connectionAttribute.inComing.MessageToBeReceived.next(response)
|
|
}
|
|
}
|
|
- console.log((response.message as MessageLog).appData.msgId)
|
|
|
|
|
|
+ // console.log(`Received ${(response.message as MessageLog).appData.msgId}`)
|
|
});
|
|
});
|
|
|
|
|
|
call.on('error', (err) => {
|
|
call.on('error', (err) => {
|
|
@@ -244,3 +248,7 @@ export class GrpcServiceMethod {
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// https://github.com/grpc/proposal/blob/master/L5-node-client-interceptors.md
|