chore(project): 添加项目配置文件和忽略规则
- 添加 Babel 配置文件支持 ES6+ 语法转换 - 添加 ESLint 忽略规则和配置文件 - 添加 Git 忽略规则文件 - 添加 Travis CI 配置文件 - 添加 1.4.2 版本变更日志文件 - 添加 Helm 图表辅助模板文件 - 添加 Helm 忽略规则文件
This commit is contained in:
85
integration/grpc/pom.xml
Normal file
85
integration/grpc/pom.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 1999-2019 Seata.io Group.
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
<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>seata-parent</artifactId>
|
||||
<groupId>io.seata</groupId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>seata-grpc</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>seata-grpc ${project.version}</name>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>seata-tm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-testing</artifactId>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.xolstice.maven.plugins</groupId>
|
||||
<artifactId>protobuf-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<protocArtifact>com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}</protocArtifact>
|
||||
<pluginId>grpc-java</pluginId>
|
||||
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.8.0: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,29 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* 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 io.seata.integration.grpc.interceptor;
|
||||
|
||||
import io.grpc.Metadata;
|
||||
import io.seata.core.context.RootContext;
|
||||
|
||||
/**
|
||||
* @author eddyxu1213@126.com
|
||||
*/
|
||||
public interface GrpcHeaderKey {
|
||||
|
||||
Metadata.Key<String> HEADER_KEY = Metadata.Key.of(RootContext.KEY_XID, Metadata.ASCII_STRING_MARSHALLER);
|
||||
|
||||
Metadata.Key<String> HEADER_KEY_LOWERCASE = Metadata.Key.of(RootContext.KEY_XID.toLowerCase(), Metadata.ASCII_STRING_MARSHALLER);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* 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 io.seata.integration.grpc.interceptor.client;
|
||||
|
||||
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.seata.core.context.RootContext;
|
||||
import io.seata.integration.grpc.interceptor.GrpcHeaderKey;
|
||||
|
||||
/**
|
||||
* @author eddyxu1213@126.com
|
||||
*/
|
||||
public class ClientTransactionInterceptor implements ClientInterceptor {
|
||||
|
||||
@Override
|
||||
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
|
||||
MethodDescriptor<ReqT, RespT> method,
|
||||
CallOptions callOptions,
|
||||
Channel next) {
|
||||
|
||||
String xid = RootContext.getXID();
|
||||
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
|
||||
|
||||
@Override
|
||||
public void start(Listener<RespT> responseListener, Metadata headers) {
|
||||
if (xid != null) {
|
||||
headers.put(GrpcHeaderKey.HEADER_KEY, xid);
|
||||
}
|
||||
super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(responseListener) {
|
||||
@Override
|
||||
public void onHeaders(Metadata headers) {
|
||||
super.onHeaders(headers);
|
||||
}
|
||||
}, headers);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* 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 io.seata.integration.grpc.interceptor.server;
|
||||
|
||||
import io.grpc.ServerCall;
|
||||
import io.seata.common.util.StringUtils;
|
||||
import io.seata.core.context.RootContext;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author eddyxu1213@126.com
|
||||
*/
|
||||
public class ServerListenerProxy<ReqT> extends ServerCall.Listener<ReqT> {
|
||||
|
||||
private ServerCall.Listener<ReqT> target;
|
||||
private String xid;
|
||||
|
||||
public ServerListenerProxy(String xid, ServerCall.Listener<ReqT> target) {
|
||||
super();
|
||||
Objects.requireNonNull(target);
|
||||
this.target = target;
|
||||
this.xid = xid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(ReqT message) {
|
||||
target.onMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHalfClose() {
|
||||
if (StringUtils.isNotBlank(xid)) {
|
||||
RootContext.bind(xid);
|
||||
}
|
||||
target.onHalfClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
if (StringUtils.isNotBlank(xid) && RootContext.inGlobalTransaction()) {
|
||||
RootContext.unbind();
|
||||
}
|
||||
target.onCancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
if (StringUtils.isNotBlank(xid) && RootContext.inGlobalTransaction()) {
|
||||
RootContext.unbind();
|
||||
}
|
||||
target.onComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReady() {
|
||||
target.onReady();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* 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 io.seata.integration.grpc.interceptor.server;
|
||||
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.ServerCall;
|
||||
import io.grpc.ServerCallHandler;
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.seata.integration.grpc.interceptor.GrpcHeaderKey;
|
||||
|
||||
/**
|
||||
* @author eddyxu1213@126.com
|
||||
*/
|
||||
public class ServerTransactionInterceptor implements ServerInterceptor {
|
||||
|
||||
@Override
|
||||
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
|
||||
ServerCall<ReqT, RespT> serverCall,
|
||||
Metadata metadata,
|
||||
ServerCallHandler<ReqT, RespT> serverCallHandler) {
|
||||
String xid = getRpcXid(metadata);
|
||||
return new ServerListenerProxy<>(xid, serverCallHandler.startCall(serverCall, metadata));
|
||||
}
|
||||
|
||||
/**
|
||||
* get rpc xid
|
||||
* @param metadata
|
||||
* @return
|
||||
*/
|
||||
private String getRpcXid(Metadata metadata) {
|
||||
String rpcXid = metadata.get(GrpcHeaderKey.HEADER_KEY);
|
||||
if (rpcXid == null) {
|
||||
rpcXid = metadata.get(GrpcHeaderKey.HEADER_KEY_LOWERCASE);
|
||||
}
|
||||
return rpcXid;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* 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 io.seata.integration.grpc.interceptor;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import io.grpc.ClientInterceptors;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.ServerInterceptors;
|
||||
import io.grpc.inprocess.InProcessChannelBuilder;
|
||||
import io.grpc.inprocess.InProcessServerBuilder;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import io.grpc.testing.GrpcCleanupRule;
|
||||
import io.seata.core.context.RootContext;
|
||||
import io.seata.integration.grpc.interceptor.client.ClientTransactionInterceptor;
|
||||
import io.seata.integration.grpc.interceptor.proto.ContextRpcGrpc;
|
||||
import io.seata.integration.grpc.interceptor.proto.Request;
|
||||
import io.seata.integration.grpc.interceptor.proto.Response;
|
||||
import io.seata.integration.grpc.interceptor.server.ServerTransactionInterceptor;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.AdditionalAnswers.delegatesTo;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author eddyxu1213@126.com
|
||||
*/
|
||||
public class GrpcTest {
|
||||
|
||||
@Rule
|
||||
public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
|
||||
private final ServerInterceptor mockServerInterceptor = mock(ServerInterceptor.class, delegatesTo(new ServerTransactionInterceptor()));
|
||||
|
||||
|
||||
@Test
|
||||
public void clientHeaderDeliveredToServer() throws Exception {
|
||||
|
||||
String serverName = InProcessServerBuilder.generateName();
|
||||
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
String[] context = {null};
|
||||
|
||||
//executor
|
||||
Executor executorService = new ThreadPoolExecutor(2, 2, 1, TimeUnit.HOURS, new LinkedBlockingQueue<>(), new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
return new Thread(r, "contextText-" + System.currentTimeMillis());
|
||||
}
|
||||
});
|
||||
|
||||
//server
|
||||
grpcCleanup.register(InProcessServerBuilder.forName(serverName).executor(executorService)
|
||||
.addService(ServerInterceptors.intercept(new ContextRpcGrpc.ContextRpcImplBase() {
|
||||
@Override
|
||||
public void contextRpc(Request request, StreamObserver<Response> responseObserver) {
|
||||
context[0] = RootContext.getXID();
|
||||
countDownLatch.countDown();
|
||||
responseObserver.onNext(Response.newBuilder().setGreet("hello! " + request.getName()).build());
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
}, mockServerInterceptor))
|
||||
.build().start());
|
||||
|
||||
//client
|
||||
ManagedChannel channel = grpcCleanup.register(InProcessChannelBuilder.forName(serverName).executor(executorService).build());
|
||||
ContextRpcGrpc.ContextRpcFutureStub stub = ContextRpcGrpc.newFutureStub(
|
||||
ClientInterceptors.intercept(channel, new ClientTransactionInterceptor()));
|
||||
RootContext.bind("test_context");
|
||||
ListenableFuture<Response> future = stub.contextRpc(Request.newBuilder().setName("seata").build());
|
||||
assertEquals("hello! seata", future.get().getGreet());
|
||||
|
||||
ArgumentCaptor<Metadata> metadataCaptor = ArgumentCaptor.forClass(Metadata.class);
|
||||
verify(mockServerInterceptor).interceptCall(ArgumentMatchers.any(), metadataCaptor.capture(), ArgumentMatchers.any());
|
||||
assertEquals("test_context", metadataCaptor.getValue().get(GrpcHeaderKey.HEADER_KEY));
|
||||
|
||||
countDownLatch.await();
|
||||
assertEquals("test_context", context[0]);
|
||||
}
|
||||
}
|
||||
34
integration/grpc/src/test/proto/contextTest.proto
Normal file
34
integration/grpc/src/test/proto/contextTest.proto
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
syntax = "proto3";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "io.seata.integration.grpc.interceptor.proto";
|
||||
option java_outer_classname = "ContextRpcTest";
|
||||
|
||||
service ContextRpc {
|
||||
rpc ContextRpc (Request) returns (Response) {
|
||||
}
|
||||
}
|
||||
|
||||
message Request {
|
||||
string name = 1;
|
||||
|
||||
}
|
||||
|
||||
message Response {
|
||||
string greet = 1;
|
||||
}
|
||||
Reference in New Issue
Block a user