add gRPC-client compression (#136)

* add upstream grpc-client compression

* fix review issues
This commit is contained in:
Vadim Vlasov
2023-02-13 22:47:48 +08:00
committed by GitHub
parent e5fa66058f
commit e91f75b367
6 changed files with 80 additions and 12 deletions

View File

@@ -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

View File

@@ -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()

View File

@@ -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()
) { ) {
/** /**

View File

@@ -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,

View File

@@ -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>()

View File

@@ -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),