HTTP/3 support for dubbo triple (#14033)

This commit is contained in:
Sean Yang 2024-06-16 14:26:29 +08:00 committed by GitHub
parent 667f98a459
commit 35adffac48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
77 changed files with 3386 additions and 484 deletions

View File

@ -75,6 +75,7 @@ dubbo-registry-zookeeper
dubbo-remoting
dubbo-remoting-api
dubbo-remoting-http12
dubbo-remoting-http3
dubbo-remoting-netty
dubbo-remoting-netty4
dubbo-remoting-zookeeper-curator5

View File

@ -27,7 +27,10 @@ import java.nio.charset.StandardCharsets;
/**
* Stream utils.
*/
public class StreamUtils {
public final class StreamUtils {
public static final ByteArrayInputStream EMPTY = new ByteArrayInputStream(new byte[0]);
private StreamUtils() {}
public static InputStream limitedInputStream(final InputStream is, final int limit) throws IOException {

View File

@ -16,6 +16,7 @@
*/
package org.apache.dubbo.config.context;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.context.ApplicationExt;
import org.apache.dubbo.common.extension.DisableInject;
import org.apache.dubbo.common.logger.Logger;
@ -72,13 +73,14 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE
TracingConfig.class));
}
public static ProtocolConfig getProtocol(URL url) {
return url.getOrDefaultApplicationModel().getApplicationConfigManager().getOrAddProtocol(url.getProtocol());
}
// ApplicationConfig correlative methods
/**
* Set application config
*
* @param application
* @return current application config instance
*/
@DisableInject
public void setApplication(ApplicationConfig application) {
@ -147,7 +149,7 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE
if (CollectionUtils.isEmpty(defaults)) {
defaults = getConfigCenters();
}
return Optional.ofNullable(defaults);
return ofNullable(defaults);
}
public Optional<ConfigCenterConfig> getConfigCenter(String id) {
@ -217,6 +219,7 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE
}
@Override
@SuppressWarnings("RedundantMethodOverride")
public <C extends AbstractConfig> List<C> getDefaultConfigs(Class<C> cls) {
return getDefaultConfigs(getConfigsMap(getTagName(cls)));
}
@ -289,7 +292,7 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE
// load dubbo.metadata-report.xxx
loadConfigsOfTypeFromProps(MetadataReportConfig.class);
// config centers has bean loaded before starting config center
// config centers has been loaded before starting config center
// loadConfigsOfTypeFromProps(ConfigCenterConfig.class);
refreshAll();
@ -319,7 +322,7 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE
// check port conflicts
Map<Integer, ProtocolConfig> protocolPortMap = new LinkedHashMap<>();
for (ProtocolConfig protocol : this.getProtocols()) {
for (ProtocolConfig protocol : getProtocols()) {
Integer port = protocol.getPort();
if (port == null || port == -1) {
continue;

View File

@ -100,6 +100,99 @@ public class TripleConfig implements Serializable {
*/
private Integer maxHeaderListSize;
/**
* Enable http3 support
* <p>The default value is false.
*/
private Boolean enableHttp3;
/**
* See <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_data">set_initial_max_data</a>.
* <p>The default value is 8MiB.
*/
private Integer http3InitialMaxData;
/**
* If configured this will enable <a href="https://tools.ietf.org/html/draft-ietf-quic-datagram-01">Datagram support.</a>
*/
private Integer http3RecvQueueLen;
/**
* If configured this will enable <a href="https://tools.ietf.org/html/draft-ietf-quic-datagram-01">Datagram support.</a>
*/
private Integer http3SendQueueLen;
/**
* See
* <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_stream_data_bidi_local">set_initial_max_stream_data_bidi_local</a>.
* <p>The default value is 1MiB.
*/
private Integer http3InitialMaxStreamDataBidiLocal;
/**
* See
* <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_stream_data_bidi_remote">set_initial_max_stream_data_bidi_remote</a>.
* <p>The default value is 1MiB.
*/
private Integer http3InitialMaxStreamDataBidiRemote;
/**
* See
* <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_stream_data_uni">set_initial_max_stream_data_uni</a>.
* <p>The default value is 0.
*/
private Integer http3InitialMaxStreamDataUni;
/**
* See
* <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_streams_bidi">set_initial_max_streams_bidi</a>.
* <p>The default value is 1B(2^30).
*/
private Long http3InitialMaxStreamsBidi;
/**
* See
* <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_streams_uni">set_initial_max_streams_uni</a>.
* <p>
* <p>The default value is 1B(2^30).
*/
private Long http3InitialMaxStreamsUni;
/**
* See
* <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_ack_delay_exponent">set_ack_delay_exponent</a>.
* <p>The default value is 3.
*/
private Integer http3MaxAckDelayExponent;
/**
* See
* <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_max_ack_delay">set_max_ack_delay</a>.
* <p>The default value is 25 milliseconds.
*/
private Integer http3MaxAckDelay;
/**
* See
* <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_disable_active_migration">set_disable_active_migration</a>.
* <p>The default value is {@code false}.
*/
private Boolean http3DisableActiveMigration;
/**
* See
* <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.enable_hystart">enable_hystart</a>.
* <p>The default value is {@code true}.
*/
private Boolean http3EnableHystart;
/**
* Sets the congestion control algorithm to use.
* <p>Supported algorithms are {@code "RENO"} or {@code "CUBIC"} or {@code "BBR"}.
* <p>The default value is {@code "CUBIC"}.
*/
private String http3CcAlgorithm;
public Integer getMaxBodySize() {
return maxBodySize;
}
@ -196,6 +289,118 @@ public class TripleConfig implements Serializable {
this.maxHeaderListSize = maxHeaderListSize;
}
public Boolean getEnableHttp3() {
return enableHttp3;
}
public void setEnableHttp3(Boolean enableHttp3) {
this.enableHttp3 = enableHttp3;
}
public Integer getHttp3InitialMaxData() {
return http3InitialMaxData;
}
public void setHttp3InitialMaxData(Integer http3InitialMaxData) {
this.http3InitialMaxData = http3InitialMaxData;
}
public Integer getHttp3RecvQueueLen() {
return http3RecvQueueLen;
}
public void setHttp3RecvQueueLen(Integer http3RecvQueueLen) {
this.http3RecvQueueLen = http3RecvQueueLen;
}
public Integer getHttp3SendQueueLen() {
return http3SendQueueLen;
}
public void setHttp3SendQueueLen(Integer http3SendQueueLen) {
this.http3SendQueueLen = http3SendQueueLen;
}
public Integer getHttp3InitialMaxStreamDataBidiLocal() {
return http3InitialMaxStreamDataBidiLocal;
}
public void setHttp3InitialMaxStreamDataBidiLocal(Integer http3InitialMaxStreamDataBidiLocal) {
this.http3InitialMaxStreamDataBidiLocal = http3InitialMaxStreamDataBidiLocal;
}
public Integer getHttp3InitialMaxStreamDataBidiRemote() {
return http3InitialMaxStreamDataBidiRemote;
}
public void setHttp3InitialMaxStreamDataBidiRemote(Integer http3InitialMaxStreamDataBidiRemote) {
this.http3InitialMaxStreamDataBidiRemote = http3InitialMaxStreamDataBidiRemote;
}
public Integer getHttp3InitialMaxStreamDataUni() {
return http3InitialMaxStreamDataUni;
}
public void setHttp3InitialMaxStreamDataUni(Integer http3InitialMaxStreamDataUni) {
this.http3InitialMaxStreamDataUni = http3InitialMaxStreamDataUni;
}
public Long getHttp3InitialMaxStreamsBidi() {
return http3InitialMaxStreamsBidi;
}
public void setHttp3InitialMaxStreamsBidi(Long http3InitialMaxStreamsBidi) {
this.http3InitialMaxStreamsBidi = http3InitialMaxStreamsBidi;
}
public Long getHttp3InitialMaxStreamsUni() {
return http3InitialMaxStreamsUni;
}
public void setHttp3InitialMaxStreamsUni(Long http3InitialMaxStreamsUni) {
this.http3InitialMaxStreamsUni = http3InitialMaxStreamsUni;
}
public Integer getHttp3MaxAckDelayExponent() {
return http3MaxAckDelayExponent;
}
public void setHttp3MaxAckDelayExponent(Integer http3MaxAckDelayExponent) {
this.http3MaxAckDelayExponent = http3MaxAckDelayExponent;
}
public Integer getHttp3MaxAckDelay() {
return http3MaxAckDelay;
}
public void setHttp3MaxAckDelay(Integer http3MaxAckDelay) {
this.http3MaxAckDelay = http3MaxAckDelay;
}
public Boolean getHttp3DisableActiveMigration() {
return http3DisableActiveMigration;
}
public void setHttp3DisableActiveMigration(Boolean http3DisableActiveMigration) {
this.http3DisableActiveMigration = http3DisableActiveMigration;
}
public Boolean getHttp3EnableHystart() {
return http3EnableHystart;
}
public void setHttp3EnableHystart(Boolean http3EnableHystart) {
this.http3EnableHystart = http3EnableHystart;
}
public String getHttp3CcAlgorithm() {
return http3CcAlgorithm;
}
public void setHttp3CcAlgorithm(String http3CcAlgorithm) {
this.http3CcAlgorithm = http3CcAlgorithm;
}
public void checkDefault() {
if (maxBodySize == null) {
maxBodySize = 1 << 23;
@ -233,5 +438,20 @@ public class TripleConfig implements Serializable {
if (maxHeaderListSize == null) {
maxHeaderListSize = 1 << 15;
}
if (http3InitialMaxData == null) {
http3InitialMaxData = 1 << 23;
}
if (http3InitialMaxStreamDataBidiLocal == null) {
http3InitialMaxStreamDataBidiLocal = 1 << 20;
}
if (http3InitialMaxStreamDataBidiRemote == null) {
http3InitialMaxStreamDataBidiRemote = 1 << 20;
}
if (http3InitialMaxStreamsBidi == null) {
http3InitialMaxStreamsBidi = (long) 1 << 30;
}
if (http3InitialMaxStreamsUni == null) {
http3InitialMaxStreamsUni = (long) 1 << 30;
}
}
}

View File

@ -127,6 +127,15 @@
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-http3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>

View File

@ -16,6 +16,7 @@
*/
package org.apache.dubbo.demo;
import org.apache.dubbo.common.stream.StreamObserver;
import org.apache.dubbo.demo.hello.HelloReply;
import org.apache.dubbo.demo.hello.HelloRequest;
@ -28,5 +29,18 @@ public interface GreeterService {
*/
HelloReply sayHello(HelloRequest request);
/**
* Sends a greeting asynchronously
*/
CompletableFuture<String> sayHelloAsync(String request);
/**
* Sends a greeting with server streaming
*/
void sayHelloServerStream(HelloRequest request, StreamObserver<HelloReply> responseObserver);
/**
* Sends greetings with bi streaming
*/
StreamObserver<HelloRequest> sayHelloBiStream(StreamObserver<HelloReply> responseObserver);
}

View File

@ -16,20 +16,65 @@
*/
package org.apache.dubbo.demo;
import org.apache.dubbo.common.stream.StreamObserver;
import org.apache.dubbo.demo.hello.HelloReply;
import org.apache.dubbo.demo.hello.HelloRequest;
import java.util.concurrent.CompletableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GreeterServiceImpl implements GreeterService {
private static final Logger LOG = LoggerFactory.getLogger(GreeterServiceImpl.class);
@Override
public HelloReply sayHello(HelloRequest request) {
return HelloReply.newBuilder().setMessage("Hello " + request.getName()).build();
LOG.info("Received sayHello request: {}", request.getName());
return toReply("Hello " + request.getName());
}
@Override
public CompletableFuture<String> sayHelloAsync(String name) {
return CompletableFuture.supplyAsync(() -> name);
LOG.info("Received sayHelloAsync request: {}", name);
return CompletableFuture.supplyAsync(() -> "Hello " + name);
}
@Override
public void sayHelloServerStream(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
LOG.info("Received sayHelloServerStream request");
for (int i = 1; i < 6; i++) {
LOG.info("sayHelloServerStream onNext: {} {} times", request.getName(), i);
responseObserver.onNext(toReply("Hello " + request.getName() + ' ' + i + " times"));
}
LOG.info("sayHelloServerStream onCompleted");
responseObserver.onCompleted();
}
@Override
public StreamObserver<HelloRequest> sayHelloBiStream(StreamObserver<HelloReply> responseObserver) {
LOG.info("Received sayHelloBiStream request");
return new StreamObserver<HelloRequest>() {
@Override
public void onNext(HelloRequest request) {
LOG.info("sayHelloBiStream onNext: {}", request.getName());
responseObserver.onNext(toReply("Hello " + request.getName()));
}
@Override
public void onError(Throwable throwable) {
LOG.error("sayHelloBiStream onError", throwable);
}
@Override
public void onCompleted() {
LOG.info("sayHelloBiStream onCompleted");
}
};
}
private static HelloReply toReply(String message) {
return HelloReply.newBuilder().setMessage(message).build();
}
}

View File

@ -17,6 +17,7 @@
package org.apache.dubbo.demo.consumer;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.stream.StreamObserver;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.ReferenceConfig;
@ -25,19 +26,23 @@ import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.demo.GreeterService;
import org.apache.dubbo.demo.hello.HelloReply;
import org.apache.dubbo.demo.hello.HelloRequest;
import org.apache.dubbo.rpc.Constants;
import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class ApiConsumer {
public static void main(String[] args) throws InterruptedException, IOException {
public static void main(String[] args) throws InterruptedException {
ReferenceConfig<GreeterService> referenceConfig = new ReferenceConfig<>();
referenceConfig.setInterface(GreeterService.class);
referenceConfig.setCheck(false);
referenceConfig.setProtocol(CommonConstants.TRIPLE);
referenceConfig.setLazy(true);
referenceConfig.setTimeout(100000);
if (args.length > 0 && Constants.HTTP3_KEY.equals(args[0])) {
referenceConfig.setParameters(Collections.singletonMap(Constants.HTTP3_KEY, "true"));
}
DubboBootstrap bootstrap = DubboBootstrap.getInstance();
bootstrap
@ -50,16 +55,63 @@ public class ApiConsumer {
GreeterService greeterService = referenceConfig.get();
System.out.println("dubbo referenceConfig started");
try {
final HelloReply reply = greeterService.sayHello(
HelloRequest.newBuilder().setName("triple").build());
TimeUnit.SECONDS.sleep(1);
System.out.println("Reply: " + reply.getMessage());
System.out.println("Call sayHello");
HelloReply reply = greeterService.sayHello(buildRequest("triple"));
System.out.println("sayHello reply: " + reply.getMessage());
System.out.println("Call sayHelloAsync");
CompletableFuture<String> sayHelloAsync = greeterService.sayHelloAsync("triple");
System.out.println("Async Reply: " + sayHelloAsync.get());
sayHelloAsync.thenAccept(value -> System.out.println("sayHelloAsync reply: " + value));
StreamObserver<HelloReply> responseObserver = new StreamObserver<HelloReply>() {
@Override
public void onNext(HelloReply reply) {
System.out.println("sayHelloServerStream onNext: " + reply.getMessage());
}
@Override
public void onError(Throwable t) {
System.out.println("sayHelloServerStream onError: " + t.getMessage());
}
@Override
public void onCompleted() {
System.out.println("sayHelloServerStream onCompleted");
}
};
System.out.println("Call sayHelloServerStream");
greeterService.sayHelloServerStream(buildRequest("triple"), responseObserver);
StreamObserver<HelloReply> biResponseObserver = new StreamObserver<HelloReply>() {
@Override
public void onNext(HelloReply reply) {
System.out.println("biRequestObserver onNext: " + reply.getMessage());
}
@Override
public void onError(Throwable t) {
System.out.println("biResponseObserver onError: " + t.getMessage());
}
@Override
public void onCompleted() {
System.out.println("biResponseObserver onCompleted");
}
};
System.out.println("Call biRequestObserver");
StreamObserver<HelloRequest> biRequestObserver = greeterService.sayHelloBiStream(biResponseObserver);
for (int i = 0; i < 5; i++) {
biRequestObserver.onNext(buildRequest("triple" + i));
}
biRequestObserver.onCompleted();
} catch (Throwable t) {
//noinspection CallToPrintStackTrace
t.printStackTrace();
}
System.in.read();
Thread.sleep(2000);
}
private static HelloRequest buildRequest(String name) {
return HelloRequest.newBuilder().setName(name).build();
}
}

View File

@ -23,8 +23,10 @@ import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.demo.GreeterWrapperService;
import org.apache.dubbo.rpc.Constants;
import java.io.IOException;
import java.util.Collections;
public class ApiWrapperConsumer {
public static void main(String[] args) throws IOException {
@ -33,6 +35,9 @@ public class ApiWrapperConsumer {
referenceConfig.setCheck(false);
referenceConfig.setProtocol("tri");
referenceConfig.setLazy(true);
if (args.length > 0 && Constants.HTTP3_KEY.equals(args[0])) {
referenceConfig.setParameters(Collections.singletonMap(Constants.HTTP3_KEY, "true"));
}
DubboBootstrap bootstrap = DubboBootstrap.getInstance();
bootstrap

View File

@ -24,12 +24,19 @@ import org.apache.dubbo.config.ServiceConfig;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.demo.GreeterService;
import org.apache.dubbo.demo.GreeterServiceImpl;
import org.apache.dubbo.rpc.Constants;
import java.util.Collections;
public class ApiProvider {
public static void main(String[] args) throws InterruptedException {
public static void main(String[] args) {
ServiceConfig<GreeterService> serviceConfig = new ServiceConfig<>();
serviceConfig.setInterface(GreeterService.class);
serviceConfig.setRef(new GreeterServiceImpl());
if (args.length > 0 && Constants.HTTP3_KEY.equals(args[0])) {
serviceConfig.setParameters(Collections.singletonMap(Constants.HTTP3_KEY, "true"));
}
DubboBootstrap bootstrap = DubboBootstrap.getInstance();
bootstrap

View File

@ -24,14 +24,19 @@ import org.apache.dubbo.config.ServiceConfig;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.demo.GreeterWrapperService;
import org.apache.dubbo.demo.GreeterWrapperServiceImpl;
import org.apache.dubbo.rpc.Constants;
import java.io.IOException;
import java.util.Collections;
public class ApiWrapperProvider {
public static void main(String[] args) throws IOException {
ServiceConfig<GreeterWrapperService> serviceConfig = new ServiceConfig<>();
serviceConfig.setInterface(GreeterWrapperService.class);
serviceConfig.setRef(new GreeterWrapperServiceImpl());
if (args.length > 0 && Constants.HTTP3_KEY.equals(args[0])) {
serviceConfig.setParameters(Collections.singletonMap(Constants.HTTP3_KEY, "true"));
}
DubboBootstrap bootstrap = DubboBootstrap.getInstance();
bootstrap

View File

@ -96,6 +96,7 @@
<byte-buddy_version>1.14.16</byte-buddy_version>
<netty_version>3.2.10.Final</netty_version>
<netty4_version>4.1.110.Final</netty4_version>
<netty_http3_version>0.0.28.Final</netty_http3_version>
<httpclient_version>4.5.14</httpclient_version>
<httpcore_version>4.4.16</httpcore_version>
<fastjson_version>1.2.83</fastjson_version>
@ -247,6 +248,11 @@
<artifactId>netty-all</artifactId>
<version>${netty4_version}</version>
</dependency>
<dependency>
<groupId>io.netty.incubator</groupId>
<artifactId>netty-incubator-codec-http3</artifactId>
<version>${netty_http3_version}</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>

View File

@ -687,6 +687,9 @@
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.transport.netty4.ChannelAddressAccessor</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.dubbo.ByteAccessor</resource>
</transformer>
@ -714,6 +717,9 @@
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.h1.Http1ServerTransportListenerFactory</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http3.Http3ServerTransportListenerFactory</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler</resource>
</transformer>
@ -834,6 +840,9 @@
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.stream.ClientStreamFactory</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory</resource>
</transformer>

View File

@ -343,6 +343,13 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-http3</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty</artifactId>
@ -518,6 +525,7 @@
<include>org.apache.dubbo:dubbo-registry-zookeeper</include>
<include>org.apache.dubbo:dubbo-remoting-api</include>
<include>org.apache.dubbo:dubbo-remoting-http12</include>
<include>org.apache.dubbo:dubbo-remoting-http3</include>
<include>org.apache.dubbo:dubbo-remoting-netty4</include>
<include>org.apache.dubbo:dubbo-remoting-netty</include>
<include>org.apache.dubbo:dubbo-remoting-zookeeper-curator5</include>
@ -726,6 +734,9 @@
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.transport.netty4.ChannelAddressAccessor</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.dubbo.ByteAccessor</resource>
</transformer>
@ -753,6 +764,9 @@
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.h1.Http1ServerTransportListenerFactory</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http3.Http3ServerTransportListenerFactory</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler</resource>
</transformer>
@ -873,6 +887,9 @@
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.stream.ClientStreamFactory</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory</resource>
</transformer>

View File

@ -395,6 +395,11 @@
<artifactId>dubbo-remoting-http12</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-http3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty</artifactId>

View File

@ -327,6 +327,9 @@
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.transport.netty4.ChannelAddressAccessor</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.dubbo.ByteAccessor</resource>
</transformer>
@ -354,6 +357,9 @@
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.h1.Http1ServerTransportListenerFactory</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http3.Http3ServerTransportListenerFactory</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.telnet.TelnetHandler</resource>
</transformer>
@ -474,6 +480,9 @@
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.stream.ClientStreamFactory</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory</resource>
</transformer>

View File

@ -61,13 +61,13 @@ public abstract class AbstractClient extends AbstractEndpoint implements Client
private final boolean needReconnect;
private final FrameworkModel frameworkModel;
protected volatile ExecutorService executor;
protected volatile ScheduledExecutorService connectivityExecutor;
private FrameworkModel frameworkModel;
protected long reconnectDuaration;
protected long reconnectDuration;
public AbstractClient(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
@ -78,7 +78,7 @@ public abstract class AbstractClient extends AbstractEndpoint implements Client
initExecutor(url);
reconnectDuaration = getReconnectDuration(url);
reconnectDuration = getReconnectDuration(url);
try {
doOpen();

View File

@ -33,6 +33,8 @@ public abstract class AbstractServerHttpChannelObserver implements CustomizableH
private HttpMessageEncoder responseEncoder;
private String altSvc;
private boolean headerSent;
private boolean completed;
@ -63,6 +65,10 @@ public abstract class AbstractServerHttpChannelObserver implements CustomizableH
this.errorResponseCustomizer = errorResponseCustomizer;
}
public void setAltSvc(String altSvc) {
this.altSvc = altSvc;
}
public HttpMessageEncoder getResponseEncoder() {
return responseEncoder;
}
@ -208,6 +214,9 @@ public abstract class AbstractServerHttpChannelObserver implements CustomizableH
HttpHeaders headers = httpMetadata.headers();
headers.set(HttpHeaderNames.STATUS.getName(), statusCode);
headers.set(HttpHeaderNames.CONTENT_TYPE.getName(), responseEncoder.contentType());
if (altSvc != null) {
headers.set(HttpHeaderNames.ALT_SVC.getName(), altSvc);
}
if (data instanceof HttpResult) {
HttpResult<?> result = (HttpResult<?>) data;
if (result.getHeaders() != null) {

View File

@ -27,6 +27,8 @@ public enum HttpHeaderNames {
TE("te"),
ALT_SVC("alt-svc"),
ACCEPT("accept");
private final String name;

View File

@ -20,7 +20,7 @@ import java.io.InputStream;
public class Http2InputMessageFrame implements Http2InputMessage {
private int id;
private long id;
private final InputStream body;
@ -39,7 +39,7 @@ public class Http2InputMessageFrame implements Http2InputMessage {
this.endStream = endStream;
}
public void setId(int id) {
public void setId(long id) {
this.id = id;
}
@ -54,7 +54,7 @@ public class Http2InputMessageFrame implements Http2InputMessage {
}
@Override
public int id() {
public long id() {
return id;
}

View File

@ -20,7 +20,7 @@ import org.apache.dubbo.remoting.http12.HttpHeaders;
public class Http2MetadataFrame implements Http2Header {
private final int streamId;
private final long streamId;
private final HttpHeaders headers;
@ -31,10 +31,10 @@ public class Http2MetadataFrame implements Http2Header {
}
public Http2MetadataFrame(HttpHeaders headers, boolean endStream) {
this(-1, headers, endStream);
this(-1L, headers, endStream);
}
public Http2MetadataFrame(int streamId, HttpHeaders headers, boolean endStream) {
public Http2MetadataFrame(long streamId, HttpHeaders headers, boolean endStream) {
this.streamId = streamId;
this.headers = headers;
this.endStream = endStream;
@ -46,7 +46,7 @@ public class Http2MetadataFrame implements Http2Header {
}
@Override
public int id() {
public long id() {
return streamId;
}

View File

@ -26,7 +26,7 @@ public interface Http2OutputMessage extends HttpOutputMessage, Http2StreamFrame
}
@Override
default int id() {
default long id() {
return -1;
}
}

View File

@ -18,7 +18,7 @@ package org.apache.dubbo.remoting.http12.h2;
public interface Http2StreamFrame {
int id();
long id();
String name();

View File

@ -16,6 +16,7 @@
*/
package org.apache.dubbo.remoting.http12.message;
import org.apache.dubbo.common.io.StreamUtils;
import org.apache.dubbo.remoting.http12.CompositeInputStream;
import org.apache.dubbo.remoting.http12.exception.DecodeException;
@ -46,7 +47,7 @@ public class LengthFieldStreamingDecoder implements StreamingDecoder {
private int requiredLength;
private InputStream dataHeader = new ByteArrayInputStream(new byte[0]);
private InputStream dataHeader = StreamUtils.EMPTY;
public LengthFieldStreamingDecoder() {
this(4);

View File

@ -40,8 +40,8 @@ public final class CodecUtils {
public CodecUtils(FrameworkModel frameworkModel) {
decoderFactories = frameworkModel.getActivateExtensions(HttpMessageDecoderFactory.class);
encoderFactories = frameworkModel.getActivateExtensions(HttpMessageEncoderFactory.class);
decoderFactories.forEach(factory -> decoderCache.put(factory.mediaType().getName(), Optional.of(factory)));
encoderFactories.forEach(factory -> encoderCache.put(factory.mediaType().getName(), Optional.of(factory)));
decoderFactories.forEach(f -> decoderCache.putIfAbsent(f.mediaType().getName(), Optional.of(f)));
encoderFactories.forEach(f -> encoderCache.putIfAbsent(f.mediaType().getName(), Optional.of(f)));
}
public HttpMessageDecoder determineHttpMessageDecoder(URL url, FrameworkModel frameworkModel, String mediaType) {

View File

@ -68,6 +68,9 @@ public class JsonCodec implements HttpMessageCodec {
public Object[] decode(InputStream is, Class<?>[] targetTypes, Charset charset) throws DecodeException {
try {
int len = targetTypes.length;
if (len == 0) {
return new Object[0];
}
Object obj = JsonUtils.toJavaObject(StreamUtils.toString(is, charset), Object.class);
if (obj instanceof List) {
List<?> list = (List<?>) obj;

View File

@ -41,14 +41,14 @@ public class CodeUtilsTest {
HttpMessageDecoder decoder =
codecUtils.determineHttpMessageDecoder(null, FrameworkModel.defaultModel(), headers.getContentType());
Assertions.assertNotNull(decoder);
Assertions.assertEquals(JsonCodec.class, decoder.getClass());
Assertions.assertEquals(JsonPbCodec.class, decoder.getClass());
HttpMessageEncoder encoder;
// If no Accept header provided, use Content-Type to find encoder
encoder = codecUtils.determineHttpMessageEncoder(
null, FrameworkModel.defaultModel(), MediaType.APPLICATION_JSON.getName());
Assertions.assertNotNull(encoder);
Assertions.assertEquals(JsonCodec.class, encoder.getClass());
Assertions.assertEquals(JsonPbCodec.class, encoder.getClass());
HttpHeaders headers1 = new HttpHeaders();
headers1.put(
@ -67,6 +67,6 @@ public class CodeUtilsTest {
encoder = codecUtils.determineHttpMessageEncoder(
null, FrameworkModel.defaultModel(), MediaType.APPLICATION_JSON.getName());
Assertions.assertNotNull(encoder);
Assertions.assertEquals(JsonCodec.class, encoder.getClass());
Assertions.assertEquals(JsonPbCodec.class, encoder.getClass());
}
}

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>dubbo-remoting-http3</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>The http3 remoting module of dubbo project</description>
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-http12</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>io.netty.incubator</groupId>
<artifactId>netty-incubator-codec-http3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,85 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.exchange;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.LoggerCodeConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.RemotingException;
import org.apache.dubbo.remoting.RemotingServer;
import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient;
import org.apache.dubbo.remoting.transport.ChannelHandlerAdapter;
import org.apache.dubbo.remoting.transport.netty4.NettyHttp3ConnectionClient;
import org.apache.dubbo.remoting.transport.netty4.NettyHttp3Server;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public final class Http3Exchanger {
private static final ErrorTypeAwareLogger LOG = LoggerFactory.getErrorTypeAwareLogger(Http3Exchanger.class);
private static final Map<String, RemotingServer> SERVERS = new ConcurrentHashMap<>();
private static final Map<String, AbstractConnectionClient> CLIENTS = new ConcurrentHashMap<>(16);
private static final ChannelHandler HANDLER = new ChannelHandlerAdapter();
private Http3Exchanger() {}
public static RemotingServer bind(URL url) {
return SERVERS.computeIfAbsent(url.getAddress(), addr -> {
try {
return new NettyHttp3Server(url, HANDLER);
} catch (RemotingException e) {
throw new RuntimeException(e);
}
});
}
public static AbstractConnectionClient connect(URL url) {
return CLIENTS.compute(url.getAddress(), (address, client) -> {
try {
if (client == null) {
AbstractConnectionClient connectionClient = new NettyHttp3ConnectionClient(url, HANDLER);
connectionClient.addCloseListener(() -> CLIENTS.remove(address, connectionClient));
client = connectionClient;
} else {
client.retain();
}
return client;
} catch (RemotingException e) {
throw new RuntimeException(e);
}
});
}
public static void close() {
if (SERVERS.isEmpty()) {
return;
}
ArrayList<RemotingServer> toClose = new ArrayList<>(SERVERS.values());
SERVERS.clear();
for (RemotingServer server : toClose) {
try {
server.close();
} catch (Throwable t) {
LOG.error(LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_SERVER, "", "", "Close Http3 server failed", t);
}
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.http3;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionScope;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
import org.apache.dubbo.rpc.model.FrameworkModel;
@SPI(scope = ExtensionScope.FRAMEWORK)
public interface Http3ServerTransportListenerFactory {
Http3TransportListener newInstance(H2StreamChannel streamChannel, URL url, FrameworkModel frameworkModel);
boolean supportContentType(String contentType);
}

View File

@ -0,0 +1,21 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.http3;
import org.apache.dubbo.remoting.http12.h2.Http2TransportListener;
public interface Http3TransportListener extends Http2TransportListener {}

View File

@ -0,0 +1,627 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.http3.netty4;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.Spliterator;
import java.util.function.Consumer;
import io.netty.handler.codec.Headers;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.incubator.codec.http3.Http3Headers;
public final class Http2HeadersAdapter implements Http2Headers {
private final Http3Headers headers;
public Http2HeadersAdapter(Http3Headers headers) {
this.headers = headers;
}
@Override
public Iterator<Entry<CharSequence, CharSequence>> iterator() {
return headers.iterator();
}
@Override
public Iterator<CharSequence> valueIterator(CharSequence name) {
return headers.valueIterator(name);
}
@Override
public Http2Headers method(CharSequence value) {
headers.method(value);
return this;
}
@Override
public Http2Headers scheme(CharSequence value) {
headers.scheme(value);
return this;
}
@Override
public Http2Headers authority(CharSequence value) {
headers.authority(value);
return this;
}
@Override
public Http2Headers path(CharSequence value) {
headers.path(value);
return this;
}
@Override
public Http2Headers status(CharSequence value) {
headers.status(value);
return this;
}
@Override
public CharSequence method() {
return headers.method();
}
@Override
public CharSequence scheme() {
return headers.scheme();
}
@Override
public CharSequence authority() {
return headers.authority();
}
@Override
public CharSequence path() {
return headers.path();
}
@Override
public CharSequence status() {
return headers.status();
}
@Override
public boolean contains(CharSequence name, CharSequence value, boolean caseInsensitive) {
return headers.contains(name, value, caseInsensitive);
}
@Override
public CharSequence get(CharSequence charSequence) {
return headers.get(charSequence);
}
@Override
public CharSequence get(CharSequence charSequence, CharSequence charSequence2) {
return headers.get(charSequence, charSequence2);
}
@Override
public CharSequence getAndRemove(CharSequence charSequence) {
return headers.getAndRemove(charSequence);
}
@Override
public CharSequence getAndRemove(CharSequence charSequence, CharSequence charSequence2) {
return headers.getAndRemove(charSequence, charSequence2);
}
@Override
public List<CharSequence> getAll(CharSequence charSequence) {
return headers.getAll(charSequence);
}
@Override
public List<CharSequence> getAllAndRemove(CharSequence charSequence) {
return headers.getAllAndRemove(charSequence);
}
@Override
public Boolean getBoolean(CharSequence charSequence) {
return headers.getBoolean(charSequence);
}
@Override
public boolean getBoolean(CharSequence charSequence, boolean b) {
return headers.getBoolean(charSequence, b);
}
@Override
public Byte getByte(CharSequence charSequence) {
return headers.getByte(charSequence);
}
@Override
public byte getByte(CharSequence charSequence, byte b) {
return headers.getByte(charSequence, b);
}
@Override
public Character getChar(CharSequence charSequence) {
return headers.getChar(charSequence);
}
@Override
public char getChar(CharSequence charSequence, char c) {
return headers.getChar(charSequence, c);
}
@Override
public Short getShort(CharSequence charSequence) {
return headers.getShort(charSequence);
}
@Override
public short getShort(CharSequence charSequence, short i) {
return headers.getShort(charSequence, i);
}
@Override
public Integer getInt(CharSequence charSequence) {
return headers.getInt(charSequence);
}
@Override
public int getInt(CharSequence charSequence, int i) {
return headers.getInt(charSequence, i);
}
@Override
public Long getLong(CharSequence charSequence) {
return headers.getLong(charSequence);
}
@Override
public long getLong(CharSequence charSequence, long l) {
return headers.getLong(charSequence, l);
}
@Override
public Float getFloat(CharSequence charSequence) {
return headers.getFloat(charSequence);
}
@Override
public float getFloat(CharSequence charSequence, float v) {
return headers.getFloat(charSequence, v);
}
@Override
public Double getDouble(CharSequence charSequence) {
return headers.getDouble(charSequence);
}
@Override
public double getDouble(CharSequence charSequence, double v) {
return headers.getDouble(charSequence, v);
}
@Override
public Long getTimeMillis(CharSequence charSequence) {
return headers.getTimeMillis(charSequence);
}
@Override
public long getTimeMillis(CharSequence charSequence, long l) {
return headers.getTimeMillis(charSequence, l);
}
@Override
public Boolean getBooleanAndRemove(CharSequence charSequence) {
return headers.getBooleanAndRemove(charSequence);
}
@Override
public boolean getBooleanAndRemove(CharSequence charSequence, boolean b) {
return headers.getBooleanAndRemove(charSequence, b);
}
@Override
public Byte getByteAndRemove(CharSequence charSequence) {
return headers.getByteAndRemove(charSequence);
}
@Override
public byte getByteAndRemove(CharSequence charSequence, byte b) {
return headers.getByteAndRemove(charSequence, b);
}
@Override
public Character getCharAndRemove(CharSequence charSequence) {
return headers.getCharAndRemove(charSequence);
}
@Override
public char getCharAndRemove(CharSequence charSequence, char c) {
return headers.getCharAndRemove(charSequence, c);
}
@Override
public Short getShortAndRemove(CharSequence charSequence) {
return headers.getShortAndRemove(charSequence);
}
@Override
public short getShortAndRemove(CharSequence charSequence, short i) {
return headers.getShortAndRemove(charSequence, i);
}
@Override
public Integer getIntAndRemove(CharSequence charSequence) {
return headers.getIntAndRemove(charSequence);
}
@Override
public int getIntAndRemove(CharSequence charSequence, int i) {
return headers.getIntAndRemove(charSequence, i);
}
@Override
public Long getLongAndRemove(CharSequence charSequence) {
return headers.getLongAndRemove(charSequence);
}
@Override
public long getLongAndRemove(CharSequence charSequence, long l) {
return headers.getLongAndRemove(charSequence, l);
}
@Override
public Float getFloatAndRemove(CharSequence charSequence) {
return headers.getFloatAndRemove(charSequence);
}
@Override
public float getFloatAndRemove(CharSequence charSequence, float v) {
return headers.getFloatAndRemove(charSequence, v);
}
@Override
public Double getDoubleAndRemove(CharSequence charSequence) {
return headers.getDoubleAndRemove(charSequence);
}
@Override
public double getDoubleAndRemove(CharSequence charSequence, double v) {
return headers.getDoubleAndRemove(charSequence, v);
}
@Override
public Long getTimeMillisAndRemove(CharSequence charSequence) {
return headers.getTimeMillisAndRemove(charSequence);
}
@Override
public long getTimeMillisAndRemove(CharSequence charSequence, long l) {
return headers.getTimeMillisAndRemove(charSequence, l);
}
@Override
public boolean contains(CharSequence charSequence) {
return headers.contains(charSequence);
}
@Override
public boolean contains(CharSequence charSequence, CharSequence charSequence2) {
return headers.contains(charSequence, charSequence2);
}
@Override
public boolean containsObject(CharSequence charSequence, Object o) {
return headers.containsObject(charSequence, o);
}
@Override
public boolean containsBoolean(CharSequence charSequence, boolean b) {
return headers.containsBoolean(charSequence, b);
}
@Override
public boolean containsByte(CharSequence charSequence, byte b) {
return headers.containsByte(charSequence, b);
}
@Override
public boolean containsChar(CharSequence charSequence, char c) {
return headers.containsChar(charSequence, c);
}
@Override
public boolean containsShort(CharSequence charSequence, short i) {
return headers.containsShort(charSequence, i);
}
@Override
public boolean containsInt(CharSequence charSequence, int i) {
return headers.containsInt(charSequence, i);
}
@Override
public boolean containsLong(CharSequence charSequence, long l) {
return headers.containsLong(charSequence, l);
}
@Override
public boolean containsFloat(CharSequence charSequence, float v) {
return headers.containsFloat(charSequence, v);
}
@Override
public boolean containsDouble(CharSequence charSequence, double v) {
return headers.containsDouble(charSequence, v);
}
@Override
public boolean containsTimeMillis(CharSequence charSequence, long l) {
return headers.containsTimeMillis(charSequence, l);
}
@Override
public int size() {
return headers.size();
}
@Override
public boolean isEmpty() {
return headers.isEmpty();
}
@Override
public Set<CharSequence> names() {
return headers.names();
}
@Override
public Http2Headers add(CharSequence charSequence, CharSequence charSequence2) {
headers.add(charSequence, charSequence2);
return this;
}
@Override
public Http2Headers add(CharSequence charSequence, Iterable<? extends CharSequence> iterable) {
headers.add(charSequence, iterable);
return this;
}
@Override
public Http2Headers add(CharSequence charSequence, CharSequence... charSequences) {
headers.add(charSequence, charSequences);
return this;
}
@Override
public Http2Headers addObject(CharSequence charSequence, Object o) {
headers.addObject(charSequence, o);
return this;
}
@Override
public Http2Headers addObject(CharSequence charSequence, Iterable<?> iterable) {
headers.addObject(charSequence, iterable);
return this;
}
@Override
public Http2Headers addObject(CharSequence charSequence, Object... objects) {
headers.addObject(charSequence, objects);
return this;
}
@Override
public Http2Headers addBoolean(CharSequence charSequence, boolean b) {
headers.addBoolean(charSequence, b);
return this;
}
@Override
public Http2Headers addByte(CharSequence charSequence, byte b) {
headers.addByte(charSequence, b);
return this;
}
@Override
public Http2Headers addChar(CharSequence charSequence, char c) {
headers.addChar(charSequence, c);
return this;
}
@Override
public Http2Headers addShort(CharSequence charSequence, short i) {
headers.addShort(charSequence, i);
return this;
}
@Override
public Http2Headers addInt(CharSequence charSequence, int i) {
headers.addInt(charSequence, i);
return this;
}
@Override
public Http2Headers addLong(CharSequence charSequence, long l) {
headers.addLong(charSequence, l);
return this;
}
@Override
public Http2Headers addFloat(CharSequence charSequence, float v) {
headers.addFloat(charSequence, v);
return this;
}
@Override
public Http2Headers addDouble(CharSequence charSequence, double v) {
headers.addDouble(charSequence, v);
return this;
}
@Override
public Http2Headers addTimeMillis(CharSequence charSequence, long l) {
headers.addTimeMillis(charSequence, l);
return this;
}
@Override
public Http2Headers add(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
this.headers.add(headers);
return this;
}
@Override
public Http2Headers set(CharSequence charSequence, CharSequence charSequence2) {
headers.set(charSequence, charSequence2);
return this;
}
@Override
public Http2Headers set(CharSequence charSequence, Iterable<? extends CharSequence> iterable) {
headers.set(charSequence, iterable);
return this;
}
@Override
public Http2Headers set(CharSequence charSequence, CharSequence... charSequences) {
headers.set(charSequence, charSequences);
return this;
}
@Override
public Http2Headers setObject(CharSequence charSequence, Object o) {
headers.setObject(charSequence, o);
return this;
}
@Override
public Http2Headers setObject(CharSequence charSequence, Iterable<?> iterable) {
headers.setObject(charSequence, iterable);
return this;
}
@Override
public Http2Headers setObject(CharSequence charSequence, Object... objects) {
headers.setObject(charSequence, objects);
return this;
}
@Override
public Http2Headers setBoolean(CharSequence charSequence, boolean b) {
headers.setBoolean(charSequence, b);
return this;
}
@Override
public Http2Headers setByte(CharSequence charSequence, byte b) {
headers.setByte(charSequence, b);
return this;
}
@Override
public Http2Headers setChar(CharSequence charSequence, char c) {
headers.setChar(charSequence, c);
return this;
}
@Override
public Http2Headers setShort(CharSequence charSequence, short i) {
headers.setShort(charSequence, i);
return this;
}
@Override
public Http2Headers setInt(CharSequence charSequence, int i) {
headers.setInt(charSequence, i);
return this;
}
@Override
public Http2Headers setLong(CharSequence charSequence, long l) {
headers.setLong(charSequence, l);
return this;
}
@Override
public Http2Headers setFloat(CharSequence charSequence, float v) {
headers.setFloat(charSequence, v);
return this;
}
@Override
public Http2Headers setDouble(CharSequence charSequence, double v) {
headers.setDouble(charSequence, v);
return this;
}
@Override
public Http2Headers setTimeMillis(CharSequence charSequence, long l) {
headers.setTimeMillis(charSequence, l);
return this;
}
@Override
public Http2Headers set(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
this.headers.set(headers);
return this;
}
@Override
public Http2Headers setAll(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
this.headers.setAll(headers);
return this;
}
@Override
public boolean remove(CharSequence charSequence) {
return headers.remove(charSequence);
}
@Override
public Http2Headers clear() {
headers.clear();
return this;
}
@Override
public void forEach(Consumer<? super Entry<CharSequence, CharSequence>> action) {
headers.forEach(action);
}
@Override
public Spliterator<Entry<CharSequence, CharSequence>> spliterator() {
return headers.spliterator();
}
@Override
public int hashCode() {
return Objects.hashCode(headers);
}
@Override
public boolean equals(Object obj) {
return this == obj || obj instanceof Http2Headers && headers.equals(obj);
}
@Override
public String toString() {
return headers.toString();
}
}

View File

@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.http3.netty4;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.transport.netty4.ChannelAddressAccessor;
import java.net.InetSocketAddress;
import io.netty.channel.Channel;
import io.netty.incubator.codec.quic.QuicChannel;
import io.netty.incubator.codec.quic.QuicStreamChannel;
@Activate(order = -100, onClass = "io.netty.incubator.codec.quic.QuicChannel")
public class Http3ChannelAddressAccessor implements ChannelAddressAccessor {
@Override
public String getProtocol() {
return "UDP";
}
@Override
public InetSocketAddress getRemoteAddress(Channel channel) {
if (channel instanceof QuicStreamChannel) {
return (InetSocketAddress) ((QuicStreamChannel) channel).parent().remoteSocketAddress();
}
if (channel instanceof QuicChannel) {
return (InetSocketAddress) ((QuicChannel) channel).remoteSocketAddress();
}
return null;
}
@Override
public InetSocketAddress getLocalAddress(Channel channel) {
if (channel instanceof QuicStreamChannel) {
return (InetSocketAddress) ((QuicStreamChannel) channel).parent().localSocketAddress();
}
if (channel instanceof QuicChannel) {
return (InetSocketAddress) ((QuicChannel) channel).localSocketAddress();
}
return null;
}
}

View File

@ -0,0 +1,155 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.http3.netty4;
import org.apache.dubbo.common.io.StreamUtils;
import org.apache.dubbo.remoting.http12.HttpHeaders;
import org.apache.dubbo.remoting.http12.h2.Http2Header;
import org.apache.dubbo.remoting.http12.h2.Http2InputMessageFrame;
import org.apache.dubbo.remoting.http12.h2.Http2MetadataFrame;
import org.apache.dubbo.remoting.http12.h2.Http2OutputMessage;
import java.io.OutputStream;
import java.net.SocketAddress;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelPromise;
import io.netty.incubator.codec.http3.DefaultHttp3DataFrame;
import io.netty.incubator.codec.http3.DefaultHttp3Headers;
import io.netty.incubator.codec.http3.DefaultHttp3HeadersFrame;
import io.netty.incubator.codec.http3.Http3DataFrame;
import io.netty.incubator.codec.http3.Http3Headers;
import io.netty.incubator.codec.http3.Http3HeadersFrame;
import io.netty.incubator.codec.http3.Http3RequestStreamInboundHandler;
import io.netty.incubator.codec.quic.QuicStreamChannel;
@Sharable
public class NettyHttp3FrameCodec extends Http3RequestStreamInboundHandler implements ChannelOutboundHandler {
public static final NettyHttp3FrameCodec INSTANCE = new NettyHttp3FrameCodec();
@Override
protected void channelRead(ChannelHandlerContext ctx, Http3HeadersFrame frame) {
HttpHeaders headers = new HttpHeaders();
for (Map.Entry<CharSequence, CharSequence> header : frame.headers()) {
headers.set(header.getKey().toString(), header.getValue().toString());
}
ctx.fireChannelRead(new Http2MetadataFrame(getStreamId(ctx), headers, false));
}
@Override
protected void channelRead(ChannelHandlerContext ctx, Http3DataFrame frame) {
Http2InputMessageFrame msg = new Http2InputMessageFrame(new ByteBufInputStream(frame.content(), true));
msg.setId(getStreamId(ctx));
ctx.fireChannelRead(msg);
}
private static long getStreamId(ChannelHandlerContext ctx) {
return ((QuicStreamChannel) ctx.channel()).streamId();
}
@Override
protected void channelInputClosed(ChannelHandlerContext ctx) {
Http2InputMessageFrame msg = new Http2InputMessageFrame(StreamUtils.EMPTY, true);
msg.setId(getStreamId(ctx));
ctx.fireChannelRead(msg);
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (msg instanceof Http2Header) {
Http3Headers headers = new DefaultHttp3Headers();
Http2Header http2Header = (Http2Header) msg;
for (Entry<String, List<String>> entry : http2Header.headers().entrySet()) {
headers.add(entry.getKey(), entry.getValue());
}
ctx.write(new DefaultHttp3HeadersFrame(headers), promise);
if (http2Header.isEndStream()) {
ctx.close();
}
} else if (msg instanceof Http2OutputMessage) {
Http2OutputMessage outputMessage = (Http2OutputMessage) msg;
try {
OutputStream body = outputMessage.getBody();
if (body == null) {
Http3DataFrame frame = new DefaultHttp3DataFrame(Unpooled.EMPTY_BUFFER);
ctx.write(frame, promise);
return;
}
if (body instanceof ByteBufOutputStream) {
Http3DataFrame frame = new DefaultHttp3DataFrame(((ByteBufOutputStream) body).buffer());
ctx.write(frame, promise);
return;
}
} finally {
if (outputMessage.isEndStream()) {
ctx.close();
}
}
throw new IllegalArgumentException("Http2OutputMessage body must be ByteBufOutputStream");
} else {
ctx.write(msg, promise);
}
}
@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
ctx.bind(localAddress, promise);
}
@Override
public void connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress,
SocketAddress localAddress,
ChannelPromise promise) {
ctx.connect(remoteAddress, localAddress, promise);
}
@Override
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) {
ctx.disconnect(promise);
}
@Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.close(promise);
}
@Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) {
ctx.deregister(promise);
}
@Override
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
@Override
public void flush(ChannelHandlerContext ctx) {
ctx.flush();
}
}

View File

@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.http3.netty4;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.remoting.http12.HttpHeaderNames;
import org.apache.dubbo.remoting.http12.HttpHeaders;
import org.apache.dubbo.remoting.http12.HttpMetadata;
import org.apache.dubbo.remoting.http12.command.HttpWriteQueue;
import org.apache.dubbo.remoting.http12.exception.UnsupportedMediaTypeException;
import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
import org.apache.dubbo.remoting.http12.h2.command.Http2WriteQueueChannel;
import org.apache.dubbo.remoting.http12.netty4.HttpWriteQueueHandler;
import org.apache.dubbo.remoting.http12.netty4.h2.NettyHttp2FrameHandler;
import org.apache.dubbo.remoting.http3.Http3ServerTransportListenerFactory;
import org.apache.dubbo.rpc.model.FrameworkModel;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.incubator.codec.quic.QuicStreamChannel;
@Sharable
public class NettyHttp3ProtocolSelectorHandler extends SimpleChannelInboundHandler<HttpMetadata> {
private final URL url;
private final FrameworkModel frameworkModel;
public NettyHttp3ProtocolSelectorHandler(URL url, FrameworkModel frameworkModel) {
this.url = url;
this.frameworkModel = frameworkModel;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpMetadata metadata) {
HttpHeaders headers = metadata.headers();
String contentType = headers.getFirst(HttpHeaderNames.CONTENT_TYPE.getName());
Http3ServerTransportListenerFactory factory = determineHttp3ServerTransportListenerFactory(contentType);
if (factory == null) {
throw new UnsupportedMediaTypeException(contentType);
}
H2StreamChannel streamChannel = new NettyHttp3StreamChannel((QuicStreamChannel) ctx.channel());
HttpWriteQueueHandler writeQueueHandler = ctx.channel().pipeline().get(HttpWriteQueueHandler.class);
if (writeQueueHandler != null) {
HttpWriteQueue writeQueue = writeQueueHandler.getWriteQueue();
streamChannel = new Http2WriteQueueChannel(streamChannel, writeQueue);
}
ChannelPipeline pipeline = ctx.pipeline();
pipeline.addLast(
new NettyHttp2FrameHandler(streamChannel, factory.newInstance(streamChannel, url, frameworkModel)));
pipeline.remove(this);
ctx.fireChannelRead(metadata);
}
private Http3ServerTransportListenerFactory determineHttp3ServerTransportListenerFactory(String contentType) {
for (Http3ServerTransportListenerFactory factory :
frameworkModel.getActivateExtensions(Http3ServerTransportListenerFactory.class)) {
if (factory.supportContentType(contentType)) {
return factory;
}
}
return null;
}
}

View File

@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.http3.netty4;
import org.apache.dubbo.remoting.http12.HttpMetadata;
import org.apache.dubbo.remoting.http12.HttpOutputMessage;
import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
import org.apache.dubbo.remoting.http12.h2.Http2OutputMessage;
import org.apache.dubbo.remoting.http12.h2.Http2OutputMessageFrame;
import org.apache.dubbo.remoting.http12.netty4.NettyHttpChannelFutureListener;
import java.net.SocketAddress;
import java.util.concurrent.CompletableFuture;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.incubator.codec.quic.QuicStreamChannel;
public class NettyHttp3StreamChannel implements H2StreamChannel {
private final QuicStreamChannel http3StreamChannel;
public NettyHttp3StreamChannel(QuicStreamChannel http3StreamChannel) {
this.http3StreamChannel = http3StreamChannel;
}
@Override
public CompletableFuture<Void> writeResetFrame(long errorCode) {
NettyHttpChannelFutureListener futureListener = new NettyHttpChannelFutureListener();
http3StreamChannel.close().addListener(futureListener);
return futureListener;
}
@Override
public Http2OutputMessage newOutputMessage(boolean endStream) {
ByteBuf buffer = http3StreamChannel.alloc().buffer();
return new Http2OutputMessageFrame(new ByteBufOutputStream(buffer), endStream);
}
@Override
public CompletableFuture<Void> writeHeader(HttpMetadata httpMetadata) {
NettyHttpChannelFutureListener futureListener = new NettyHttpChannelFutureListener();
http3StreamChannel.write(httpMetadata).addListener(futureListener);
return futureListener;
}
@Override
public CompletableFuture<Void> writeMessage(HttpOutputMessage httpOutputMessage) {
NettyHttpChannelFutureListener futureListener = new NettyHttpChannelFutureListener();
http3StreamChannel.write(httpOutputMessage).addListener(futureListener);
return futureListener;
}
@Override
public SocketAddress remoteAddress() {
return http3StreamChannel.parent().remoteSocketAddress();
}
@Override
public SocketAddress localAddress() {
return http3StreamChannel.parent().localSocketAddress();
}
@Override
public void flush() {
http3StreamChannel.flush();
}
}

View File

@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.transport.netty4;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.context.ConfigManager;
import org.apache.dubbo.config.nested.TripleConfig;
import io.netty.incubator.codec.quic.QuicCodecBuilder;
import io.netty.incubator.codec.quic.QuicCongestionControlAlgorithm;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
final class Helper {
@SuppressWarnings("unchecked")
static <T extends QuicCodecBuilder<T>> T configCodec(QuicCodecBuilder<T> builder, URL url) {
TripleConfig tripleConfig = ConfigManager.getProtocol(url).getTriple();
if (tripleConfig.getHttp3InitialMaxData() != null) {
builder.initialMaxData(tripleConfig.getHttp3InitialMaxData());
}
if (tripleConfig.getHttp3RecvQueueLen() != null && tripleConfig.getHttp3SendQueueLen() != null) {
builder.datagram(tripleConfig.getHttp3RecvQueueLen(), tripleConfig.getHttp3SendQueueLen());
}
if (tripleConfig.getHttp3InitialMaxStreamDataBidiLocal() != null) {
builder.initialMaxStreamDataBidirectionalLocal(tripleConfig.getHttp3InitialMaxStreamDataBidiLocal());
}
if (tripleConfig.getHttp3InitialMaxStreamDataBidiRemote() != null) {
builder.initialMaxStreamDataBidirectionalRemote(tripleConfig.getHttp3InitialMaxStreamDataBidiRemote());
}
if (tripleConfig.getHttp3InitialMaxStreamDataUni() != null) {
builder.initialMaxStreamDataUnidirectional(tripleConfig.getHttp3InitialMaxStreamDataUni());
}
if (tripleConfig.getHttp3InitialMaxStreamsBidi() != null) {
builder.initialMaxStreamsBidirectional(tripleConfig.getHttp3InitialMaxStreamsBidi());
}
if (tripleConfig.getHttp3InitialMaxStreamsUni() != null) {
builder.initialMaxStreamsUnidirectional(tripleConfig.getHttp3InitialMaxStreamsUni());
}
if (tripleConfig.getHttp3MaxAckDelayExponent() != null) {
builder.ackDelayExponent(tripleConfig.getHttp3MaxAckDelayExponent());
}
if (tripleConfig.getHttp3MaxAckDelay() != null) {
builder.maxAckDelay(tripleConfig.getHttp3MaxAckDelay(), MILLISECONDS);
}
if (tripleConfig.getHttp3DisableActiveMigration() != null) {
builder.activeMigration(tripleConfig.getHttp3DisableActiveMigration());
}
if (tripleConfig.getHttp3EnableHystart() != null) {
builder.hystart(tripleConfig.getHttp3EnableHystart());
}
if (tripleConfig.getHttp3CcAlgorithm() != null) {
if ("RENO".equalsIgnoreCase(tripleConfig.getHttp3CcAlgorithm())) {
builder.congestionControlAlgorithm(QuicCongestionControlAlgorithm.RENO);
} else if ("BBR".equalsIgnoreCase(tripleConfig.getHttp3CcAlgorithm())) {
builder.congestionControlAlgorithm(QuicCongestionControlAlgorithm.BBR);
}
}
return (T) builder;
}
}

View File

@ -0,0 +1,138 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.transport.netty4;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.remoting.RemotingException;
import org.apache.dubbo.remoting.utils.UrlUtils;
import java.util.concurrent.atomic.AtomicReference;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPromise;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.incubator.codec.http3.Http3;
import io.netty.incubator.codec.http3.Http3ClientConnectionHandler;
import io.netty.incubator.codec.quic.QuicChannel;
import io.netty.incubator.codec.quic.QuicChannelBootstrap;
import io.netty.incubator.codec.quic.QuicSslContext;
import io.netty.incubator.codec.quic.QuicSslContextBuilder;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
public final class NettyHttp3ConnectionClient extends AbstractNettyConnectionClient {
private AtomicReference<io.netty.channel.Channel> datagramChannel;
private QuicChannelBootstrap bootstrap;
public NettyHttp3ConnectionClient(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
}
@Override
protected void initConnectionClient() {
super.initConnectionClient();
datagramChannel = new AtomicReference<>();
}
@Override
protected void initBootstrap() throws Exception {
QuicSslContext context = QuicSslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.applicationProtocols(Http3.supportedApplicationProtocols())
.build();
int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
io.netty.channel.ChannelHandler codec = Helper.configCodec(Http3.newQuicClientCodecBuilder(), getUrl())
.maxIdleTimeout(idleTimeout, MILLISECONDS)
.sslContext(context)
.build();
io.netty.channel.Channel nettyDatagramChannel = new Bootstrap()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getConnectTimeout())
.group(NettyEventLoopFactory.NIO_EVENT_LOOP_GROUP.get())
.channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<NioDatagramChannel>() {
@Override
protected void initChannel(NioDatagramChannel ch) {
ch.pipeline().addLast(codec);
}
})
.bind(0)
.sync()
.channel();
datagramChannel.set(nettyDatagramChannel);
nettyDatagramChannel.closeFuture().addListener(channelFuture -> datagramChannel.set(null));
int heartbeat = UrlUtils.getHeartbeat(getUrl());
NettyConnectionHandler connectionHandler = new NettyConnectionHandler(this);
bootstrap = QuicChannel.newBootstrap(nettyDatagramChannel)
.handler(new ChannelInitializer<QuicChannel>() {
@Override
protected void initChannel(QuicChannel ch) {
ch.pipeline()
.addLast(new IdleStateHandler(heartbeat, 0, 0, MILLISECONDS))
.addLast(Constants.CONNECTION_HANDLER_NAME, connectionHandler)
.addLast(new Http3ClientConnectionHandler());
ch.closeFuture().addListener(channelFuture -> clearNettyChannel());
}
})
.remoteAddress(getConnectAddress());
}
@Override
protected ChannelFuture performConnect() {
Channel channel = getNettyDatagramChannel();
if (channel == null) {
return null;
}
ChannelPromise promise = channel.newPromise();
GenericFutureListener<Future<QuicChannel>> listener = f -> {
if (f.isSuccess()) {
promise.setSuccess(null);
} else {
promise.setFailure(f.cause());
}
};
bootstrap.connect().addListener(listener);
return promise;
}
@Override
protected void performClose() {
super.performClose();
io.netty.channel.Channel current = getNettyDatagramChannel();
if (current != null) {
current.close();
}
datagramChannel.set(null);
}
private io.netty.channel.Channel getNettyDatagramChannel() {
return datagramChannel.get();
}
}

View File

@ -0,0 +1,207 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.transport.netty4;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.RemotingException;
import org.apache.dubbo.remoting.http12.netty4.HttpWriteQueueHandler;
import org.apache.dubbo.remoting.http3.netty4.NettyHttp3FrameCodec;
import org.apache.dubbo.remoting.http3.netty4.NettyHttp3ProtocolSelectorHandler;
import org.apache.dubbo.remoting.transport.AbstractServer;
import org.apache.dubbo.remoting.transport.dispatcher.ChannelHandlers;
import org.apache.dubbo.remoting.utils.UrlUtils;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.ScopeModelUtil;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.incubator.codec.http3.Http3;
import io.netty.incubator.codec.http3.Http3ServerConnectionHandler;
import io.netty.incubator.codec.quic.InsecureQuicTokenHandler;
import io.netty.incubator.codec.quic.QuicChannel;
import io.netty.incubator.codec.quic.QuicSslContext;
import io.netty.incubator.codec.quic.QuicSslContextBuilder;
import io.netty.incubator.codec.quic.QuicStreamChannel;
import io.netty.util.concurrent.Future;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_CLOSE;
import static org.apache.dubbo.remoting.Constants.EVENT_LOOP_BOSS_POOL_NAME;
public class NettyHttp3Server extends AbstractServer {
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NettyHttp3Server.class);
private Map<String, Channel> channels;
private Bootstrap bootstrap;
private EventLoopGroup bossGroup;
private io.netty.channel.Channel channel;
private final int serverShutdownTimeoutMills;
public NettyHttp3Server(URL url, ChannelHandler handler) throws RemotingException {
super(url, ChannelHandlers.wrap(handler, url));
serverShutdownTimeoutMills = ConfigurationUtils.getServerShutdownTimeout(getUrl().getOrDefaultModuleModel());
}
@Override
protected void doOpen() throws Throwable {
bootstrap = new Bootstrap();
bossGroup = NettyEventLoopFactory.eventLoopGroup(1, EVENT_LOOP_BOSS_POOL_NAME);
NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
channels = nettyServerHandler.getChannels();
FrameworkModel frameworkModel = ScopeModelUtil.getFrameworkModel(getUrl().getScopeModel());
NettyHttp3ProtocolSelectorHandler selectorHandler =
new NettyHttp3ProtocolSelectorHandler(getUrl(), frameworkModel);
SelfSignedCertificate certificate = new SelfSignedCertificate();
QuicSslContext context = QuicSslContextBuilder.forServer(
certificate.privateKey(), null, certificate.certificate())
.applicationProtocols(Http3.supportedApplicationProtocols())
.build();
int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
io.netty.channel.ChannelHandler codec = Helper.configCodec(Http3.newQuicServerCodecBuilder(), getUrl())
.sslContext(context)
.maxIdleTimeout(idleTimeout, MILLISECONDS)
.tokenHandler(InsecureQuicTokenHandler.INSTANCE)
.handler(new ChannelInitializer<QuicChannel>() {
@Override
protected void initChannel(QuicChannel ch) {
ch.pipeline()
.addLast(nettyServerHandler)
.addLast(new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
.addLast(new Http3ServerConnectionHandler(new ChannelInitializer<QuicStreamChannel>() {
@Override
protected void initChannel(QuicStreamChannel ch) {
ch.pipeline()
.addLast(NettyHttp3FrameCodec.INSTANCE)
.addLast(new HttpWriteQueueHandler())
.addLast(selectorHandler);
}
}));
}
})
.build();
// bind
try {
ChannelFuture channelFuture = bootstrap
.group(bossGroup)
.channel(NioDatagramChannel.class)
.handler(codec)
.bind(getBindAddress());
channelFuture.syncUninterruptibly();
channel = channelFuture.channel();
} catch (Throwable t) {
closeBootstrap();
throw t;
}
}
@Override
protected void doClose() {
try {
if (channel != null) {
// unbind.
channel.close();
}
} catch (Throwable e) {
logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e);
}
try {
Collection<Channel> channels = getChannels();
if (CollectionUtils.isNotEmpty(channels)) {
for (Channel channel : channels) {
try {
channel.close();
} catch (Throwable e) {
logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e);
}
}
}
} catch (Throwable e) {
logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e);
}
closeBootstrap();
try {
if (channels != null) {
channels.clear();
}
} catch (Throwable e) {
logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e);
}
}
private void closeBootstrap() {
try {
if (bootstrap != null) {
long timeout = ConfigurationUtils.reCalShutdownTime(serverShutdownTimeoutMills);
long quietPeriod = Math.min(2000L, timeout);
Future<?> bossGroupShutdownFuture = bossGroup.shutdownGracefully(quietPeriod, timeout, MILLISECONDS);
bossGroupShutdownFuture.syncUninterruptibly();
}
} catch (Throwable e) {
logger.warn(TRANSPORT_FAILED_CLOSE, "", "", e.getMessage(), e);
}
}
@Override
protected int getChannelsSize() {
return channels.size();
}
@Override
public Collection<Channel> getChannels() {
return new ArrayList<>(channels.values());
}
@Override
public Channel getChannel(InetSocketAddress remoteAddress) {
return channels.get(NetUtils.toAddressString(remoteAddress));
}
@Override
public boolean canHandleIdle() {
return true;
}
@Override
public boolean isBound() {
return channel.isActive();
}
}

View File

@ -0,0 +1 @@
http3=org.apache.dubbo.remoting.http3.netty4.Http3ChannelAddressAccessor

View File

@ -0,0 +1,354 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.transport.netty4;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.RemotingException;
import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.Promise;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_CLIENT_CONNECT_TIMEOUT;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_CONNECT_PROVIDER;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RECONNECT;
public abstract class AbstractNettyConnectionClient extends AbstractConnectionClient {
private static final ErrorTypeAwareLogger LOGGER =
LoggerFactory.getErrorTypeAwareLogger(AbstractNettyConnectionClient.class);
private AtomicReference<Promise<Object>> connectingPromise;
private Promise<Void> closePromise;
private AtomicReference<io.netty.channel.Channel> channel;
private AtomicBoolean isReconnecting;
private ConnectionListener connectionListener;
public static final AttributeKey<AbstractConnectionClient> CONNECTION = AttributeKey.valueOf("connection");
public AbstractNettyConnectionClient(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
}
@Override
protected void doOpen() throws Throwable {
initConnectionClient();
initBootstrap();
}
@Override
protected void initConnectionClient() {
this.remote = getConnectAddress();
this.connectingPromise = new AtomicReference<>();
this.connectionListener = new ConnectionListener();
this.channel = new AtomicReference<>();
this.isReconnecting = new AtomicBoolean(false);
this.closePromise = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
this.init = new AtomicBoolean(false);
this.increase();
}
protected abstract void initBootstrap() throws Exception;
@Override
protected void doClose() {
// AbstractPeer close can set closed true.
if (isClosed()) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("Connection:%s freed ", this));
}
performClose();
closePromise.setSuccess(null);
}
}
protected void performClose() {
io.netty.channel.Channel current = getNettyChannel();
if (current != null) {
current.close();
}
clearNettyChannel();
}
@Override
protected void doConnect() throws RemotingException {
if (!isReconnecting.compareAndSet(false, true)) {
return;
}
if (isClosed()) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("%s aborted to reconnect cause connection closed. ", this));
}
}
init.compareAndSet(false, true);
long start = System.currentTimeMillis();
createConnectingPromise();
Future<Void> promise = performConnect();
promise.addListener(connectionListener);
boolean ret = connectingPromise.get().awaitUninterruptibly(getConnectTimeout(), TimeUnit.MILLISECONDS);
// destroy connectingPromise after used
synchronized (this) {
connectingPromise.set(null);
}
if (promise.cause() != null) {
Throwable cause = promise.cause();
// 6-1 Failed to connect to provider server by other reason.
RemotingException remotingException = new RemotingException(
this,
"client(url: " + getUrl() + ") failed to connect to server " + getConnectAddress()
+ ", error message is:" + cause.getMessage(),
cause);
LOGGER.error(
TRANSPORT_FAILED_CONNECT_PROVIDER,
"network disconnected",
"",
"Failed to connect to provider server by other reason.",
cause);
throw remotingException;
} else if (!ret || !promise.isSuccess()) {
// 6-2 Client-side timeout
RemotingException remotingException = new RemotingException(
this,
"client(url: " + getUrl() + ") failed to connect to server " + getConnectAddress()
+ " client-side timeout " + getConnectTimeout() + "ms (elapsed: "
+ (System.currentTimeMillis() - start) + "ms) from netty client " + NetUtils.getLocalHost()
+ " using dubbo version "
+ Version.getVersion());
LOGGER.error(
TRANSPORT_CLIENT_CONNECT_TIMEOUT, "provider crash", "", "Client-side timeout.", remotingException);
throw remotingException;
}
}
protected abstract ChannelFuture performConnect();
@Override
protected void doDisConnect() {
NettyChannel.removeChannelIfDisconnected(getNettyChannel());
}
@Override
public void onConnected(Object channel) {
if (!(channel instanceof io.netty.channel.Channel)) {
return;
}
io.netty.channel.Channel nettyChannel = ((io.netty.channel.Channel) channel);
if (isClosed()) {
nettyChannel.close();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("%s is closed, ignoring connected event", this));
}
return;
}
// Close the existing channel before setting a new channel
io.netty.channel.Channel current = getNettyChannel();
if (current != null) {
current.close();
}
this.channel.set(nettyChannel);
// This indicates that the connection is available.
if (connectingPromise.get() != null) {
connectingPromise.get().trySuccess(CONNECTED_OBJECT);
}
nettyChannel.attr(CONNECTION).set(this);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("%s connected ", this));
}
}
@Override
public void onGoaway(Object channel) {
if (!(channel instanceof io.netty.channel.Channel)) {
return;
}
io.netty.channel.Channel nettyChannel = (io.netty.channel.Channel) channel;
if (this.channel.compareAndSet(nettyChannel, null)) {
// Ensure the channel is closed
if (nettyChannel.isOpen()) {
nettyChannel.close();
}
NettyChannel.removeChannelIfDisconnected(nettyChannel);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("%s goaway", this));
}
}
}
@Override
protected Channel getChannel() {
io.netty.channel.Channel c = getNettyChannel();
if (c == null) {
return null;
}
return NettyChannel.getOrAddChannel(c, getUrl(), this);
}
private io.netty.channel.Channel getNettyChannel() {
return channel.get();
}
protected void clearNettyChannel() {
channel.set(null);
}
@Override
public Object getChannel(Boolean generalizable) {
return Boolean.TRUE.equals(generalizable) ? getNettyChannel() : getChannel();
}
@Override
public boolean isAvailable() {
if (isClosed()) {
return false;
}
io.netty.channel.Channel nettyChannel = getNettyChannel();
if (nettyChannel != null && nettyChannel.isActive()) {
return true;
}
if (init.compareAndSet(false, true)) {
try {
doConnect();
} catch (RemotingException e) {
LOGGER.error(TRANSPORT_FAILED_RECONNECT, "", "", "Failed to connect to server: " + getConnectAddress());
}
}
createConnectingPromise();
connectingPromise.get().awaitUninterruptibly(getConnectTimeout(), TimeUnit.MILLISECONDS);
// destroy connectingPromise after used
synchronized (this) {
connectingPromise.set(null);
}
nettyChannel = getNettyChannel();
return nettyChannel != null && nettyChannel.isActive();
}
@Override
public void createConnectingPromise() {
connectingPromise.compareAndSet(null, new DefaultPromise<>(GlobalEventExecutor.INSTANCE));
}
public Promise<Void> getClosePromise() {
return closePromise;
}
public static AbstractConnectionClient getConnectionClientFromChannel(io.netty.channel.Channel channel) {
return channel.attr(CONNECTION).get();
}
public ChannelFuture write(Object request) throws RemotingException {
if (!isAvailable()) {
throw new RemotingException(
null,
null,
"Failed to send request " + request + ", cause: The channel to " + remote + " is closed!");
}
return ((io.netty.channel.Channel) getChannel()).writeAndFlush(request);
}
@Override
public void addCloseListener(Runnable func) {
getClosePromise().addListener(future -> func.run());
}
@Override
public void destroy() {
close();
}
@Override
public String toString() {
return super.toString() + " (Ref=" + getCounter() + ",local="
+ Optional.ofNullable(getChannel())
.map(Channel::getLocalAddress)
.orElse(null) + ",remote=" + getRemoteAddress();
}
class ConnectionListener implements ChannelFutureListener {
@Override
public void operationComplete(ChannelFuture future) {
if (!isReconnecting.compareAndSet(true, false)) {
return;
}
if (future.isSuccess()) {
return;
}
AbstractNettyConnectionClient connectionClient = AbstractNettyConnectionClient.this;
if (connectionClient.isClosed() || connectionClient.getCounter() == 0) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format(
"%s aborted to reconnect. %s",
connectionClient, future.cause().getMessage()));
}
return;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format(
"%s is reconnecting, attempt=%d cause=%s",
connectionClient, 0, future.cause().getMessage()));
}
connectivityExecutor.schedule(
() -> {
try {
connectionClient.doConnect();
} catch (RemotingException e) {
LOGGER.error(
TRANSPORT_FAILED_RECONNECT,
"",
"",
"Failed to connect to server: " + getConnectAddress());
}
},
reconnectDuration,
TimeUnit.MILLISECONDS);
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.transport.netty4;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.List;
import io.netty.channel.Channel;
public final class AddressUtils {
private static final List<ChannelAddressAccessor> ACCESSORS =
FrameworkModel.defaultModel().getActivateExtensions(ChannelAddressAccessor.class);
private AddressUtils() {}
public static InetSocketAddress getRemoteAddress(Channel channel) {
InetSocketAddress address;
for (int i = 0, len = ACCESSORS.size(); i < len; i++) {
address = ACCESSORS.get(i).getRemoteAddress(channel);
if (address != null) {
return address;
}
}
return (InetSocketAddress) channel.remoteAddress();
}
public static InetSocketAddress getLocalAddress(Channel channel) {
InetSocketAddress address;
for (int i = 0, len = ACCESSORS.size(); i < len; i++) {
address = ACCESSORS.get(i).getLocalAddress(channel);
if (address != null) {
return address;
}
}
return (InetSocketAddress) channel.localAddress();
}
public static String getRemoteAddressKey(Channel channel) {
InetSocketAddress address;
for (int i = 0, len = ACCESSORS.size(); i < len; i++) {
ChannelAddressAccessor accessor = ACCESSORS.get(i);
address = accessor.getRemoteAddress(channel);
if (address != null) {
return accessor.getProtocol() + ' ' + NetUtils.toAddressString(address);
}
}
InetSocketAddress remoteAddress = (InetSocketAddress) channel.remoteAddress();
if (remoteAddress == null) {
return "UNKNOWN";
}
return NetUtils.toAddressString(remoteAddress);
}
public static String getLocalAddressKey(Channel channel) {
InetSocketAddress address;
for (int i = 0, len = ACCESSORS.size(); i < len; i++) {
ChannelAddressAccessor accessor = ACCESSORS.get(i);
address = accessor.getLocalAddress(channel);
if (address != null) {
return accessor.getProtocol() + ' ' + NetUtils.toAddressString(address);
}
}
SocketAddress localAddress = channel.localAddress();
if (localAddress == null) {
return "UNKNOWN";
}
return NetUtils.toAddressString((InetSocketAddress) localAddress);
}
}

View File

@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.remoting.transport.netty4;
import org.apache.dubbo.common.extension.ExtensionScope;
import org.apache.dubbo.common.extension.SPI;
import java.net.InetSocketAddress;
import io.netty.channel.Channel;
@SPI(scope = ExtensionScope.FRAMEWORK)
public interface ChannelAddressAccessor {
String getProtocol();
InetSocketAddress getRemoteAddress(Channel channel);
InetSocketAddress getLocalAddress(Channel channel);
}

View File

@ -150,12 +150,12 @@ final class NettyChannel extends AbstractChannel {
@Override
public InetSocketAddress getLocalAddress() {
return (InetSocketAddress) channel.localAddress();
return AddressUtils.getLocalAddress(channel);
}
@Override
public InetSocketAddress getRemoteAddress() {
return (InetSocketAddress) channel.remoteAddress();
return AddressUtils.getRemoteAddress(channel);
}
@Override

View File

@ -46,15 +46,18 @@ public class NettyChannelHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
io.netty.channel.Channel ch = ctx.channel();
NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
if (channel != null) {
dubboChannels.put(
NetUtils.toAddressString((InetSocketAddress) ctx.channel().remoteAddress()), channel);
dubboChannels.put(NetUtils.toAddressString((InetSocketAddress) ch.remoteAddress()), channel);
handler.connected(channel);
if (logger.isInfoEnabled()) {
logger.info("The connection of " + channel.getRemoteAddress() + " -> " + channel.getLocalAddress()
+ " is established.");
logger.info(
"The connection {} of {} -> {} is established.",
ch,
AddressUtils.getRemoteAddressKey(ch),
AddressUtils.getLocalAddressKey(ch));
}
}
}
@ -62,19 +65,22 @@ public class NettyChannelHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
io.netty.channel.Channel ch = ctx.channel();
NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
try {
dubboChannels.remove(
NetUtils.toAddressString((InetSocketAddress) ctx.channel().remoteAddress()));
dubboChannels.remove(NetUtils.toAddressString((InetSocketAddress) ch.remoteAddress()));
if (channel != null) {
handler.disconnected(channel);
if (logger.isInfoEnabled()) {
logger.info("The connection of " + channel.getRemoteAddress() + " -> " + channel.getLocalAddress()
+ " is disconnected.");
logger.info(
"The connection {} of {} -> {} is disconnected.",
ch,
AddressUtils.getRemoteAddressKey(ch),
AddressUtils.getLocalAddressKey(ch));
}
}
} finally {
NettyChannel.removeChannel(ctx.channel());
NettyChannel.removeChannel(ch);
}
}
}

View File

@ -23,6 +23,7 @@ import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.exchange.Request;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
@ -54,26 +55,34 @@ public class NettyClientHandler extends ChannelDuplexHandler {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
Channel ch = ctx.channel();
NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
handler.connected(channel);
if (logger.isInfoEnabled()) {
logger.info("The connection of " + channel.getLocalAddress() + " -> " + channel.getRemoteAddress()
+ " is established.");
logger.info(
"The connection {} of {} -> {} is established.",
ch,
AddressUtils.getLocalAddressKey(ch),
AddressUtils.getRemoteAddressKey(ch));
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
Channel ch = ctx.channel();
NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
try {
handler.disconnected(channel);
} finally {
NettyChannel.removeChannel(ctx.channel());
NettyChannel.removeChannel(ch);
}
if (logger.isInfoEnabled()) {
logger.info("The connection of " + channel.getLocalAddress() + " -> " + channel.getRemoteAddress()
+ " is disconnected.");
logger.info(
"The connection {} of {} -> {} is disconnected.",
ch,
AddressUtils.getLocalAddressKey(ch),
AddressUtils.getRemoteAddressKey(ch));
}
}

View File

@ -17,93 +17,46 @@
package org.apache.dubbo.remoting.transport.netty4;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.remoting.RemotingException;
import org.apache.dubbo.remoting.api.WireProtocol;
import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient;
import org.apache.dubbo.remoting.transport.netty4.ssl.SslClientTlsHandler;
import org.apache.dubbo.remoting.transport.netty4.ssl.SslContexts;
import org.apache.dubbo.remoting.utils.UrlUtils;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.Promise;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_CLIENT_CONNECT_TIMEOUT;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_CONNECT_PROVIDER;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RECONNECT;
import static org.apache.dubbo.remoting.transport.netty4.NettyEventLoopFactory.socketChannelClass;
public class NettyConnectionClient extends AbstractConnectionClient {
private static final ErrorTypeAwareLogger LOGGER =
LoggerFactory.getErrorTypeAwareLogger(NettyConnectionClient.class);
private AtomicReference<Promise<Object>> connectingPromise;
private Promise<Void> closePromise;
private AtomicReference<io.netty.channel.Channel> channel;
private ConnectionListener connectionListener;
public final class NettyConnectionClient extends AbstractNettyConnectionClient {
private Bootstrap bootstrap;
public static final AttributeKey<AbstractConnectionClient> CONNECTION = AttributeKey.valueOf("connection");
private AtomicBoolean isReconnecting;
public NettyConnectionClient(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
}
@Override
protected void initConnectionClient() {
this.protocol = getUrl().getOrDefaultFrameworkModel()
protocol = getUrl().getOrDefaultFrameworkModel()
.getExtensionLoader(WireProtocol.class)
.getExtension(getUrl().getProtocol());
this.remote = getConnectAddress();
this.connectingPromise = new AtomicReference<>();
this.connectionListener = new ConnectionListener();
this.channel = new AtomicReference<>();
this.closePromise = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
this.init = new AtomicBoolean(false);
this.increase();
this.isReconnecting = new AtomicBoolean(false);
super.initConnectionClient();
}
@Override
protected void doOpen() throws Throwable {
initConnectionClient();
initBootstrap();
}
private void initBootstrap() {
final Bootstrap nettyBootstrap = new Bootstrap();
nettyBootstrap
protected void initBootstrap() {
Bootstrap bootstrap = new Bootstrap();
bootstrap
.group(NettyEventLoopFactory.NIO_EVENT_LOOP_GROUP.get())
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
@ -111,10 +64,10 @@ public class NettyConnectionClient extends AbstractConnectionClient {
.remoteAddress(getConnectAddress())
.channel(socketChannelClass());
final NettyConnectionHandler connectionHandler = new NettyConnectionHandler(this);
nettyBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getConnectTimeout());
NettyConnectionHandler connectionHandler = new NettyConnectionHandler(this);
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getConnectTimeout());
SslContext sslContext = SslContexts.buildClientSslContext(getUrl());
nettyBootstrap.handler(new ChannelInitializer<SocketChannel>() {
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
NettyChannel nettyChannel = NettyChannel.getOrAddChannel(ch, getUrl(), getChannelHandler());
@ -134,273 +87,16 @@ public class NettyConnectionClient extends AbstractConnectionClient {
NettyConfigOperator operator = new NettyConfigOperator(nettyChannel, getChannelHandler());
protocol.configClientPipeline(getUrl(), operator, nettySslContextOperator);
// set null but do not close this client, it will be reconnect in the future
ch.closeFuture().addListener(channelFuture -> channel.set(null));
// set null but do not close this client, it will be reconnecting in the future
ch.closeFuture().addListener(channelFuture -> clearNettyChannel());
// TODO support Socks5
}
});
this.bootstrap = nettyBootstrap;
this.bootstrap = bootstrap;
}
@Override
protected void doClose() {
// AbstractPeer close can set closed true.
if (isClosed()) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("Connection:%s freed ", this));
}
final io.netty.channel.Channel current = getNettyChannel();
if (current != null) {
current.close();
}
this.channel.set(null);
closePromise.setSuccess(null);
}
}
@Override
protected void doConnect() throws RemotingException {
if (!isReconnecting.compareAndSet(false, true)) {
return;
}
if (isClosed()) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
String.format("%s aborted to reconnect cause connection closed. ", NettyConnectionClient.this));
}
}
init.compareAndSet(false, true);
long start = System.currentTimeMillis();
createConnectingPromise();
final ChannelFuture promise = bootstrap.connect();
promise.addListener(this.connectionListener);
boolean ret = connectingPromise.get().awaitUninterruptibly(getConnectTimeout(), TimeUnit.MILLISECONDS);
// destroy connectingPromise after used
synchronized (this) {
connectingPromise.set(null);
}
if (promise.cause() != null) {
Throwable cause = promise.cause();
// 6-1 Failed to connect to provider server by other reason.
RemotingException remotingException = new RemotingException(
this,
"client(url: " + getUrl() + ") failed to connect to server " + getConnectAddress()
+ ", error message is:" + cause.getMessage(),
cause);
LOGGER.error(
TRANSPORT_FAILED_CONNECT_PROVIDER,
"network disconnected",
"",
"Failed to connect to provider server by other reason.",
cause);
throw remotingException;
} else if (!ret || !promise.isSuccess()) {
// 6-2 Client-side timeout
RemotingException remotingException = new RemotingException(
this,
"client(url: " + getUrl() + ") failed to connect to server "
+ getConnectAddress() + " client-side timeout "
+ getConnectTimeout() + "ms (elapsed: " + (System.currentTimeMillis() - start)
+ "ms) from netty client "
+ NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion());
LOGGER.error(
TRANSPORT_CLIENT_CONNECT_TIMEOUT, "provider crash", "", "Client-side timeout.", remotingException);
throw remotingException;
}
}
@Override
protected void doDisConnect() {
NettyChannel.removeChannelIfDisconnected(getNettyChannel());
}
@Override
public void onConnected(Object channel) {
if (!(channel instanceof io.netty.channel.Channel)) {
return;
}
io.netty.channel.Channel nettyChannel = ((io.netty.channel.Channel) channel);
if (isClosed()) {
nettyChannel.close();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("%s is closed, ignoring connected event", this));
}
return;
}
// Close the existing channel before setting a new channel
final io.netty.channel.Channel current = getNettyChannel();
if (current != null) {
current.close();
}
this.channel.set(nettyChannel);
// This indicates that the connection is available.
if (this.connectingPromise.get() != null) {
this.connectingPromise.get().trySuccess(CONNECTED_OBJECT);
}
nettyChannel.attr(CONNECTION).set(this);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("%s connected ", this));
}
}
@Override
public void onGoaway(Object channel) {
if (!(channel instanceof io.netty.channel.Channel)) {
return;
}
io.netty.channel.Channel nettyChannel = (io.netty.channel.Channel) channel;
if (this.channel.compareAndSet(nettyChannel, null)) {
// Ensure the channel is closed
if (nettyChannel.isOpen()) {
nettyChannel.close();
}
NettyChannel.removeChannelIfDisconnected(nettyChannel);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("%s goaway", this));
}
}
}
@Override
protected Channel getChannel() {
io.netty.channel.Channel c = getNettyChannel();
if (c == null) {
return null;
}
return NettyChannel.getOrAddChannel(c, getUrl(), this);
}
io.netty.channel.Channel getNettyChannel() {
return this.channel.get();
}
@Override
public Object getChannel(Boolean generalizable) {
return Boolean.TRUE.equals(generalizable) ? getNettyChannel() : getChannel();
}
@Override
public boolean isAvailable() {
if (isClosed()) {
return false;
}
io.netty.channel.Channel nettyChannel = getNettyChannel();
if (nettyChannel != null && nettyChannel.isActive()) {
return true;
}
if (init.compareAndSet(false, true)) {
try {
doConnect();
} catch (RemotingException e) {
LOGGER.error(TRANSPORT_FAILED_RECONNECT, "", "", "Failed to connect to server: " + getConnectAddress());
}
}
createConnectingPromise();
connectingPromise.get().awaitUninterruptibly(getConnectTimeout(), TimeUnit.MILLISECONDS);
// destroy connectingPromise after used
synchronized (this) {
connectingPromise.set(null);
}
nettyChannel = getNettyChannel();
return nettyChannel != null && nettyChannel.isActive();
}
@Override
public void createConnectingPromise() {
connectingPromise.compareAndSet(null, new DefaultPromise<>(GlobalEventExecutor.INSTANCE));
}
public Promise<Void> getClosePromise() {
return closePromise;
}
public static AbstractConnectionClient getConnectionClientFromChannel(io.netty.channel.Channel channel) {
return channel.attr(CONNECTION).get();
}
public ChannelFuture write(Object request) throws RemotingException {
if (!isAvailable()) {
throw new RemotingException(
null,
null,
"Failed to send request " + request + ", cause: The channel to " + remote + " is closed!");
}
return ((io.netty.channel.Channel) getChannel()).writeAndFlush(request);
}
@Override
public void addCloseListener(Runnable func) {
getClosePromise().addListener(future -> func.run());
}
@Override
public void destroy() {
close();
}
@Override
public String toString() {
return super.toString() + " (Ref=" + this.getCounter() + ",local="
+ Optional.ofNullable(getChannel())
.map(Channel::getLocalAddress)
.orElse(null) + ",remote=" + getRemoteAddress();
}
class ConnectionListener implements ChannelFutureListener {
@Override
public void operationComplete(ChannelFuture future) {
if (!isReconnecting.compareAndSet(true, false)) {
return;
}
if (future.isSuccess()) {
return;
}
final NettyConnectionClient connectionClient = NettyConnectionClient.this;
if (connectionClient.isClosed() || connectionClient.getCounter() == 0) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format(
"%s aborted to reconnect. %s",
connectionClient, future.cause().getMessage()));
}
return;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format(
"%s is reconnecting, attempt=%d cause=%s",
connectionClient, 0, future.cause().getMessage()));
}
connectivityExecutor.schedule(
() -> {
try {
connectionClient.doConnect();
} catch (RemotingException e) {
LOGGER.error(
TRANSPORT_FAILED_RECONNECT,
"",
"",
"Failed to connect to server: " + getConnectAddress());
}
},
reconnectDuaration,
TimeUnit.MILLISECONDS);
}
protected ChannelFuture performConnect() {
return bootstrap.connect();
}
}

View File

@ -23,7 +23,7 @@ import org.apache.dubbo.remoting.api.connection.ConnectionHandler;
import java.util.concurrent.TimeUnit;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.EventLoop;
@ -33,16 +33,16 @@ import io.netty.util.AttributeKey;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RECONNECT;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_UNEXPECTED_EXCEPTION;
@ChannelHandler.Sharable
@Sharable
public class NettyConnectionHandler extends ChannelInboundHandlerAdapter implements ConnectionHandler {
private static final ErrorTypeAwareLogger LOGGER =
LoggerFactory.getErrorTypeAwareLogger(NettyConnectionHandler.class);
private static final AttributeKey<Boolean> GO_AWAY_KEY = AttributeKey.valueOf("dubbo_channel_goaway");
private final NettyConnectionClient connectionClient;
private final AbstractNettyConnectionClient connectionClient;
public NettyConnectionHandler(NettyConnectionClient connectionClient) {
public NettyConnectionHandler(AbstractNettyConnectionClient connectionClient) {
this.connectionClient = connectionClient;
}
@ -52,7 +52,7 @@ public class NettyConnectionHandler extends ChannelInboundHandlerAdapter impleme
return;
}
Channel nettyChannel = ((Channel) channel);
final Attribute<Boolean> attr = nettyChannel.attr(GO_AWAY_KEY);
Attribute<Boolean> attr = nettyChannel.attr(GO_AWAY_KEY);
if (Boolean.TRUE.equals(attr.get())) {
return;
}
@ -76,7 +76,7 @@ public class NettyConnectionHandler extends ChannelInboundHandlerAdapter impleme
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("Connection %s is reconnecting, attempt=%d", connectionClient, 1));
}
final EventLoop eventLoop = nettyChannel.eventLoop();
EventLoop eventLoop = nettyChannel.eventLoop();
if (connectionClient.isClosed()) {
LOGGER.info("The client has been closed and will not reconnect. ");
return;
@ -101,12 +101,16 @@ public class NettyConnectionHandler extends ChannelInboundHandlerAdapter impleme
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.fireChannelActive();
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), connectionClient.getUrl(), connectionClient);
Channel ch = ctx.channel();
NettyChannel.getOrAddChannel(ch, connectionClient.getUrl(), connectionClient);
if (!connectionClient.isClosed()) {
connectionClient.onConnected(ctx.channel());
connectionClient.onConnected(ch);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("The connection of " + channel.getLocalAddress() + " -> " + channel.getRemoteAddress()
+ " is established.");
LOGGER.info(
"The connection {} of {} -> {} is established.",
ch,
AddressUtils.getLocalAddressKey(ch),
AddressUtils.getRemoteAddressKey(ch));
}
} else {
ctx.close();
@ -114,17 +118,27 @@ public class NettyConnectionHandler extends ChannelInboundHandlerAdapter impleme
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
LOGGER.warn(TRANSPORT_UNEXPECTED_EXCEPTION, "", "", String.format("Channel error:%s", ctx.channel()), cause);
ctx.close();
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
Channel ch = ctx.channel();
try {
Attribute<Boolean> goawayAttr = ch.attr(GO_AWAY_KEY);
if (!Boolean.TRUE.equals(goawayAttr.get())) {
reconnect(ch);
}
LOGGER.info(
"The connection {} of {} -> {} is disconnected.",
ch,
AddressUtils.getLocalAddressKey(ch),
AddressUtils.getRemoteAddressKey(ch));
} finally {
NettyChannel.removeChannel(ch);
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
final Attribute<Boolean> goawayAttr = ctx.channel().attr(GO_AWAY_KEY);
if (!Boolean.TRUE.equals(goawayAttr.get())) {
reconnect(ctx.channel());
}
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
LOGGER.warn(TRANSPORT_UNEXPECTED_EXCEPTION, "", "", String.format("Channel error:%s", ctx.channel()), cause);
ctx.close();
}
}

View File

@ -191,7 +191,7 @@ public class NettyServer extends AbstractServer {
}
@Override
protected void doClose() throws Throwable {
protected void doClose() {
try {
if (channel != null) {
// unbind.
@ -247,10 +247,7 @@ public class NettyServer extends AbstractServer {
@Override
public Collection<Channel> getChannels() {
Collection<Channel> chs = new ArrayList<>(this.channels.size());
// pick channels from NettyServerHandler ( needless to check connectivity )
chs.addAll(this.channels.values());
return chs;
return new ArrayList<>(channels.values());
}
@Override

View File

@ -23,7 +23,6 @@ import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.ChannelHandler;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -65,33 +64,39 @@ public class NettyServerHandler extends ChannelDuplexHandler {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
io.netty.channel.Channel ch = ctx.channel();
NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
if (channel != null) {
channels.put(
NetUtils.toAddressString((InetSocketAddress) ctx.channel().remoteAddress()), channel);
channels.put(NetUtils.toAddressString(channel.getRemoteAddress()), channel);
}
handler.connected(channel);
if (logger.isInfoEnabled()) {
logger.info("The connection of " + channel.getRemoteAddress() + " -> " + channel.getLocalAddress()
+ " is established.");
if (logger.isInfoEnabled() && channel != null) {
logger.info(
"The connection {} of {} -> {} is established.",
ch,
AddressUtils.getRemoteAddressKey(ch),
AddressUtils.getLocalAddressKey(ch));
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
io.netty.channel.Channel ch = ctx.channel();
NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
try {
channels.remove(
NetUtils.toAddressString((InetSocketAddress) ctx.channel().remoteAddress()));
channels.remove(NetUtils.toAddressString(channel.getRemoteAddress()));
handler.disconnected(channel);
} finally {
NettyChannel.removeChannel(ctx.channel());
NettyChannel.removeChannel(ch);
}
if (logger.isInfoEnabled()) {
logger.info("The connection of " + channel.getRemoteAddress() + " -> " + channel.getLocalAddress()
+ " is disconnected.");
logger.info(
"The connection {} of {} -> {} is disconnected.",
ch,
AddressUtils.getRemoteAddressKey(ch),
AddressUtils.getLocalAddressKey(ch));
}
}

View File

@ -33,6 +33,7 @@
<module>dubbo-remoting-zookeeper-curator5</module>
<module>dubbo-remoting-netty4</module>
<module>dubbo-remoting-http12</module>
<module>dubbo-remoting-http3</module>
</modules>
<properties>
<skip_maven_deploy>false</skip_maven_deploy>

View File

@ -97,12 +97,16 @@ public interface Constants {
String INVOCATION_KEY = "invocation";
String SERIALIZATION_ID_KEY = "serialization_id";
String HTTP3_KEY = "http3";
String H2_SETTINGS_SUPPORT_NO_LOWER_HEADER_KEY = "dubbo.rpc.tri.support-no-lower-header";
String H2_SETTINGS_IGNORE_1_0_0_KEY = "dubbo.rpc.tri.ignore-1.0.0-version";
String H2_SETTINGS_RESOLVE_FALLBACK_TO_DEFAULT_KEY = "dubbo.rpc.tri.resolve-fallback-to-default";
String H2_SETTINGS_BUILTIN_SERVICE_INIT = "dubbo.tri.builtin.service.init";
String H2_SETTINGS_PASS_THROUGH_STANDARD_HTTP_HEADERS = "dubbo.rpc.tri.pass-through-standard-http-headers";
String H3_SETTINGS_HTTP3_ENABLE = "dubbo.protocol.triple.enable-http3";
String ADAPTIVE_LOADBALANCE_ATTACHMENT_KEY = "lb_adaptive";
String ADAPTIVE_LOADBALANCE_START_TIME = "adaptive_startTime";
}

View File

@ -41,11 +41,16 @@
<artifactId>dubbo-remoting-http12</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-http3</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.netty</groupId>

View File

@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.protocol.tri;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.config.context.ConfigManager;
import org.apache.dubbo.config.nested.TripleConfig;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.api.AbstractWireProtocol;
@ -86,7 +87,7 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo
@Override
public void configClientPipeline(URL url, ChannelOperator operator, ContextOperator contextOperator) {
TripleConfig tripleConfig = getTripleConfig(url);
TripleConfig tripleConfig = ConfigManager.getProtocol(url).getTriple();
final Http2FrameCodec codec = Http2FrameCodecBuilder.forClient()
.gracefulShutdownTimeoutMillis(10000)
.initialSettings(new Http2Settings()
@ -130,7 +131,7 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo
}
private void configurerHttp1Handlers(URL url, List<ChannelHandler> handlers) {
TripleConfig tripleConfig = getTripleConfig(url);
TripleConfig tripleConfig = ConfigManager.getProtocol(url).getTriple();
final HttpServerCodec sourceCodec = new HttpServerCodec(new HttpDecoderConfig()
.setMaxChunkSize(tripleConfig.getMaxChunkSize())
.setMaxHeaderSize(tripleConfig.getMaxHeaderSize())
@ -176,7 +177,7 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo
}
private void configurerHttp2Handlers(URL url, List<ChannelHandler> handlers) {
TripleConfig tripleConfig = getTripleConfig(url);
TripleConfig tripleConfig = ConfigManager.getProtocol(url).getTriple();
final Http2FrameCodec codec = buildHttp2FrameCodec(tripleConfig);
final Http2MultiplexHandler handler = buildHttp2MultiplexHandler(url, tripleConfig);
handlers.add(new ChannelHandlerPretender(new HttpWriteQueueHandler()));
@ -202,11 +203,4 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo
.validateHeaders(false)
.build();
}
private TripleConfig getTripleConfig(URL url) {
return url.getOrDefaultApplicationModel()
.getApplicationConfigManager()
.getOrAddProtocol(url.getProtocol())
.getTriple();
}
}

View File

@ -25,7 +25,9 @@ import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
import org.apache.dubbo.common.utils.ExecutorUtil;
import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient;
import org.apache.dubbo.remoting.api.pu.DefaultPuHandler;
import org.apache.dubbo.remoting.exchange.Http3Exchanger;
import org.apache.dubbo.remoting.exchange.PortUnificationExchanger;
import org.apache.dubbo.rpc.Constants;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.PathResolver;
@ -55,6 +57,7 @@ import static org.apache.dubbo.rpc.Constants.H2_SETTINGS_IGNORE_1_0_0_KEY;
import static org.apache.dubbo.rpc.Constants.H2_SETTINGS_PASS_THROUGH_STANDARD_HTTP_HEADERS;
import static org.apache.dubbo.rpc.Constants.H2_SETTINGS_RESOLVE_FALLBACK_TO_DEFAULT_KEY;
import static org.apache.dubbo.rpc.Constants.H2_SETTINGS_SUPPORT_NO_LOWER_HEADER_KEY;
import static org.apache.dubbo.rpc.Constants.H3_SETTINGS_HTTP3_ENABLE;
public class TripleProtocol extends AbstractProtocol {
@ -69,6 +72,7 @@ public class TripleProtocol extends AbstractProtocol {
public static boolean IGNORE_1_0_0_VERSION = false;
public static boolean RESOLVE_FALLBACK_TO_DEFAULT = true;
public static boolean PASS_THROUGH_STANDARD_HTTP_HEADERS = false;
public static boolean HTTP3_ENABLED = false;
public TripleProtocol(FrameworkModel frameworkModel) {
this.frameworkModel = frameworkModel;
@ -83,6 +87,9 @@ public class TripleProtocol extends AbstractProtocol {
IGNORE_1_0_0_VERSION = conf.getBoolean(H2_SETTINGS_IGNORE_1_0_0_KEY, false);
RESOLVE_FALLBACK_TO_DEFAULT = conf.getBoolean(H2_SETTINGS_RESOLVE_FALLBACK_TO_DEFAULT_KEY, true);
PASS_THROUGH_STANDARD_HTTP_HEADERS = conf.getBoolean(H2_SETTINGS_PASS_THROUGH_STANDARD_HTTP_HEADERS, false);
Configuration globalConf = ConfigurationUtils.getGlobalConfiguration(frameworkModel.defaultApplication());
HTTP3_ENABLED = globalConf.getBoolean(H3_SETTINGS_HTTP3_ENABLE, false);
}
@Override
@ -160,6 +167,11 @@ public class TripleProtocol extends AbstractProtocol {
.createExecutorIfAbsent(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME));
PortUnificationExchanger.bind(url, new DefaultPuHandler());
if (isHttp3Enabled(url)) {
Http3Exchanger.bind(url);
}
optimizeSerialization(url);
return exporter;
}
@ -168,7 +180,9 @@ public class TripleProtocol extends AbstractProtocol {
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
optimizeSerialization(url);
ExecutorService streamExecutor = getOrCreateStreamExecutor(url.getOrDefaultApplicationModel(), url);
AbstractConnectionClient connectionClient = PortUnificationExchanger.connect(url, new DefaultPuHandler());
AbstractConnectionClient connectionClient = isHttp3Enabled(url)
? Http3Exchanger.connect(url)
: PortUnificationExchanger.connect(url, new DefaultPuHandler());
TripleInvoker<T> invoker =
new TripleInvoker<>(type, url, acceptEncodings, connectionClient, invokers, streamExecutor);
invokers.add(invoker);
@ -195,8 +209,13 @@ public class TripleProtocol extends AbstractProtocol {
logger.info("Destroying protocol [" + getClass().getSimpleName() + "] ...");
}
PortUnificationExchanger.close();
Http3Exchanger.close();
pathResolver.destroy();
mappingRegistry.destroy();
super.destroy();
}
public static boolean isHttp3Enabled(URL url) {
return HTTP3_ENABLED || url.getParameter(Constants.HTTP3_KEY, false);
}
}

View File

@ -27,15 +27,14 @@ import org.apache.dubbo.rpc.protocol.tri.compressor.Compressor;
import org.apache.dubbo.rpc.protocol.tri.compressor.Identity;
import org.apache.dubbo.rpc.protocol.tri.observer.ClientCallToObserverAdapter;
import org.apache.dubbo.rpc.protocol.tri.stream.ClientStream;
import org.apache.dubbo.rpc.protocol.tri.stream.ClientStreamFactory;
import org.apache.dubbo.rpc.protocol.tri.stream.StreamUtils;
import org.apache.dubbo.rpc.protocol.tri.stream.TripleClientStream;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleWriteQueue;
import java.util.Map;
import java.util.concurrent.Executor;
import io.netty.channel.Channel;
import io.netty.handler.codec.http2.Http2Exception;
import io.netty.handler.codec.http2.Http2Exception.StreamException;
import static io.netty.handler.codec.http2.Http2Error.FLOW_CONTROL_ERROR;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_RESPONSE;
@ -55,7 +54,7 @@ public class TripleClientCall implements ClientCall, ClientStream.Listener {
private boolean headerSent;
private boolean autoRequest = true;
private boolean done;
private Http2Exception.StreamException streamException;
private StreamException streamException;
public TripleClientCall(
AbstractConnectionClient connectionClient,
@ -138,7 +137,7 @@ public class TripleClientCall implements ClientCall, ClientStream.Listener {
@Override
public void onStart() {
listener.onStart(TripleClientCall.this);
listener.onStart(this);
}
@Override
@ -154,13 +153,12 @@ public class TripleClientCall implements ClientCall, ClientStream.Listener {
if (stream == null) {
return;
}
if (t instanceof Http2Exception.StreamException
&& ((Http2Exception.StreamException) t).error().equals(FLOW_CONTROL_ERROR)) {
if (t instanceof StreamException && ((StreamException) t).error().equals(FLOW_CONTROL_ERROR)) {
TriRpcStatus status = TriRpcStatus.CANCELLED
.withCause(t)
.withDescription("Due flowcontrol over pendingbytes, Cancelled by client");
stream.cancelByLocal(status);
streamException = (Http2Exception.StreamException) t;
streamException = (StreamException) t;
} else {
TriRpcStatus status = TriRpcStatus.CANCELLED.withCause(t).withDescription("Cancelled by client");
stream.cancelByLocal(status);
@ -235,16 +233,22 @@ public class TripleClientCall implements ClientCall, ClientStream.Listener {
@Override
public void setCompression(String compression) {
this.requestMetadata.compressor = Compressor.getCompressor(frameworkModel, compression);
requestMetadata.compressor = Compressor.getCompressor(frameworkModel, compression);
}
@Override
public StreamObserver<Object> start(RequestMetadata metadata, ClientCall.Listener responseListener) {
this.requestMetadata = metadata;
this.listener = responseListener;
this.stream = new TripleClientStream(
frameworkModel, executor, (Channel) connectionClient.getChannel(true), this, writeQueue);
return new ClientCallToObserverAdapter<>(this);
ClientStream stream;
for (ClientStreamFactory factory : frameworkModel.getActivateExtensions(ClientStreamFactory.class)) {
stream = factory.createClientStream(connectionClient, frameworkModel, executor, this, writeQueue);
if (stream != null) {
this.requestMetadata = metadata;
this.listener = responseListener;
this.stream = stream;
return new ClientCallToObserverAdapter<>(this);
}
}
throw new IllegalStateException("No available ClientStreamFactory");
}
@Override

View File

@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.command;
import org.apache.dubbo.rpc.protocol.tri.stream.TripleStreamChannelFuture;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPromise;
import io.netty.incubator.codec.http3.Http3;
import io.netty.incubator.codec.quic.QuicChannel;
import io.netty.incubator.codec.quic.QuicStreamChannel;
public class Http3CreateStreamQueueCommand extends QueuedCommand {
private final ChannelInitializer<QuicStreamChannel> initializer;
private final TripleStreamChannelFuture streamChannelFuture;
private Http3CreateStreamQueueCommand(
ChannelInitializer<QuicStreamChannel> initializer, TripleStreamChannelFuture future) {
this.initializer = initializer;
this.streamChannelFuture = future;
this.promise(future.getParentChannel().newPromise());
this.channel(future.getParentChannel());
}
public static Http3CreateStreamQueueCommand create(
ChannelInitializer<QuicStreamChannel> initializer, TripleStreamChannelFuture future) {
return new Http3CreateStreamQueueCommand(initializer, future);
}
@Override
public void doSend(ChannelHandlerContext ctx, ChannelPromise promise) {}
@Override
public void run(Channel channel) {
Http3.newRequestStream((QuicChannel) channel, initializer).addListener(future -> {
if (future.isSuccess()) {
streamChannelFuture.complete((Channel) future.getNow());
} else {
streamChannelFuture.completeExceptionally(future.cause());
}
});
}
}

View File

@ -232,9 +232,16 @@ public abstract class AbstractServerTransportListener<HEADER extends RequestMeta
// customizer RpcInvocation
headerFilters.forEach(f -> f.invoke(invoker, inv));
initializeAltSvc(url);
return onBuildRpcInvocationCompletion(inv);
}
/**
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Alt-Svc">Alt-Svc</a>
*/
protected void initializeAltSvc(URL url) {}
protected RpcInvocation onBuildRpcInvocationCompletion(RpcInvocation invocation) {
String timeoutString = httpMetadata.headers().getFirst(TripleHeaderEnum.SERVICE_TIMEOUT.getHeader());
try {

View File

@ -59,8 +59,9 @@ public class GrpcHttp2ServerTransportListener extends GenericHttp2ServerTranspor
}
private void grpcTrailersCustomize(HttpHeaders httpHeaders, Throwable throwable) {
httpHeaders.set(GrpcHeaderNames.GRPC_STATUS.getName(), "0");
if (throwable != null) {
if (throwable == null) {
httpHeaders.set(GrpcHeaderNames.GRPC_STATUS.getName(), "0");
} else {
httpHeaders.set(GrpcHeaderNames.GRPC_STATUS.getName(), httpStatusToGrpcStatus(throwable));
httpHeaders.set(GrpcHeaderNames.GRPC_MESSAGE.getName(), throwable.getMessage());
}

View File

@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.stream.StreamObserver;
import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
import org.apache.dubbo.common.threadpool.serial.SerializingExecutor;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.remoting.http12.HttpChannel;
import org.apache.dubbo.remoting.http12.HttpHeaderNames;
import org.apache.dubbo.remoting.http12.HttpInputMessage;
@ -37,6 +38,7 @@ import org.apache.dubbo.rpc.executor.ExecutorSupport;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.MethodDescriptor;
import org.apache.dubbo.rpc.protocol.tri.RpcInvocationBuildContext;
import org.apache.dubbo.rpc.protocol.tri.TripleProtocol;
import org.apache.dubbo.rpc.protocol.tri.h12.AbstractServerTransportListener;
import org.apache.dubbo.rpc.protocol.tri.h12.DefaultHttpMessageListener;
import org.apache.dubbo.rpc.protocol.tri.h12.HttpMessageListener;
@ -108,6 +110,13 @@ public class DefaultHttp11ServerTransportListener
serverChannelObserver.onError(throwable);
}
@Override
protected void initializeAltSvc(URL url) {
String protocolId = TripleProtocol.isHttp3Enabled(url) ? "h3" : "h2";
int bindPort = url.getParameter(Constants.BIND_PORT_KEY, url.getPort());
serverChannelObserver.setAltSvc(protocolId + "=\":" + bindPort + "\"");
}
@Override
public void close() throws Exception {
serverChannelObserver.close();

View File

@ -19,6 +19,7 @@ package org.apache.dubbo.rpc.protocol.tri.h12.http2;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
import org.apache.dubbo.common.threadpool.serial.SerializingExecutor;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.remoting.http12.h2.CancelStreamException;
import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
import org.apache.dubbo.remoting.http12.h2.Http2Header;
@ -40,6 +41,7 @@ import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.MethodDescriptor;
import org.apache.dubbo.rpc.protocol.tri.ReflectionPackableMethod;
import org.apache.dubbo.rpc.protocol.tri.RpcInvocationBuildContext;
import org.apache.dubbo.rpc.protocol.tri.TripleProtocol;
import org.apache.dubbo.rpc.protocol.tri.h12.AbstractServerTransportListener;
import org.apache.dubbo.rpc.protocol.tri.h12.BiStreamServerCallListener;
import org.apache.dubbo.rpc.protocol.tri.h12.HttpMessageListener;
@ -158,6 +160,14 @@ public class GenericHttp2ServerTransportListener extends AbstractServerTransport
return new BiStreamServerCallListener(invocation, invoker, responseObserver);
}
@Override
protected void initializeAltSvc(URL url) {
if (TripleProtocol.isHttp3Enabled(url)) {
int bindPort = url.getParameter(Constants.BIND_PORT_KEY, url.getPort());
serverChannelObserver.setAltSvc("h3=\":" + bindPort + "\"");
}
}
@Override
protected void onMetadataCompletion(Http2Header metadata) {
serverChannelObserver.setResponseEncoder(getContext().getHttpMessageEncoder());

View File

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.h12.http2;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.call.TripleClientCall;
import org.apache.dubbo.rpc.protocol.tri.stream.ClientStream;
import org.apache.dubbo.rpc.protocol.tri.stream.ClientStreamFactory;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleWriteQueue;
import java.util.concurrent.Executor;
import io.netty.channel.Channel;
@Activate
public class Http2ClientStreamFactory implements ClientStreamFactory {
@Override
public ClientStream createClientStream(
AbstractConnectionClient client,
FrameworkModel frameworkModel,
Executor executor,
TripleClientCall clientCall,
TripleWriteQueue writeQueue) {
return new Http2TripleClientStream(
frameworkModel, executor, (Channel) client.getChannel(true), clientCall, writeQueue);
}
}

View File

@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.h12.http2;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.command.CreateStreamQueueCommand;
import org.apache.dubbo.rpc.protocol.tri.stream.AbstractTripleClientStream;
import org.apache.dubbo.rpc.protocol.tri.stream.ClientStream;
import org.apache.dubbo.rpc.protocol.tri.stream.TripleStreamChannelFuture;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleCommandOutBoundHandler;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleHttp2ClientResponseHandler;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleWriteQueue;
import java.util.concurrent.Executor;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http2.Http2StreamChannel;
import io.netty.handler.codec.http2.Http2StreamChannelBootstrap;
public final class Http2TripleClientStream extends AbstractTripleClientStream {
public Http2TripleClientStream(
FrameworkModel frameworkModel,
Executor executor,
Channel parent,
ClientStream.Listener listener,
TripleWriteQueue writeQueue) {
super(frameworkModel, executor, writeQueue, listener, parent);
}
/**
* For test only
*/
public Http2TripleClientStream(
FrameworkModel frameworkModel,
Executor executor,
TripleWriteQueue writeQueue,
ClientStream.Listener listener,
Http2StreamChannel http2StreamChannel) {
super(frameworkModel, executor, writeQueue, listener, http2StreamChannel);
}
@Override
protected TripleStreamChannelFuture initStreamChannel(Channel parent) {
Http2StreamChannelBootstrap bootstrap = new Http2StreamChannelBootstrap(parent);
bootstrap.handler(new ChannelInboundHandlerAdapter() {
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
ctx.channel()
.pipeline()
.addLast(new TripleCommandOutBoundHandler())
.addLast(new TripleHttp2ClientResponseHandler(createTransportListener()));
}
});
TripleStreamChannelFuture streamChannelFuture = new TripleStreamChannelFuture(parent);
writeQueue.enqueue(CreateStreamQueueCommand.create(bootstrap, streamChannelFuture));
return streamChannelFuture;
}
}

View File

@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.h3;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
import org.apache.dubbo.remoting.http12.h2.Http2InputMessage;
import org.apache.dubbo.remoting.http3.Http3TransportListener;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.h12.http2.GenericHttp2ServerTransportListener;
public final class GenericHttp3ServerTransportListener extends GenericHttp2ServerTransportListener
implements Http3TransportListener {
public GenericHttp3ServerTransportListener(
H2StreamChannel h2StreamChannel, URL url, FrameworkModel frameworkModel) {
super(h2StreamChannel, url, frameworkModel);
}
@Override
protected void doOnData(Http2InputMessage message) {
if (message.isEndStream()) {
onDataCompletion(message);
return;
}
super.doOnData(message);
}
@Override
protected void initializeAltSvc(URL url) {}
}

View File

@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.h3;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
import org.apache.dubbo.remoting.http3.Http3ServerTransportListenerFactory;
import org.apache.dubbo.remoting.http3.Http3TransportListener;
import org.apache.dubbo.rpc.model.FrameworkModel;
@Activate
public class GenericHttp3ServerTransportListenerFactory implements Http3ServerTransportListenerFactory {
@Override
public Http3TransportListener newInstance(H2StreamChannel streamChannel, URL url, FrameworkModel frameworkModel) {
return new GenericHttp3ServerTransportListener(streamChannel, url, frameworkModel);
}
@Override
public boolean supportContentType(String contentType) {
return true;
}
}

View File

@ -0,0 +1,107 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.h3;
import org.apache.dubbo.remoting.http3.netty4.Http2HeadersAdapter;
import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum;
import java.util.Map;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http2.DefaultHttp2DataFrame;
import io.netty.handler.codec.http2.DefaultHttp2GoAwayFrame;
import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame;
import io.netty.handler.codec.http2.DefaultHttp2ResetFrame;
import io.netty.handler.codec.http2.Http2DataFrame;
import io.netty.handler.codec.http2.Http2HeadersFrame;
import io.netty.incubator.codec.http3.DefaultHttp3DataFrame;
import io.netty.incubator.codec.http3.DefaultHttp3Headers;
import io.netty.incubator.codec.http3.DefaultHttp3HeadersFrame;
import io.netty.incubator.codec.http3.Http3DataFrame;
import io.netty.incubator.codec.http3.Http3ErrorCode;
import io.netty.incubator.codec.http3.Http3Exception;
import io.netty.incubator.codec.http3.Http3GoAwayFrame;
import io.netty.incubator.codec.http3.Http3Headers;
import io.netty.incubator.codec.http3.Http3HeadersFrame;
import io.netty.incubator.codec.quic.QuicStreamChannel;
@Sharable
public class Http3ClientFrameCodec extends ChannelDuplexHandler {
public static final Http3ClientFrameCodec INSTANCE = new Http3ClientFrameCodec();
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof Http3HeadersFrame) {
Http2HeadersAdapter headers = new Http2HeadersAdapter(((Http3HeadersFrame) msg).headers());
boolean endStream = headers.contains(TripleHeaderEnum.STATUS_KEY.getHeader());
ctx.fireChannelRead(new DefaultHttp2HeadersFrame(headers, endStream));
} else if (msg instanceof Http3DataFrame) {
ctx.fireChannelRead(new DefaultHttp2DataFrame(((Http3DataFrame) msg).content()));
} else if (msg instanceof Http3GoAwayFrame) {
ctx.fireUserEventTriggered(new DefaultHttp2GoAwayFrame(((Http3GoAwayFrame) msg).id()));
} else {
ctx.fireChannelRead(msg);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.fireChannelRead(new DefaultHttp2DataFrame(Unpooled.EMPTY_BUFFER, true));
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (msg instanceof Http2HeadersFrame) {
Http2HeadersFrame frame = (Http2HeadersFrame) msg;
Http3Headers headers = new DefaultHttp3Headers();
for (Map.Entry<CharSequence, CharSequence> header : frame.headers()) {
headers.set(header.getKey(), header.getValue());
}
ctx.write(new DefaultHttp3HeadersFrame(headers), promise);
if (frame.isEndStream()) {
((QuicStreamChannel) ctx.channel()).shutdownOutput(promise);
}
} else if (msg instanceof Http2DataFrame) {
Http2DataFrame frame = (Http2DataFrame) msg;
if (frame.isEndStream()) {
((QuicStreamChannel) ctx.channel()).shutdownOutput(promise);
return;
}
ctx.write(new DefaultHttp3DataFrame(frame.content()), promise);
} else {
ctx.write(msg, promise);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (cause instanceof Http3Exception) {
Http3Exception e = (Http3Exception) cause;
Http3ErrorCode errorCode = e.errorCode();
if (errorCode == Http3ErrorCode.H3_CLOSED_CRITICAL_STREAM) {
ctx.fireUserEventTriggered(new DefaultHttp2ResetFrame(256 + errorCode.ordinal()));
return;
}
}
super.exceptionCaught(ctx, cause);
}
}

View File

@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.h3;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient;
import org.apache.dubbo.remoting.transport.netty4.NettyHttp3ConnectionClient;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.call.TripleClientCall;
import org.apache.dubbo.rpc.protocol.tri.stream.ClientStream;
import org.apache.dubbo.rpc.protocol.tri.stream.ClientStreamFactory;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleWriteQueue;
import java.util.concurrent.Executor;
import io.netty.channel.Channel;
@Activate(order = -100, onClass = "io.netty.incubator.codec.quic.QuicChannel")
public class Http3ClientStreamFactory implements ClientStreamFactory {
@Override
public ClientStream createClientStream(
AbstractConnectionClient client,
FrameworkModel frameworkModel,
Executor executor,
TripleClientCall clientCall,
TripleWriteQueue writeQueue) {
if (client instanceof NettyHttp3ConnectionClient) {
return new Http3TripleClientStream(
frameworkModel, executor, (Channel) client.getChannel(true), clientCall, writeQueue);
}
return null;
}
}

View File

@ -0,0 +1,77 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.h3;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.command.Http3CreateStreamQueueCommand;
import org.apache.dubbo.rpc.protocol.tri.stream.AbstractTripleClientStream;
import org.apache.dubbo.rpc.protocol.tri.stream.ClientStream;
import org.apache.dubbo.rpc.protocol.tri.stream.TripleStreamChannelFuture;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleCommandOutBoundHandler;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleGoAwayHandler;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleHttp2ClientResponseHandler;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleTailHandler;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleWriteQueue;
import java.util.concurrent.Executor;
import io.netty.channel.Channel;
import io.netty.handler.codec.http2.Http2StreamChannel;
import io.netty.incubator.codec.http3.Http3RequestStreamInitializer;
import io.netty.incubator.codec.quic.QuicStreamChannel;
public final class Http3TripleClientStream extends AbstractTripleClientStream {
public Http3TripleClientStream(
FrameworkModel frameworkModel,
Executor executor,
Channel parent,
ClientStream.Listener listener,
TripleWriteQueue writeQueue) {
super(frameworkModel, executor, writeQueue, listener, parent);
}
/**
* For test only
*/
public Http3TripleClientStream(
FrameworkModel frameworkModel,
Executor executor,
TripleWriteQueue writeQueue,
ClientStream.Listener listener,
Http2StreamChannel http2StreamChannel) {
super(frameworkModel, executor, writeQueue, listener, http2StreamChannel);
}
@Override
protected TripleStreamChannelFuture initStreamChannel(Channel parent) {
Http3RequestStreamInitializer initializer = new Http3RequestStreamInitializer() {
@Override
protected void initRequestStream(QuicStreamChannel ch) {
ch.pipeline()
.addLast(Http3ClientFrameCodec.INSTANCE)
.addLast(new TripleCommandOutBoundHandler())
.addLast(new TripleHttp2ClientResponseHandler(createTransportListener()))
.addLast(new TripleGoAwayHandler())
.addLast(new TripleTailHandler());
}
};
TripleStreamChannelFuture future = new TripleStreamChannelFuture(parent);
writeQueue.enqueue(Http3CreateStreamQueueCommand.create(initializer, future));
return future;
}
}

View File

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.h3.grpc;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
import org.apache.dubbo.remoting.http12.h2.Http2InputMessage;
import org.apache.dubbo.remoting.http3.Http3TransportListener;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcHttp2ServerTransportListener;
public final class GrpcHttp3ServerTransportListener extends GrpcHttp2ServerTransportListener
implements Http3TransportListener {
public GrpcHttp3ServerTransportListener(H2StreamChannel h2StreamChannel, URL url, FrameworkModel frameworkModel) {
super(h2StreamChannel, url, frameworkModel);
}
@Override
protected void doOnData(Http2InputMessage message) {
if (message.isEndStream()) {
onDataCompletion(message);
return;
}
super.doOnData(message);
}
@Override
protected void initializeAltSvc(URL url) {}
}

View File

@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.h3.grpc;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
import org.apache.dubbo.remoting.http3.Http3ServerTransportListenerFactory;
import org.apache.dubbo.remoting.http3.Http3TransportListener;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum;
@Activate(order = -100, onClass = "com.google.protobuf.Message")
public class GrpcHttp3ServerTransportListenerFactory implements Http3ServerTransportListenerFactory {
@Override
public Http3TransportListener newInstance(H2StreamChannel streamChannel, URL url, FrameworkModel frameworkModel) {
return new GrpcHttp3ServerTransportListener(streamChannel, url, frameworkModel);
}
@Override
public boolean supportContentType(String contentType) {
return contentType != null && contentType.startsWith(TripleHeaderEnum.APPLICATION_GRPC.getHeader());
}
}

View File

@ -25,7 +25,6 @@ import org.apache.dubbo.rpc.protocol.tri.ClassLoadUtil;
import org.apache.dubbo.rpc.protocol.tri.ExceptionUtils;
import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum;
import org.apache.dubbo.rpc.protocol.tri.command.CancelQueueCommand;
import org.apache.dubbo.rpc.protocol.tri.command.CreateStreamQueueCommand;
import org.apache.dubbo.rpc.protocol.tri.command.DataQueueCommand;
import org.apache.dubbo.rpc.protocol.tri.command.EndStreamQueueCommand;
import org.apache.dubbo.rpc.protocol.tri.command.HeaderQueueCommand;
@ -35,10 +34,7 @@ import org.apache.dubbo.rpc.protocol.tri.frame.Deframer;
import org.apache.dubbo.rpc.protocol.tri.frame.TriDecoder;
import org.apache.dubbo.rpc.protocol.tri.transport.AbstractH2TransportListener;
import org.apache.dubbo.rpc.protocol.tri.transport.H2TransportListener;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleCommandOutBoundHandler;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleHttp2ClientResponseHandler;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleWriteQueue;
import org.apache.dubbo.rpc.protocol.tri.transport.WriteQueue;
import java.io.IOException;
import java.net.SocketAddress;
@ -55,27 +51,25 @@ import com.google.rpc.Status;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http2.Http2Error;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2StreamChannel;
import io.netty.handler.codec.http2.Http2StreamChannelBootstrap;
import io.netty.util.ReferenceCountUtil;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_RESPONSE;
/**
* ClientStream is an abstraction for bi-directional messaging. It maintains a {@link WriteQueue} to
* ClientStream is an abstraction for bidirectional messaging. It maintains a {@link TripleWriteQueue} to
* write Http2Frame to remote. A {@link H2TransportListener} receives Http2Frame from remote.
* Instead of maintaining state, this class depends on upper layer or transport layer's states.
*/
public class TripleClientStream extends AbstractStream implements ClientStream {
public abstract class AbstractTripleClientStream extends AbstractStream implements ClientStream {
private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(TripleClientStream.class);
private static final ErrorTypeAwareLogger LOGGER =
LoggerFactory.getErrorTypeAwareLogger(AbstractTripleClientStream.class);
public final ClientStream.Listener listener;
private final TripleWriteQueue writeQueue;
private final ClientStream.Listener listener;
protected final TripleWriteQueue writeQueue;
private Deframer deframer;
private final Channel parent;
private final TripleStreamChannelFuture streamChannelFuture;
@ -84,8 +78,7 @@ public class TripleClientStream extends AbstractStream implements ClientStream {
private boolean isReturnTriException = false;
// for test
TripleClientStream(
protected AbstractTripleClientStream(
FrameworkModel frameworkModel,
Executor executor,
TripleWriteQueue writeQueue,
@ -95,37 +88,23 @@ public class TripleClientStream extends AbstractStream implements ClientStream {
this.parent = http2StreamChannel.parent();
this.listener = listener;
this.writeQueue = writeQueue;
this.streamChannelFuture = initHttp2StreamChannel(http2StreamChannel);
this.streamChannelFuture = initStreamChannel(http2StreamChannel);
}
public TripleClientStream(
protected AbstractTripleClientStream(
FrameworkModel frameworkModel,
Executor executor,
Channel parent,
TripleWriteQueue writeQueue,
ClientStream.Listener listener,
TripleWriteQueue writeQueue) {
Channel parent) {
super(executor, frameworkModel);
this.parent = parent;
this.listener = listener;
this.writeQueue = writeQueue;
this.streamChannelFuture = initHttp2StreamChannel(parent);
this.streamChannelFuture = initStreamChannel(parent);
}
private TripleStreamChannelFuture initHttp2StreamChannel(Channel parent) {
TripleStreamChannelFuture streamChannelFuture = new TripleStreamChannelFuture(parent);
Http2StreamChannelBootstrap bootstrap = new Http2StreamChannelBootstrap(parent);
bootstrap.handler(new ChannelInboundHandlerAdapter() {
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channel.pipeline().addLast(new TripleCommandOutBoundHandler());
channel.pipeline().addLast(new TripleHttp2ClientResponseHandler(createTransportListener()));
}
});
CreateStreamQueueCommand cmd = CreateStreamQueueCommand.create(bootstrap, streamChannelFuture);
this.writeQueue.enqueue(cmd);
return streamChannelFuture;
}
protected abstract TripleStreamChannelFuture initStreamChannel(Channel parent);
public ChannelFuture sendHeader(Http2Headers headers) {
if (this.writeQueue == null) {
@ -157,7 +136,7 @@ public class TripleClientStream extends AbstractStream implements ClientStream {
}
final CancelQueueCommand cmd = CancelQueueCommand.createCommand(streamChannelFuture, Http2Error.CANCEL);
TripleClientStream.this.rst = true;
rst = true;
return this.writeQueue.enqueue(cmd);
}
@ -212,7 +191,7 @@ public class TripleClientStream extends AbstractStream implements ClientStream {
/**
* @return transport listener
*/
H2TransportListener createTransportListener() {
protected H2TransportListener createTransportListener() {
return new ClientTransportListener();
}
@ -225,7 +204,7 @@ public class TripleClientStream extends AbstractStream implements ClientStream {
void handleH2TransportError(TriRpcStatus status) {
writeQueue.enqueue(CancelQueueCommand.createCommand(streamChannelFuture, Http2Error.NO_ERROR));
TripleClientStream.this.rst = true;
rst = true;
finishProcess(status, null, false);
}
@ -304,7 +283,7 @@ public class TripleClientStream extends AbstractStream implements ClientStream {
TriDecoder.Listener listener = new TriDecoder.Listener() {
@Override
public void onRawMessage(byte[] data) {
TripleClientStream.this.listener.onMessage(data, isReturnTriException);
AbstractTripleClientStream.this.listener.onMessage(data, isReturnTriException);
}
public void close() {
@ -312,7 +291,7 @@ public class TripleClientStream extends AbstractStream implements ClientStream {
}
};
deframer = new TriDecoder(decompressor, listener);
TripleClientStream.this.listener.onStart();
AbstractTripleClientStream.this.listener.onStart();
}
void onTrailersReceived(Http2Headers trailers) {
@ -421,7 +400,7 @@ public class TripleClientStream extends AbstractStream implements ClientStream {
executor.execute(() -> {
if (endStream) {
if (!halfClosed) {
Http2StreamChannel channel = streamChannelFuture.getNow();
Channel channel = streamChannelFuture.getNow();
if (channel.isActive() && !rst) {
writeQueue.enqueue(
CancelQueueCommand.createCommand(streamChannelFuture, Http2Error.CANCEL));

View File

@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.stream;
import org.apache.dubbo.common.extension.ExtensionScope;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.call.TripleClientCall;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleWriteQueue;
import java.util.concurrent.Executor;
@SPI(scope = ExtensionScope.FRAMEWORK)
public interface ClientStreamFactory {
ClientStream createClientStream(
AbstractConnectionClient client,
FrameworkModel frameworkModel,
Executor executor,
TripleClientCall clientCall,
TripleWriteQueue writeQueue);
}

View File

@ -23,7 +23,7 @@ import java.util.concurrent.CompletableFuture;
import io.netty.channel.Channel;
import io.netty.handler.codec.http2.Http2StreamChannel;
public class TripleStreamChannelFuture extends CompletableFuture<Http2StreamChannel> {
public class TripleStreamChannelFuture extends CompletableFuture<Channel> {
private final Channel parentChannel;
@ -60,7 +60,7 @@ public class TripleStreamChannelFuture extends CompletableFuture<Http2StreamChan
return isDone() && cause() == null;
}
public Http2StreamChannel getNow() {
public Channel getNow() {
return getNow(null);
}
}

View File

@ -0,0 +1,2 @@
default=org.apache.dubbo.rpc.protocol.tri.h3.GenericHttp3ServerTransportListenerFactory
grpc=org.apache.dubbo.rpc.protocol.tri.h3.grpc.GrpcHttp3ServerTransportListenerFactory

View File

@ -0,0 +1,2 @@
http2=org.apache.dubbo.rpc.protocol.tri.h12.http2.Http2ClientStreamFactory
http3=org.apache.dubbo.rpc.protocol.tri.h3.Http3ClientStreamFactory

View File

@ -32,6 +32,7 @@ import org.apache.dubbo.rpc.protocol.tri.command.EndStreamQueueCommand;
import org.apache.dubbo.rpc.protocol.tri.command.HeaderQueueCommand;
import org.apache.dubbo.rpc.protocol.tri.command.QueuedCommand;
import org.apache.dubbo.rpc.protocol.tri.compressor.Compressor;
import org.apache.dubbo.rpc.protocol.tri.h12.http2.Http2TripleClientStream;
import org.apache.dubbo.rpc.protocol.tri.support.IGreeter;
import org.apache.dubbo.rpc.protocol.tri.transport.H2TransportListener;
import org.apache.dubbo.rpc.protocol.tri.transport.TripleWriteQueue;
@ -78,7 +79,7 @@ class TripleClientStreamTest {
when(http2StreamChannel.eventLoop()).thenReturn(new NioEventLoopGroup().next());
when(http2StreamChannel.newPromise()).thenReturn(channel.newPromise());
when(http2StreamChannel.parent()).thenReturn(channel);
TripleClientStream stream = new TripleClientStream(
AbstractTripleClientStream stream = new Http2TripleClientStream(
url.getOrDefaultFrameworkModel(),
ImmediateEventExecutor.INSTANCE,
writeQueue,

View File

@ -300,6 +300,11 @@
<artifactId>dubbo-remoting-http12</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-http3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty</artifactId>