Redis连接池

        本次实现的Redis连接池是一个单例且多线程安全的连接池。

        主要实现的功能为:读取配置,将配置中对应建立redis连接并加入到连接池中,然后从连接池中取出连接使用。每当配置进行修改,重新往池子中加入连接。

  • 通用类

        实现一些基础都会使用的接口,定义宏等。

        有些类是利用RAII思想,构造时获得对象,初始化锁,加锁等,析构时释放对象,释放锁资源,释放锁等。

#pragma once
#include <iostream>
#include "../../hiredis/hiredis/hiredis.h"
#include <pthread.h>
#include <cstring>
#include <algorithm>
#include <map>

#define SAFE_DELETE(x) {if (x != nullptr){ delete x, (x) = nullptr; }}

class AutoReply
{
public:
  AutoReply(redisReply* reply)
  {
    reply_ = nullptr;
    if(reply)
    {
      reply_ = reply;
    }
  }

  ~AutoReply()
  {
    if(reply_)
    {
      freeReplyObject(reply_);
      reply_ = nullptr;

    }
  }

  redisReply* get()
  {
    return reply_;
  }

  bool isErr()
  {
    if (!reply_)
      return true;
    if (reply_->type == REDIS_REPLY_ERROR)
    {
      std::cout << "reply error " << reply_->str << std::endl;
      return true;
    }

    if (reply_->type == REDIS_REPLY_STATUS)
    {
      std::string tmp_str;
       //大写转小写
      //std::transform(reply_->str, reply_->str + strlen(reply_->str), tmp_str.begin(), tolower);
      for(int i = 0; i < strlen(reply_->str); ++i)
      {
        char c = reply_->str[i];
        if(isupper(reply_->str[i]))
        {
          c = tolower(c);
        }
        tmp_str += tolower(c);
      }
      //std::cout << "iserr str" << tmp_str.c_str() << std::endl;
      if (strcmp(tmp_str.c_str(), "ok") != 0)
      {
        std::cout << "reply statue not ok " << tmp_str << "replystr: " << reply_->str << std::endl;
        return true;
      }
    }
    return false;
  }

    bool isPingErr()
  {
    if (!reply_)
      return true;
    if (reply_->type == REDIS_REPLY_ERROR)
    {
      std::cout << "reply error " << reply_->str << std::endl;
      return true;
    }

    if (reply_->type == REDIS_REPLY_STATUS)
    {
      std::string tmp_str;
    //大写转小写
      std::transform(reply_->str, reply_->str + strlen(reply_->str), tmp_str.begin(), tolower);
      //std::cout << "isPingErr str" << tmp_str.c_str() << std::endl;
      if (strcmp(tmp_str.c_str(), "pong") != 0)
      {
        std::cout << "reply statue not pong " << tmp_str << std::endl;
        return true;
      }
    }
    return false;
  }

  AutoReply(const AutoReply& ar)
  {
    reply_ = ar.reply_;
  }
private:
  AutoReply operator=(const AutoReply& ar);
  redisReply* reply_;
};


class qMutex
{
public:
  qMutex()
  {
    pthread_mutex_init(&mt_, nullptr);
  }

  ~qMutex()
  {
    pthread_mutex_destroy(&mt_);
  }

  void lock()
  {
    pthread_mutex_lock(&mt_);
  }

  void unlock()
  {
    pthread_mutex_unlock(&mt_);
  }


private:
  qMutex(const qMutex& qmt);
  qMutex operator=(const qMutex& qmt);
  pthread_mutex_t mt_;
};

class AutoMutex
{
public:
  AutoMutex(qMutex& mutex):mutex_(mutex)
  {
    mutex_.lock();
  }
  ~AutoMutex()
  {
    mutex_.unlock();
  }
private:
  //AutoMutex(const AutoMutex& am);
  AutoMutex& operator=(const AutoMutex& am);
  qMutex& mutex_;
};
  • 操作redis类

        主要是连接redis,操作redis,有密码需要使用auth命令验证密码。

#pragma once

#include "common.h"

enum ConnectState {
  CONNECTSTATE_NONE,
  CONNECTSTATE_CONN,//已连接
  CONNECTSTATE_UNCONN,//断开连接
};

class redisConn{
public:
  redisConn(size_t id, std::string ip = "127.0.0.1", size_t port = 6379, std::string passwd = "123456");
  ~redisConn();

  bool connect();
  bool connectUnblock();

  bool disConnect();

  bool isRun();
  
  bool auth();

  bool ping();
  bool reconnect();
  //断线重连
  void resetReconnectTime() \
  {
    AutoMutex mt(locker_); 
    reconnect_times_ = 0; 
  }
  bool keepAlive();

  bool set(const char* key, const char* val);
  std::string get(const char* key);
private:
  size_t id_;

  std::string ip_;
  size_t port_;
  std::string passwd_;
  ConnectState state_;//连接状态
  size_t reconnect_times_;//连接次数

  redisContext* context_;
  redisReply* reply_;

  qMutex locker_;//为了保证线程安全
};
#include "redisConn.h"

redisConn::redisConn(size_t id, std::string ip, size_t port, std::string passwd)
{
  id_ = id;
  ip_ = ip;
  port_ = port;
  passwd_ = passwd;
  state_ = CONNECTSTATE_NONE;
  context_ = nullptr;
}
redisConn::~redisConn()
{
  disConnect();
}

bool redisConn::connect()
{
  if (context_ != nullptr)
  {
    redisFree(context_);
    context_ = nullptr;      
  }
  context_ = redisConnect(ip_.c_str(), port_);
  if (!context_)
  {
    std::cerr << "connect fail" << std::endl;
    return false;
  }
  if(context_->err)
  {
    std::cerr << "connect err " << context_->errstr << std::endl;
    redisFree(context_);
    context_ = nullptr;
    return false;
  }
  state_ = CONNECTSTATE_CONN;
  std::cout << "connect succ" << std::endl;

  //验证密码
  auth();
  return true;
}

bool redisConn::connectUnblock()
{
  if (context_)
  {
    redisFree(context_);
    context_ = nullptr;
  }
  
  context_ = redisConnectNonBlock(ip_.c_str(), port_);
  if(!context_)
  {
    std::cout << "connect fail" << std::endl;
    return false;
  }

  if(context_->err)
  {
    std::cout << "connect err " << context_->errstr << std::endl;
    redisFree(context_);
    context_ = nullptr;
    return false; 
  }
  state_ = CONNECTSTATE_CONN;
  std::cout << "conn no block succ" << std::endl;

  //验证密码
  //auth();
  return true;
}

bool redisConn::disConnect()
{
  AutoMutex mt(locker_);
  if (context_)
  {
    redisFree(context_);
    context_ = nullptr;
    //SAFE_DELETE(context_);
  }
  state_ = CONNECTSTATE_UNCONN;
  std::cout << "disConnect succ" << std::endl;
  return true;
}

bool redisConn::isRun()
{
  AutoMutex mt(locker_);
  return state_ == CONNECTSTATE_CONN;
}

bool redisConn::auth()
{
  AutoMutex mt(locker_);
  AutoReply reply = (redisReply*)redisCommand(context_, "auth %s", passwd_.c_str());
  if (reply.isErr())
    return false;
  std::cout << "auth succ" << std::endl;
  return true;
}

bool redisConn::ping()
{
  //检查服务器是否在运行
  AutoMutex mt(locker_);
  AutoReply reply = (redisReply*)redisCommand(context_, "ping");
  if(reply.isPingErr())
    return false;
  return true;
}

bool redisConn::reconnect()
{
  if(!connect() && !auth())
  {
    std::cout << "connect un block fail" << std::endl;
    return false;
  }
  state_ = CONNECTSTATE_CONN;
  std::cout << "reconnect succ" << std::endl;
  return true;
}

bool redisConn::keepAlive()
{
  if (state_ == CONNECTSTATE_CONN)
  {
    if(!ping())
    {
      if(!reconnect())
      {
        std::cout << "断线" << ip_ << ":" << port_ << std::endl;
        state_ = CONNECTSTATE_UNCONN;
        return false;

      }
      return true;
    }
    else if(state_ == CONNECTSTATE_UNCONN)
    {
      //断线重连
      if (reconnect_times_ < 10)
      {
        reconnect_times_++;
        if(reconnect())
        {
          reconnect_times_ = 0;
          state_ = CONNECTSTATE_CONN;
          return true;
        }
      }
    }
  }

  return false;
}


bool redisConn::set(const char* key, const char* val)
{
  AutoMutex mt(locker_);
  if(!context_)
    return false;
  AutoReply reply = (redisReply*)redisCommand(context_, "set %s %s", key, val);
  if(reply.isErr())
  {
    return false;
  }
  return true;
}

std::string redisConn::get(const char* key)
{
  AutoMutex mt(locker_);
  if(!context_)
    return "";
  AutoReply reply = (redisReply*)redisCommand(context_, "get %s", key);
  if(reply.isErr())
  {
    return "";
  }

  return reply.get()->str ? reply.get()->str : "";

}
  • 单例泛型类

        对应的连接池和读取配置只需要一个对象,可以实现为单例。

         使用这个单例:

  1. 继承该单例类(继承单例接口),并且设为友元(由于构造,拷贝构造,赋值重载运算符设为私有,单例类需要使用)。
  2. 设为友元。

        两种方式使用方法不同,看下面例子。

#pragma once

#include "common.h"

template<class T>
class Singleton
{
public:
    static T* getMe()
    {
        if(single_ == nullptr)
        {
            mt_.lock();
            if(single_ == nullptr)
            {
                single_ = new T;
            }
            mt_.unlock();
        }
        return single_;
    }

    static void delMe()
    {
        if(single_)
        {
            SAFE_DELETE(single_);
        }
    }
protected:
    //需要写出protected,不然继承的构造无法调用
    Singleton()
    {
    }
    ~Singleton()
    {
        delMe();
    }
private:
    //外部不允许赋值喝拷贝构造
    Singleton(const Singleton& );
    const Singleton& operator=(const Singleton& );
    static T* single_;
    static qMutex mt_;
};

template<class T>
T* Singleton<T>::single_ = nullptr;

template<class T>
qMutex Singleton<T>::mt_;

        使用实例:

 

  •  连接池类和读配置类

        连接池主要实现了将连接加入到连接池中,和从连接池中拿出连接。

        读取配置,配置主要为读取唯一标识——服务器ip——redis端口。如果需要和可以读取密码等。

        连接池中的每一个连接有一个唯一标识,通过标识来获取对一个连接,标识可以是对应表冈功能(比如:如果是游戏中的排行榜,可以是不同的排行榜,也可以是不同玩家,每个玩家对应一个redis)。

#pragma once 

#include "qSingleton.h"
#include "redisConn.h"
#include <fstream>
#include <cstring>


struct ConnectInfo
{
  ConnectInfo()
  {
    state_ = 0;
  }

  redisConn* con_;
  int state_;
};

class redisPool : public Singleton<redisPool>
{
  friend class Singleton<redisPool>;
public:

  bool addClient(size_t id, std::string ip, size_t port);
  bool delClient();
  bool keepAlive();

  bool updateCfgConn();
  redisConn* getClientByID(size_t id);
private:
  redisPool();
  ~redisPool();
  redisPool(const redisPool& );
  const redisPool& operator=(const redisPool&);

  ConnectInfo* getConnectInfo(size_t id);
  typedef std::map<size_t, ConnectInfo> ConnectInfoT;
  ConnectInfoT connect_;

};

struct RedisConnCfg
{
  RedisConnCfg()
  {
    id_ = 0;
    port_ = 0;
  }

  void print()
  {
      std::cout << id_ << ":" << ip_ << ":" << port_ << std::endl;
  }
  size_t id_;
  std::string ip_;
  size_t port_;
};

class RedisConnCfgMgr : public Singleton<RedisConnCfgMgr>
{
  friend class Singleton<RedisConnCfgMgr>;
public:
  bool loadCfg();
  const std::vector<RedisConnCfg>& getRedisConnCfg(){ return cfg_; }
private:
  RedisConnCfgMgr();
  ~RedisConnCfgMgr();
  RedisConnCfgMgr(const RedisConnCfgMgr&);
  const RedisConnCfgMgr& operator=(const RedisConnCfgMgr& c);

  std::vector<RedisConnCfg> cfg_;
};
#include "redisPool.h"

redisPool::redisPool()
{

}

redisPool::~redisPool()
{
  delClient();
}

bool redisPool::addClient(size_t id, std::string ip, size_t port)
{
  ConnectInfo* pInfo = getConnectInfo(id);
  if(!pInfo)
  {
    pInfo = &connect_[id];
    pInfo->con_ = new redisConn(id, ip.c_str(), port, "123456");
    pInfo->state_ = 1;
  }

  if(!pInfo->con_->isRun())
  {
    if(pInfo->con_->connect() && pInfo->con_->auth())
    {
      return true;
    }
    else 
    {
      pInfo->con_->disConnect();
      return false;
    }
  }

  return true;
}

bool redisPool::delClient()
{
  ConnectInfoT::iterator it = connect_.begin();
  for(; it != connect_.end(); ++it)
  {
    it->second.con_->disConnect();
    it->second.state_ = 0;
    SAFE_DELETE(it->second.con_);
    it->second.con_ = nullptr;
  }  
  connect_.clear();
}

redisConn* redisPool::getClientByID(size_t id)
{
  ConnectInfoT::iterator it = connect_.find(id);
  if(it != connect_.end())
  {
    return it->second.con_;
  }
  return nullptr;
}

ConnectInfo* redisPool::getConnectInfo(size_t id)
{
  ConnectInfoT::iterator it = connect_.find(id);
  if(it != connect_.end())
  {
    return &it->second;
  }
  return nullptr;
}

bool redisPool::keepAlive()
{
  ConnectInfoT::iterator it = connect_.begin();
  for(; it != connect_.end(); ++it)
  {
    it->second.con_->keepAlive();
  }
}

bool redisPool::updateCfgConn()
{
  //重置连接状态
  ConnectInfoT::iterator it = connect_.begin();
  for(; it != connect_.end(); ++it)
  {
    it->second.state_ = 0;
  }
  //更新配置中的redis到redisPool中
  const std::vector<RedisConnCfg>& cfg_vec = RedisConnCfgMgr::getMe()->getRedisConnCfg();
  for(int i = 0; i < cfg_vec.size(); ++i)
  {
    //在连接池中进行重连
    const RedisConnCfg& tmp = cfg_vec[i];
    it = connect_.find(tmp.id_);
    if(it != connect_.end())
    {
      it->second.con_->resetReconnectTime();
      it->second.con_->keepAlive();
      it->second.state_ = 1;
    }
    else
    {
      //不在进行添加并连接redis
      addClient(tmp.id_, tmp.ip_, tmp.port_);
    }
  }

  //删除掉未连接的redis
  it = connect_.begin();
  for(; it != connect_.end(); )
  {
    if(!it->second.state_)
    {
      it->second.con_->disConnect();
      SAFE_DELETE(it->second.con_);
      connect_.erase(it++);
    }
    else
    {
      it++;
    }
  }

  return true;
}

RedisConnCfgMgr::RedisConnCfgMgr()
{

}

RedisConnCfgMgr::~RedisConnCfgMgr()
{

}

bool RedisConnCfgMgr::loadCfg()
{
  std::ifstream infile("redisCfg.txt");
  if(!infile.is_open())
  {
    std::cout << "file open fail" << std::endl;
    return false;
  }

  std::vector<std::string> cfg_vec;
  char buf[1024];
  while(infile.getline(buf, sizeof(buf)))
  {
    //std::cout << buf << std::endl;
    char* p = strtok(buf, "-");
    while(p)
    {
      cfg_vec.push_back(p);
      p = strtok(NULL, "-");
    }
    if(cfg_vec.size() >= 3)
    {
      RedisConnCfg tmp;
      tmp.id_ = atoi(cfg_vec[0].c_str());
      tmp.ip_ = cfg_vec[1];
      tmp.port_ = atoi(cfg_vec[2].c_str());
      tmp.print();
      cfg_.push_back(tmp);
    }
    cfg_vec.clear();
  }
  infile.close();
  return true;
}

        测试:

#include "redisPool.h"


void* setHandleFunc(void* arg)
{
  //std::cout << pthread_self() << std::endl;
  RedisConnCfg* tmp = (RedisConnCfg*)arg;
  redisConn* conn =  redisPool::getMe()->getClientByID(tmp->id_);
  if(conn)
  {
    char kbuf[64];
    char vbuf[64];
    snprintf(kbuf, sizeof(kbuf), "k%d", tmp->id_);
    snprintf(vbuf, sizeof(vbuf), "v%ld", pthread_self());
    //std::cout << kbuf << "---" << vbuf << std::endl;
    conn->set(kbuf, vbuf);
  }
  return nullptr;
}

void* getHandleFunc(void* arg)
{
  //std::cout << pthread_self() << std::endl;
  RedisConnCfg* tmp = (RedisConnCfg*)arg;
  redisConn* conn =  redisPool::getMe()->getClientByID(tmp->id_);
  if(conn)
  {
    char kbuf[64];
    snprintf(kbuf, sizeof(kbuf), "k%d", tmp->id_);
    std::string str = conn->get(kbuf);
    std::cout << kbuf << ":" << str << std::endl;
  }
  return nullptr;
}

int main()
{
  //加载配置
  RedisConnCfgMgr::getMe()->loadCfg();
  redisPool::getMe()->updateCfgConn();

  std::vector<pthread_t> pt_vec;
  const std::vector<RedisConnCfg>& cfg_vec = RedisConnCfgMgr::getMe()->getRedisConnCfg();
  for(int i = 0; i < cfg_vec.size(); ++i)
  {
    //创建线程
    pthread_t pid = 0;
    pthread_create(&pid, nullptr, setHandleFunc, (void*)&cfg_vec[i]);
    pt_vec.push_back(pid);
  }

  for(int i = 0; i < pt_vec.size(); ++i)
  {
    pthread_detach(pt_vec[i]);
  }

  pt_vec.clear();
  for(int i = 0; i < cfg_vec.size(); ++i)
  {
    //创建线程
    pthread_t pid = 0;
    pthread_create(&pid, nullptr, getHandleFunc, (void*)&cfg_vec[i]);
    pt_vec.push_back(pid);
  }

  for(int i = 0; i < pt_vec.size(); ++i)
  {
    pthread_detach(pt_vec[i]);
  }

  while(1)
  {}

  return 0;
}

int main()
{
  RedisConnCfgMgr rm;
  rm.loadCfg();

  return 0;
}

         Makefile编译文件:

        主要需要下载hiredis库。

main:main.cpp redisPool.cpp redisConn.cpp
	g++ -g $^ -o $@ -std=c++11 -lhiredis  -lpthread


.PHONY:clean
clean:
	rm -rf main

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

记一次cms代码审计

000&#xff1a;前言 记录一次小型cms代码审计 001&#xff1a;任意文件删除 由于代码繁杂&#xff0c;不再一一展示 /app/controller/kindeditor.class.php 关键漏洞代码 public function delete() {$path ROOT_PATH.$_GET[pic];unlink($path);$flash M("flash&qu…

冶金比例换向阀放大器

冶金比例换向阀是一种重要的液压控制元件&#xff0c;它通过BEUEC比例放大器驱动调节阀门开度来精确控制流量&#xff0c;进而控制压力或速度。在液压系统中&#xff0c;比例阀的接线设备是确保其正常工作和实现精确控制的关键部分。比例阀的接线方式主要包括电流输入和电压输入…

Unix、Linux 软件包管理快速入门对照

Linux&#xff08;RHEL、Ubuntu&#xff09;或者 Unix&#xff08;macOS、FreeBSD&#xff09;可以参看下表快速入门: 命令功能/系统Darwin (macOS)FreeBSDDebian/UbuntuRHEL&#xff08;dnf yum&#xff09;搜索和查找软件包brew searchpkg searchapt listyum list查看软件包…

基于python flask+pyecharts实现的中药数据可视化大屏,实现基于Apriori算法的药品功效关系的关联规则

背景 在中医药学中&#xff0c;物品与功效之间的关联关系研究是一个非常重要的课题。传统中医药学中&#xff0c;很多药物都具有多种功效&#xff0c;而且不同药物对同一种疾病可能具有不同的疗效。因此&#xff0c;挖掘物品与功效之间的关联关系&#xff0c;可以帮助我们更加…

WIN系统 -> 以太网未识别的网络问题

1.方法1 2. 3. 根据诊断提示解决问题。 方法2. 右键以太网属性

Java mybatis

nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for proper 注意 mapper 中&#xff0c;insert into values 中 values 字段和 Java 对象保持一直

快速排序的实现

目录 一、递归 1、霍尔法&#xff1a; 2、挖坑法&#xff1a; 3、前后指针法&#xff1a; 二、非递归 三、完整代码&#xff1a; 基本思想&#xff1a; 先取这个待排序元素序列中的某一个元素最为key值&#xff0c;然后通过这个key值将这个序列分为两边&#xff0c;一边小…

vue-2

vue-cli的安装 vue-cli是一个脚手架工具&#xff0c;它集成了诸多前端技术&#xff0c;包括但不仅限于&#xff1a; webpack、 babel、eslint、http-proxy-middleware、typescript、css pre-prosessor、css module、… 这些工具&#xff0c;他们大部分都要依赖两个东西&…

访问限制

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在类的内部可以定义属性和方法&#xff0c;而在类的外部则可以直接调用属性或方法来操作数据&#xff0c;从而隐藏了类内部的复杂逻辑。但是Python并…

HALCON-从入门到入门-花式读取图片的N种方式

1.废话 很多时候我们不止读取一张图片-读取文件夹下的多张 甚至我们可能想在多个文件夹中读取多张图片。 再变态点我们想再任意若干路径下读取任意若干张图片。 没关系&#xff0c;这些halcon开发人员都替我们考虑到了。 只需要使用下面一个算子 list_files (, files, Fi…

【CMake】CMake入门(五)打包安装程序 使用CMake管理库 打包调试版和发行版

本篇文章不是新手入门教学文章&#xff0c;主要是记录笔者个人的学习笔记 CMake入门&#xff08;五&#xff09; 一、打包二、使用CMake管理库三、打包调试版和发行版 一、打包 发布程序可以有多种形式&#xff0c;比如安装包、压缩包、源文件等。CMake也提供了打包程序cpack可…

电脑数据恢复,6个方法,恢复文件很简单!

“我在使用电脑时&#xff0c;一个不小心就误删了部分重要数据&#xff0c;现在想恢复电脑数据却不知怎么操作了&#xff0c;希望大家帮帮我&#xff01;” 在数字化时代&#xff0c;电脑数据不仅是工作和学习的重要载体&#xff0c;更是我们生活中珍贵的记忆和情感的存储地。然…

二叉树创建和遍历(及相关OJ题)

个人主页 &#xff1a;敲上瘾-CSDN博客二叉树介绍&#xff1a;二叉树(详解)-CSDN博客 目录 一、二叉树的创建 二、二叉树的遍历 1.前序遍历 2.中序遍历 3.后序遍历 4.层序遍历 三、相关计算 1.总节点个数计算 2.叶子节点个数计算 3.深度计算 一、二叉树的创建 关于…

❤机器学习正则化算法的总结。耗时10个小时完成。❤

❤纯 干 货~❤ 目录 纯干货 1、L1 正则化&#xff08;Lasso 正则化&#xff09; 2、L2 正则化&#xff08;岭正则化&#xff09; 3、弹性网络正则化&#xff08;Elastic Net 正则化&#xff09; 4、Dropout 正则化&#xff08;用于神经网络&#xff09; 5、贝叶斯Rid…

风力发电机常见故障分析

风力发电机常见故障分析 风力发电机是风电机组中的核心部件&#xff0c;其运行的可靠性和稳定性对整个风电系统的发电效率至关重要。然而&#xff0c;由于复杂的机械结构和长期暴露在严酷环境中&#xff0c;风力发电机在运行过程中可能会出现各种故障。本文将详细介绍风力发电…

【Linux】深入理解文件操作:从C语言接口到系统调用与缓冲区管理

文章目录 前言&#xff1a;1. 铺垫2. 重新使用C文件接口&#xff1a;对比一下重定向2.1. 什么叫当前路径&#xff1f;2.2. 写入文件2.3. 读文件2.4. 程序默认打开的文件流2.5. 输出2.6. 输入 3. 系统调用提供的文件接口3.1. open 打开文件3.2. open函数返回值 4. 缓冲区问题总结…

MongoDB~索引使用与优化

Study by&#xff1a; https://docs.mongoing.com/indexeshttps://www.cnblogs.com/Neeo/articles/14325130.html#%E5%85%B6%E4%BB%96%E7%B4%A2%E5%BC%95 作用 如果你把数据库类比为一本书&#xff0c;那书的具体内容是数据&#xff0c;书的目录就是索引&#xff0c;所以索引…

【随笔】Git 实战篇 -- 开心 commit 之后,发现有一处bug还需要改,只能 reset 撤销然后再次提交 -- git reset --(四十三)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

RabbitMQ小结

MQ分类 Acitvemq kafka 优点&#xff1a;性能好&#xff0c;吞吐量高百万级&#xff0c;分布式&#xff0c;消息有序 缺点&#xff1a;单机超过64分区&#xff0c;cpu会飙高&#xff0c;消费失败不支持重试 &#xff0c; Rocket 阿里的mq产品 优点&#xff1a;单机吞吐量也…

如何赋予LLM多模态能力(MLLM)

基本概念 多模态大型语言模型&#xff08;MLLMs&#xff09;是人工智能领域的一项前沿技术&#xff0c;旨在设计能够理解和生成跨越多种形式数据输入&#xff08;如文本和图像&#xff09;内容的模型。 链接文本和视觉模态&#xff1a;MLLMs能够整合文本和视觉数据源的信息。…