add gRPC-client compression (#136)
* add upstream grpc-client compression * fix review issues
This commit is contained in:
@@ -26,6 +26,8 @@ compression:
|
|||||||
grpc:
|
grpc:
|
||||||
server:
|
server:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
client:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
cluster:
|
cluster:
|
||||||
include: "upstreams.yaml"
|
include: "upstreams.yaml"
|
||||||
@@ -35,7 +37,8 @@ It configures following:
|
|||||||
|
|
||||||
- server is listening with gRCP API on `0.0.0.0:2449`
|
- server is listening with gRCP API on `0.0.0.0:2449`
|
||||||
- TLS is enabled
|
- TLS is enabled
|
||||||
- compression is disabled for gRPC server (enabled by default)
|
- compression is disabled for gRPC-server (enabled by default)
|
||||||
|
- compression is disabled for upstream gRPC-client (enabled by default)
|
||||||
- server certificate is located at `server.crt` with the key for it at `server.p8.key`
|
- server certificate is located at `server.crt` with the key for it at `server.p8.key`
|
||||||
- the server requires a client authentication by TLS client certificate signed by `ca.crt` certificate
|
- the server requires a client authentication by TLS client certificate signed by `ca.crt` certificate
|
||||||
- no JSON RPC is configured
|
- no JSON RPC is configured
|
||||||
@@ -72,12 +75,23 @@ a| `upstreams`
|
|||||||
|===
|
|===
|
||||||
|
|
||||||
=== Compression
|
=== Compression
|
||||||
Compression is enabled by default on the gRPC server
|
Compression is enabled by default on the gRPC-server.
|
||||||
(the server accepts compressed requests and can send compressed responses).
|
Responses will be compressed if a client supports compression
|
||||||
Responses will be compressed if a client supports compression (sends relevant headers),
|
(sends relevant headers), otherwise, communication will be uncompressed.
|
||||||
otherwise, communication will be uncompressed.
|
But if for some reason you need to disable responses compression
|
||||||
But if for some reason you need to disable compression forcibly,
|
forcibly (the server will still be able to accept compressed requests),
|
||||||
add the lines below to the config:
|
add following lines to the config:
|
||||||
|
[source,yaml]
|
||||||
|
----
|
||||||
|
compression:
|
||||||
|
grpc:
|
||||||
|
server:
|
||||||
|
enabled: false
|
||||||
|
----
|
||||||
|
Compression is enabled by default for gRPC-requests to upstreams.
|
||||||
|
If you want to disable it,
|
||||||
|
(gRPC-client will still be able to accept compressed responses),
|
||||||
|
add following lines to the config:
|
||||||
[source,yaml]
|
[source,yaml]
|
||||||
----
|
----
|
||||||
compression:
|
compression:
|
||||||
@@ -85,6 +99,31 @@ compression:
|
|||||||
client:
|
client:
|
||||||
enabled: false
|
enabled: false
|
||||||
----
|
----
|
||||||
|
Thus, all possible combinations of compression configuration for
|
||||||
|
interacting dshackle grpc-client and grpc-server look like this:
|
||||||
|
|===
|
||||||
|
| Client | Server | Requests | Responses
|
||||||
|
|
||||||
|
| enabled
|
||||||
|
| enabled
|
||||||
|
| compressed
|
||||||
|
| compressed
|
||||||
|
|
||||||
|
| enabled
|
||||||
|
| disabled
|
||||||
|
| compressed
|
||||||
|
| plain
|
||||||
|
|
||||||
|
| disabled
|
||||||
|
| enabled
|
||||||
|
| plain
|
||||||
|
| compressed
|
||||||
|
|
||||||
|
| disabled
|
||||||
|
| disabled
|
||||||
|
| plain
|
||||||
|
| plain
|
||||||
|
|===
|
||||||
|
|
||||||
=== Enabling JSON RPC proxy
|
=== Enabling JSON RPC proxy
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,16 @@
|
|||||||
*/
|
*/
|
||||||
package io.emeraldpay.dshackle
|
package io.emeraldpay.dshackle
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.config.*
|
import io.emeraldpay.dshackle.config.CacheConfig
|
||||||
|
import io.emeraldpay.dshackle.config.ChainsConfig
|
||||||
|
import io.emeraldpay.dshackle.config.CompressionConfig
|
||||||
|
import io.emeraldpay.dshackle.config.HealthConfig
|
||||||
|
import io.emeraldpay.dshackle.config.MainConfig
|
||||||
|
import io.emeraldpay.dshackle.config.MainConfigReader
|
||||||
|
import io.emeraldpay.dshackle.config.MonitoringConfig
|
||||||
|
import io.emeraldpay.dshackle.config.SignatureConfig
|
||||||
|
import io.emeraldpay.dshackle.config.TokensConfig
|
||||||
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
@@ -110,6 +119,11 @@ open class Config(
|
|||||||
return mainConfig.upstreams
|
return mainConfig.upstreams
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun compressionConfig(@Autowired mainConfig: MainConfig): CompressionConfig {
|
||||||
|
return mainConfig.compression
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
open fun cacheConfig(@Autowired mainConfig: MainConfig): CacheConfig {
|
open fun cacheConfig(@Autowired mainConfig: MainConfig): CacheConfig {
|
||||||
return mainConfig.cache ?: CacheConfig()
|
return mainConfig.cache ?: CacheConfig()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
class CompressionConfig(
|
open class CompressionConfig(
|
||||||
var grpc: GRPC = GRPC()
|
var grpc: GRPC = GRPC()
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import io.emeraldpay.dshackle.Chain
|
|||||||
import io.emeraldpay.dshackle.FileResolver
|
import io.emeraldpay.dshackle.FileResolver
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
import io.emeraldpay.dshackle.config.ChainsConfig
|
import io.emeraldpay.dshackle.config.ChainsConfig
|
||||||
|
import io.emeraldpay.dshackle.config.CompressionConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||||
import io.emeraldpay.dshackle.upstream.*
|
import io.emeraldpay.dshackle.upstream.*
|
||||||
@@ -62,6 +63,7 @@ import kotlin.math.abs
|
|||||||
open class ConfiguredUpstreams(
|
open class ConfiguredUpstreams(
|
||||||
private val fileResolver: FileResolver,
|
private val fileResolver: FileResolver,
|
||||||
private val config: UpstreamsConfig,
|
private val config: UpstreamsConfig,
|
||||||
|
private val compressionConfig: CompressionConfig,
|
||||||
private val callTargets: CallTargetsHolder,
|
private val callTargets: CallTargetsHolder,
|
||||||
private val eventPublisher: ApplicationEventPublisher,
|
private val eventPublisher: ApplicationEventPublisher,
|
||||||
@Qualifier("grpcChannelExecutor")
|
@Qualifier("grpcChannelExecutor")
|
||||||
@@ -86,7 +88,7 @@ open class ConfiguredUpstreams(
|
|||||||
log.debug("Start upstream ${up.id}")
|
log.debug("Start upstream ${up.id}")
|
||||||
if (up.connection is UpstreamsConfig.GrpcConnection) {
|
if (up.connection is UpstreamsConfig.GrpcConnection) {
|
||||||
val options = up.options ?: UpstreamsConfig.Options()
|
val options = up.options ?: UpstreamsConfig.Options()
|
||||||
buildGrpcUpstream(up.nodeId, up.cast(UpstreamsConfig.GrpcConnection::class.java), options)
|
buildGrpcUpstream(up.nodeId, up.cast(UpstreamsConfig.GrpcConnection::class.java), options, compressionConfig.grpc.clientEnabled)
|
||||||
} else {
|
} else {
|
||||||
val chain = Global.chainById(up.chain)
|
val chain = Global.chainById(up.chain)
|
||||||
if (chain == Chain.UNSPECIFIED) {
|
if (chain == Chain.UNSPECIFIED) {
|
||||||
@@ -307,7 +309,8 @@ open class ConfiguredUpstreams(
|
|||||||
private fun buildGrpcUpstream(
|
private fun buildGrpcUpstream(
|
||||||
nodeId: Int?,
|
nodeId: Int?,
|
||||||
config: UpstreamsConfig.Upstream<UpstreamsConfig.GrpcConnection>,
|
config: UpstreamsConfig.Upstream<UpstreamsConfig.GrpcConnection>,
|
||||||
options: UpstreamsConfig.Options
|
options: UpstreamsConfig.Options,
|
||||||
|
compression: Boolean
|
||||||
) {
|
) {
|
||||||
if (!this::grpcUpstreamsScheduler.isInitialized) {
|
if (!this::grpcUpstreamsScheduler.isInitialized) {
|
||||||
grpcUpstreamsScheduler = Schedulers.fromExecutorService(
|
grpcUpstreamsScheduler = Schedulers.fromExecutorService(
|
||||||
@@ -324,6 +327,7 @@ open class ConfiguredUpstreams(
|
|||||||
endpoint.host!!,
|
endpoint.host!!,
|
||||||
endpoint.port,
|
endpoint.port,
|
||||||
endpoint.auth,
|
endpoint.auth,
|
||||||
|
compression,
|
||||||
fileResolver,
|
fileResolver,
|
||||||
endpoint.upstreamRating,
|
endpoint.upstreamRating,
|
||||||
config.labels,
|
config.labels,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import io.emeraldpay.dshackle.upstream.Lifecycle
|
|||||||
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcGrpcClient
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcGrpcClient
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.RpcMetrics
|
import io.emeraldpay.dshackle.upstream.rpcclient.RpcMetrics
|
||||||
|
import io.grpc.Codec
|
||||||
import io.grpc.netty.NettyChannelBuilder
|
import io.grpc.netty.NettyChannelBuilder
|
||||||
import io.micrometer.core.instrument.Counter
|
import io.micrometer.core.instrument.Counter
|
||||||
import io.micrometer.core.instrument.Metrics
|
import io.micrometer.core.instrument.Metrics
|
||||||
@@ -63,6 +64,7 @@ class GrpcUpstreams(
|
|||||||
private val host: String,
|
private val host: String,
|
||||||
private val port: Int,
|
private val port: Int,
|
||||||
private val auth: AuthConfig.ClientTlsAuth? = null,
|
private val auth: AuthConfig.ClientTlsAuth? = null,
|
||||||
|
private val compression: Boolean,
|
||||||
private val fileResolver: FileResolver,
|
private val fileResolver: FileResolver,
|
||||||
private val nodeRating: Int,
|
private val nodeRating: Int,
|
||||||
private val labels: UpstreamsConfig.Labels,
|
private val labels: UpstreamsConfig.Labels,
|
||||||
@@ -94,7 +96,10 @@ class GrpcUpstreams(
|
|||||||
chanelBuilder.usePlaintext()
|
chanelBuilder.usePlaintext()
|
||||||
}
|
}
|
||||||
|
|
||||||
val client = ReactorBlockchainGrpc.newReactorStub(chanelBuilder.build())
|
var client = ReactorBlockchainGrpc.newReactorStub(chanelBuilder.build())
|
||||||
|
if (compression) {
|
||||||
|
client = client.withCompression(Codec.Gzip().messageEncoding)
|
||||||
|
}
|
||||||
this.client = client
|
this.client = client
|
||||||
|
|
||||||
val statusSubscription = AtomicReference<Disposable>()
|
val statusSubscription = AtomicReference<Disposable>()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.emeraldpay.dshackle.startup
|
|||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
import io.emeraldpay.dshackle.FileResolver
|
import io.emeraldpay.dshackle.FileResolver
|
||||||
import io.emeraldpay.dshackle.config.ChainsConfig
|
import io.emeraldpay.dshackle.config.ChainsConfig
|
||||||
|
import io.emeraldpay.dshackle.config.CompressionConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
import io.emeraldpay.dshackle.quorum.NonEmptyQuorum
|
import io.emeraldpay.dshackle.quorum.NonEmptyQuorum
|
||||||
import io.emeraldpay.dshackle.upstream.CallTargetsHolder
|
import io.emeraldpay.dshackle.upstream.CallTargetsHolder
|
||||||
@@ -20,6 +21,7 @@ class ConfiguredUpstreamsSpec extends Specification {
|
|||||||
def configurer = new ConfiguredUpstreams(
|
def configurer = new ConfiguredUpstreams(
|
||||||
Stub(FileResolver),
|
Stub(FileResolver),
|
||||||
Stub(UpstreamsConfig),
|
Stub(UpstreamsConfig),
|
||||||
|
Stub(CompressionConfig),
|
||||||
callTargetsHolder,
|
callTargetsHolder,
|
||||||
Mock(ApplicationEventPublisher),
|
Mock(ApplicationEventPublisher),
|
||||||
Executors.newFixedThreadPool(1),
|
Executors.newFixedThreadPool(1),
|
||||||
@@ -47,6 +49,7 @@ class ConfiguredUpstreamsSpec extends Specification {
|
|||||||
def configurer = new ConfiguredUpstreams(
|
def configurer = new ConfiguredUpstreams(
|
||||||
Stub(FileResolver),
|
Stub(FileResolver),
|
||||||
Stub(UpstreamsConfig),
|
Stub(UpstreamsConfig),
|
||||||
|
Stub(CompressionConfig),
|
||||||
callTargetsHolder,
|
callTargetsHolder,
|
||||||
Mock(ApplicationEventPublisher),
|
Mock(ApplicationEventPublisher),
|
||||||
Executors.newFixedThreadPool(1),
|
Executors.newFixedThreadPool(1),
|
||||||
@@ -73,6 +76,7 @@ class ConfiguredUpstreamsSpec extends Specification {
|
|||||||
def configurer = new ConfiguredUpstreams(
|
def configurer = new ConfiguredUpstreams(
|
||||||
Stub(FileResolver),
|
Stub(FileResolver),
|
||||||
Stub(UpstreamsConfig),
|
Stub(UpstreamsConfig),
|
||||||
|
Stub(CompressionConfig),
|
||||||
callTargetsHolder,
|
callTargetsHolder,
|
||||||
Mock(ApplicationEventPublisher),
|
Mock(ApplicationEventPublisher),
|
||||||
Executors.newFixedThreadPool(1),
|
Executors.newFixedThreadPool(1),
|
||||||
@@ -94,6 +98,7 @@ class ConfiguredUpstreamsSpec extends Specification {
|
|||||||
def configurer = new ConfiguredUpstreams(
|
def configurer = new ConfiguredUpstreams(
|
||||||
Stub(FileResolver),
|
Stub(FileResolver),
|
||||||
Stub(UpstreamsConfig),
|
Stub(UpstreamsConfig),
|
||||||
|
Stub(CompressionConfig),
|
||||||
callTargetsHolder,
|
callTargetsHolder,
|
||||||
Mock(ApplicationEventPublisher),
|
Mock(ApplicationEventPublisher),
|
||||||
Executors.newFixedThreadPool(1),
|
Executors.newFixedThreadPool(1),
|
||||||
@@ -120,6 +125,7 @@ class ConfiguredUpstreamsSpec extends Specification {
|
|||||||
def configurer = new ConfiguredUpstreams(
|
def configurer = new ConfiguredUpstreams(
|
||||||
Stub(FileResolver),
|
Stub(FileResolver),
|
||||||
Stub(UpstreamsConfig),
|
Stub(UpstreamsConfig),
|
||||||
|
Stub(CompressionConfig),
|
||||||
callTargetsHolder,
|
callTargetsHolder,
|
||||||
Mock(ApplicationEventPublisher),
|
Mock(ApplicationEventPublisher),
|
||||||
Executors.newFixedThreadPool(1),
|
Executors.newFixedThreadPool(1),
|
||||||
|
|||||||
Reference in New Issue
Block a user