diff --git a/joylive-bom/pom.xml b/joylive-bom/pom.xml index bddffde92..06e90c134 100644 --- a/joylive-bom/pom.xml +++ b/joylive-bom/pom.xml @@ -253,6 +253,11 @@ joylive-router-sofarpc ${revision} + + com.jd.live + joylive-router-grpc + ${revision} + com.jd.live joylive-router-rocketmq5 diff --git a/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/pom.xml b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/pom.xml new file mode 100644 index 000000000..68a94ff5c --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/pom.xml @@ -0,0 +1,90 @@ + + 4.0.0 + + com.jd.live + joylive-demo-grpc + ${revision} + + + joylive-demo-grpc-consumer + + + 2.2.4.RELEASE + Hoxton.SR1 + 1.29.0 + 2.2.0.RELEASE + 2.8.0.RELEASE + 1.8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-parent + ${spring.boot.version} + pom + import + + + org.springframework.cloud + spring-cloud-dependencies + ${spring.cloud.version} + pom + import + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring.cloud.alibaba.version} + pom + import + + + + + + + + com.jd.live + joylive-demo-grpc-service-api + ${revision} + + + + + org.springframework.boot + spring-boot-starter-web + + + + + net.devh + grpc-client-spring-boot-starter + ${grpc.spring.cloud.version} + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + ${basedir}/target + joylive-demo-grpc-consumer + + + org.springframework.boot + spring-boot-maven-plugin + + com.jd.live.agent.demo.grpc.consumer.GRpcConsumerApplication + + + + + + diff --git a/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/src/main/java/com/jd/live/agent/demo/grpc/consumer/GRpcConsumerApplication.java b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/src/main/java/com/jd/live/agent/demo/grpc/consumer/GRpcConsumerApplication.java new file mode 100644 index 000000000..ab7062414 --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/src/main/java/com/jd/live/agent/demo/grpc/consumer/GRpcConsumerApplication.java @@ -0,0 +1,13 @@ +package com.jd.live.agent.demo.grpc.consumer; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class GRpcConsumerApplication { + + public static void main(String[] args) { + SpringApplication.run(GRpcConsumerApplication.class, args); + } + +} diff --git a/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/src/main/java/com/jd/live/agent/demo/grpc/consumer/UserServiceController.java b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/src/main/java/com/jd/live/agent/demo/grpc/consumer/UserServiceController.java new file mode 100644 index 000000000..b432bdeea --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/src/main/java/com/jd/live/agent/demo/grpc/consumer/UserServiceController.java @@ -0,0 +1,35 @@ +package com.jd.live.agent.demo.grpc.consumer; + +import com.jd.live.agent.demo.grpc.service.api.*; +import net.devh.boot.grpc.client.inject.GrpcClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/demo") +public class UserServiceController { + + @GrpcClient("user-provider") + private UserServiceGrpc.UserServiceBlockingStub userServiceGrpc; + + @GetMapping("/get") + public String get(@RequestParam("id") Integer id) { + UserGetRequest request = UserGetRequest.newBuilder().setId(id).build(); + UserGetResponse response = userServiceGrpc.get(request); + return response.getName(); + } + + @GetMapping("/create") // 为了方便测试,实际使用 @PostMapping + public Integer create(@RequestParam("name") String name, + @RequestParam("gender") Integer gender) { + UserCreateRequest request = UserCreateRequest.newBuilder() + .setName(name) + .setGender(gender) + .build(); + UserCreateResponse response = userServiceGrpc.create(request); + return response.getId(); + } + +} diff --git a/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/src/main/resources/application.yml b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/src/main/resources/application.yml new file mode 100644 index 000000000..090228847 --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/src/main/resources/application.yml @@ -0,0 +1,20 @@ +spring: + application: + name: user-consumer # 应用名 + cloud: + nacos: + # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 + discovery: + server-addr: 127.0.0.1:8848 # Nacos 服务器地址 + +grpc: + # gRPC 客户端配置,对应 GrpcChannelsProperties 配置类的映射 + client: + user-provider: + #服务发现,注意本地host配置 127.0.0.1 user-provider + address: 'discovery:///user-provider' + #静态地址 + #address: 'static://127.0.0.1:9898' + enableKeepAlive: true + keepAliveWithoutCalls: true + negotiationType: plaintext diff --git a/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/src/main/resources/nacos-logback.xml b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/src/main/resources/nacos-logback.xml new file mode 100644 index 000000000..8fc27e166 --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-consumer/src/main/resources/nacos-logback.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/pom.xml b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/pom.xml new file mode 100644 index 000000000..68ddc8433 --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/pom.xml @@ -0,0 +1,97 @@ + + 4.0.0 + + com.jd.live + joylive-demo-grpc + ${revision} + + + joylive-demo-grpc-provider + + + 2.2.4.RELEASE + Hoxton.SR1 + 1.29.0 + 2.2.0.RELEASE + 2.8.0.RELEASE + 1.8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-parent + ${spring.boot.version} + pom + import + + + org.springframework.cloud + spring-cloud-dependencies + ${spring.cloud.version} + pom + import + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring.cloud.alibaba.version} + pom + import + + + + + + + + com.jd.live + joylive-demo-grpc-service-api + ${revision} + + + + + io.grpc + grpc-all + ${grpc-all.version} + + + + + org.springframework.boot + spring-boot-starter-web + + + + + net.devh + grpc-server-spring-boot-starter + ${grpc.spring.cloud.version} + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + ${basedir}/target + joylive-demo-grpc-provider + + + org.springframework.boot + spring-boot-maven-plugin + + com.jd.live.agent.demo.grpc.provider.GRpcProviderApplication + + + + + + diff --git a/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/src/main/java/com/jd/live/agent/demo/grpc/provider/GRpcProviderApplication.java b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/src/main/java/com/jd/live/agent/demo/grpc/provider/GRpcProviderApplication.java new file mode 100644 index 000000000..8c6c22a00 --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/src/main/java/com/jd/live/agent/demo/grpc/provider/GRpcProviderApplication.java @@ -0,0 +1,13 @@ +package com.jd.live.agent.demo.grpc.provider; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class GRpcProviderApplication { + + public static void main(String[] args) { + SpringApplication.run(GRpcProviderApplication.class, args); + } + +} diff --git a/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/src/main/java/com/jd/live/agent/demo/grpc/provider/UserServiceGrpcImpl.java b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/src/main/java/com/jd/live/agent/demo/grpc/provider/UserServiceGrpcImpl.java new file mode 100644 index 000000000..e50916977 --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/src/main/java/com/jd/live/agent/demo/grpc/provider/UserServiceGrpcImpl.java @@ -0,0 +1,32 @@ +package com.jd.live.agent.demo.grpc.provider; + +import com.jd.live.agent.demo.grpc.service.api.*; +import io.grpc.stub.StreamObserver; +import net.devh.boot.grpc.server.service.GrpcService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@GrpcService +public class UserServiceGrpcImpl extends UserServiceGrpc.UserServiceImplBase { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + public void get(UserGetRequest request, StreamObserver responseObserver) { + UserGetResponse.Builder builder = UserGetResponse.newBuilder(); + builder.setId(request.getId()) + .setName("index :" + request.getId() + " time : " + System.currentTimeMillis()) + .setGender(request.getId() % 2 + 1); + responseObserver.onNext(builder.build()); + responseObserver.onCompleted(); + } + + @Override + public void create(UserCreateRequest request, StreamObserver responseObserver) { + UserCreateResponse.Builder builder = UserCreateResponse.newBuilder(); + builder.setId((int) (System.currentTimeMillis() / 1000)); + responseObserver.onNext(builder.build()); + responseObserver.onCompleted(); + } + +} diff --git a/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/src/main/resources/application.yml b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/src/main/resources/application.yml new file mode 100644 index 000000000..112c9fa4d --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/src/main/resources/application.yml @@ -0,0 +1,16 @@ +spring: + application: + name: user-provider # 应用名 + cloud: + nacos: + # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 + discovery: + server-addr: 127.0.0.1:8848 # Nacos 服务器地址 + +grpc: + # gRPC 服务器配置,对应 GrpcServerProperties 配置类 + server: + port: 9898 # gRPC Server 随机端口 + +server: + port: 9899 # Web Server 随机端口 diff --git a/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/src/main/resources/nacos-logback.xml b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/src/main/resources/nacos-logback.xml new file mode 100644 index 000000000..8fc27e166 --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-provider/src/main/resources/nacos-logback.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-service-api/pom.xml b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-service-api/pom.xml new file mode 100644 index 000000000..6531b0ae4 --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-service-api/pom.xml @@ -0,0 +1,69 @@ + + 4.0.0 + + com.jd.live + joylive-demo-grpc + ${revision} + + + joylive-demo-grpc-service-api + + + 1.60.0 + + 1.8 + 1.8 + 1.6.2 + 0.6.1 + osx-x86_64 + + + + + + io.grpc + grpc-protobuf + ${io.grpc.version} + + + + io.grpc + grpc-stub + ${io.grpc.version} + + + + + + + + kr.motd.maven + os-maven-plugin + ${os-maven-plugin.version} + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + ${protobuf-maven-plugin.version} + + grpc-java + com.google.protobuf:protoc:3.9.1:exe:${os.detected.classifier} + io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier} + + + + + compile + compile-custom + + + + + + + + diff --git a/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-service-api/src/main/proto/UserService.proto b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-service-api/src/main/proto/UserService.proto new file mode 100644 index 000000000..bf54d5482 --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/joylive-demo-grpc-service-api/src/main/proto/UserService.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; +option java_multiple_files = true; + +package com.jd.live.agent.demo.grpc.service.api; + +message UserGetRequest { + int32 id = 1; +} + +message UserGetResponse { + int32 id = 1; + string name = 2; + int32 gender = 3; +} + +message UserCreateRequest { + string name = 1; + int32 gender = 2; +} + +message UserCreateResponse { + int32 id = 1; +} + +service UserService { + + rpc get(UserGetRequest) returns (UserGetResponse); + + rpc create(UserCreateRequest) returns (UserCreateResponse); + +} diff --git a/joylive-demo/joylive-demo-grpc/pom.xml b/joylive-demo/joylive-demo-grpc/pom.xml new file mode 100644 index 000000000..93310b78c --- /dev/null +++ b/joylive-demo/joylive-demo-grpc/pom.xml @@ -0,0 +1,109 @@ + + 4.0.0 + + com.jd.live + joylive-demo + ${revision} + + + joylive-demo-grpc + pom + + + 1.60.0 + 2021.0.9 + 2.7.18 + + + + joylive-demo-grpc-provider + joylive-demo-grpc-consumer + joylive-demo-grpc-service-api + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + org.springframework.cloud + spring-cloud-dependencies + ${spring.cloud.version} + pom + import + + + + io.grpc + grpc-all + ${grpc-all.version} + import + + + + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-autoconfigure + + + + org.springframework.boot + spring-boot-starter-logging + + + + io.grpc + grpc-all + + + + + + + + org.apache.maven.plugins + maven-source-plugin + + true + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-install-plugin + + true + + + + + + diff --git a/joylive-demo/pom.xml b/joylive-demo/pom.xml index e8ffd76e0..c8aec8c8d 100644 --- a/joylive-demo/pom.xml +++ b/joylive-demo/pom.xml @@ -20,6 +20,7 @@ joylive-demo-multilive joylive-demo-rocketmq joylive-demo-sofarpc + joylive-demo-grpc diff --git a/joylive-package/pom.xml b/joylive-package/pom.xml index bbd6a2434..d328780f0 100644 --- a/joylive-package/pom.xml +++ b/joylive-package/pom.xml @@ -187,6 +187,10 @@ com.jd.live joylive-router-rocketmq4 + + com.jd.live + joylive-router-grpc + com.jd.live joylive-transmission-thread diff --git a/joylive-package/src/main/assembly/assembly.xml b/joylive-package/src/main/assembly/assembly.xml index cb5c6e72e..5c90ea18e 100644 --- a/joylive-package/src/main/assembly/assembly.xml +++ b/joylive-package/src/main/assembly/assembly.xml @@ -204,6 +204,7 @@ false com.jd.live:joylive-transmission-grpc + com.jd.live:joylive-router-grpc diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/pom.xml b/joylive-plugin/joylive-router/joylive-router-grpc/pom.xml new file mode 100644 index 000000000..9de4905a1 --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + com.jd.live + joylive-router + ${revision} + + + joylive-router-grpc + + + 1.60.0 + 2.8.0.RELEASE + 2021.0.6.0 + + + + + + io.grpc + grpc-all + ${grpc-all.version} + provided + + + + + net.devh + grpc-spring-boot-starter + ${grpc-spring.version} + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + ${alibaba-nacos-discovery.version} + + + + diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/readme.md b/joylive-plugin/joylive-router/joylive-router-grpc/readme.md new file mode 100644 index 000000000..cabab2fce --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/readme.md @@ -0,0 +1,21 @@ + +#�ͻ��˵��� +io.grpc.stub.ClientCalls +#����˵��� +io.grpc.stub.ServerCalls + +���������������dependency, ���Գ�����ǿ�������������� + + io.grpc + grpc-stub + 1.30.0 + + +���÷�ʽ���Բο���Ŀ��SpringBoot-Labs�е� labx-30-spring-cloud-grpc + + +gRpc demo���ԣ� + +��Ҫ�Ƚ���Ŀ�л���jdk8��Ȼ�󵥶���joylive-demo-grpc-service-api����clean package�����ɳ�grpc��java�࣬ +���ɵ�ַ��target/generated-sources/protobufĿ¼�£�������Ϻ��ٽ���Ŀ�����е�jdk22���б��롣 + diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/cluster/AbstractClientCluster.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/cluster/AbstractClientCluster.java new file mode 100644 index 000000000..ead7e69f7 --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/cluster/AbstractClientCluster.java @@ -0,0 +1,219 @@ +///* +// * Copyright © ${year} ${owner} (${email}) +// * +// * 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.jd.live.agent.plugin.router.gprc.cluster; +// +//import com.jd.live.agent.bootstrap.exception.RejectException; +//import com.jd.live.agent.bootstrap.exception.RejectException.RejectCircuitBreakException; +//import com.jd.live.agent.bootstrap.exception.RejectException.RejectLimitException; +//import com.jd.live.agent.bootstrap.exception.RejectException.RejectNoProviderException; +//import com.jd.live.agent.core.util.http.HttpMethod; +//import com.jd.live.agent.governance.exception.RetryException.RetryExhaustedException; +//import com.jd.live.agent.governance.invoke.OutboundInvocation; +//import com.jd.live.agent.governance.invoke.cluster.ClusterInvoker; +//import com.jd.live.agent.governance.invoke.cluster.LiveCluster; +//import com.jd.live.agent.governance.policy.service.cluster.ClusterPolicy; +//import com.jd.live.agent.governance.policy.service.cluster.RetryPolicy; +//import com.jd.live.agent.governance.response.ServiceResponse.OutboundResponse; +//import com.jd.live.agent.plugin.router.gprc.instance.GrpcEndpoint; +//import com.jd.live.agent.plugin.router.gprc.request.AbstractClusterRequest; +//import org.springframework.cloud.client.ServiceInstance; +//import org.springframework.cloud.client.loadbalancer.CompletionContext; +//import org.springframework.cloud.client.loadbalancer.DefaultResponse; +//import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties; +//import org.springframework.core.NestedRuntimeException; +//import java.util.*; +//import java.util.concurrent.CompletableFuture; +//import java.util.concurrent.CompletionStage; +// +///** +// * Provides an abstract base for implementing client clusters that can send requests and receive responses from +// * various endpoints. This class serves as a foundation for managing a cluster of client endpoints and handling +// * common operations such as creating HTTP headers and exceptions. +// * +// * @param the type of outbound requests the cluster handles +// * @param the type of outbound responses the cluster can expect +// */ +//public abstract class AbstractClientCluster< +// R extends AbstractClusterRequest, +// O extends OutboundResponse> +// implements LiveCluster { +// +// @Override +// public ClusterPolicy getDefaultPolicy(R request) { +// if (isRetryable()) { +// RetryPolicy retryPolicy = null; +// LoadBalancerProperties properties = request.getProperties(); +// LoadBalancerProperties.Retry retry = properties == null ? null : properties.getRetry(); +// if (retry != null && retry.isEnabled() && (request.getHttpMethod() == HttpMethod.GET || retry.isRetryOnAllOperations())) { +// Set statuses = new HashSet<>(retry.getRetryableStatusCodes().size()); +// retry.getRetryableStatusCodes().forEach(status -> statuses.add(String.valueOf(status))); +// retryPolicy = new RetryPolicy(); +// retryPolicy.setRetry(retry.getMaxRetriesOnNextServiceInstance()); +// retryPolicy.setRetryInterval(retry.getBackoff().getMinBackoff().toMillis()); +// retryPolicy.setRetryStatuses(statuses); +// } +// return new ClusterPolicy(retryPolicy == null ? ClusterInvoker.TYPE_FAILFAST : ClusterInvoker.TYPE_FAILOVER, retryPolicy); +// } +// return new ClusterPolicy(ClusterInvoker.TYPE_FAILFAST); +// } +// +// /** +// * Determines if the current cluster support for retry. +// * +// * @return {@code true} if the operation is retryable; {@code false} otherwise. +// */ +// protected abstract boolean isRetryable(); +// +// /** +// * Discover the service instances for the requested service. +// * +// * @param request The outbound request to be routed. +// * @return ServiceInstance list +// */ +// @Override +// public CompletionStage> route(R request) { +// CompletableFuture> future = new CompletableFuture<>(); +// ServiceInstanceListSupplier supplier = request.getInstanceSupplier(); +// if (supplier == null) { +// future.complete(new ArrayList<>()); +// } else { +// Mono> mono = supplier.get(request.getLbRequest()).next(); +// mono.subscribe( +// v -> { +// List endpoints = new ArrayList<>(); +// if (v != null) { +// v.forEach(i -> endpoints.add(new GrpcEndpoint(i))); +// } +// future.complete(endpoints); +// }, +// future::completeExceptionally +// ); +// } +// return future; +// } +// +// @Override +// public NestedRuntimeException createUnReadyException(String message, R request) { +// return createException(HttpStatus.FORBIDDEN, message); +// } +// +// @Override +// public NestedRuntimeException createUnReadyException(R request) { +// return createUnReadyException("The cluster is not ready. ", request); +// } +// +// @Override +// public NestedRuntimeException createException(Throwable throwable, R request, GrpcEndpoint endpoint) { +// if (throwable instanceof NestedRuntimeException) { +// return (NestedRuntimeException) throwable; +// } else if (throwable instanceof RejectException) { +// return createRejectException((RejectException) throwable, request); +// } +// return createException(HttpStatus.INTERNAL_SERVER_ERROR, throwable.getMessage(), throwable); +// } +// +// @Override +// public NestedRuntimeException createNoProviderException(R request) { +// return createException(HttpStatus.SERVICE_UNAVAILABLE, +// "LoadBalancer does not contain an instance for the service " + request.getService()); +// } +// +// @Override +// public NestedRuntimeException createLimitException(RejectException exception, R request) { +// return createException(HttpStatus.SERVICE_UNAVAILABLE, exception.getMessage()); +// } +// +// @Override +// public NestedRuntimeException createCircuitBreakException(RejectException exception, R request) { +// return createException(HttpStatus.SERVICE_UNAVAILABLE, exception.getMessage(), exception); +// } +// +// @Override +// public NestedRuntimeException createRejectException(RejectException exception, R request) { +// if (exception instanceof RejectNoProviderException) { +// return createNoProviderException(request); +// } else if (exception instanceof RejectLimitException) { +// return createLimitException(exception, request); +// } else if (exception instanceof RejectCircuitBreakException) { +// return createCircuitBreakException(exception, request); +// } +// return createException(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE, exception.getMessage()); +// } +// +// @Override +// public NestedRuntimeException createRetryExhaustedException(RetryExhaustedException exception, +// OutboundInvocation invocation) { +// return createException(exception, invocation.getRequest(), null); +// } +// +// @SuppressWarnings("unchecked") +// @Override +// public void onStart(R request) { +// request.lifecycles(l -> l.onStart(request.getLbRequest())); +// } +// +// @SuppressWarnings("unchecked") +// @Override +// public void onStartRequest(R request, GrpcEndpoint endpoint) { +// request.lifecycles(l -> l.onStartRequest(request.getLbRequest(), +// endpoint == null ? new DefaultResponse(null) : endpoint.getResponse())); +// } +// +// @SuppressWarnings("unchecked") +// @Override +// public void onError(Throwable throwable, R request, GrpcEndpoint endpoint) { +// request.lifecycles(l -> l.onComplete(new CompletionContext<>( +// CompletionContext.Status.FAILED, +// throwable, +// request.getLbRequest(), +// endpoint == null ? new DefaultResponse(null) : endpoint.getResponse()))); +// } +// +// /** +// * Creates an {@link HttpHeaders} instance from a map of header names to lists of header values. +// * If the input map is {@code null}, this method returns an empty {@link HttpHeaders} instance. +// * +// * @param headers a map of header names to lists of header values +// * @return an {@link HttpHeaders} instance representing the provided headers +// */ +// protected HttpHeaders getHttpHeaders(Map> headers) { +// return headers == null ? new HttpHeaders() : new HttpHeaders(new MultiValueMapAdapter<>(headers)); +// } +// +// /** +// * Creates an {@link NestedRuntimeException} using the provided status, message, and headers map. +// * +// * @param status the HTTP status code of the error +// * @param message the error message +// * @return an {@link NestedRuntimeException} instance with the specified details +// */ +// public static NestedRuntimeException createException(HttpStatus status, String message) { +// return createException(status, message, null); +// } +// +// /** +// * Creates an {@link NestedRuntimeException} using the provided status, message, and {@link HttpHeaders}. +// * +// * @param status the HTTP status code of the error +// * @param message the error message +// * @param throwable the exception +// * @return an {@link NestedRuntimeException} instance with the specified details +// */ +// public static NestedRuntimeException createException(HttpStatus status, String message, Throwable throwable) { +// return new ResponseStatusException(status.value(), message, throwable); +// } +//} +// diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/cluster/GrpcCluster.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/cluster/GrpcCluster.java new file mode 100644 index 000000000..73dec3928 --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/cluster/GrpcCluster.java @@ -0,0 +1,73 @@ +package com.jd.live.agent.plugin.router.gprc.cluster; + +import com.jd.live.agent.bootstrap.exception.RejectException; +import com.jd.live.agent.governance.exception.RetryException; +import com.jd.live.agent.governance.invoke.OutboundInvocation; +import com.jd.live.agent.governance.invoke.cluster.LiveCluster; +import com.jd.live.agent.plugin.router.gprc.instance.GrpcEndpoint; +import java.net.SocketAddress; +import java.util.List; +import java.util.concurrent.CompletionStage; +import com.jd.live.agent.plugin.router.gprc.response.GrpcResponse.GrpcOutboundResponse; +import com.jd.live.agent.plugin.router.gprc.request.GrpcRequest.GrpcOutboundRequest; + + +public class GrpcCluster implements LiveCluster { + + public GrpcCluster(SocketAddress socketAddress) { + } + + @Override + public CompletionStage> route(GrpcOutboundRequest request) { + System.out.println("---->route"); + return null; + } + + @Override + public CompletionStage invoke(GrpcOutboundRequest request, GrpcEndpoint endpoint) { + System.out.println("---->invoke"); + return null; + } + + @Override + public GrpcOutboundResponse createResponse(Throwable throwable, GrpcOutboundRequest request, GrpcEndpoint endpoint) { + System.out.println("---->createResponse"); + return null; + } + + @Override + public RuntimeException createException(Throwable throwable, GrpcOutboundRequest request, GrpcEndpoint endpoint) { + System.out.println("---->createException"); + return null; + } + + @Override + public RuntimeException createNoProviderException(GrpcOutboundRequest request) { + System.out.println("---->createNoProviderException"); + return null; + } + + @Override + public RuntimeException createLimitException(RejectException exception, GrpcOutboundRequest request) { + System.out.println("---->createLimitException"); + return null; + } + + @Override + public RuntimeException createCircuitBreakException(RejectException exception, GrpcOutboundRequest request) { + System.out.println("---->createCircuitBreakException"); + return null; + } + + @Override + public RuntimeException createRejectException(RejectException exception, GrpcOutboundRequest request) { + System.out.println("---->createRejectException"); + return null; + } + + @Override + public RuntimeException createRetryExhaustedException(RetryException.RetryExhaustedException exception, OutboundInvocation invocation) { + System.out.println("---->createRetryExhaustedException"); + return null; + } +} diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/definition/ClusterDefinition.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/definition/ClusterDefinition.java new file mode 100644 index 000000000..ec46f3fb4 --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/definition/ClusterDefinition.java @@ -0,0 +1,66 @@ +/* + * Copyright © ${year} ${owner} (${email}) + * + * 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.jd.live.agent.plugin.router.gprc.definition; + +import com.jd.live.agent.core.bytekit.matcher.MatcherBuilder; +import com.jd.live.agent.core.extension.annotation.ConditionalOnClass; +import com.jd.live.agent.core.extension.annotation.ConditionalOnProperty; +import com.jd.live.agent.core.extension.annotation.Extension; +import com.jd.live.agent.core.inject.annotation.Inject; +import com.jd.live.agent.core.inject.annotation.Injectable; +import com.jd.live.agent.core.parser.ObjectParser; +import com.jd.live.agent.core.plugin.definition.InterceptorDefinition; +import com.jd.live.agent.core.plugin.definition.InterceptorDefinitionAdapter; +import com.jd.live.agent.core.plugin.definition.PluginDefinitionAdapter; +import com.jd.live.agent.governance.config.GovernanceConfig; +import com.jd.live.agent.governance.invoke.InvocationContext; +import com.jd.live.agent.plugin.router.gprc.interceptor.ClusterInterceptor; + +@Injectable +@Extension(value = "GrpcClusterDefinition") +@ConditionalOnProperty(name = GovernanceConfig.CONFIG_FLOW_CONTROL_ENABLED, matchIfMissing = true) +@ConditionalOnProperty(name = GovernanceConfig.CONFIG_LIVE_SOFARPC_ENABLED, matchIfMissing = true) +@ConditionalOnClass(ClusterDefinition.TYPE_ABSTRACT_CLUSTER) +public class ClusterDefinition extends PluginDefinitionAdapter { + + protected static final String TYPE_ABSTRACT_CLUSTER = "net.devh.boot.grpc.client.nameresolver.DiscoveryClientNameResolver$Resolve"; + + private static final String METHOD_DO_INVOKE = "Resolve"; + + private static final String[] ARGUMENT_DO_INVOKE = new String[]{ + + }; + + @Inject(InvocationContext.COMPONENT_INVOCATION_CONTEXT) + private InvocationContext context; + + @Inject(ObjectParser.JSON) + private ObjectParser parser; + + public ClusterDefinition() { + System.out.println("----> ClusterDefinition"); + this.matcher = () -> MatcherBuilder.isSubTypeOf(TYPE_ABSTRACT_CLUSTER) + .and(MatcherBuilder.not(MatcherBuilder.isAbstract())); + this.interceptors = new InterceptorDefinition[]{ + new InterceptorDefinitionAdapter( + MatcherBuilder.named(METHOD_DO_INVOKE) + .or(MatcherBuilder.arguments(ARGUMENT_DO_INVOKE)), + () -> new ClusterInterceptor(context, parser) + ) + }; + } + +} diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/definition/GrpcClientCallDefinition.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/definition/GrpcClientCallDefinition.java new file mode 100644 index 000000000..76ef35737 --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/definition/GrpcClientCallDefinition.java @@ -0,0 +1,52 @@ +///* +// * Copyright © ${year} ${owner} (${email}) +// * +// * 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.jd.live.agent.plugin.router.gprc.definition; +// +//import com.jd.live.agent.core.bytekit.matcher.MatcherBuilder; +//import com.jd.live.agent.core.extension.annotation.*; +//import com.jd.live.agent.core.plugin.definition.InterceptorDefinitionAdapter; +//import com.jd.live.agent.core.plugin.definition.PluginDefinition; +//import com.jd.live.agent.core.plugin.definition.PluginDefinitionAdapter; +//import com.jd.live.agent.governance.config.GovernanceConfig; +//import com.jd.live.agent.plugin.router.gprc.interceptor.ClientCallImplInterceptor; +// +//@Extension(value = "GrpcClientCallDefinition", order = PluginDefinition.ORDER_TRANSMISSION) +//@ConditionalOnProperties(value = { +// @ConditionalOnProperty(value = GovernanceConfig.CONFIG_LIVE_ENABLED, matchIfMissing = true), +// @ConditionalOnProperty(value = GovernanceConfig.CONFIG_LANE_ENABLED, matchIfMissing = true), +// @ConditionalOnProperty(value = GovernanceConfig.CONFIG_FLOW_CONTROL_ENABLED, matchIfMissing = true) +//}, relation = ConditionalRelation.OR) +//@ConditionalOnClass(GrpcClientCallDefinition.TYPE_CLIENT_CALL_IMPL) +//public class GrpcClientCallDefinition extends PluginDefinitionAdapter { +// +// public static final String TYPE_CLIENT_CALL_IMPL = "io.grpc.internal.ClientCallImpl"; +// +// private static final String METHOD_START = "start"; +// +// private static final String[] ARGUMENTS_START = new String[]{ +// "io.grpc.ClientCall.Listener", +// "io.grpc.Metadata" +// }; +// +// +// public GrpcClientCallDefinition() { +// super(MatcherBuilder.named(TYPE_CLIENT_CALL_IMPL), +// new InterceptorDefinitionAdapter( +// MatcherBuilder.named(METHOD_START). +// and(MatcherBuilder.arguments(ARGUMENTS_START)), +// new ClientCallImplInterceptor())); +// } +//} diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/definition/GrpcServerCallDefinition.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/definition/GrpcServerCallDefinition.java new file mode 100644 index 000000000..31cbdcc0e --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/definition/GrpcServerCallDefinition.java @@ -0,0 +1,56 @@ +///* +// * Copyright © ${year} ${owner} (${email}) +// * +// * 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.jd.live.agent.plugin.router.gprc.definition; +// +//import com.jd.live.agent.core.bytekit.matcher.MatcherBuilder; +//import com.jd.live.agent.core.extension.annotation.*; +//import com.jd.live.agent.core.inject.annotation.Inject; +//import com.jd.live.agent.core.inject.annotation.Injectable; +//import com.jd.live.agent.core.plugin.definition.InterceptorDefinition; +//import com.jd.live.agent.core.plugin.definition.InterceptorDefinitionAdapter; +//import com.jd.live.agent.core.plugin.definition.PluginDefinition; +//import com.jd.live.agent.core.plugin.definition.PluginDefinitionAdapter; +//import com.jd.live.agent.governance.config.GovernanceConfig; +//import com.jd.live.agent.governance.context.bag.CargoRequire; +//import com.jd.live.agent.plugin.router.gprc.interceptor.GrpcServerInterceptor; +//import java.util.List; +// +//@Injectable +//@Extension(value = "GrpcServerCallDefinition", order = PluginDefinition.ORDER_TRANSMISSION) +//@ConditionalOnProperties(value = { +// @ConditionalOnProperty(value = GovernanceConfig.CONFIG_LIVE_ENABLED, matchIfMissing = true), +// @ConditionalOnProperty(value = GovernanceConfig.CONFIG_LANE_ENABLED, matchIfMissing = true), +// @ConditionalOnProperty(value = GovernanceConfig.CONFIG_FLOW_CONTROL_ENABLED, matchIfMissing = true) +//}, relation = ConditionalRelation.OR) +//@ConditionalOnClass(GrpcServerCallDefinition.TYPE_ABSTRACT_SERVER_IMPL_BUILDER) +//public class GrpcServerCallDefinition extends PluginDefinitionAdapter { +// +// public static final String TYPE_ABSTRACT_SERVER_IMPL_BUILDER = "io.grpc.internal.AbstractServerImplBuilder"; +// +// private static final String METHOD_BUILD = "build"; +// +// @Inject +// private List requires; +// +// public GrpcServerCallDefinition() { +// this.matcher = () -> MatcherBuilder.named(TYPE_ABSTRACT_SERVER_IMPL_BUILDER); +// this.interceptors = new InterceptorDefinition[]{ +// new InterceptorDefinitionAdapter( +// MatcherBuilder.named(METHOD_BUILD). +// and(MatcherBuilder.arguments(0)), +// () -> new GrpcServerInterceptor(requires))}; +// } +//} diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/instance/GrpcEndpoint.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/instance/GrpcEndpoint.java new file mode 100644 index 000000000..592b3791f --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/instance/GrpcEndpoint.java @@ -0,0 +1,40 @@ +package com.jd.live.agent.plugin.router.gprc.instance; + +import com.jd.live.agent.governance.instance.AbstractEndpoint; +import com.jd.live.agent.governance.instance.EndpointState; +import com.jd.live.agent.governance.request.ServiceRequest; +import net.devh.boot.grpc.client.config.GrpcChannelProperties; + +public class GrpcEndpoint extends AbstractEndpoint { + + private final GrpcChannelProperties properties; + + public GrpcEndpoint(GrpcChannelProperties properties) { + this.properties = properties; + } + + @Override + protected int computeWeight(ServiceRequest request) { + return 0; + } + + @Override + public String getHost() { + return properties.getAddress().getHost(); + } + + @Override + public int getPort() { + return properties.getAddress().getPort(); + } + + @Override + public String getLabel(String key) { + return ""; + } + + @Override + public EndpointState getState() { + return null; + } +} diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/interceptor/ClientCallImplInterceptor.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/interceptor/ClientCallImplInterceptor.java new file mode 100644 index 000000000..328aad908 --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/interceptor/ClientCallImplInterceptor.java @@ -0,0 +1,34 @@ +///* +// * Copyright © ${year} ${owner} (${email}) +// * +// * 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.jd.live.agent.plugin.router.gprc.interceptor; +// +//import com.jd.live.agent.bootstrap.bytekit.context.ExecutableContext; +//import com.jd.live.agent.core.plugin.definition.InterceptorAdaptor; +//import com.jd.live.agent.governance.context.RequestContext; +//import io.grpc.Metadata; +// +//public class ClientCallImplInterceptor extends InterceptorAdaptor { +// +// @Override +// public void onEnter(ExecutableContext ctx) { +// attachTag((Metadata) ctx.getArguments()[1]); +// } +// +// private void attachTag(Metadata metadata) { +// RequestContext.cargos(tag -> metadata.put(Metadata.Key.of(tag.getKey(), Metadata.ASCII_STRING_MARSHALLER), tag.getValue())); +// } +// +//} diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/interceptor/ClusterInterceptor.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/interceptor/ClusterInterceptor.java new file mode 100644 index 000000000..2e8bd12f0 --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/interceptor/ClusterInterceptor.java @@ -0,0 +1,51 @@ +package com.jd.live.agent.plugin.router.gprc.interceptor; + +import com.jd.live.agent.bootstrap.bytekit.context.ExecutableContext; +import com.jd.live.agent.bootstrap.bytekit.context.MethodContext; +import com.jd.live.agent.core.parser.ObjectParser; +import com.jd.live.agent.core.plugin.definition.InterceptorAdaptor; +import com.jd.live.agent.governance.invoke.InvocationContext; +//import com.jd.live.agent.governance.invoke.OutboundInvocation; +//import com.jd.live.agent.governance.response.ServiceError; +import com.jd.live.agent.plugin.router.gprc.cluster.GrpcCluster; +import java.net.SocketAddress; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +//import com.jd.live.agent.plugin.router.gprc.request.GrpcRequest; +//import com.jd.live.agent.plugin.router.gprc.request.GrpcRequest.GrpcOutboundRequest; +//import com.jd.live.agent.plugin.router.gprc.response.GrpcResponse; + +public class ClusterInterceptor extends InterceptorAdaptor { + + private final InvocationContext context; + + private final ObjectParser parser; + + private final Map clusters = new ConcurrentHashMap<>(); + + public ClusterInterceptor(InvocationContext context, ObjectParser parser) { + this.context = context; + this.parser = parser; + } + + @Override + public void onEnter(ExecutableContext ctx) { + MethodContext mc = (MethodContext) ctx; + Object[] arguments = ctx.getArguments(); +// GrpcCluster cluster = clusters.computeIfAbsent((SocketAddress) ctx.getTarget(), GrpcCluster::new); +// GrpcOutboundRequest request = new GrpcOutboundRequest((GrpcRequest) arguments[0], cluster); +// if (!request.isSystem() && !request.isDisabled()) { +// OutboundInvocation.RpcOutboundInvocation invocation = new OutboundInvocation.RpcOutboundInvocation(request, context); +// GrpcResponse.GrpcOutboundResponse response = cluster.request(invocation, null); +// ServiceError error = response.getError(); +// if (error != null && !error.isServerError()) { +// mc.setThrowable(error.getThrowable()); +// } else { +// mc.setResult(response.getResponse()); +// } +// mc.setSkip(true); +// } + } + +} diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/interceptor/GrpcServerInterceptor.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/interceptor/GrpcServerInterceptor.java new file mode 100644 index 000000000..92c15dc7a --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/interceptor/GrpcServerInterceptor.java @@ -0,0 +1,63 @@ +///* +// * Copyright © ${year} ${owner} (${email}) +// * +// * 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.jd.live.agent.plugin.router.gprc.interceptor; +// +//import com.jd.live.agent.bootstrap.bytekit.context.ExecutableContext; +//import com.jd.live.agent.core.plugin.definition.InterceptorAdaptor; +//import com.jd.live.agent.core.util.tag.Label; +//import com.jd.live.agent.governance.context.RequestContext; +//import com.jd.live.agent.governance.context.bag.CargoRequire; +//import com.jd.live.agent.governance.context.bag.CargoRequires; +//import io.grpc.*; +// +//import java.util.List; +// +//public class GrpcServerInterceptor extends InterceptorAdaptor { +// +// private final CargoRequire require; +// +// public GrpcServerInterceptor(List requires) { +// this.require = new CargoRequires(requires); +// } +// +// @Override +// public void onEnter(ExecutableContext ctx) { +// Object target = ctx.getTarget(); +// if (target instanceof ServerBuilder) { +// ((ServerBuilder) target).intercept(new ServerInterceptor() { +// @Override +// public ServerCall.Listener interceptCall( +// ServerCall call, Metadata headers, ServerCallHandler next) { +// if (headers != null) { +// RequestContext.create().addCargo(require, headers.keys(), +// name -> Label.parseValue(headers.get(Metadata.Key.of(name, Metadata.ASCII_STRING_MARSHALLER)))); +// } +// return next.startCall(new ForwardingServerCall() { +// @Override +// protected ServerCall delegate() { +// return call; +// } +// +// @Override +// public MethodDescriptor getMethodDescriptor() { +// return call.getMethodDescriptor(); +// } +// }, headers); +// } +// }); +// } +// } +//} diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/request/AbstractClusterRequest.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/request/AbstractClusterRequest.java new file mode 100644 index 000000000..df928bb92 --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/request/AbstractClusterRequest.java @@ -0,0 +1,242 @@ +///* +// * Copyright © ${year} ${owner} (${email}) +// * +// * 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.jd.live.agent.plugin.router.gprc.request; +// +//import com.jd.live.agent.core.util.cache.CacheObject; +//import com.jd.live.agent.core.util.cache.UnsafeLazyObject; +//import com.jd.live.agent.core.util.type.ClassDesc; +//import com.jd.live.agent.core.util.type.ClassUtils; +//import com.jd.live.agent.core.util.type.FieldDesc; +//import com.jd.live.agent.governance.request.AbstractHttpRequest.AbstractHttpOutboundRequest; +//import org.springframework.beans.factory.ObjectProvider; +//import org.springframework.cloud.client.ServiceInstance; +//import org.springframework.cloud.client.loadbalancer.*; +//import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; +//import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +//import org.springframework.http.HttpCookie; +//import org.springframework.http.server.reactive.ServerHttpRequest; +//import org.springframework.util.MultiValueMap; +// +//import java.util.HashSet; +//import java.util.Map; +//import java.util.Set; +//import java.util.concurrent.ConcurrentHashMap; +//import java.util.function.Consumer; +// +///** +// * Represents an outbound HTTP request in a reactive microservices architecture, +// * extending the capabilities of an abstract HTTP outbound request model to include +// * client-specific functionalities. This class encapsulates features such as load balancing, +// * service instance discovery, and lifecycle management, making it suitable for handling +// * dynamic client requests in a distributed system. +// */ +//public abstract class AbstractClusterRequest extends AbstractHttpOutboundRequest implements SpringClusterRequest { +// +// protected static final String FIELD_SERVICE_INSTANCE_LIST_SUPPLIER_PROVIDER = "serviceInstanceListSupplierProvider"; +// +// protected static final Map> LOAD_BALANCER_LIFE_CYCLES = new ConcurrentHashMap<>(); +// +// protected static final Map> SERVICE_INSTANCE_LIST_SUPPLIERS = new ConcurrentHashMap<>(); +// +// /** +// * A factory for creating instances of {@code ReactiveLoadBalancer} for service instances. +// * This factory is used to obtain a load balancer instance for the service associated with +// * this request. +// */ +// protected final ReactiveLoadBalancer.Factory loadBalancerFactory; +// +// /** +// * A lazy-initialized object of {@code Set}, representing the lifecycle +// * processors for the load balancer. These processors provide hooks for custom logic at various +// * stages of the load balancing process. +// */ +// protected final UnsafeLazyObject> lifecycles; +// +// /** +// * A lazy-initialized {@code Request} object that encapsulates the original request data +// * along with any hints to influence load balancing decisions. +// */ +// protected final UnsafeLazyObject> lbRequest; +// +// /** +// * A lazy-initialized {@code LoadBalancerProperties} object, containing configuration +// * properties for load balancing. +// */ +// protected final UnsafeLazyObject properties; +// +// /** +// * A lazy-initialized {@code RequestData} object, representing the data of the original +// * request that will be used by the load balancer to select an appropriate service instance. +// */ +// protected final UnsafeLazyObject requestData; +// +// /** +// * A lazy-initialized {@code ServiceInstanceListSupplier} object, responsible for providing +// * a list of available service instances for load balancing. +// */ +// protected final UnsafeLazyObject instanceSupplier; +// +// protected final UnsafeLazyObject stickyId; +// +// /** +// * Constructs a new ClientOutboundRequest with the specified parameters. +// * +// * @param request The original client request to be processed. +// * @param loadBalancerFactory A factory for creating instances of ReactiveLoadBalancer for service instances. +// */ +// public AbstractClusterRequest(T request, +// ReactiveLoadBalancer.Factory loadBalancerFactory) { +// super(request); +// this.loadBalancerFactory = loadBalancerFactory; +// this.lifecycles = new UnsafeLazyObject<>(this::buildLifecycleProcessors); +// this.properties = new UnsafeLazyObject<>(this::buildProperties); +// this.lbRequest = new UnsafeLazyObject<>(this::buildLbRequest); +// this.instanceSupplier = new UnsafeLazyObject<>(this::buildServiceInstanceListSupplier); +// this.requestData = new UnsafeLazyObject<>(this::buildRequestData); +// this.stickyId = new UnsafeLazyObject<>(this::buildStickyId); +// } +// +// @Override +// public String getCookie(String key) { +// if (key == null || key.isEmpty()) { +// return null; +// } else if (request instanceof ServerHttpRequest) { +// ServerHttpRequest httpRequest = (ServerHttpRequest) request; +// HttpCookie cookie = httpRequest.getCookies().getFirst(key); +// return cookie == null ? null : cookie.getValue(); +// } else { +// return super.getCookie(key); +// } +// } +// +// @Override +// public String getStickyId() { +// return stickyId.get(); +// } +// +// @Override +// public void lifecycles(Consumer consumer) { +// if (lifecycles != null && consumer != null) { +// lifecycles.get().forEach(consumer); +// } +// } +// +// public Request getLbRequest() { +// return lbRequest.get(); +// } +// +// public LoadBalancerProperties getProperties() { +// return properties.get(); +// } +// +// public ServiceInstanceListSupplier getInstanceSupplier() { +// return instanceSupplier.get(); +// } +// +// public RequestData getRequestData() { +// return requestData.get(); +// } +// +// /** +// * Creates a new {@code RequestData} object representing the data of the original request. +// * This abstract method must be implemented by subclasses to provide specific request data +// * for the load balancing process. +// * +// * @return a new {@code RequestData} object +// */ +// protected abstract RequestData buildRequestData(); +// +// private LoadBalancerProperties buildProperties() { +// return loadBalancerFactory == null ? null : loadBalancerFactory.getProperties(getService()); +// } +// +// /** +// * Constructs a set of lifecycle processors for the load balancer. These processors are responsible +// * for providing custom logic that can be executed during various stages of the load balancing process, +// * such as before and after choosing a server, and before and after the request is completed. +// * +// * @return A set of LoadBalancerLifecycle objects that are compatible with the current service and request/response types. +// */ +// private Set buildLifecycleProcessors() { +// return LOAD_BALANCER_LIFE_CYCLES.computeIfAbsent(getService(), service -> loadBalancerFactory == null +// ? new HashSet<>() +// : LoadBalancerLifecycleValidator.getSupportedLifecycleProcessors( +// loadBalancerFactory.getInstances(service, LoadBalancerLifecycle.class), +// RequestDataContext.class, +// ResponseData.class, +// ServiceInstance.class)); +// } +// +// /** +// * Creates a new load balancer request object that encapsulates the original request data along with +// * any hints that may influence load balancing decisions. This object is used by the load balancer to +// * select an appropriate service instance based on the provided hints and other criteria. +// * +// * @return A DefaultRequest object containing the context for the load balancing operation. +// */ +// private DefaultRequest buildLbRequest() { +// LoadBalancerProperties properties = getProperties(); +// Map hints = properties == null ? null : properties.getHint(); +// String defaultHint = hints == null ? null : hints.getOrDefault("default", "default"); +// String hint = hints == null ? null : hints.getOrDefault(getService(), defaultHint); +// return new DefaultRequest<>(new RequestDataContext(getRequestData(), hint)); +// } +// +// /** +// * Builds a supplier of service instances for load balancing. This supplier is responsible for providing +// * a list of available service instances that the load balancer can use to distribute the incoming requests. +// * The supplier is obtained from the load balancer instance if it provides one. +// * +// * @return A ServiceInstanceListSupplier that provides a list of available service instances, or null if the +// * load balancer does not provide such a supplier. +// */ +// @SuppressWarnings("unchecked") +// private ServiceInstanceListSupplier buildServiceInstanceListSupplier() { +// return SERVICE_INSTANCE_LIST_SUPPLIERS.computeIfAbsent(getService(), service -> { +// ReactiveLoadBalancer loadBalancer = loadBalancerFactory == null ? null : loadBalancerFactory.getInstance(getService()); +// if (loadBalancer != null) { +// ClassDesc describe = ClassUtils.describe(loadBalancer.getClass()); +// FieldDesc field = describe.getFieldList().getField(FIELD_SERVICE_INSTANCE_LIST_SUPPLIER_PROVIDER); +// if (field != null) { +// ObjectProvider provider = (ObjectProvider) field.get(loadBalancer); +// return CacheObject.of(provider.getIfAvailable()); +// } +// } +// return CacheObject.of(null); +// }).get(); +// +// } +// +// /** +// * Extracts the identifier from a sticky session cookie. +// * +// * @return The value of the sticky session cookie if present; otherwise, {@code null}. +// * This value is used to identify the server instance that should handle requests +// * from this client to ensure session persistence. +// */ +// private String buildStickyId() { +// LoadBalancerProperties properties = getProperties(); +// if (properties != null) { +// String instanceIdCookieName = properties.getStickySession().getInstanceIdCookieName(); +// Object context = getLbRequest().getContext(); +// if (context instanceof RequestDataContext) { +// MultiValueMap cookies = ((RequestDataContext) context).getClientRequest().getCookies(); +// return cookies == null ? null : cookies.getFirst(instanceIdCookieName); +// } +// } +// return null; +// } +//} diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/request/GrpcClusterRequest.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/request/GrpcClusterRequest.java new file mode 100644 index 000000000..86dc80593 --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/request/GrpcClusterRequest.java @@ -0,0 +1,74 @@ +///* +// * Copyright © ${year} ${owner} (${email}) +// * +// * 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.jd.live.agent.plugin.router.gprc.request; +// +//import com.jd.live.agent.governance.request.HttpRequest.HttpOutboundRequest; +// +//import java.util.function.Consumer; +// +///** +// * Defines the contract for an HTTP outbound request within a reactive microservices +// * architecture, focusing on integration with Spring Cloud's load balancing features. +// */ +//public interface GrpcClusterRequest extends HttpOutboundRequest { +// +// /** +// * Retrieves the load balancer request object that encapsulates the original request +// * data along with any hints that may influence load balancing decisions. This object +// * is used by the load balancer to select an appropriate service instance based on the +// * provided hints and other criteria. +// * +// * @return A {@code Request} object containing the context for the load balancing operation. +// */ +// Request getLbRequest(); +// +// /** +// * Gets the properties associated with the load balancing operation. These properties +// * may include configurations and hints that help tailor the load balancing behavior +// * to the needs of the specific request or service. +// * +// * @return An instance of {@code LoadBalancerProperties} containing load balancing configuration. +// */ +// LoadBalancerProperties getProperties(); +// +// /** +// * Obtains a supplier of service instances for load balancing. This supplier is responsible +// * for providing a list of available service instances that the load balancer can use to +// * distribute incoming requests. The implementation of this method is crucial for enabling +// * dynamic service discovery and selection. +// * +// * @return A {@code ServiceInstanceListSupplier} that provides a list of available service instances. +// */ +// ServiceInstanceListSupplier getInstanceSupplier(); +// +// /** +// * Retrieves the request data that will be used by the load balancer to make service instance +// * selection decisions. This data typically includes the original request information and any +// * additional metadata or hints relevant to load balancing. +// * +// * @return An instance of {@code RequestData} representing the data of the original request. +// */ +// RequestData getRequestData(); +// +// /** +// * Executes custom logic across the set of lifecycle processors associated with the load balancer, +// * allowing for enhanced control and monitoring of the load balancing process. +// * +// * @param consumer A consumer that accepts {@code LoadBalancerLifecycle} instances for processing. +// */ +// void lifecycles(Consumer consumer); +//} +// diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/request/GrpcRequest.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/request/GrpcRequest.java new file mode 100644 index 000000000..5ed686edc --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/request/GrpcRequest.java @@ -0,0 +1,58 @@ +/* + * Copyright © ${year} ${owner} (${email}) + * + * 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.jd.live.agent.plugin.router.gprc.request; + +import com.jd.live.agent.governance.request.AbstractRpcRequest.AbstractRpcInboundRequest; +import com.jd.live.agent.governance.request.AbstractRpcRequest.AbstractRpcOutboundRequest; +import com.jd.live.agent.plugin.router.gprc.cluster.GrpcCluster; + +public interface GrpcRequest { + + class GrpcInboundRequest extends AbstractRpcInboundRequest implements GrpcRequest { + + public GrpcInboundRequest(T request) { + super(request); + } + + @Override + public String getClientIp() { + return null; + } + + @Override + public boolean isSystem() { + return false; + } + } + + class GrpcOutboundRequest extends AbstractRpcOutboundRequest implements GrpcRequest { + + public GrpcOutboundRequest(T request) { + super(request); + + } + + public GrpcOutboundRequest(T request, GrpcCluster cluster) { + super(request); + + } + + @Override + public boolean isSystem() { + return false; + } + } +} diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/request/invoke/GrpcInvocation.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/request/invoke/GrpcInvocation.java new file mode 100644 index 000000000..6603eb3fa --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/request/invoke/GrpcInvocation.java @@ -0,0 +1,39 @@ +/* + * Copyright © ${year} ${owner} (${email}) + * + * 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.jd.live.agent.plugin.router.gprc.request.invoke; + +import com.jd.live.agent.governance.invoke.InboundInvocation.RpcInboundInvocation; +import com.jd.live.agent.governance.invoke.InvocationContext; +import com.jd.live.agent.governance.invoke.OutboundInvocation.RpcOutboundInvocation; +import com.jd.live.agent.plugin.router.gprc.request.GrpcRequest.GrpcOutboundRequest; +import com.jd.live.agent.plugin.router.gprc.request.GrpcRequest.GrpcInboundRequest; + +public interface GrpcInvocation { + + class GrpcInboundInvocation extends RpcInboundInvocation implements GrpcInvocation { + + public GrpcInboundInvocation(GrpcInboundRequest request, InvocationContext context) { + super(request, context); + } + } + + class GrpcOutboundInvocation extends RpcOutboundInvocation implements GrpcInvocation { + + public GrpcOutboundInvocation(GrpcOutboundRequest request, InvocationContext context) { + super(request, context); + } + } +} diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/response/GrpcResponse.java b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/response/GrpcResponse.java new file mode 100644 index 000000000..7b100a2c2 --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/java/com/jd/live/agent/plugin/router/gprc/response/GrpcResponse.java @@ -0,0 +1,43 @@ +/* + * Copyright © ${year} ${owner} (${email}) + * + * 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.jd.live.agent.plugin.router.gprc.response; + +import com.jd.live.agent.governance.response.AbstractRpcResponse.AbstractRpcOutboundResponse; +import com.jd.live.agent.governance.response.ServiceError; +import java.util.function.Predicate; + +/** + * Represents a generic response in the Dubbo RPC framework. This interface serves as a marker + * for responses that are specific to Dubbo's communication model, allowing for a common handling + * mechanism for all types of Dubbo responses. + */ +public interface GrpcResponse { + + class GrpcOutboundResponse extends AbstractRpcOutboundResponse implements GrpcResponse { + + public GrpcOutboundResponse(T response) { + this(response, null); + } + + public GrpcOutboundResponse(ServiceError error, Predicate predicate) { + super(null, error, predicate); + } + + public GrpcOutboundResponse(T response, Predicate predicate) { + super(response, null, predicate); + } + } +} diff --git a/joylive-plugin/joylive-router/joylive-router-grpc/src/main/resources/META-INF/services/com.jd.live.agent.core.plugin.definition.PluginDefinition b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/resources/META-INF/services/com.jd.live.agent.core.plugin.definition.PluginDefinition new file mode 100644 index 000000000..6f023df34 --- /dev/null +++ b/joylive-plugin/joylive-router/joylive-router-grpc/src/main/resources/META-INF/services/com.jd.live.agent.core.plugin.definition.PluginDefinition @@ -0,0 +1,2 @@ +com.jd.live.agent.plugin.router.gprc.definition.ClusterDefinition +#com.jd.live.agent.plugin.router.gprc.definition.GrpcClientCallDefinition diff --git a/joylive-plugin/joylive-router/pom.xml b/joylive-plugin/joylive-router/pom.xml index 2c88abace..d49b84e11 100644 --- a/joylive-plugin/joylive-router/pom.xml +++ b/joylive-plugin/joylive-router/pom.xml @@ -25,6 +25,7 @@ joylive-router-rocketmq5 joylive-router-rocketmq4 joylive-router-kafka3 + joylive-router-grpc \ No newline at end of file