Kafka系列(二)将消息数据写入Kafka系统--生产者【异步发送、同步发送、单线程发送、多线程发送、配置生产者属性、自定义序列化、自定义主题分区】

Kafka系列

    • 发送消息到 Kafka 主题
    • 了解异步模式
    • 了解同步模式
    • 线程发送消息的步骤
      • 生产者用单线程发送消息
      • 生产者用多线程发送消息
    • 配置生产者属性
    • 保存对象的各个属性一序列化
      • 序列化一个对象
      • 序列化对象的存储格式
      • 自己实现 序列化的步骤
        • 1. 创建序列化对象
        • 2. 编写序列化工具类
        • 3. 编写自定义序列化逻辑代码
        • 4. 编写生产者应用程序
    • 自定义主题分区
      • 编写自定义主题分区算法
      • 演示自定义分区的作用

转自 《Kafka并不难学!入门、进阶、商业实战》

发送消息到 Kafka 主题

Kafka 0.10.0.0 及以后的版本,对生产者代码的底层实现进行了重构。kafka.producer.Producer类被 org.apache.kafka.clients.producer.KafkaProducer 类替换
Kafka 系统支持两种不同的发送方式–同步模式(Sync)和异步模式(ASync)。

了解异步模式

在 Kafka 0.10.0.0 及以后的版本中,客户端应用程序调用生产者应用接口,默认使用异步的方式发送消息。
生产者客户端在通过异步模式发送消息时,通常会调用回调函数的 send()方法发送消息。生产者端收到 Kafka 代理节点的响应后会触发回调函数

  1. 什么场景下需要使用异步模式
    假如生产者客户端与 Kafka 集群节点间存在网络延时(100ms),此时发送 10 条消息记录,则延时将达到 1s。而大数据场景下有着海量的消息记录,发送的消息记录是远不止 10条,延时将非常严重。
    大数据场景下,如果采用异步模式发送消息记录,几乎没有任何耗时,通过回调函数可以知道消息发送的结果。
  2. 异步模式数据写入流程
    例如,一个业务主题(ip login)有6个分区。生产者客户端写入一条消息记录时,消息记录会先写入某个缓冲区,生产者客户端直接得到结果(这时,缓冲区里的数据并没有写到 Kafka代理节点中主题的某个分区)。之后,缓冲区中的数据会通过异步模式发送到 Kafka 代理节点中主题的某个分区中
	//实例化一个消息记录对象,用来保存主题名、分区索引、键、值和时间戳ProducerRecord<byte[],byte[]> record =new ProducerRecord<byte[],byte[]>("ip login", key, value);//调用 send()方法和回调函数producer.send(myRecord,new Callback() {public void onCompletion (RecordMetadata metadata, Exception e){if (e != null) {e.printStackTrace();} else {System.out.println("The offset of the record we just sent is:" + metadata.offset());}}};

消息记录提交给 send()方法后,实际上该消息记录被放入一个缓冲区的发送队列,然后通过后台线程将其从缓冲区队列中取出并进行发送;发送成功后会触发send方法的回调函数-Callback.

了解同步模式

生产者客户端通过 send()方法实现同步模式发送消息,并返回一个 Future 对象,同时调用get()方法等待 Future 对象,看 send()方法是否发送成功。

  1. 什么场景下使用同步模式
    如果要收集用户访问网页的数据,在写数据到 Kafka 集群代理节点时需要立即知道消息是否写入成功,此时应使用同步模式。
// 将字符串转换成字节数组
byte[] key = "key".getBytes();
byte[] value ="value".getBytes();
// 实例化一个消息记录对象,用来保存主题名、分区索引、键、值和时间戳
ProducerRecord<byte[],byte[]> record = new ProducerRecord<byte[],byte[]>("ip_login",key, value);
//调用 send()函数后,再通过 get()方法等待返回结果
producer.send(record).get();

这里通过调用 Future 接口中的 get()方法等待 Kafka 集群代理节点(Broker)的状态返回如果 Producer 发送消息记录成功了,则返回 RecordMetadata 对象,该对象可用来查看消息记录的偏移量(Offset)。

线程发送消息的步骤

在 Kafka 系统中,为了保证生产者客户端应用程序的独立运行,通常使用线程的方式发送消息。
创建一个简单的生产者应用程序的步骤如下。
(1)实例化 Properties 类对象,配置生产者应答机制。有以下三个属性是必须设置的。其他属性一般都会有默认值,可以按需添加设置。

  • bootstrap.servers:配置Kafka集群代理节点地址;
  • key.serializer:序列化消息主键;
  • value.serializer:序列化消息数据内容,

(2)根据属性对象实例化一个 KafkaProducer.
(3)通过实例化一个ProducerRecord 对象,将消息记录以“键-值”对的形式进行封装。
(4)通过调用 KafkaProducer 对象中带有回调函数的 send方法发送消息给 Kafka 集群
(5)关闭KafkaProducer 对象,释放连接资源,

生产者用单线程发送消息

/*** 实现一个生产者客户端应用程序.*/
public class JProducer extends Thread {private final Logger LOG = LoggerFactory.getLogger(JProducer.class);/** 配置Kafka连接信息. */public Properties configure() {Properties props = new Properties();props.put("bootstrap.servers", "dn1:9092,dn2:9092,dn3:9092");// 指定Kafka集群地址props.put("acks", "1"); // 设置应答模式, 1表示有一个Kafka代理节点返回结果props.put("retries", 0); // 重试次数props.put("batch.size", 16384); // 批量提交大小props.put("linger.ms", 1); // 延时提交props.put("buffer.memory", 33554432); // 缓冲大小props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); // 序列化主键props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");// 序列化值return props;}public static void main(String[] args) {JProducer producer = new JProducer();producer.start();}/** 实现一个单线程生产者客户端. */public void run() {Producer<String, String> producer = new KafkaProducer<>(configure());// 发送100条JSON格式的数据for (int i = 0; i < 100; i++) {// 封装JSON格式JSONObject json = new JSONObject();json.put("id", i);json.put("ip", "192.168.0." + i);json.put("date", new Date().toString());String k = "key" + i;// 异步发送producer.send(new ProducerRecord<String, String>("test_kafka_game_x", k, json.toJSONString()), new Callback() {public void onCompletion(RecordMetadata metadata, Exception e) {if (e != null) {LOG.error("Send error, msg is " + e.getMessage());} else {LOG.info("The offset of the record we just sent is: " + metadata.offset());}}});}try {sleep(3000);// 间隔3秒} catch (InterruptedException e) {LOG.error("Interrupted thread error, msg is " + e.getMessage());}producer.close();// 关闭生产者对象}
}

这里的主题只有一个分区和一个副本,所以,发送的所有消息会写入同一个分区中
如果希望发送完消息后获取一些返回信息(比如获取消息的偏移量、分区索引值、提交的时间戳等),则可以通过回调函数 CallBack 返回的 RecordMetadata 对象来实现。
由于 Kafka 系统的生产者对象是线程安全的,所以,可通过增加生产者对象的线程数来提高 Kafka 系统的吞吐量。

生产者用多线程发送消息

public class JProducerThread extends Thread {// 创建一个日志对象private final Logger LOG = LoggerFactory.getLogger(JProducerThread.class);// 声明最大线程数private final static int MAX_THREAD_SIZE = 6;/** 配置Kafka连接信息. */public Properties configure() {Properties props = new Properties();props.put("bootstrap.servers", "dn1:9092,dn2:9092,dn3:9092");// 指定Kafka集群地址props.put("acks", "1"); // 设置应答模式, 1表示有一个Kafka代理节点返回结果props.put("retries", 0); // 重试次数props.put("batch.size", 16384); // 批量提交大小props.put("linger.ms", 1); // 延时提交props.put("buffer.memory", 33554432); // 缓冲大小props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); // 序列化主键props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");// 序列化值props.put("partitioner.class", "org.smartloli.kafka.game.x.book_4.JPartitioner");// 指定自定义分区类return props;}public static void main(String[] args) {// 创建一个固定线程数量的线程池ExecutorService executorService = Executors.newFixedThreadPool(MAX_THREAD_SIZE);// 提交任务executorService.submit(new JProducerThread());// 关闭线程池executorService.shutdown();}/** 实现一个单线程生产者客户端. */public void run() {Producer<String, String> producer = new KafkaProducer<>(configure());// 发送100条JSON格式的数据for (int i = 0; i < 10; i++) {// 封装JSON格式JSONObject json = new JSONObject();json.put("id", i);json.put("ip", "192.168.0." + i);json.put("date", new Date().toString());String k = "key" + i;// 异步发送producer.send(new ProducerRecord<String, String>("ip_login_rt", k, json.toJSONString()), new Callback() {public void onCompletion(RecordMetadata metadata, Exception e) {if (e != null) {LOG.error("Send error, msg is " + e.getMessage());} else {LOG.info("The offset of the record we just sent is: " + metadata.offset());}}});}try {sleep(3000);// 间隔3秒} catch (InterruptedException e) {LOG.error("Interrupted thread error, msg is " + e.getMessage());}producer.close();// 关闭生产者对象}}

配置生产者属性

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

保存对象的各个属性一序列化

序列化一个对象

在分布式环境下,无论哪种格式的数据都会被分解成二进制,以便存储在文件中或者在网络上传输。
序列化就是,将对象以一连串的字节进行描述,用来解决对象在进行读写操作时所引发的问题。
序列化可以将对象的状态写成数据流,并进行网络传输或者保存在文件或数据库中,在需要时再把该数据流读取出来,重新构造一个相同的对象。

  1. 为什么需要序列化
    在传统的企业应用中,不同的组件分布在不同的系统和网络中。如果两个组件之间想要进行通信,那么它们之间必须有数据转换机制。实现这个过程需要遵照一个协议来传输对象,这意味着,接收端需要知道发送端所使用的协议才能重新构建对象,以此来保证两个组件之间的通信是安全的。
public class JObjectSerial implements Serializable {private static Logger LOG = LoggerFactory.getLogger(JObjectSerial.class);/*** 序列化版本ID.*/private static final long serialVersionUID = 1L;public byte id = 1; // 用户IDpublic byte money = 100; // 充值金额/** 实例化入口函数. */public static void main(String[] args) {try {FileOutputStream fos = new FileOutputStream("/tmp/salary.out"); // 实例化一个输出流对象ObjectOutputStream oos = new ObjectOutputStream(fos);// 实例化一个对象输出流JObjectSerial jos = new JObjectSerial(); // 实例化序列化类oos.writeObject(jos); // 写入对象oos.flush(); // 刷新数据流oos.close();// 关闭连接} catch (Exception e) {LOG.error("Serial has error, msg is " + e.getMessage());// 打印异常信息}}
}

序列化对象的存储格式

在这里插入图片描述

自己实现 序列化的步骤

在这里插入图片描述
如果使用原生的序列化方式,则需要将传输的内容拼接成字符串或转成字符数组,抑或是其他类型,这样在实现代码时就会比较麻烦。而 Kafka 为了解决这种问题,提供了序列化的接口,让用户可以自定义对象的序列化方式,来完成对象的传输。
以下实例将演示生产者客户端应用程序中序列化的用法,利用 Serializable 接口来序列化对象。

1. 创建序列化对象
/*** 声明一个序列化类.* * @author smartloli.**         Created by Apr 30, 2018*/
public class JSalarySerial implements Serializable {/*** 序列化版本ID.*/private static final long serialVersionUID = 1L;private String id;// 用户IDprivate String salary;// 金额public String getId() {return id;}public void setId(String id) {this.id = id;}public String getSalary() {return salary;}public void setSalary(String salary) {this.salary = salary;}// 打印对象属性值@Overridepublic String toString() {return "JSalarySerial [id=" + id + ", salary=" + salary + "]";}}
2. 编写序列化工具类
/*** 封装一个序列化的工具类.* * @author smartloli.**         Created by Apr 30, 2018*/
public class SerializeUtils {/** 实现序列化. */public static byte[] serialize(Object object) {try {return object.toString().getBytes("UTF8");// 返回字节数组} catch (Exception e) {e.printStackTrace(); // 抛出异常信息}return null;}/** 实现反序列化. */public static <T> Object deserialize(byte[] bytes) {try {return new String(bytes, "UTF8");// 反序列化} catch (Exception e) {e.printStackTrace();}return null;}}
3. 编写自定义序列化逻辑代码
/*** 自定义序列化实现.* * @author smartloli.**         Created by Apr 30, 2018*/
public class JSalarySeralizer implements Serializer<JSalarySerial> {@Overridepublic void configure(Map<String, ?> configs, boolean isKey) {}/** 实现自定义序列化. */@Overridepublic byte[] serialize(String topic, JSalarySerial data) {return SerializeUtils.serialize(data);}@Overridepublic void close() {}}
4. 编写生产者应用程序
/*** 自定义序列化, 发送消息给Kafka.* * @author smartloli.**         Created by Apr 30, 2018*/
public class JProducerSerial extends Thread {private static Logger LOG = LoggerFactory.getLogger(JProducerSerial.class);/** 配置Kafka连接信息. */public Properties configure() {Properties props = new Properties();props.put("bootstrap.servers", "dn1:9092,dn2:9092,dn3:9092");// 指定Kafka集群地址props.put("acks", "1"); // 设置应答模式, 1表示有一个Kafka代理节点返回结果props.put("retries", 0); // 重试次数props.put("batch.size", 16384); // 批量提交大小props.put("linger.ms", 1); // 延时提交props.put("buffer.memory", 33554432); // 缓冲大小props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); // 序列化主键props.put("value.serializer", "org.smartloli.kafka.game.x.book_4.serialization.JSalarySeralizer");// 自定义序列化值return props;}public static void main(String[] args) {JProducerSerial producer = new JProducerSerial();producer.start();}/** 实现一个单线程生产者客户端. */public void run() {Producer<String, JSalarySerial> producer = new KafkaProducer<>(configure());JSalarySerial jss = new JSalarySerial();jss.setId("2018");jss.setSalary("100");producer.send(new ProducerRecord<String, JSalarySerial>("test_topic_ser_des", "key", jss), new Callback() {public void onCompletion(RecordMetadata metadata, Exception e) {if (e != null) {LOG.error("Send error, msg is " + e.getMessage());} else {LOG.info("The offset of the record we just sent is: " + metadata.offset());}}});try {sleep(3000);// 间隔3秒} catch (InterruptedException e) {LOG.error("Interrupted thread error, msg is " + e.getMessage());}producer.close();// 关闭生产者对象}
}

在这里插入图片描述

自定义主题分区

在这里插入图片描述

编写自定义主题分区算法

/*** 实现一个自定义分区类.** @author smartloli.**         Created by Apr 30, 2018*/
public class JPartitioner implements Partitioner {@Overridepublic void configure(Map<String, ?> configs) {}/** 实现Kafka主题分区索引算法. */@Overridepublic int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {int partition = 0;String k = (String) key;partition = Math.abs(k.hashCode()) % cluster.partitionCountForTopic(topic);return partition;}@Overridepublic void close() {}}

演示自定义分区的作用

/*** 实现一个生产者客户端应用程序.* * @author smartloli.**         Created by Apr 27, 2018*/
public class JProducerThread extends Thread {// 创建一个日志对象private final Logger LOG = LoggerFactory.getLogger(JProducerThread.class);// 声明最大线程数private final static int MAX_THREAD_SIZE = 6;/** 配置Kafka连接信息. */public Properties configure() {Properties props = new Properties();props.put("bootstrap.servers", "dn1:9092,dn2:9092,dn3:9092");// 指定Kafka集群地址props.put("acks", "1"); // 设置应答模式, 1表示有一个Kafka代理节点返回结果props.put("retries", 0); // 重试次数props.put("batch.size", 16384); // 批量提交大小props.put("linger.ms", 1); // 延时提交props.put("buffer.memory", 33554432); // 缓冲大小props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); // 序列化主键props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");// 序列化值props.put("partitioner.class", "org.smartloli.kafka.game.x.book_4.JPartitioner");// 指定自定义分区类return props;}public static void main(String[] args) {// 创建一个固定线程数量的线程池ExecutorService executorService = Executors.newFixedThreadPool(MAX_THREAD_SIZE);// 提交任务executorService.submit(new JProducerThread());// 关闭线程池executorService.shutdown();}/** 实现一个单线程生产者客户端. */public void run() {Producer<String, String> producer = new KafkaProducer<>(configure());// 发送100条JSON格式的数据for (int i = 0; i < 10; i++) {// 封装JSON格式JSONObject json = new JSONObject();json.put("id", i);json.put("ip", "192.168.0." + i);json.put("date", new Date().toString());String k = "key" + i;// 异步发送producer.send(new ProducerRecord<String, String>("ip_login_rt", k, json.toJSONString()), new Callback() {public void onCompletion(RecordMetadata metadata, Exception e) {if (e != null) {LOG.error("Send error, msg is " + e.getMessage());} else {LOG.info("The offset of the record we just sent is: " + metadata.offset());}}});}try {sleep(3000);// 间隔3秒} catch (InterruptedException e) {LOG.error("Interrupted thread error, msg is " + e.getMessage());}producer.close();// 关闭生产者对象}}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/666503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

13.2 Web与Servlet进阶(❤❤)

13.2 Web与Servlet进阶 1. 请求与响应1.1 URL与URI1.2 HTTP请求的结构1. 结构2.后端获取访问工具类型:getHeader().toLowerCase方法1.3 响应的结构1. 结构2. 响应常见状态码3. 后端设置响应参数4. 响应的ContentType作用1.4 请求转发与响应重定向应用1. 请求转发:getRequestDis…

Mysql运维篇(四) MySQL常用命令

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。如有侵权&#xff0c;请留言&#xff0c;我及时删除&#xff01; 一、MySQL命令速查表 https://www.cnblogs.com/pyng/p/15560059.html Mysql DBA运维命令大全 - 墨…

【机器学习、深度学习和强化学习原理】

目录 机器学习、深度学习和强化学习都是人工智能的重要领域&#xff0c;它们的代码原理与实现有所不同。机器学习是一种通过训练模型来从数据中学习规律和模式的技术。其代码实现通常包括以下步骤&#xff1a;深度学习是一种模仿人脑神经网络的算法&#xff0c;通过多层神经网络…

批量注册与自动下单:探索速卖通跨境智能系统的操作方法

速卖通跨境智能系统是一款功能强大的软件&#xff0c;可以帮助用户批量注册速卖通买家号、绑定地址、加购加心愿单以及自动下单等任务。 该软件具有以下优势&#xff1a; 强大的指纹系统&#xff1a;采用最新的反指纹技术&#xff0c;可以设置与代理IP相对应的语言和时区&…

调试prplmesh指南

前提&#xff1a;安装ubuntu虚拟机 本文使用的linux发行版本&#xff1a;Ubuntu 20.04.6 LTS 1 安装依赖 安装相关依赖包 sudo apt-get update && sudo apt-get upgrade sudo apt-get install vim git unzip curl binutils cmake gcc bison curl flex gcovr binuti…

Java swing——创建对话框JDialog

之前我们讲了怎么建立一个简易的窗口&#xff0c;链接&#xff1a;http://t.csdnimg.cn/l7QSs&#xff0c;接下来继续讲解窗口的进阶。 对话框 上一篇文章中我们讲到了JFrame是一种顶层容器&#xff0c;本文接下来介绍其余的顶层容器。 跟JFrame一样&#xff0c;&#xff0c;这…

C/C++ 回调函数 callback 异步编程

一、C语言的回调函数 1.小试牛刀 #include <iostream> using namespace std; #include <memory> #include <stdlib.h>int add(int a, int b) {return a b; }void test01() {// 函数指针可以指向任何类型的函数&#xff0c;只要函数的参数列表和返回值类型…

如何结合ChatGPT生成个人魔法咒语词库

3.6.1 ChatGPT辅助力AI绘画 3.6.1.1 给定主题让ChatGPT直接描述 上面给了一个简易主题演示一下&#xff0c;这是完全我没有细化的提问&#xff0c;然后把直接把这些关键词组合在一起。 关键词&#xff1a; 黄山的美景&#xff0c;生机勃勃&#xff0c;湛蓝天空&#xff0c;青…

厕所革命与可持续发展的“九牧方案”

人类文明的历史&#xff0c;就是厕所的革命史&#xff0c;小小的厕所里&#xff0c;承载着大故事。 2015 年&#xff0c;印度一个名叫娜尔的女孩&#xff0c;因为丈夫不愿意在家盖厕所&#xff0c;向法庭提出了离婚申请&#xff0c;由此引发了全印度“无厕所&#xff0c;无新娘…

从零开始 TensorRT(2)Python 篇:原生 API 构建网络

前言 学习资料&#xff1a; TensorRT 源码示例 官方文档&#xff1a;Working With TensorRT Using The Python API 官方文档&#xff1a;TensorRT Python API 官方文档&#xff1a;CUDA Python B站视频&#xff1a;TensorRT 教程 | 基于 8.6.1 版本 B站视频配套代码 cookbook …

OSG帧渲染,如何实现自定义动画效果

看到这个标题,老司机可能会想到OSG动画相关的内容,比如osg::AnimationPath类和osg::AnimationPathCallback类,这些动画类,可以实现按照一定的插值方式,生成路径,物体对象按照生成的路径或者预先指定的路径来完成相应的动作的动画。 路径动画有三种动画模式,分别为单摆环…

幻兽帕鲁服务器游戏版本怎么升级更新?

哈喽大家好&#xff0c;我是咕噜美乐蒂&#xff0c;很高兴又见面啦&#xff01; 幻兽帕鲁&#xff08;Monster Hunter: World&#xff09;是一款热门的多人在线游戏&#xff0c;玩家可以在服务器上与其他玩家一起探险、狩猎怪物。为了保持游戏的平衡性和提供更好的游戏体验&am…

【百度Apollo】探索创新之路:深入了解Apollo开放平台

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《linux深造日志》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! ⛳️ 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下…

docker部署RedisCluster集群简单介绍

本文介绍自己建立一个redis-cluster集群的实践&#xff0c;三主三从&#xff0c;使用docker搭建。 其实搭建很简单&#xff0c;就是建立6个容器&#xff0c;每个容器配置不同的端口号&#xff0c;其他的都是一样的。 假设建立6个端口号分别为 5555到5560。 配置文件 port 5…

redis相关问题

面试官&#xff1a;什么是缓存穿透 ? 怎么解决 ? 候选人&#xff1a; 嗯~~&#xff0c;我想一下 缓存穿透是指查询一个一定不存在的数据&#xff0c;如果从存储层查不到数据则不写入缓存&#xff0c;这将导致这个不存在的数据每次请求都要到 DB 去查询&#xff0c;可能导致 D…

树莓派突然不能ssh远程连接的踩坑记录及解决方案

在家研究树莓派&#xff0c;远程连接树莓派吃了不少苦&#xff0c;总是一些意想不到的问题出现&#xff0c;明明昨天还能远程连接&#xff0c;今天又不能了。经过一系列排查&#xff0c;终于锁定&#xff1a; 因为我之前设置的树莓派的静态ip地址&#xff0c;但是可能因为是家…

ai创作软件有哪些?这5个软件了解一下

ai创作软件有哪些&#xff1f;随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;在各个领域都展现出了惊人的实力。特别是在内容创作领域&#xff0c;AI技术已经成为了助力创作者们提高效率、释放创意的得力助手。今天&#xff0c;我们将为大家介绍五款AI创作…

开源模型应用落地-业务优化篇(四)

一、前言 经过线程池优化、请求排队和服务实例水平扩容等措施,整个AI服务链路的性能得到了显著地提升。但是,作为追求卓越的大家,绝不会止步于此。我们的目标是在降低成本和提高效率方面不断努力,追求最佳结果。如果你们在实施AI项目方面有经验,那一定会对GPU服务器的高昂…

软件工程(最简式总结)

目录 第一章:概述 1.软件危机的表现原因 2.常见的软件开发方法包括&#xff1a; 3.软件工程基本原则 4.软件工程三要素 5.设计模式的分类 6.针对变换型数据流设计步骤 7.针对事务型数据流设计步骤 第二章&#xff1a;软件过程 1.软件生命周期 2.软件过程模型 &…

视觉惯性SLAM系列——ORB-SLAM3的重定位线程(Relocalization Thread)(九)

ORB-SLAM3的重定位线程&#xff08;Relocalization Thread&#xff09;&#xff08;九&#xff09; 重定位线程&#xff08;Relocalization Thread&#xff09;在ORB-SLAM3系统中扮演着关键的角色&#xff0c;尤其是在跟踪丢失的情况下。这个线程的主要任务是在相机失去当前轨迹…