00.概述
网关:要从三个角度来理解
- 对系统外部访问者来说,网关起到隐藏系统内部细节,统一访问入口的作用;
- 站在网关自身的角度看,所有与业务无关的通用性功能,全部由网关来承载;
- 从系统内部的功能来看,网关主要负责路由转发和负载均衡。
立体监控平台:可怕的不是出现问题,而是出现了问题却不知道。
在互联网中,服务与服务之间的通信方式主要有两种:一种是RPC(远程过程调用),另一种是通过消息中间件(如MQ)来通信。当生产者(被调用的)服务产生的结果会影响消费者(调用的)服务的处理逻辑时,使用RPC;反之,当消费者服务对生产者服务的处理结果不关心时,使用消息中间件。
平台、基础组件、基础服务等本质上都是为了更好地服务于核心业务。
当所有数据都存放在一个数据库时,数据库本身可以保证事务分布性,可一旦数据库被分库分表存放在不同的数据库中,分布式事务的重要性就体现出来了。
传统的关系型数据库如MySQL,虽然便于使用,但其数据是存储在表中难以扩展,因此诞生基于K/V(即Key/Value键值对)的数据库。目前已有结合二者优点的NewSQL数据库如TiDB,完全兼容MySQL。Codis与TiDB的作者同为黄东旭。
无/有 0–>1
坏/好 1–>10
01.RPC实现原理深入分析
RPC(Remote Procedure Call):远程过程调用,Remote Procedure Call Protocol,它是一个计算机通信协议。它允许像调用本地方法一样调用远程服务。由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
函数是组织代码的一个单位,在相同内存空间的函数调用,其实就是一个不断压栈和出栈的过程。
RPC作用
- 屏蔽组包解包
- 屏蔽数据发送/接收
- 提高开发效率
- 业务发展的必然产物
RPC核心组成
- 远程方法对象代理
- 连接管理
- 序列化/反序列化
- 寻址与负载均衡
RPC调用方式
- 同步调用
- 异步调用
此处的同步调用与异步调用和IO中的同步与异步是完全不同的概念。
02.精减版RPC调用代码实现
序列化协议:远程调用涉及数据的传输,就会涉及组包和解包,需要调用方和服务方约定数据格式。
public class RpcProtocol implements Serializable {
public static int CMD_CREATE_USER;
private int version;
private int cmd;
private int magicNum;
private int bodylen = 0;
private byte[] body;
public static final int HEAD_LEN = 16;
......
}
该图上行为请求方法,下行为返回结果。RPC的关键在于序列化/反序列化。
- 序列化 对象–>字节流
- 反序列化 字节流–>对象
以int型数据的序列化/反序列化为例,展示部分核心代码:
public static byte[] intToBytes(int n) {
byte[] buf = new byte[4];
for (int i = 0; i < buf.length; i++) {
buf[i] = (byte) (n >> (8 * i));
}
return buf;
}
public static int bytesToInt(byte[] buf, int offset) {
return (buf[offset] && 0xff)
| ((buf[offset + 1] << 8) && 0xff00)
| ((buf[offset + 2] << 16) && 0xff0000)
| ((buf[offset + 3] << 24) && 0xff000000);
}
对象序列化过程
- 序列化请求参数到body
- 序列化RpcProtocol
public class User implements Serializable {
private long uid;
private short age;
private short sex;
......
}
public byte[] userInfoToByteArray(User info) {
byte[] data = new byte[Long.BYTES + Short.BYTES + Short.BYTES];
int index = 0;
System.arraycopy(ByteConverter.longToBytes(info.getUid()), 0, data, index, Long.BYTES);
index += Long.BYTES;
System.arraycopy(ByteConverter.shortToBytes(info.getAge()), 0, data, index, Short.BYTES);
index += Short.BYTES;
System.arraycopy(ByteConverter.shortToBytes(info.getSex()), 0, data, index, Short.BYTES);
return data;
}
把header和body合并成一个完整的RPC协议包
public byte[] generateByteArray() {
byte[] data = new byte[HEAD_LEN + bodyLen];
int index = 0;
System.arraycopy(ByteConverter.intToBytes(version), 0, data, index, Integer.BYTES);
index += Integer.BYTES;
System.arraycopy(ByteConverter.intToBytes(cmd), 0, data, index, Integer.BYTES);
index += Integer.BYTES;
System.arraycopy(ByteConverter.intToBytes(magicNum), 0, data, index, Integer.BYTES);
index += Integer.BYTES;
System.arraycopy(ByteConverter.intToBytes(bodyLen), 0, data, index, Integer.BYTES);
index += Integer.BYTES;
System.arraycopy(body, 0, data, index, body.length);
return data;
}
反序列化过程
- 反序列化RpcProtocol
- 反序列化body
public RpcProtocol byteArrayToRpcHeader(byte[] data) {
int index = 0;
this.setVersion(ByteConverter.bytesToInt(data, index));
index += Integer.BYTES;
this.setCmd(ByteConverter.bytesToInt(data, index));
index += Integer.BYTES;
this.setMagicNum(ByteConverter.bytesToInt(data, index));
index += Integer.BYTES;
this.setBodyLen(ByteConverter.bytesToInt(data, index));
index += Integer.BYTES;
this.body = new byte[this.bodyLen];
System.arraycopy(data, index, this.body, 0, this.bodyLen);
return this;
}
public User byteArrayToUserInfo(byte[] data) {
User user = new User();
int index = 0;
user.setUid(ByteConverter.bytesToLong(data, index));
index += Long.BYTES;
user.setAge(ByteConverter.bytesToShort(data, index));
index += Short.BYTES;
user.setSex(ByteConverter.bytesToShort(data, index));
index += Short.BYTES;
return user;
}
Provider代码实现
- 启动服务监听端口
public class RpcServer {
private static Logger logger = LoggerFactory.getLogger(RpcServer.class);
private static int SERVER_LISTEN_PORT = 58885;
public static void main(String[] args) throws Exception {
Thread tcpServerThread = new Thread("tcpServer") {
@Override
public void run() {
TcpServer tcpServer = new TcpServer(SERVER_LISTEN_PORT);
try {
tcpServer.start();
} catch (Exception e) {
logger.info("TcpServer start exception: " + e.getMessage());
}
}
};
tcpServerThread.start();
tcpServerThread.join();
}
}
Server启动
- 设置解码器
- 设置处理类
- 绑定端口
public void start() throws Exception {
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup);
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024); //连接数
serverBootstrap.localAddress(this.port);
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new PkgDecoder());
pipeline.addLast(new ServerHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind().sync();
if (channelFuture.isSuccess()) {
logger.info("rpc server start success!");
} else {
logger.info("rpc server start fail!");
}
channelFuture.channel().closeFuture().sync();
} catch (Exception ex) {
logger.error("exception occurred exception=" + ex.getMessage());
} finally {
bossGroup.shutdownGracefully().sync(); // 释放线程池资源
workerGroup.shutdownGracefully().sync();
}
}
请求包完整性校验
- 判断包头是否完整
- 判断Body是否完整
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception
{
if (buffer.readableBytes() < RpcProtocol.HEAD_LEN) {
return; //未读完足够的字节流,缓存后继续读
}
byte[] intBuf = new byte[4];
buffer.getBytes(buffer.readerIndex() + RpcProtocol.HEAD_LEN - 4, intBuf);
int bodyLen = ByteConverter.bytesToIntBigEndian(intBuf);
if (buffer.readableBytes() < RpcProtocol.HEAD_LEN + bodyLen) {
return; //未读完足够的字节流,缓存后继续读
}
byte[] bytesReady = new byte[RpcProtocol.HEAD_LEN + bodyLen];
buffer.readBytes(bytesReady);
out.add(bytesReady);
}