Skip to content

Commit 3e4deca

Browse files
authored
Merge pull request #548 from cfredri4/support-unix-domain-sockets
Support unix domain sockets
2 parents ae68aa9 + 31f9209 commit 3e4deca

File tree

11 files changed

+170
-9
lines changed

11 files changed

+170
-9
lines changed

docs/en/client/configuration.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ If you prefer to read the sources instead, you can do so
3838
[here](https://github.com/yidongnan/grpc-spring-boot-starter/blob/master/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/config/GrpcChannelProperties.java#L58).
3939

4040
The properties for the channels are all prefixed with `grpc.client.__name__.` and `grpc.client.__name__.security.`
41-
respectively. The channel name is taken from the `@GrpcClient` annotation. If you wish to configure some options such as
42-
trusted certificates for all servers at once you can do so using `GLOBAL` as name. Properties that are defined for a
43-
name channel take precedence over global once.
41+
respectively. The channel name is taken from the `@GrpcClient("__name__")` annotation.
42+
If you wish to configure some options such as trusted certificates for all servers at once,
43+
you can do so using `GLOBAL` as name.
44+
Properties that are defined for a specific/named channel take precedence over `GLOBAL` ones.
4445

4546
### Choosing the Target
4647

@@ -55,6 +56,7 @@ There are a number of supported schemes, that you can use to determine the targe
5556

5657
- `static` (Prio 4): \
5758
A simple static list of IPs (both v4 and v6), that can be use connect to the server (Supports `localhost`). \
59+
For resolvable hostnames please use `dns` instead. \
5860
Example: `static://192.168.1.1:8080,10.0.0.1:1337`
5961
- [`dns`](https://github.com/grpc/grpc-java/blob/master/core/src/main/java/io/grpc/internal/DnsNameResolver.java#L66)
6062
(Prio 5): \
@@ -77,6 +79,11 @@ There are a number of supported schemes, that you can use to determine the targe
7779
This is a special scheme that will bypass the normal channel factory and will use the `InProcessChannelFactory`
7880
instead. Use it to connect to the [`InProcessServer`](../server/configuration.md#enabling-the-inprocessserver). \
7981
Example: `in-process:foobar`
82+
- `unix` (Available on Unix based systems only): \
83+
This is a special scheme that uses unix's domain socket addresses to connect to a server. \
84+
If you are using `grpc-netty` you also need the `netty-transport-native-epoll` dependency.
85+
`grpc-netty-shaded` already contains that dependency, so there is no need to add anything for it to work. \
86+
Example: `unix:/run/grpc-server`
8087
- *custom*: \
8188
You can define custom
8289
[`NameResolverProvider`s](https://javadoc.io/page/io.grpc/grpc-all/latest/io/grpc/NameResolverProvider.html) those
@@ -267,6 +274,7 @@ you have to define it via spring context (unless you wish to use `static`).
267274
- [Getting Started](getting-started.md)
268275
- *Configuration*
269276
- [Security](security.md)
277+
- [Tests with Grpc-Stubs](testing.md)
270278

271279
----------
272280

docs/en/server/configuration.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,21 @@ grpc.client.inProcess.address=in-process:<SomeName>
7575
This is especially useful for tests as they don't need to open a specific port and thus can run concurrently (on a build
7676
server).
7777

78+
### Using Unix's Domain Sockets
79+
80+
On Unix based systems you can also use domain sockets to locally communicate between server and clients.
81+
82+
Simply configure the address like this:
83+
84+
````properties
85+
grpc.server.address=unix:/run/grpc-server
86+
````
87+
88+
Clients can then connect to the server using the same address.
89+
90+
If you are using `grpc-netty` you also need the `netty-transport-native-epoll` dependency.
91+
`grpc-netty-shaded` already contains that dependency, so there is no need to add anything for it to work.
92+
7893
## Configuration via Beans
7994

8095
While this library intents to provide most of the features as configuration option, sometimes the overhead for adding it

grpc-client-spring-boot-autoconfigure/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ dependencies {
2424
// api comes from java-library, and allows exposure of the dependency to consumers of the library
2525
// this means it can be used implicitly without specifying the dependency (unless you wish to override with a different version, or exclude)
2626
optionalSupportApi 'io.grpc:grpc-netty'
27+
optionalSupportApi 'io.netty:netty-transport-native-epoll'
2728
api 'io.grpc:grpc-netty-shaded'
2829
api 'io.grpc:grpc-protobuf'
2930
api 'io.grpc:grpc-stub'

grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/NettyChannelFactory.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package net.devh.boot.grpc.client.channelfactory;
1919

2020
import static java.util.Objects.requireNonNull;
21+
import static net.devh.boot.grpc.common.util.GrpcUtils.DOMAIN_SOCKET_ADDRESS_SCHEME;
2122

2223
import java.io.IOException;
2324
import java.io.InputStream;
@@ -30,12 +31,16 @@
3031

3132
import io.grpc.netty.GrpcSslContexts;
3233
import io.grpc.netty.NettyChannelBuilder;
34+
import io.netty.channel.epoll.EpollDomainSocketChannel;
35+
import io.netty.channel.epoll.EpollEventLoopGroup;
36+
import io.netty.channel.unix.DomainSocketAddress;
3337
import io.netty.handler.ssl.SslContextBuilder;
3438
import net.devh.boot.grpc.client.config.GrpcChannelProperties;
3539
import net.devh.boot.grpc.client.config.GrpcChannelProperties.Security;
3640
import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
3741
import net.devh.boot.grpc.client.config.NegotiationType;
3842
import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
43+
import net.devh.boot.grpc.common.util.GrpcUtils;
3944

4045
/**
4146
* This channel factory creates and manages netty based {@link GrpcChannelFactory}s.
@@ -71,8 +76,15 @@ protected NettyChannelBuilder newChannelBuilder(final String name) {
7176
if (address == null) {
7277
address = URI.create(name);
7378
}
74-
return NettyChannelBuilder.forTarget(address.toString())
75-
.defaultLoadBalancingPolicy(properties.getDefaultLoadBalancingPolicy());
79+
if (DOMAIN_SOCKET_ADDRESS_SCHEME.equals(address.getScheme())) {
80+
final String path = GrpcUtils.extractDomainSocketAddressPath(address.toString());
81+
return NettyChannelBuilder.forAddress(new DomainSocketAddress(path))
82+
.channelType(EpollDomainSocketChannel.class)
83+
.eventLoopGroup(new EpollEventLoopGroup());
84+
} else {
85+
return NettyChannelBuilder.forTarget(address.toString())
86+
.defaultLoadBalancingPolicy(properties.getDefaultLoadBalancingPolicy());
87+
}
7688
}
7789

7890
@Override

grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/ShadedNettyChannelFactory.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package net.devh.boot.grpc.client.channelfactory;
1919

2020
import static java.util.Objects.requireNonNull;
21+
import static net.devh.boot.grpc.common.util.GrpcUtils.DOMAIN_SOCKET_ADDRESS_SCHEME;
2122

2223
import java.io.IOException;
2324
import java.io.InputStream;
@@ -30,12 +31,16 @@
3031

3132
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
3233
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
34+
import io.grpc.netty.shaded.io.netty.channel.epoll.EpollDomainSocketChannel;
35+
import io.grpc.netty.shaded.io.netty.channel.epoll.EpollEventLoopGroup;
36+
import io.grpc.netty.shaded.io.netty.channel.unix.DomainSocketAddress;
3337
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;
3438
import net.devh.boot.grpc.client.config.GrpcChannelProperties;
3539
import net.devh.boot.grpc.client.config.GrpcChannelProperties.Security;
3640
import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
3741
import net.devh.boot.grpc.client.config.NegotiationType;
3842
import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
43+
import net.devh.boot.grpc.common.util.GrpcUtils;
3944

4045
/**
4146
* This channel factory creates and manages shaded netty based {@link GrpcChannelFactory}s.
@@ -69,8 +74,15 @@ protected NettyChannelBuilder newChannelBuilder(final String name) {
6974
if (address == null) {
7075
address = URI.create(name);
7176
}
72-
return NettyChannelBuilder.forTarget(address.toString())
73-
.defaultLoadBalancingPolicy(properties.getDefaultLoadBalancingPolicy());
77+
if (DOMAIN_SOCKET_ADDRESS_SCHEME.equals(address.getScheme())) {
78+
final String path = GrpcUtils.extractDomainSocketAddressPath(address.toString());
79+
return NettyChannelBuilder.forAddress(new DomainSocketAddress(path))
80+
.channelType(EpollDomainSocketChannel.class)
81+
.eventLoopGroup(new EpollEventLoopGroup());
82+
} else {
83+
return NettyChannelBuilder.forTarget(address.toString())
84+
.defaultLoadBalancingPolicy(properties.getDefaultLoadBalancingPolicy());
85+
}
7486
}
7587

7688
@Override

grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/config/GrpcChannelProperties.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ public void setAddress(final URI address) {
104104
* <li>{@code dns:///example.com:9090}</li>
105105
* <li>{@code discovery:/foo-service}</li>
106106
* <li>{@code discovery:///foo-service}</li>
107+
* <li>{@code unix:<relative-path>} (Additional dependencies may be required)</li>
108+
* <li>{@code unix://</absolute-path>} (Additional dependencies may be required)</li>
107109
* </ul>
108110
*
109111
* @param address The string representation of an uri to use as target address or null to use a fallback.

grpc-common-spring-boot/src/main/java/net/devh/boot/grpc/common/util/GrpcUtils.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@
2626
*/
2727
public final class GrpcUtils {
2828

29+
/**
30+
* A constant that defines, the scheme of a Unix domain socket address.
31+
*/
32+
public static final String DOMAIN_SOCKET_ADDRESS_SCHEME = "unix";
33+
34+
/**
35+
* A constant that defines, the scheme prefix of a Unix domain socket address.
36+
*/
37+
public static final String DOMAIN_SOCKET_ADDRESS_PREFIX = DOMAIN_SOCKET_ADDRESS_SCHEME + ":";
38+
2939
/**
3040
* The cloud discovery metadata key used to identify the grpc port.
3141
*/
@@ -36,6 +46,30 @@ public final class GrpcUtils {
3646
*/
3747
public static final int INTER_PROCESS_DISABLE = -1;
3848

49+
/**
50+
* Extracts the domain socket address specific path from the given full address. The address must fulfill the
51+
* requirements as specified by <a href="https://grpc.github.io/grpc/cpp/md_doc_naming.html">grpc</a>.
52+
*
53+
* @param address The address to extract it from.
54+
* @return The extracted domain socket address specific path.
55+
* @throws IllegalArgumentException If the given address is not a valid address.
56+
*/
57+
public static String extractDomainSocketAddressPath(final String address) {
58+
if (!address.startsWith(DOMAIN_SOCKET_ADDRESS_PREFIX)) {
59+
throw new IllegalArgumentException(address + " is not a valid domain socket address.");
60+
}
61+
String path = address.substring(DOMAIN_SOCKET_ADDRESS_PREFIX.length());
62+
if (path.startsWith("//")) {
63+
path = path.substring(2);
64+
// We don't check this as there is no reliable way to check that it's an absolute path,
65+
// especially when Windows adds support for these in the future
66+
// if (!path.startsWith("/")) {
67+
// throw new IllegalArgumentException("If the path is prefixed with '//', then the path must be absolute");
68+
// }
69+
}
70+
return path;
71+
}
72+
3973
/**
4074
* Extracts the service name from the given method.
4175
*

grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/config/GrpcServerProperties.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;
4040
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;
4141
import lombok.Data;
42+
import net.devh.boot.grpc.common.util.GrpcUtils;
4243

4344
/**
4445
* The properties for the gRPC server that will be started as part of the application.
@@ -69,6 +70,8 @@ public class GrpcServerProperties {
6970
/**
7071
* Bind address for the server. Defaults to {@link #ANY_IP_ADDRESS "*"}. Alternatively you can restrict this to
7172
* {@link #ANY_IPv4_ADDRESS "0.0.0.0"} or {@link #ANY_IPv6_ADDRESS "::"}. Or restrict it to exactly one IP address.
73+
* On unix systems it is also possible to prefix it with {@link GrpcUtils#DOMAIN_SOCKET_ADDRESS_PREFIX unix:} to use
74+
* domain socket addresses/paths (Additional dependencies may be required).
7275
*
7376
* @param address The address to bind to.
7477
* @return The address the server should bind to.

grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/serverfactory/NettyGrpcServerFactory.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
package net.devh.boot.grpc.server.serverfactory;
1919

2020
import static java.util.Objects.requireNonNull;
21+
import static net.devh.boot.grpc.common.util.GrpcUtils.DOMAIN_SOCKET_ADDRESS_PREFIX;
22+
import static net.devh.boot.grpc.server.config.GrpcServerProperties.ANY_IP_ADDRESS;
2123

2224
import java.io.IOException;
2325
import java.io.InputStream;
@@ -33,7 +35,11 @@
3335

3436
import io.grpc.netty.GrpcSslContexts;
3537
import io.grpc.netty.NettyServerBuilder;
38+
import io.netty.channel.epoll.EpollEventLoopGroup;
39+
import io.netty.channel.epoll.EpollServerDomainSocketChannel;
40+
import io.netty.channel.unix.DomainSocketAddress;
3641
import io.netty.handler.ssl.SslContextBuilder;
42+
import net.devh.boot.grpc.common.util.GrpcUtils;
3743
import net.devh.boot.grpc.server.config.ClientAuth;
3844
import net.devh.boot.grpc.server.config.GrpcServerProperties;
3945
import net.devh.boot.grpc.server.config.GrpcServerProperties.Security;
@@ -61,7 +67,13 @@ public NettyGrpcServerFactory(final GrpcServerProperties properties,
6167
protected NettyServerBuilder newServerBuilder() {
6268
final String address = getAddress();
6369
final int port = getPort();
64-
if (GrpcServerProperties.ANY_IP_ADDRESS.equals(address)) {
70+
if (address.startsWith(DOMAIN_SOCKET_ADDRESS_PREFIX)) {
71+
final String path = GrpcUtils.extractDomainSocketAddressPath(address);
72+
return NettyServerBuilder.forAddress(new DomainSocketAddress(path))
73+
.channelType(EpollServerDomainSocketChannel.class)
74+
.bossEventLoopGroup(new EpollEventLoopGroup(1))
75+
.workerEventLoopGroup(new EpollEventLoopGroup());
76+
} else if (ANY_IP_ADDRESS.equals(address)) {
6577
return NettyServerBuilder.forPort(port);
6678
} else {
6779
return NettyServerBuilder.forAddress(new InetSocketAddress(InetAddresses.forString(address), port));

grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/serverfactory/ShadedNettyGrpcServerFactory.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
package net.devh.boot.grpc.server.serverfactory;
1919

2020
import static java.util.Objects.requireNonNull;
21+
import static net.devh.boot.grpc.common.util.GrpcUtils.DOMAIN_SOCKET_ADDRESS_PREFIX;
22+
import static net.devh.boot.grpc.server.config.GrpcServerProperties.ANY_IP_ADDRESS;
2123

2224
import java.io.IOException;
2325
import java.io.InputStream;
@@ -33,7 +35,11 @@
3335

3436
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
3537
import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;
38+
import io.grpc.netty.shaded.io.netty.channel.epoll.EpollEventLoopGroup;
39+
import io.grpc.netty.shaded.io.netty.channel.epoll.EpollServerDomainSocketChannel;
40+
import io.grpc.netty.shaded.io.netty.channel.unix.DomainSocketAddress;
3641
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;
42+
import net.devh.boot.grpc.common.util.GrpcUtils;
3743
import net.devh.boot.grpc.server.config.ClientAuth;
3844
import net.devh.boot.grpc.server.config.GrpcServerProperties;
3945
import net.devh.boot.grpc.server.config.GrpcServerProperties.Security;
@@ -62,7 +68,13 @@ public ShadedNettyGrpcServerFactory(final GrpcServerProperties properties,
6268
protected NettyServerBuilder newServerBuilder() {
6369
final String address = getAddress();
6470
final int port = getPort();
65-
if (GrpcServerProperties.ANY_IP_ADDRESS.equals(address)) {
71+
if (address.startsWith(DOMAIN_SOCKET_ADDRESS_PREFIX)) {
72+
final String path = GrpcUtils.extractDomainSocketAddressPath(address);
73+
return NettyServerBuilder.forAddress(new DomainSocketAddress(path))
74+
.channelType(EpollServerDomainSocketChannel.class)
75+
.bossEventLoopGroup(new EpollEventLoopGroup(1))
76+
.workerEventLoopGroup(new EpollEventLoopGroup());
77+
} else if (ANY_IP_ADDRESS.equals(address)) {
6678
return NettyServerBuilder.forPort(port);
6779
} else {
6880
return NettyServerBuilder.forAddress(new InetSocketAddress(InetAddresses.forString(address), port));

0 commit comments

Comments
 (0)