HTTP/3 support for dubbo triple (#14033)
This commit is contained in:
parent
667f98a459
commit
35adffac48
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ public enum HttpHeaderNames {
|
|||
|
||||
TE("te"),
|
||||
|
||||
ALT_SVC("alt-svc"),
|
||||
|
||||
ACCEPT("accept");
|
||||
|
||||
private final String name;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public interface Http2OutputMessage extends HttpOutputMessage, Http2StreamFrame
|
|||
}
|
||||
|
||||
@Override
|
||||
default int id() {
|
||||
default long id() {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package org.apache.dubbo.remoting.http12.h2;
|
|||
|
||||
public interface Http2StreamFrame {
|
||||
|
||||
int id();
|
||||
long id();
|
||||
|
||||
String name();
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 {}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
http3=org.apache.dubbo.remoting.http3.netty4.Http3ChannelAddressAccessor
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
default=org.apache.dubbo.rpc.protocol.tri.h3.GenericHttp3ServerTransportListenerFactory
|
||||
grpc=org.apache.dubbo.rpc.protocol.tri.h3.grpc.GrpcHttp3ServerTransportListenerFactory
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
http2=org.apache.dubbo.rpc.protocol.tri.h12.http2.Http2ClientStreamFactory
|
||||
http3=org.apache.dubbo.rpc.protocol.tri.h3.Http3ClientStreamFactory
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue