init
This commit is contained in:
36
sentinel/sentinel-adapter/sentinel-grpc-adapter/README.md
Normal file
36
sentinel/sentinel-adapter/sentinel-grpc-adapter/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Sentinel gRPC Adapter
|
||||
|
||||
Sentinel gRPC Adapter provides client and server interceptor for gRPC services.
|
||||
|
||||
> Note that currently the interceptor only supports unary methods in gRPC.
|
||||
|
||||
## Client Interceptor
|
||||
|
||||
Example:
|
||||
|
||||
```java
|
||||
public class ServiceClient {
|
||||
|
||||
private final ManagedChannel channel;
|
||||
|
||||
ServiceClient(String host, int port) {
|
||||
this.channel = ManagedChannelBuilder.forAddress(host, port)
|
||||
.intercept(new SentinelGrpcClientInterceptor()) // Add the client interceptor.
|
||||
.build();
|
||||
// Init your stub here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Server Interceptor
|
||||
|
||||
Example:
|
||||
|
||||
```java
|
||||
import io.grpc.Server;
|
||||
|
||||
Server server = ServerBuilder.forPort(port)
|
||||
.addService(new MyServiceImpl()) // Add your service.
|
||||
.intercept(new SentinelGrpcServerInterceptor()) // Add the server interceptor.
|
||||
.build();
|
||||
```
|
98
sentinel/sentinel-adapter/sentinel-grpc-adapter/pom.xml
Normal file
98
sentinel/sentinel-adapter/sentinel-grpc-adapter/pom.xml
Normal file
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>sentinel-adapter</artifactId>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<version>1.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>sentinel-grpc-adapter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<grpc.version>1.30.2</grpc.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty</artifactId>
|
||||
<version>${grpc.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
<version>${grpc.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
<version>${grpc.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- workaround for compile in JDK 11 -->
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<version>${javax.annotation-api.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<extensions>
|
||||
<extension>
|
||||
<groupId>kr.motd.maven</groupId>
|
||||
<artifactId>os-maven-plugin</artifactId>
|
||||
<version>1.5.0.Final</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.xolstice.maven.plugins</groupId>
|
||||
<artifactId>protobuf-maven-plugin</artifactId>
|
||||
<version>0.5.1</version>
|
||||
<configuration>
|
||||
<protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact>
|
||||
<pluginId>grpc-java</pluginId>
|
||||
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
|
||||
</pluginArtifact>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
<goal>test-compile-custom</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.grpc;
|
||||
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.SphU;
|
||||
import com.alibaba.csp.sentinel.Tracer;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import io.grpc.CallOptions;
|
||||
import io.grpc.Channel;
|
||||
import io.grpc.ClientCall;
|
||||
import io.grpc.ClientInterceptor;
|
||||
import io.grpc.ForwardingClientCall;
|
||||
import io.grpc.ForwardingClientCallListener;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.MethodDescriptor;
|
||||
import io.grpc.Status;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* <p>gRPC client interceptor for Sentinel. Currently it only works with unary methods.</p>
|
||||
* <p>
|
||||
* Example code:
|
||||
* <pre>
|
||||
* public class ServiceClient {
|
||||
*
|
||||
* private final ManagedChannel channel;
|
||||
*
|
||||
* ServiceClient(String host, int port) {
|
||||
* this.channel = ManagedChannelBuilder.forAddress(host, port)
|
||||
* .intercept(new SentinelGrpcClientInterceptor()) // Add the client interceptor.
|
||||
* .build();
|
||||
* // Init your stub here.
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* For server interceptor, see {@link SentinelGrpcServerInterceptor}.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class SentinelGrpcClientInterceptor implements ClientInterceptor {
|
||||
private static final Status FLOW_CONTROL_BLOCK = Status.UNAVAILABLE.withDescription(
|
||||
"Flow control limit exceeded (client side)");
|
||||
|
||||
@Override
|
||||
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> methodDescriptor,
|
||||
CallOptions callOptions, Channel channel) {
|
||||
String fullMethodName = methodDescriptor.getFullMethodName();
|
||||
Entry entry = null;
|
||||
try {
|
||||
entry = SphU.asyncEntry(fullMethodName, EntryType.OUT);
|
||||
final AtomicReference<Entry> atomicReferenceEntry = new AtomicReference<>(entry);
|
||||
// Allow access, forward the call.
|
||||
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
|
||||
channel.newCall(methodDescriptor, callOptions)) {
|
||||
@Override
|
||||
public void start(Listener<RespT> responseListener, Metadata headers) {
|
||||
super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(responseListener) {
|
||||
@Override
|
||||
public void onClose(Status status, Metadata trailers) {
|
||||
Entry entry = atomicReferenceEntry.get();
|
||||
if (entry != null) {
|
||||
// Record the exception metrics.
|
||||
if (!status.isOk()) {
|
||||
Tracer.traceEntry(status.asRuntimeException(), entry);
|
||||
}
|
||||
entry.exit();
|
||||
atomicReferenceEntry.set(null);
|
||||
}
|
||||
super.onClose(status, trailers);
|
||||
}
|
||||
}, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some Exceptions will only call cancel.
|
||||
*/
|
||||
@Override
|
||||
public void cancel(@Nullable String message, @Nullable Throwable cause) {
|
||||
Entry entry = atomicReferenceEntry.get();
|
||||
// Some Exceptions will call onClose and cancel.
|
||||
if (entry != null) {
|
||||
// Record the exception metrics.
|
||||
Tracer.traceEntry(cause, entry);
|
||||
entry.exit();
|
||||
atomicReferenceEntry.set(null);
|
||||
}
|
||||
super.cancel(message, cause);
|
||||
}
|
||||
};
|
||||
} catch (BlockException e) {
|
||||
// Flow control threshold exceeded, block the call.
|
||||
return new ClientCall<ReqT, RespT>() {
|
||||
@Override
|
||||
public void start(Listener<RespT> responseListener, Metadata headers) {
|
||||
responseListener.onClose(FLOW_CONTROL_BLOCK, new Metadata());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void request(int numMessages) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(@Nullable String message, @Nullable Throwable cause) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void halfClose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(ReqT message) {
|
||||
}
|
||||
};
|
||||
} catch (RuntimeException e) {
|
||||
// Catch the RuntimeException newCall throws, entry is guaranteed to exit.
|
||||
if (entry != null) {
|
||||
Tracer.traceEntry(e, entry);
|
||||
entry.exit();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.grpc;
|
||||
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.SphU;
|
||||
import com.alibaba.csp.sentinel.Tracer;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import io.grpc.ForwardingServerCall;
|
||||
import io.grpc.ForwardingServerCallListener;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.ServerCall;
|
||||
import io.grpc.ServerCallHandler;
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* <p>gRPC server interceptor for Sentinel. Currently it only works with unary methods.</p>
|
||||
* <p>
|
||||
* Example code:
|
||||
* <pre>
|
||||
* Server server = ServerBuilder.forPort(port)
|
||||
* .addService(new MyServiceImpl()) // Add your service.
|
||||
* .intercept(new SentinelGrpcServerInterceptor()) // Add the server interceptor.
|
||||
* .build();
|
||||
* </pre>
|
||||
* <p>
|
||||
* For client interceptor, see {@link SentinelGrpcClientInterceptor}.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class SentinelGrpcServerInterceptor implements ServerInterceptor {
|
||||
private static final Status FLOW_CONTROL_BLOCK = Status.UNAVAILABLE.withDescription(
|
||||
"Flow control limit exceeded (server side)");
|
||||
private static final StatusRuntimeException STATUS_RUNTIME_EXCEPTION = new StatusRuntimeException(Status.CANCELLED);
|
||||
|
||||
@Override
|
||||
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
|
||||
String fullMethodName = call.getMethodDescriptor().getFullMethodName();
|
||||
// Remote address: serverCall.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR);
|
||||
Entry entry = null;
|
||||
try {
|
||||
entry = SphU.asyncEntry(fullMethodName, EntryType.IN);
|
||||
final AtomicReference<Entry> atomicReferenceEntry = new AtomicReference<>(entry);
|
||||
// Allow access, forward the call.
|
||||
return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(
|
||||
next.startCall(
|
||||
new ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT>(call) {
|
||||
@Override
|
||||
public void close(Status status, Metadata trailers) {
|
||||
Entry entry = atomicReferenceEntry.get();
|
||||
if (entry != null) {
|
||||
// Record the exception metrics.
|
||||
if (!status.isOk()) {
|
||||
Tracer.traceEntry(status.asRuntimeException(), entry);
|
||||
}
|
||||
//entry exit when the call be closed
|
||||
entry.exit();
|
||||
}
|
||||
super.close(status, trailers);
|
||||
}
|
||||
}, headers)) {
|
||||
/**
|
||||
* If call was canceled, onCancel will be called. and the close will not be called
|
||||
* so the server is encouraged to abort processing to save resources by onCancel
|
||||
* @see ServerCall.Listener#onCancel()
|
||||
*/
|
||||
@Override
|
||||
public void onCancel() {
|
||||
Entry entry = atomicReferenceEntry.get();
|
||||
if (entry != null) {
|
||||
Tracer.traceEntry(STATUS_RUNTIME_EXCEPTION, entry);
|
||||
entry.exit();
|
||||
atomicReferenceEntry.set(null);
|
||||
}
|
||||
super.onCancel();
|
||||
}
|
||||
};
|
||||
} catch (BlockException e) {
|
||||
call.close(FLOW_CONTROL_BLOCK, new Metadata());
|
||||
return new ServerCall.Listener<ReqT>() {
|
||||
};
|
||||
} catch (RuntimeException e) {
|
||||
// Catch the RuntimeException startCall throws, entry is guaranteed to exit.
|
||||
if (entry != null) {
|
||||
Tracer.traceEntry(e, entry);
|
||||
entry.exit();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.grpc;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
|
||||
import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
|
||||
import com.alibaba.csp.sentinel.adapter.grpc.gen.FooServiceGrpc;
|
||||
import io.grpc.ClientInterceptor;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A simple wrapped gRPC client for FooService.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
final class FooServiceClient {
|
||||
private final ManagedChannel channel;
|
||||
private final FooServiceGrpc.FooServiceBlockingStub blockingStub;
|
||||
|
||||
FooServiceClient(String host, int port) {
|
||||
this.channel = ManagedChannelBuilder.forAddress(host, port)
|
||||
.usePlaintext()
|
||||
.build();
|
||||
this.blockingStub = FooServiceGrpc.newBlockingStub(this.channel);
|
||||
}
|
||||
|
||||
FooServiceClient(String host, int port, ClientInterceptor interceptor) {
|
||||
this.channel = ManagedChannelBuilder.forAddress(host, port)
|
||||
.usePlaintext()
|
||||
.intercept(interceptor)
|
||||
.build();
|
||||
this.blockingStub = FooServiceGrpc.newBlockingStub(this.channel);
|
||||
}
|
||||
|
||||
FooResponse sayHello(FooRequest request) {
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("Request cannot be null");
|
||||
}
|
||||
return blockingStub.sayHello(request);
|
||||
}
|
||||
|
||||
FooResponse anotherHello(FooRequest request) {
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("Request cannot be null");
|
||||
}
|
||||
return blockingStub.anotherHello(request);
|
||||
}
|
||||
|
||||
void shutdown() throws InterruptedException {
|
||||
channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.grpc;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
|
||||
import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
|
||||
import com.alibaba.csp.sentinel.adapter.grpc.gen.FooServiceGrpc;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
/**
|
||||
* Implementation of FooService defined in proto.
|
||||
*/
|
||||
class FooServiceImpl extends FooServiceGrpc.FooServiceImplBase {
|
||||
@Override
|
||||
public void sayHello(FooRequest request, StreamObserver<FooResponse> responseObserver) {
|
||||
int id = request.getId();
|
||||
switch (id) {
|
||||
// Exception test
|
||||
case -1:
|
||||
responseObserver.onError(new IllegalAccessException("The id is error!"));
|
||||
break;
|
||||
// RT test
|
||||
case -2:
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
responseObserver.onError(e);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
String message = String.format("Hello %s! Your ID is %d.", request.getName(), id);
|
||||
FooResponse response = FooResponse.newBuilder().setMessage(message).build();
|
||||
responseObserver.onNext(response);
|
||||
responseObserver.onCompleted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void anotherHello(FooRequest request, StreamObserver<FooResponse> responseObserver) {
|
||||
int id = request.getId();
|
||||
switch (id) {
|
||||
// Exception test
|
||||
case -1:
|
||||
responseObserver.onError(new IllegalAccessException("The id is error!"));
|
||||
break;
|
||||
// RT test
|
||||
case -2:
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
responseObserver.onError(e);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
String message = String.format("Good day, %s (%d)", request.getName(), id);
|
||||
FooResponse response = FooResponse.newBuilder().setMessage(message).build();
|
||||
responseObserver.onNext(response);
|
||||
responseObserver.onCompleted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.grpc;
|
||||
|
||||
import io.grpc.Server;
|
||||
import io.grpc.ServerBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
class GrpcTestServer {
|
||||
|
||||
private Server server;
|
||||
|
||||
GrpcTestServer() {}
|
||||
|
||||
void start(int port, boolean shouldIntercept) throws IOException {
|
||||
if (server != null) {
|
||||
throw new IllegalStateException("Server already running!");
|
||||
}
|
||||
ServerBuilder<?> serverBuild = ServerBuilder.forPort(port)
|
||||
.addService(new FooServiceImpl());
|
||||
if (shouldIntercept) {
|
||||
serverBuild.intercept(new SentinelGrpcServerInterceptor());
|
||||
}
|
||||
server = serverBuild.build();
|
||||
server.start();
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (server != null) {
|
||||
server.shutdown();
|
||||
server = null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.grpc;
|
||||
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
|
||||
import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
|
||||
import com.alibaba.csp.sentinel.node.ClusterNode;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test cases for {@link SentinelGrpcClientInterceptor}.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class SentinelGrpcClientInterceptorTest {
|
||||
private final String fullMethodName = "com.alibaba.sentinel.examples.FooService/sayHello";
|
||||
private final GrpcTestServer server = new GrpcTestServer();
|
||||
private FooServiceClient client;
|
||||
|
||||
private void configureFlowRule(int count) {
|
||||
FlowRule rule = new FlowRule()
|
||||
.setCount(count)
|
||||
.setGrade(RuleConstant.FLOW_GRADE_QPS)
|
||||
.setResource(fullMethodName)
|
||||
.setLimitApp("default")
|
||||
.as(FlowRule.class);
|
||||
FlowRuleManager.loadRules(Collections.singletonList(rule));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrpcClientInterceptor() throws Exception {
|
||||
final int port = 19328;
|
||||
server.start(port, false);
|
||||
client = new FooServiceClient("localhost", port, new SentinelGrpcClientInterceptor());
|
||||
|
||||
configureFlowRule(Integer.MAX_VALUE);
|
||||
assertTrue(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(666).build()));
|
||||
ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(fullMethodName, EntryType.OUT);
|
||||
assertNotNull(clusterNode);
|
||||
assertEquals(1, clusterNode.totalPass());
|
||||
|
||||
// Not allowed to pass.
|
||||
configureFlowRule(0);
|
||||
// The second request will be blocked.
|
||||
assertFalse(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(666).build()));
|
||||
assertEquals(1, clusterNode.blockRequest());
|
||||
|
||||
configureFlowRule(Integer.MAX_VALUE);
|
||||
assertFalse(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(-1).build()));
|
||||
assertEquals(1, clusterNode.totalException());
|
||||
|
||||
configureFlowRule(Integer.MAX_VALUE);
|
||||
assertTrue(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(-2).build()));
|
||||
assertTrue(clusterNode.avgRt() >= 1000);
|
||||
|
||||
server.stop();
|
||||
}
|
||||
|
||||
private boolean sendRequest(FooRequest request) {
|
||||
try {
|
||||
FooResponse response = client.sayHello(request);
|
||||
System.out.println("Response: " + response);
|
||||
return true;
|
||||
} catch (StatusRuntimeException ex) {
|
||||
System.out.println("Blocked, cause: " + ex.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void cleanUpBefore() {
|
||||
FlowRuleManager.loadRules(null);
|
||||
ClusterBuilderSlot.getClusterNodeMap().clear();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUpAfter() {
|
||||
FlowRuleManager.loadRules(null);
|
||||
ClusterBuilderSlot.getClusterNodeMap().clear();
|
||||
}
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.grpc;
|
||||
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
|
||||
import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
|
||||
import com.alibaba.csp.sentinel.node.ClusterNode;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test cases for {@link SentinelGrpcServerInterceptor}.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class SentinelGrpcServerInterceptorTest {
|
||||
private final String resourceName = "com.alibaba.sentinel.examples.FooService/anotherHello";
|
||||
private final GrpcTestServer server = new GrpcTestServer();
|
||||
private FooServiceClient client;
|
||||
|
||||
private void configureFlowRule(int count) {
|
||||
FlowRule rule = new FlowRule()
|
||||
.setCount(count)
|
||||
.setGrade(RuleConstant.FLOW_GRADE_QPS)
|
||||
.setResource(resourceName)
|
||||
.setLimitApp("default")
|
||||
.as(FlowRule.class);
|
||||
FlowRuleManager.loadRules(Collections.singletonList(rule));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrpcServerInterceptor() throws Exception {
|
||||
final int port = 19329;
|
||||
server.start(port, true);
|
||||
client = new FooServiceClient("localhost", port);
|
||||
|
||||
configureFlowRule(Integer.MAX_VALUE);
|
||||
assertTrue(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(666).build()));
|
||||
ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(resourceName, EntryType.IN);
|
||||
assertNotNull(clusterNode);
|
||||
assertEquals(1, clusterNode.totalPass());
|
||||
|
||||
// Not allowed to pass.
|
||||
configureFlowRule(0);
|
||||
// The second request will be blocked.
|
||||
assertFalse(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(666).build()));
|
||||
assertEquals(1, clusterNode.blockRequest());
|
||||
|
||||
configureFlowRule(Integer.MAX_VALUE);
|
||||
assertFalse(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(-1).build()));
|
||||
assertEquals(1, clusterNode.totalException());
|
||||
|
||||
configureFlowRule(Integer.MAX_VALUE);
|
||||
assertTrue(sendRequest(FooRequest.newBuilder().setName("Sentinel").setId(-2).build()));
|
||||
assertTrue(clusterNode.avgRt() >= 1000);
|
||||
|
||||
server.stop();
|
||||
}
|
||||
|
||||
private boolean sendRequest(FooRequest request) {
|
||||
try {
|
||||
FooResponse response = client.anotherHello(request);
|
||||
System.out.println("Response: " + response);
|
||||
return true;
|
||||
} catch (StatusRuntimeException ex) {
|
||||
System.out.println("Blocked, cause: " + ex.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void cleanUpBefore() {
|
||||
FlowRuleManager.loadRules(null);
|
||||
ClusterBuilderSlot.getClusterNodeMap().clear();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUpAfter() {
|
||||
FlowRuleManager.loadRules(null);
|
||||
ClusterBuilderSlot.getClusterNodeMap().clear();
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.alibaba.csp.sentinel.adapter.grpc.gen";
|
||||
option java_outer_classname = "FooProto";
|
||||
|
||||
package com.alibaba.sentinel.examples;
|
||||
|
||||
message FooRequest {
|
||||
string name = 1;
|
||||
int32 id = 2;
|
||||
}
|
||||
|
||||
message FooResponse {
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
// Example service definition.
|
||||
service FooService {
|
||||
rpc sayHello(FooRequest) returns (FooResponse) {}
|
||||
rpc anotherHello(FooRequest) returns (FooResponse) {}
|
||||
}
|
Reference in New Issue
Block a user