gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。
在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。
这里我们来提供一个GRPC的初始化工具类 我们引入对应的maven
4.0.0
model.grpc.util
GrpcUtil
1.0-SNAPSHOT
io.grpc
grpc-netty-shaded
1.20.0
io.grpc
grpc-protobuf
1.20.0
io.grpc
grpc-stub
1.20.0
io.netty
netty-all
4.1.29.Final
org.slf4j
slf4j-api
1.7.25
org.slf4j
log4j-over-slf4j
1.7.25
ch.qos.logback
logback-core
1.2.3
ch.qos.logback
logback-classic
1.2.3
org.yaml
snakeyaml
1.24
org.apache.maven.plugins
maven-compiler-plugin
3.2
1.8
-parameters
maven-resources-plugin
default-resources
none
default-testResources
none
maven-compiler-plugin
default-compile
none
default-testCompile
none
maven-surefire-plugin
default-test
none
客户端辅助类
package RpcUtil;
import io.grpc.Channel;
import io.grpc.ManagedChannel;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
import io.grpc.stub.AbstractStub;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
public class RPCClient {
public static final Logger logger = LoggerFactory.getLogger(RPCClient.class);
private ManagedChannel channel;
public RPCClient(String host, int port) {
this(NettyChannelBuilder.forAddress(host, port)
.usePlaintext()
.build());
}
public RPCClient(String host, int port, SslContext sslContext) {
this(NettyChannelBuilder.forAddress(host, port)
.sslContext(sslContext)
.build());
}
public RPCClient(ManagedChannel channel) {
this.channel = channel;
}
public ManagedChannel getChannel() {
return channel;
}
public void shutdown() throws InterruptedException {
this.channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
public T newStub(Class> service) {
return getStub(service, "newStub");
}
public T newBlockingStub(Class> service) {
return getStub(service, "newBlockingStub");
}
public T newFutureStub(Class> service) {
return getStub(service, "newFutureStub");
}
@SuppressWarnings("unchecked")
private T getStub(Class> service, String type) {
try {
Method method = service.getMethod(type, Channel.class);
return (T) method.invoke(null, this.channel);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
logger.error("rpc client new stub error", e);
}
return null;
}
}
服务端辅助类
package RpcUtil;
import io.grpc.BindableService;
import io.grpc.Server;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;
import io.grpc.netty.shaded.io.netty.handler.ssl.ClientAuth;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
public class RPCServer {
private Server server;
private final int port;
private String certChainFilePath;
private String privateKeyFilePath;
private String trustCertCollectionFilePath;
public RPCServer(int port) {
this.port = port;
}
public RPCServer(int port, String certChainFilePath, String privateKeyFilePath, String trustCertCollectionFilePath) {
this.port = port;
this.certChainFilePath = certChainFilePath;
this.privateKeyFilePath = privateKeyFilePath;
this.trustCertCollectionFilePath = trustCertCollectionFilePath;
}
/**
* 启动RPC服务
*/
public void start(Collection services) throws IOException {
NettyServerBuilder builder = NettyServerBuilder.forPort(this.port);
if (this.certChainFilePath == "" || this.certChainFilePath == null) {
builder.sslContext(this.getSslContextBuilder().build());
}
services.forEach(builder::addService);
this.server = builder.build();
this.server.start();
}
/**
* 获取SSL上下文
*/
private SslContextBuilder getSslContextBuilder() {
SslContextBuilder sslClientContextBuilder = SslContextBuilder.forServer(
new File(this.certChainFilePath),
new File(this.privateKeyFilePath));
if (trustCertCollectionFilePath != null) {
sslClientContextBuilder.trustManager(new File(trustCertCollectionFilePath));
sslClientContextBuilder.clientAuth(ClientAuth.REQUIRE);
}
return GrpcSslContexts.configure(sslClientContextBuilder);
}
/**
* 停止RPC服务
*/
public void stop() {
if (server != null) {
server.shutdown();
}
}
/**
* 阻塞等待RPC服务器停止
*/
public void blockUntilShutdown() {
if (server != null) {
try {
server.awaitTermination();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
以下是自定义远程GRPC管理类
package RpcUtil;
import io.grpc.BindableService;
import java.util.HashSet;
import java.util.Set;
public class RPCServiceBuilder {
private Set> services;
public RPCServiceBuilder addService(Class extends BindableService> service) {
if (this.services == null) {
this.services = new HashSet<>();
}
this.services.add(service);
return this;
}
public Set> build() {
assert this.services != null;
return this.services;
}
public static RPCServiceBuilder newBuilder() {
return new RPCServiceBuilder();
}
}
枚举类
package RpcUtil.cluster;
public enum NodeType {
UNKNOWN(-1, "unknown", "未知节点"),
COMMON(0, "common", "公共服节点"),
;
public int type;
public String name;
public String desc;
NodeType(int type, String name, String desc) {
this.type = type;
this.name = name;
this.desc = desc;
}
public static NodeType valueOfType(String name) {
for (NodeType type : NodeType.values()) {
if (type.name.equals(name)) {
return type;
}
}
return UNKNOWN;
}
}
yaml读取工具类:
package RpcUtil.cluster;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import java.io.InputStream;
import java.util.Map;
public final class YamlUtils {
public YamlUtils() {
}
public static Map loadMap(String fileName) {
InputStream inputStream = YamlUtils.class.getClassLoader().getResourceAsStream(fileName);
return (Map) (new Yaml()).load(inputStream);
}
public static T loadCustomType(Class type, String fileName) {
InputStream inputStream = type.getClassLoader().getResourceAsStream(fileName);
return (new Yaml(new Constructor(type))).load(inputStream);
}
}
yaml对应的java配置
package RpcUtil.cluster;
import java.util.HashSet;
import java.util.Set;
public class RPCClusterConfig {
private String server;
private Set addresses = new HashSet<>();
public RPCClusterConfig() {
}
public RPCClusterConfig(String node) {
this.server = node;
}
public String getServer() {
return server;
}
public void setServer(String server) {
this.server = server;
}
public Set getAddresses() {
return addresses;
}
public void setAddresses(Set addresses) {
this.addresses = addresses;
}
public static class RPCAddress {
private String node;
private String host;
private int port;
public RPCAddress() {
}
public String getNode() {
return node;
}
public void setNode(String node) {
this.node = node;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
}
初始化类:
package RpcUtil.cluster;
import RpcUtil.RPCClient;
import RpcUtil.RPCServer;
import io.grpc.BindableService;
import io.grpc.stub.AbstractStub;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class RPCCluster {
private static RPCServer server;
private static Map clients = new HashMap<>();
private static final Logger logger = LoggerFactory.getLogger(RPCCluster.class);
/**
* 初始化RPC集群,并自动加载配置文件
*/
public static void init() {
RPCClusterConfig config = YamlUtils.loadCustomType(RPCClusterConfig.class, "cluster.yaml");
init(config);
}
/**
* 初始化RPC集群
*/
public static RPCCluster init(RPCClusterConfig config) {
RPCCluster cluster = new RPCCluster();
for (RPCClusterConfig.RPCAddress address : config.getAddresses()) {
if (address.getNode().equals(config.getServer())) {
initServer(address);
} else {
initClient(address);
}
}
return cluster;
}
private static void initServer(RPCClusterConfig.RPCAddress address) {
server = new RPCServer(address.getPort());
}
private static void initClient(RPCClusterConfig.RPCAddress address) {
RPCClient client = new RPCClient(address.getHost(), address.getPort());
clients.put(address.getNode(), client);
}
/**
* 打开RPC服务器
*
* @param services RPC 服务列表
*/
public static void start(Collection services) {
try {
server.start(services);
} catch (IOException e) {
logger.error("open rpc cluster server error", e);
}
logger.info("rpc server started.");
}
/**
* 关闭RPC服务
*/
public static void shutdown() {
server.stop();
clients.forEach((k, v) -> {
try {
v.shutdown();
} catch (InterruptedException e) {
logger.error("shutdown rpc cluster client error", e);
}
});
}
public static RPCClient getClient(String node) {
return clients.get(node);
}
public static RPCClient getClinet(NodeType type) {
return clients.get(type.name);
}
public static T newStub(NodeType type, Class> service) {
return clients.get(type.name).newStub(service);
}
public static T newBlockingStub(NodeType type, Class> service) {
return clients.get(type.name).newBlockingStub(service);
}
public static T newFutureStub(NodeType type, Class> service) {
return clients.get(type.name).newFutureStub(service);
}
}
以上是GRPC客户端和服务端连接的工具类 只需要在客户端和服务器端配置对应的cluster.yaml ,即可连接成功!
cluster.yaml :
# RPC集群配置
# 当前服务节点
server: "server1"
# 节点集群地址
addresses:
# 公共服务器
- node: "common"
host: "127.0.0.1"
port: 8888