Simplify through use of grpc builder APIs

This commit is contained in:
Dave Syer
2024-09-09 07:17:39 +01:00
parent 4beb624978
commit 6323cf9d86
9 changed files with 76 additions and 195 deletions

View File

@@ -15,6 +15,7 @@ import org.springframework.grpc.sample.proto.HelloRequest;
import org.springframework.grpc.sample.proto.SimpleGrpc;
import org.springframework.test.annotation.DirtiesContext;
import io.grpc.Channel;
import io.grpc.Grpc;
import io.grpc.InsecureChannelCredentials;
@@ -45,11 +46,17 @@ public class GrpcServerApplicationTests {
@TestConfiguration
static class ExtraConfiguration {
@Bean
SimpleGrpc.SimpleBlockingStub stub() {
var channel = Grpc.newChannelBuilderForAddress("0.0.0.0", 9090, InsecureChannelCredentials.create())
.build();
SimpleGrpc.SimpleBlockingStub stub(Channel channel) {
return SimpleGrpc.newBlockingStub(channel);
}
@Bean(destroyMethod = "shutdown")
Channel channel() {
return Grpc.newChannelBuilderForAddress("0.0.0.0", 9090, InsecureChannelCredentials.create()).build();
}
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2024-2024 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.grpc.client;
import io.grpc.Channel;
public interface GrpcChannelFactory extends AutoCloseable {
Channel createChannel(String name);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2024 the original author or authors.
* Copyright 2024-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Partial copy from net.devh:grpc-spring-boot-starter.
*/
package org.springframework.grpc.server;
@@ -32,7 +31,7 @@ import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.ServerServiceDefinition;
public abstract class AbstractGrpcServerFactory<T extends ServerBuilder<T>> implements GrpcServerFactory {
public class DefaultGrpcServerFactory<T extends ServerBuilder<T>> implements GrpcServerFactory {
/** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
@@ -45,7 +44,7 @@ public abstract class AbstractGrpcServerFactory<T extends ServerBuilder<T>> impl
private final int port;
protected AbstractGrpcServerFactory(String address, int port, final List<GrpcServerConfigurer> serverConfigurers) {
public DefaultGrpcServerFactory(String address, int port, final List<GrpcServerConfigurer> serverConfigurers) {
this.serverConfigurers = requireNonNull(serverConfigurers, "serverConfigurers");
this.address = address;
this.port = port;
@@ -70,7 +69,11 @@ public abstract class AbstractGrpcServerFactory<T extends ServerBuilder<T>> impl
* Creates a new server builder.
* @return The newly created server builder.
*/
protected abstract T newServerBuilder();
@SuppressWarnings("unchecked")
protected T newServerBuilder() {
// TODO: Add support for address resolution
return (T) ServerBuilder.forPort(getPort());
}
/**
* Configures the given server builder. This method can be overwritten to add features

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2016-2024 the original author or authors.
*
* Licensed 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.
*
* Partial copy from net.devh:grpc-spring-boot-starter.
*/
package org.springframework.grpc.server;
import java.net.InetSocketAddress;
import java.util.List;
import org.springframework.grpc.util.GrpcUtils;
import com.google.common.net.InetAddresses;
import io.grpc.netty.NettyServerBuilder;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerDomainSocketChannel;
import io.netty.channel.unix.DomainSocketAddress;
/**
* Factory for netty based grpc servers.
*
* @author Michael (yidongnan@gmail.com)
* @author Dave Syer
*/
public class NettyGrpcServerFactory extends AbstractGrpcServerFactory<NettyServerBuilder> {
/**
* Creates a new netty server factory with the given properties.
* @param address The address to bind the server to.
* @param port The port to bind the server to (0 for ephemeral port).
* @param properties The properties used to configure the server.
* @param serverConfigurers The server configurers to use. Can be empty.
*/
public NettyGrpcServerFactory(String address, int port, final List<GrpcServerConfigurer> serverConfigurers) {
super(address, port, serverConfigurers);
}
@Override
protected NettyServerBuilder newServerBuilder() {
if (getAddres().startsWith(GrpcUtils.DOMAIN_SOCKET_ADDRESS_PREFIX)) {
final String path = GrpcUtils.extractDomainSocketAddressPath(getAddres());
return NettyServerBuilder.forAddress(new DomainSocketAddress(path))
.channelType(EpollServerDomainSocketChannel.class)
.bossEventLoopGroup(new EpollEventLoopGroup(1))
.workerEventLoopGroup(new EpollEventLoopGroup());
}
else if (GrpcUtils.ANY_IP_ADDRESS.equals(getAddres())) {
return NettyServerBuilder.forPort(getPort());
}
else {
return NettyServerBuilder
.forAddress(new InetSocketAddress(InetAddresses.forString(getAddres()), getPort()));
}
}
}

View File

@@ -1,100 +0,0 @@
/*
* Copyright 2016-2024 the original author or authors.
*
* Licensed 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.
*
* Partial copy from net.devh:grpc-spring-boot-starter.
*/
package org.springframework.grpc.util;
import io.grpc.MethodDescriptor;
/**
* Utility class that contains methods to extract some information from grpc classes.
*
* @author Daniel Theuke (daniel.theuke@heuboe.de)
* @author Dave Syer
*/
public final class GrpcUtils {
/**
* A constant that defines the scheme of a Unix domain socket address.
*/
public static final String DOMAIN_SOCKET_ADDRESS_SCHEME = "unix";
/**
* A constant that defines the scheme prefix of a Unix domain socket address.
*/
public static final String DOMAIN_SOCKET_ADDRESS_PREFIX = DOMAIN_SOCKET_ADDRESS_SCHEME + ":";
/**
* A constant that defines that the server should listen to any IPv4 and IPv6 address.
*/
public static final String ANY_IP_ADDRESS = "*";
/**
* Extracts the domain socket address specific path from the given full address. The
* address must fulfill the requirements as specified by
* <a href="https://grpc.github.io/grpc/cpp/md_doc_naming.html">grpc</a>.
* @param address The address to extract it from.
* @return The extracted domain socket address specific path.
* @throws IllegalArgumentException If the given address is not a valid address.
*/
public static String extractDomainSocketAddressPath(final String address) {
if (!address.startsWith(DOMAIN_SOCKET_ADDRESS_PREFIX)) {
throw new IllegalArgumentException(address + " is not a valid domain socket address.");
}
String path = address.substring(DOMAIN_SOCKET_ADDRESS_PREFIX.length());
if (path.startsWith("//")) {
path = path.substring(2);
// We don't check this as there is no reliable way to check that it's an
// absolute path, especially when Windows adds support for these in the future
// if (!path.startsWith("/")) {
// throw new IllegalArgumentException("If the path is prefixed with '//', then
// the path must be absolute");
// }
}
return path;
}
/**
* Extracts the service name from the given method.
* @param method The method to get the service name from.
* @return The extracted service name.
* @see MethodDescriptor#extractFullServiceName(String)
* @see #extractMethodName(MethodDescriptor)
*/
public static String extractServiceName(final MethodDescriptor<?, ?> method) {
return MethodDescriptor.extractFullServiceName(method.getFullMethodName());
}
/**
* Extracts the method name from the given method.
* @param method The method to get the method name from.
* @return The extracted method name.
* @see #extractServiceName(MethodDescriptor)
*/
public static String extractMethodName(final MethodDescriptor<?, ?> method) {
// This method is the equivalent of MethodDescriptor.extractFullServiceName
final String fullMethodName = method.getFullMethodName();
final int index = fullMethodName.lastIndexOf('/');
if (index == -1) {
return fullMethodName;
}
return fullMethodName.substring(index + 1);
}
private GrpcUtils() {
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2024-2024 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.grpc.autoconfigure.client;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.grpc.server.GrpcServerFactory;
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(GrpcServerFactory.class)
public class GrpcClientAutoConfiguration {
}

View File

@@ -17,17 +17,14 @@ package org.springframework.grpc.autoconfigure.server;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.grpc.server.DefaultGrpcServerFactory;
import org.springframework.grpc.server.GrpcServerConfigurer;
import org.springframework.grpc.server.GrpcServerFactory;
import org.springframework.grpc.server.NettyGrpcServerFactory;
import io.grpc.ServerServiceDefinition;
@@ -37,17 +34,12 @@ import io.grpc.ServerServiceDefinition;
@EnableConfigurationProperties(GrpcServerProperties.class)
public class GrpcServerFactoryAutoConfiguration {
private static final Log logger = LogFactory.getLog(GrpcServerFactoryAutoConfiguration.class);
@ConditionalOnMissingBean(GrpcServerFactory.class)
@ConditionalOnClass(name = { "io.netty.channel.Channel", "io.grpc.netty.NettyServerBuilder" })
@Bean
public NettyGrpcServerFactory nettyGrpcServerFactory(final GrpcServerProperties properties,
public DefaultGrpcServerFactory<?> defaultGrpcServerFactory(final GrpcServerProperties properties,
final GrpcServiceDiscoverer serviceDiscoverer, final List<GrpcServerConfigurer> serverConfigurers) {
logger.info("Detected grpc-netty: Creating NettyGrpcServerFactory");
final NettyGrpcServerFactory factory = new NettyGrpcServerFactory(properties.getAddress(), properties.getPort(),
serverConfigurers);
final DefaultGrpcServerFactory<?> factory = new DefaultGrpcServerFactory<>(properties.getAddress(),
properties.getPort(), serverConfigurers);
for (final ServerServiceDefinition service : serviceDiscoverer.findGrpcServices()) {
factory.addService(service);
}

View File

@@ -20,12 +20,11 @@ import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.grpc.util.GrpcUtils;
@ConfigurationProperties(prefix = "spring.grpc.server")
public class GrpcServerProperties {
private String address = GrpcUtils.ANY_IP_ADDRESS;
private String address = "*";
private int port = 9090;

View File

@@ -37,7 +37,7 @@ public class GrpcServiceAutoConfiguration {
@ConditionalOnMissingBean
@Bean
public GrpcServiceDiscoverer defaultGrpcServiceDiscoverer(ApplicationContext applicationContext) {
return new AnnotationGrpcServiceDiscoverer(applicationContext);
return new DefaultGrpcServiceDiscoverer(applicationContext);
}
@ConditionalOnMissingBean
@@ -49,11 +49,11 @@ public class GrpcServiceAutoConfiguration {
}
class AnnotationGrpcServiceDiscoverer implements GrpcServiceDiscoverer {
class DefaultGrpcServiceDiscoverer implements GrpcServiceDiscoverer {
private final ApplicationContext applicationContext;
public AnnotationGrpcServiceDiscoverer(ApplicationContext applicationContext) {
public DefaultGrpcServiceDiscoverer(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}