当前位置: 首页 > news >正文

手写RPC框架--7.封装响应

RPC框架-Gitee代码(麻烦点个Starred, 支持一下吧)
RPC框架-GitHub代码(麻烦点个Starred, 支持一下吧)

封装响应

  • 封装响应
    • a.封装响应
    • b.请求id生成器(雪花算法)
    • c.抽象序列化
    • d.建立序列化工厂
    • e.hessian的序列化方式(拓展)

封装响应

a.封装响应

在core模块下的com.dcyrpcenumeration

在包内创建ResponseCode 枚举:定义响应码枚举

/*** 响应码枚举*/
public enum ResponseCode {SUCCESS((byte) 1, "成功"), FAIL((byte) 2, "失败");private byte code;private String desc;ResponseCode(byte code, String desc) {this.code = code;this.desc = desc;}
}

在core模块下com.dcyrpctransprt.message包下

创建DcyRpcResponse类:服务提供方回复的响应

/*** 服务提供方回复的响应*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class DcyRpcResponse {// 请求的idprivate long requestId;// 压缩的类型private byte compressType;// 序列化的方式private byte serializeType;// 响应码类型:1 成功,2 异常private byte code;// 具体的消息体private Object body;
}

在core模块channelhandler.handler包下创建DcyRpcResponseEncoder类:对调用结果进行编码

/*** 编码器** 4B magic(魔数值) -- Drpc.getBytes()* 1B version(版本) -- 1* 2B header length(首部的长度)* 4B full length(报文的总长度)* 1B serialize (序列化类型的长度)* 1B compress(压缩类型的长度)* 1B code(响应码)* 8B requestId** Object body*/
@Slf4j
public class DcyRpcResponseEncoder extends MessageToByteEncoder<DcyRpcResponse> {@Overrideprotected void encode(ChannelHandlerContext channelHandlerContext, DcyRpcResponse dcyRpcResponse, ByteBuf byteBuf) throws Exception {// 4个字节魔数值byteBuf.writeBytes(MessageFormatConstant.MAGIC);// 1个字节版本号byteBuf.writeByte(MessageFormatConstant.VERSION);// 2个字节的头部的长度byteBuf.writeShort(MessageFormatConstant.HEADER_LENGTH);// 总长度未知,不知道body的长度byteBuf.writerIndex(byteBuf.writerIndex() + MessageFormatConstant.FULL_FIELD_LENGTH);// 响应码byteBuf.writeByte(dcyRpcResponse.getCode());// 序列化类型byteBuf.writeByte(dcyRpcResponse.getSerializeType());// 压缩类型byteBuf.writeByte(dcyRpcResponse.getCompressType());// 8个字节的请求idbyteBuf.writeLong(dcyRpcResponse.getRequestId());// 写入请求体body(requestPayload)byte[] body = getBodyBytes(dcyRpcResponse.getBody());if (body != null) {byteBuf.writeBytes(body);byteBuf.writeInt(MessageFormatConstant.HEADER_LENGTH + body.length);}int bodyLength = body ==null ? 0 : body.length;// 重新处理报文的总长度// 先获取当前的写指针的位置int writerIndex = byteBuf.writerIndex();// 将写指针的位置移动到总长度的位置上byteBuf.writerIndex(MessageFormatConstant.MAGIC.length + MessageFormatConstant.VERSION_LENGTH + MessageFormatConstant.HEADER_FIELD_LENGTH);byteBuf.writeInt(MessageFormatConstant.HEADER_LENGTH + bodyLength);// 将写指针归位byteBuf.writerIndex(writerIndex);}private byte[] getBodyBytes(Object body) {// 心跳请求没有payloadif (body == null) {return null;}// 对象序列化成字节数组try {ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream outputStream = new ObjectOutputStream(baos);outputStream.writeObject(body);return baos.toByteArray();} catch (IOException e) {log.error("序列化时出现异常");throw new RuntimeException(e);}}
}

修改DcyRpcBootstrapstart()方法

  • 添加 DcyRpcResponseEncoder 响应编码器
// 略....
// 3.配置服务器
serverBootstrap = serverBootstrap.group(boss, worker).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {// TODO 核心内容,需要添加很多入栈和出栈的handlersocketChannel.pipeline().addLast(new LoggingHandler(LogLevel.INFO))// 对报文进行解码.addLast(new DcyRpcRequestDecoder())// 根据请求进行方法调用.addLast(new MethodCallHandler())// 对响应结果进行编码.addLast(new DcyRpcResponseEncoder());}});
// 略....

在core模块channelhandler.handler包下创建DcyRpcResponseEncoder类:服务请求方对响应结果进行解码

/*** 解码器*/
@Slf4j
public class DcyRpcResponseDecoder extends LengthFieldBasedFrameDecoder {public DcyRpcResponseDecoder() {// 找到当前报文的总长度,截取报文,截取出来的报文可以进行解析super(// 最大帧的长度,超过这个maxFrameLength值,会直接丢弃MessageFormatConstant.MAX_FRAME_LENGTH,// 长度字段偏移量MessageFormatConstant.MAGIC.length + MessageFormatConstant.VERSION_LENGTH + MessageFormatConstant.HEADER_FIELD_LENGTH,// 长度的字段的长度MessageFormatConstant.FULL_FIELD_LENGTH,// todo 负载的适配长度-(MessageFormatConstant.MAGIC.length + MessageFormatConstant.VERSION_LENGTH + MessageFormatConstant.HEADER_FIELD_LENGTH + MessageFormatConstant.FULL_FIELD_LENGTH),// 跳过的字段0);}@Overrideprotected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {Object decode = super.decode(ctx, in);if (decode instanceof ByteBuf) {ByteBuf byteBuf = (ByteBuf) decode;return decodeFrame(byteBuf);}return null;}private Object decodeFrame(ByteBuf byteBuf) {// 1.解析魔数byte[] magic = new byte[MessageFormatConstant.MAGIC.length];byteBuf.readBytes(magic);// 检测魔数值是否匹配for (int i = 0; i < magic.length; i++) {if (magic[i] != MessageFormatConstant.MAGIC[i]) {throw new RuntimeException("获得的请求类型不匹配");}}// 2.解析版本号byte version = byteBuf.readByte();if (version > MessageFormatConstant.VERSION) {throw new RuntimeException("获得的请求版本不被支持");}// 3.解析头部的长度short headLength = byteBuf.readShort();// 4.解析总长度int fullLength = byteBuf.readInt();// 5.解析响应码byte responseCode = byteBuf.readByte();// 6.解析序列化类型byte serializeType = byteBuf.readByte();// 7.解析压缩型byte compressType = byteBuf.readByte();// 8.解析请求Idlong requestId = byteBuf.readLong();DcyRpcResponse dcyRpcResponse = new DcyRpcResponse();dcyRpcResponse.setCode(responseCode);dcyRpcResponse.setCompressType(compressType);dcyRpcResponse.setSerializeType(serializeType);dcyRpcResponse.setRequestId(requestId);// 9.解析消息体payloadint bodyLength = fullLength - headLength;byte[] body = new byte[bodyLength];byteBuf.readBytes(body);// 解压缩和反序列化// todo 解压缩// 反序列化try (ByteArrayInputStream bis = new ByteArrayInputStream(body);ObjectInputStream ois = new ObjectInputStream(bis)) {Object object = ois.readObject();dcyRpcResponse.setBody(object);} catch (IOException | ClassNotFoundException e) {log.error("请求【{}】反序列化时出现异常", requestId, e);throw new RuntimeException(e);}return dcyRpcResponse;}
}

修改channelHandler包下的ConsumerChannelInitializer类:添加入站的解码器 DcyRpcResponseDecoder()

public class ConsumerChannelInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline()// netty自带的日志处理器.addLast(new LoggingHandler(LogLevel.INFO))// 消息编码器.addLast(new DcyRpcRequestEncoder())// 入站的解码器.addLast(new DcyRpcResponseDecoder())// 处理结果.addLast(new MySimpleChannelInboundHandler());}
}

修改channelhandler.handler包下的MySimpleChannelInboundHandler类:将响应结果的ByteBuf改成DcyRpcResponse

/*** 处理响应结果*/
public class MySimpleChannelInboundHandler extends SimpleChannelInboundHandler<DcyRpcResponse> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, DcyRpcResponse dcyRpcResponse) throws Exception {// 异步// 服务提供方,给予的结果Object returnValue = dcyRpcResponse.getBody();// 从全局的挂起的请求中,寻找与之匹配的待处理 completeFutureCompletableFuture<Object> completableFuture = DcyRpcBootstrap.PENDING_REQUEST.get(1L);completableFuture.complete(returnValue);}
}

在core模块channelhandler.handler包的MethodCallHandler类:封装响应结果

// 略...
// 3.封装响应
DcyRpcResponse dcyRpcResponse = DcyRpcResponse.builder().code(ResponseCode.SUCCESS.getCode()).requestId(dcyRpcRequest.getRequestId()).compressType(dcyRpcRequest.getCompressType()).serializeType(dcyRpcRequest.getSerializeType()).body(result).build();// 4.写出响应
channelHandlerContext.channel().writeAndFlush(dcyRpcResponse);// 略...

b.请求id生成器(雪花算法)

在当前项目中,我们需要给请求一个唯一标识,用来标识一个请求和响应的关联关系,我们要求请求的id必须唯一,且不能占用过大的空间,可用的方案如下:

  • 1.自增id,单机的自增id不能解决不重复的问题,微服务情况下我们需要一个稳定的发号服务才能保证,但是这样做性能偏低。

  • 2.uuid,将uuid作为唯一标识占用空间太大

  • 3.雪花算法,最优解。

雪花算法(snowflake)最早是twitter内部使用分布式环境下的唯一ID生成算法,他使用64位long类型的数据存储

id,具体如下:

0 - 0000000000 0000000000 0000000000 0000000000 0 - 0000000000 - 000000000000
符号位 时间戳 机器码 序列号
最高位表示符号位,其中0代表整数,1代表负数,而id一般都是正数,所以最高位为0。

通过雪花算法实现 – (世界上没有一片雪花是一样的) 5+5+42+12=64位

  • 机房号(数据中心)5bit
  • 机器号 5bit
  • 时间戳(long)原本64位表示的时间,必须减少到42位,可以自由选择一个时间。如: 公司的成立日期
  • 序列化 12bit:同一个机房的同一个机器号的同一个时间可能因并发量需要很多个Id
时间戳 (42) 机房号 (5) 机器号 (5) 序列号 (12)
101010101010101010101010101010101010101011 10101 10101 101011010101

在common块下的com.dcy包下创建IdGenerator

/*** 请求id的生成器:雪花算法*/
public class IdGenerator {// 起始时间戳private static final long START_STAMP = DateUtil.get("2022-1-1").getTime();// 机房号public static final long DATA_CENTER_BIT = 5L;// 机器号public static final long MACHINE_BIT = 5L;// 序列化号public static final long SEQUENCE_BIT = 5L;// 机房号的最大值public static final long DATA_CENTER_MAX = ~(-1L << DATA_CENTER_BIT);// 机器号的最大值public static final long MACHINE_MAX = ~(-1L << MACHINE_BIT);// 序列号的最大值public static final long SEQUENCE_MAX = ~(-1L << SEQUENCE_BIT);// 时间戳需要左移的位数public static final long TIMESTAMP_LEFT = DATA_CENTER_BIT + MACHINE_BIT + SEQUENCE_BIT;// 机房号需要左移的位数public static final long DATA_CENTER_LEFT = MACHINE_BIT + SEQUENCE_BIT;// 机器号需要左移的位数public static final long MACHINE_LEFT = SEQUENCE_BIT;private long dataCenterId;private long machineId;private LongAdder sequenceId = new LongAdder();private long lastTimeStamp = -1;public IdGenerator(long dataCenterId, long machineId) {//参数是否合法if (dataCenterId > DATA_CENTER_MAX || machineId > MACHINE_MAX) {throw new IllegalArgumentException("传入的数据中心编号和机器编号不合法");}this.dataCenterId = dataCenterId;this.machineId = machineId;}public long getId() {// 1.处理时间戳的问题long currentTime = System.currentTimeMillis();long timeStamp = currentTime - START_STAMP;// 2.判断时钟回拨if (timeStamp < lastTimeStamp) {throw new RuntimeException("服务器进行了时钟回调");}// 3.对sequenceId做一些处理:如果是同一个时间节点,必须自增if (timeStamp == lastTimeStamp) {sequenceId.increment();if (sequenceId.sum() >= SEQUENCE_MAX) {timeStamp = getNextTimeStamp();sequenceId.reset();}} else {sequenceId.reset();}// 执行结束将时间戳赋值给lastTimeStamplastTimeStamp = timeStamp;long sequence = sequenceId.sum();return timeStamp << TIMESTAMP_LEFT | dataCenterId << DATA_CENTER_LEFT | machineId << MACHINE_LEFT | sequence;}private long getNextTimeStamp() {// 获取当前的时间戳long current = System.currentTimeMillis() - START_STAMP;// 如果一样就一直循环,直到下一个时间戳while (current == lastTimeStamp) {current = System.currentTimeMillis() - START_STAMP;}return current;}
}

在common块下的com.dcy包下创建DateUtil类:用于时间日期相关的工具类

/*** 时间日期相关的工具类*/
public class DateUtil {public static Date get(String pattern) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");try {return sdf.parse(pattern);} catch (ParseException e) {throw new RuntimeException(e);}}
}

DcyRpcBootstrap类中添加IdGenerator

// 略...
private int port = 8088;public static final IdGenerator ID_GENERATOR = new IdGenerator(1, 2);
// 略...

RpcConsumerInvocationHandler类的封装报文位置,将requestId的值设置为通过id生成器获取

// 略...
DcyRpcRequest dcyRpcRequest = DcyRpcRequest.builder().requestId(DcyRpcBootstrap.ID_GENERATOR.getId()).compressType((byte) 1).serializeType((byte) 1).requestType(RequestType.REQUEST.getId()).requestPayload(requestPayload).build();

c.抽象序列化

在core模块下创建serialize

在该包下创建Serializer接口:序列化器

public interface Serializer {/*** 序列化* @param object 待序列化的对象实例* @return 字节数组*/byte[] serializer(Object object);/*** 反序列化* @param bytes 待反序列化的字节数组* @param clazz 目标类的class对象* @return 目标实例* @param <T> 目标类泛型*/<T> T deserialize(byte[] bytes, Class<T> clazz);
}

serialize包下创建impl包,创建JdkSerializer类,实现Serializer接口:jdk序列化器

@Slf4j
public class JdkSerializer implements Serializer{@Overridepublic byte[] serializer(Object object) {// 心跳请求没有payloadif (object == null) {return null;}// 对象序列化成字节数组try (ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream outputStream = new ObjectOutputStream(baos)) {outputStream.writeObject(object);return baos.toByteArray();} catch (IOException e) {log.error("序列化对象【{}】时出现异常", object);throw new SerializeException(e);}}@Overridepublic <T> T deserialize(byte[] bytes, Class<T> clazz) {if (bytes == null || clazz == null) {return null;}// 字节数组转成对象序列化try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);ObjectInputStream objectInputStream = new ObjectInputStream(bais)) {return (T) objectInputStream.readObject();} catch (IOException | ClassNotFoundException e) {log.error("反序列化对象【{}】时出现异常", clazz);throw new SerializeException(e);}}
}

在common的exceptions中创建SerializeException类:序列化异常处理

public class SerializeException extends RuntimeException{public SerializeException() {super();}public SerializeException(String message) {super(message);}public SerializeException(Throwable cause) {super(cause);}
}

d.建立序列化工厂

consumer包下的Application启动类中添加序列化方法

// 略...
DcyRpcBootstrap.getInstance().application("first-dcyrpc-consumer").registry(new RegistryConfig("zookeeper://127.0.0.1:2181")).serialize("jdk").reference(reference);
// 略...

DcyRpcBootstrap类中,添加序列化配置项和方法

// 略...
public static String SERIALIZE_TYPE = "jdk";
// 略...
/*** 配置序列化的方式* @param serializeType* @return*/
public DcyRpcBootstrap serialize(String serializeType) {SERIALIZE_TYPE = serializeType;return this;
}

在core模块下的serialize包下创建SerializerWrapper类:序列化包装类

@NoArgsConstructor
@AllArgsConstructor
@Data
public class SerializerWrapper {private byte code;private String type;private Serializer serializer;
}

在core模块下的serialize包下创建SerializerFactory类:序列化工厂类

/*** 序列化工厂类*/
public class SerializerFactory {private final static Map<String, SerializerWrapper> SERIALIZER_CACHE = new ConcurrentHashMap<>(8);private final static Map<Byte, SerializerWrapper> SERIALIZER_CACHE_CODE = new ConcurrentHashMap<>(8);static {SerializerWrapper jdk = new SerializerWrapper((byte) 1, "jdk", new JdkSerializer());SerializerWrapper json = new SerializerWrapper((byte) 2, "json", new JsonSerializer());SERIALIZER_CACHE.put("jdk", jdk);SERIALIZER_CACHE.put("json", json);SERIALIZER_CACHE_CODE.put((byte) 1, jdk);SERIALIZER_CACHE_CODE.put((byte) 2, json);}/*** 使用工厂方法获取一个SerializerWrapper* @param serializeType 序列化的类型* @return*/public static SerializerWrapper getSerializer(String serializeType) {return SERIALIZER_CACHE.get(serializeType);}public static SerializerWrapper getSerializer(byte serializeCode) {return SERIALIZER_CACHE_CODE.get(serializeCode);}
}

修改DcyRpcRequestEncoder类,在请求类,对请求添加有关序列化的代码

//略...
// 8个字节的请求id
byteBuf.writeLong(dcyRpcRequest.getRequestId());// 写入请求体body(requestPayload)
// 1.根据配置的序列化方式进行序列化
Serializer serializer = SerializerFactory.getSerializer(dcyRpcRequest.getSerializeType()).getSerializer();
byte[] body = serializer.serializer(dcyRpcRequest.getRequestPayload());// 2.根据配置的压缩方式进行压缩if (body != null) {byteBuf.writeBytes(body);byteBuf.writeInt(MessageFormatConstant.HEADER_LENGTH + body.length);
}
//略...

修改RpcConsumerInvocationHandler类:修改填写序列化器的代码

// 略...
DcyRpcRequest dcyRpcRequest = DcyRpcRequest.builder().requestId(DcyRpcBootstrap.ID_GENERATOR.getId()).compressType((byte) 1).serializeType(SerializerFactory.getSerializer(DcyRpcBootstrap.SERIALIZE_TYPE).getCode()).requestType(RequestType.REQUEST.getId()).requestPayload(requestPayload).build();
// 略...

修改DcyRpcRequestDecoder类,在响应类,对请求添加反序列化的代码

// 略...
// 9.解析消息体payload
int payloadLength = fullLength - headLength;
byte[] payload = new byte[payloadLength];
byteBuf.readBytes(payload);// 解压缩和反序列化
// todo 解压缩// 反序列化
Serializer serializer = SerializerFactory.getSerializer(serializeType).getSerializer();
RequestPayload requestPayload = serializer.deserialize(payload, RequestPayload.class);dcyRpcRequest.setRequestPayload(requestPayload);return dcyRpcRequest;

修改DcyRpcResponseEncoder类:在响应类,对响应添加序列化的代码

// 略...
// 8个字节的请求id
byteBuf.writeLong(dcyRpcResponse.getRequestId());// 写入请求体body(requestPayload)
// 对响应做序列化器
Serializer serializer = SerializerFactory.getSerializer(dcyRpcResponse.getSerializeType()).getSerializer();
byte[] body = serializer.serializer(dcyRpcResponse.getBody());if (body != null) {byteBuf.writeBytes(body);byteBuf.writeInt(MessageFormatConstant.HEADER_LENGTH + body.length);
}
// 略...

修改DcyRpcResponseDecoder类,在请求类,对响应添加反序列化的代码

// 略...
// 9.解析消息体payload
int payloadLength = fullLength - headLength;
byte[] payload = new byte[payloadLength];
byteBuf.readBytes(payload);// 解压缩和反序列化
// todo 解压缩Serializer serializer = SerializerFactory.getSerializer(dcyRpcResponse.getSerializeType()).getSerializer();
Object body = serializer.deserialize(payload, Object.class);dcyRpcResponse.setBody(body);return dcyRpcResponse;
// 略...

e.hessian的序列化方式(拓展)

Hessian序列化是一种支持动态类型、跨语言、基于对象传输的网络协议,Java对象序列化的二进制流可以被其他语言(如,c++,python)。特性如下:

  • 1.自描述序列化类型。不依赖外部描述文件或者接口定义,用一个字节表示常用的基础类型,极大缩短二进制流。
  • 2.语言无关,支持脚本语言
  • 3.协议简单,比Java原生序列化高效
  • 4.相比hessian1,hessian2中增加了压缩编码,其序列化二进制流大小是Java序列化的50%,序列化耗时是Java序列化的30%,反序列化耗时是Java序列化的20%

序列化操作:

  • 1.new一个Hessian2Output,传入一个OutputStream
  • 2.writeObject(),传入具体要序列化的对象
  • 3.flush()

反序列化操作:

  • 1.new一个Hessian2Output,传入一个InputStream
  • 2.readObject()
/*** hessian序列化器*/
@Slf4j
public class HessianSerializer implements Serializer {@Overridepublic byte[] serializer(Object object) {// 心跳请求没有payloadif (object == null) {return null;}// 对象序列化成字节数组try (ByteArrayOutputStream baos = new ByteArrayOutputStream();) {Hessian2Output hessian2Output = new Hessian2Output(baos);hessian2Output.writeObject(object);hessian2Output.flush();log.info("对象使用hessian【{}】完成了序列化", object);return baos.toByteArray();} catch (IOException e) {log.error("使用hessian序列化对象【{}】时出现异常", object);throw new SerializeException(e);}}@Overridepublic <T> T deserialize(byte[] bytes, Class<T> clazz) {if (bytes == null || clazz == null) {return null;}// 字节数组转成对象序列化try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);) {HessianInput hessianInput = new HessianInput(bais);T t = (T) hessianInput.readObject();log.info("对象使用hessian【{}】完成了反序列化", clazz);return t;} catch (IOException e) {log.error("使用hessian反序列化对象【{}】时出现异常", clazz);throw new SerializeException(e);}}
}

相关文章:

手写RPC框架--7.封装响应

RPC框架-Gitee代码(麻烦点个Starred, 支持一下吧) RPC框架-GitHub代码(麻烦点个Starred, 支持一下吧) 封装响应 封装响应a.封装响应b.请求id生成器(雪花算法)c.抽象序列化d.建立序列化工厂e.hessian的序列化方式&#xff08;拓展&#xff09; 封装响应 a.封装响应 在core模块…...

Linux入门教程||Linux系统目录结构

登录系统后&#xff0c;在当前命令窗口下输入命令&#xff1a; ls / 你会看到如下图所示: 树状目录结构&#xff1a; 以下是对这些目录的解释&#xff1a; /bin&#xff1a; bin是Binary的缩写, 这个目录存放着最经常使用的命令。 /boot&#xff1a; 这里存放的是启动Linux时…...

LeetCode 88. 合并两个有序数组

文章目录 一、题目二、C# 题解 一、题目 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意&a…...

C语言实现扫雷小游戏

1.首先扫雷游戏要存储布置好的雷信息&#xff0c;需要一个二维数组 不是雷放* 雷&#xff1a;# 不是雷&#xff1a;0 雷&#xff1a;1 2. 给2个二维数组 9*9 一个存放雷的信息&#xff0c;一个存放布置好雷的信息 3.为了防止在统计坐标周围的…...

【linux基础(五)】Linux中的开发工具(上)---yum和vim

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到开通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux中的开发工具 1. 前言2.…...

C++学习之list的实现

在了解学习list实现之前我们首先了解一下关于迭代器的分类&#xff1a; 按功能分类&#xff1a; 正向迭代器 反向迭代器 const正向迭代器 const反向迭代器 按性质分类&#xff1a; 单向迭代器 只能 例如单链表 双向迭代器 可&#xff0c;也可-- 例如双…...

一种高效且节约内存的聚合数据结构的实现

一种高效且节约内存的聚合数据结构的实现 在特定的场景中&#xff0c;特殊定制数据结构能够得到更加好的性能且更节约内存。 聚合函数GroupArray的问题 GroupArray聚合函数是将分组内容组成一个个数组&#xff0c;例如下面的例子&#xff1a; SELECT groupArray(concat(ABC…...

机器学习(10)---特征选择

文章目录 一、概述二、Filter过滤法2.1 过滤法说明2.2 方差过滤2.3 方差过滤对模型影响 三、相关性过滤3.1 卡方过滤3.2 F检验3.3 互信息法3.4 过滤法总结 四、Embedded嵌入法4.1 嵌入法说明4.2 以随机森林为例的嵌入法 五、Wrapper包装法5.1 包装法说明5.2 以随机森林为例的包…...

Python之数据库(MYSQL)连接

一&#xff09;数据库SQL语言基础 MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQL AB 公司开发&#xff0c;目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一&#xff0c;在 WEB 应用方面&#xff0c;MySQL是最好的 RDBMS (Relational Database…...

【建站教程】使用阿里云服务器怎么搭建网站?

使用阿里云服务器快速搭建网站教程&#xff0c;先为云服务器安装宝塔面板&#xff0c;然后在宝塔面板上新建站点&#xff0c;阿里云服务器网以搭建WordPress网站博客为例&#xff0c;阿小云来详细说下从阿里云服务器CPU内存配置选择、Web环境、域名解析到网站上线全流程&#x…...

【自然语言处理】关系抽取 —— MPDD 讲解

MPDD 论文信息 标题:MPDD: A Multi-Party Dialogue Dataset for Analysis of Emotions and Interpersonal Relationships 作者:Yi-Ting Chen, Hen-Hsen Huang, Hsin-Hsi Chen 期刊:LREC 2020 发布时间与更新时间:2020 主题:自然语言处理、关系抽取、对话场景、情感预测 数…...

深入理解JVM虚拟机第三篇:JVM的指令集架构模型和JVM的生命周期

文章目录 一:JVM的指令集架构模型 1:基于栈式架构的特点...

[小尾巴 UI 组件库] 组件库配置与使用

文章归档于&#xff1a;https://www.yuque.com/u27599042/row3c6 组件库地址 npm&#xff1a;https://www.npmjs.com/package/xwb-ui?activeTabreadme小尾巴 UI 组件库源码 gitee&#xff1a;https://gitee.com/tongchaowei/xwb-ui小尾巴 UI 组件库测试代码 gitee&#xff1a…...

Linux系统中fork()函数的理解

fork() 函数是一个在Unix和类Unix操作系统中常见的系统调用&#xff0c;用于创建一个新的进程&#xff0c;该进程是调用进程&#xff08;父进程&#xff09;的副本。fork() 函数的工作原理如下&#xff1a; 1. 当父进程调用 fork() 时&#xff0c;操作系统会创建一个新的进程&a…...

Linux网络编程:网络协议及网络传输的基本流程

目录 一. 计算机网络的发展 二. 网络协议的认识 2.1 对于协议分层的理解 2.2 TCP/IP五层协议模型 2.3 OSI七层模型 三. 网络传输的流程 3.1 同一网段中计算机通信的流程 3.2 不同网段中计算机设备的通信 3.3 对于IP地址和MAC地址的理解 3.4 数据的封装和解包 四. 总结…...

【大数据之Kafka】十、Kafka消费者工作流程

1 Kafka消费方式 &#xff08;1&#xff09;pull&#xff08;拉&#xff09;模式&#xff1a;消费者从broker中主动拉取数据。&#xff08;Kafka中使用&#xff09; 不足&#xff1a;如果Kafka中没有数据&#xff0c;消费者可能会陷入循环&#xff0c;一直返回空数据。 &#…...

如何确保ChatGPT的文本生成对特定行业术语的正确使用?

确保ChatGPT在特定行业术语的正确使用是一个重要而复杂的任务。这涉及到许多方面&#xff0c;包括数据预处理、模型训练、微调、评估和监控。下面我将详细介绍如何确保ChatGPT的文本生成对特定行业术语的正确使用&#xff0c;并探讨这一过程中的关键考虑因素。 ### 1. 数据预处…...

行业追踪,2023-09-11

自动复盘 2023-09-11 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…...

LVS + Keepalived群集

文章目录 1. Keepalived工具概述1.1 什么是Keepalived1.2 工作原理1.3 Keepailved实现原理1.4 Keepalived体系主要模块及其作用1.5 keepalived的抢占与非抢占模式 2. 脑裂现象 &#xff08;拓展&#xff09;2.1 什么是脑裂2.2 脑裂的产生原因2.3 如何解决脑裂2.4 如何预防脑裂 …...

springboot将jar改成war

一、maven项目 1、修改pom文件 <packaging>war</packaging>2、添加Servlet API依赖&#xff0c;Spring Boot的Starter依赖通常会包含这个依赖&#xff0c;所以你可能已经有了&#xff0c;没有就需要添加 <dependency><groupId>javax.servlet</gr…...

从9.10拼多多笔试第四题产生的01背包感悟

文章目录 题面基本的01背包问题本题变式 本文参考&#xff1a; 9.10拼多多笔试ak_牛客网 (nowcoder.com) 拼多多 秋招 2023.09.10 编程题目与题解 (xiaohongshu.com) 题面 拼多多9.10笔试的最后一题&#xff0c;是一道比较好的01背包变式问题&#xff0c;可以学习其解法加深对…...

搭建自己的OCR服务,第一步:选择合适的开源OCR项目

一、OCR是什么&#xff1f; 光学字符识别&#xff08;Optical Character Recognition, OCR&#xff09;是指对文本资料的图像文件进行分析识别处理&#xff0c;获取文字及版面信息的过程。 亦即将图像中的文字进行识别&#xff0c;并以文本的形式返回。 二、OCR的基本流程 1…...

【C++】VScode配置C/C++语言环境(简洁易懂版)

目录 一、下载VScode&#xff08;装好直接跳第五步&#xff09;二、安装VScode三、VScode设置语言为中文四、VScode切换主题&#xff08;个人爱好&#xff09;五、下载C语言编译器&#xff08;MinGW-W64 GCC&#xff09;六、配置编译器环境变量七、配置VScode八、使用单独窗口…...

【hive】—原有分区表新增加列(alter table xxx add columns (xxx string) cascade;)

项目场景&#xff1a; 需求&#xff1a;需要在之前上线的分区报表中新增加一列。 实现方案&#xff1a; 1、创建分区测试表并插入测试数据 drop table test_1; create table test_1 (id string, score int, name string ) partitioned by (class string) row format delimit…...

verilog学习笔记7——PMOS和NMOS、TTL电路和CMOS电路

文章目录 前言一、PMOS和NMOS1、NMOS2、PMOS3、增强型和耗尽型4、两者面积大小 二、CMOS门电路1、非门2、与非门3、或非门4、线与逻辑5、CMOS传输门6、三态门 三、TTL电路四、TTL电路 VS CMOS电路五、数字电平六、使用CMOS电路实现逻辑函数1、上拉网络 PUN2、下拉网络 PDN3、实…...

Java知识点二

Java知识点二 1、Comparable内部比较器&#xff0c;Comparator外部比较器2、源码结构的区别:1&#xff09;Comparable接口&#xff1a;2&#xff09;Comparator接口&#xff1a; 2、Java反射 1、Comparable内部比较器&#xff0c;Comparator外部比较器 我们一般把Comparable叫…...

基于单片机压力传感器MPX4115检测-报警系统-proteus仿真-源程序

一、系统方案 本设计采用52单片机作为主控器&#xff0c;液晶1602显示&#xff0c;MPX4115检测压力&#xff0c;按键设置报警&#xff0c;LED报警。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 /***************************************…...

Pytorch02 神经网路搭建步骤

文章目录 import numpy as np import torch from PIL.Image import Image from torch.autograd import Variable# 获取数据 def get_data():train_Xnp.asarray([3.3,4.4,5.5,6.71,6.93,4.168,9.779,6.182,7.59,2.167,7.042,10.791,5.313,7.997,5.654,9.27,3.1])train_Ynp.asarr…...

【源码】JavaWeb+Mysql招聘管理系统 课设

简介 用idea和eclipse都可以&#xff0c;数据库是mysql&#xff0c;这是一个Java和mysql做的web系统&#xff0c;用于期末课设作业 cout<<"如果需要的小伙伴可以http://www.codeying.top";可定做课设 线上招聘平台整合了各种就业指导资源&#xff0c;通过了…...

Java中级编程大师班<第一篇:初识数据结构与算法-数组(2)>

数组&#xff08;Array&#xff09; 数组是计算机编程中最基本的数据结构之一。它是一个有序的元素集合&#xff0c;每个元素都可以通过索引进行访问。本文将详细介绍数组的特性、用法和注意事项。 数组的基本特性 数组具有以下基本特性&#xff1a; 有序性&#xff1a; 数…...

网站改版计划/今日国内新闻大事件

java基础之““ 与 ”equals ”的区别 前言&#xff1a; 作为Java的基础知识&#xff0c;我相信可能还有许多朋友对于 "" 与 "equals " 之间的关系还不是很明白&#xff0c;今天就总结一下两者的区别&#xff1a; 知识点一&#xff1a;“”与 equals 比…...

python自动写wordpress/免费网站alexa排名查询

2019年10月31日&#xff0c;奇安信CERT监测到安全研究人员_S00pY将Solr通过Velocity模板代码执行任意命令漏洞的POC放到了Github。经研判&#xff0c;该POC对Solr的多个版本有效&#xff0c;且Apache Solr官方并未发布该漏洞的补丁。不过此漏洞在默认配置下无法利用。建议受影响…...

洛阳专业网站设计开发制作建站公司/推广普通话手抄报简单漂亮

题图&#xff1a;https://unsplash.com/nielsenramon在OO(面向对象)编程中&#xff0c;类中的方法有多种形式&#xff1a;实例方法、静态方法、类方法、甚至还可以有抽象方法&#xff0c;本文来说说实例方法在Python中是如何工作的&#xff0c;后面再来谈其他方法。先来定义一个…...

聊城网站建设公司/石首seo排名

")怎样用Python实现FTP自动上传请看PYTHON FTP模块的用法。怎么样通过Python实现自动添加脚本头信息的示例代码#!/usr/bin/python#title :test4.py#description :I am test script#author :python技术#date :20160902#version :0.1#…...

天津电商网站建设/市场营销网络

在此示例中&#xff0c;您可以获取网页上所有出现的电子邮件地址。 如果您必须更改站点的特定页面或子部分的电子邮件地址&#xff0c;则很有用。 也可能对从网页上筛选爬网电子邮件地址有用&#xff0c;尽管这不是我在这里要实现的目标。 查看更多jQuery .each示例 。 i 不敏感…...

网站建设支出/互联网营销案例

代理模式 代理(Proxy)是一种设计模式,提供了间接对目标对象进行访问的方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的功能上,增加额外的功能补充,即扩展目标对象的功能. 这就符合了设计模式的开闭原则&#xff0c;即在对既有代码不改动的情况下进行功…...