代理模式(结构型模式)

news2024/12/23 18:29:18

目录

1、概述

2、结构

2.1、角色分类

2.2、类图

3、静态代理

3.1、案例类图

3.2、案例代码

4、JDK 动态代理

4.1、案例代码

4.2、底层原理

4.3、执行流程说明

5、CGLib 动态代理

5.1、案例代码

6、三种代理的对比

6.1、JDK代理和CGLib代理

6.2、动态代理和静态代理

7、优缺点

7.1、优点

7.2、缺点

8、使用场景

8.1、远程(Remote)代理

8.2、防火墙(Firewall)代理

8.3、保护(Project or Access)代理

1、概述

由于某些原因需要给某对象提供一个代理以控制该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的媒介。

代理模式也叫做委托模式,它是一项基本设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应用中,代理模式可以提供非常好的访问控制。

Java 中的代理按照生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则在 Java 运行时动态生成,程序结束时内存会自动释放。动态代理又分为 JDK 动态代理和 CGLib 动态代理。

2、结构

2.1、角色分类

代理(Proxy)模式分为三种角色,分别如下:

(1)抽象主题(Subject)角色:通过接口或抽象类声明真实主题和代理对象实现的业务方法。

(2)具体主题(Real Subject)角色:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象,也叫做被委托角色、被代理角色。

(3)代理(Proxy)角色:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

2.2、类图

3、静态代理

静态代理是由程序创建或者特定工具自动生成源代码,在程序运行前,代理类的.class文件已经存在。

案例:

房东出租房屋,将出租和收回房子的事情都交给中介来完成,中介出租房屋后可以赚取到中介费。

3.1、案例类图

3.2、案例代码
/**
  * @ClassName IHouseOwner
  * @Description 出租房屋的接口-抽象主题角色
  * @Author chengjunyu
  * @Date 2022/12/8
  * @Version V1.0
  */
 public interface IHouseOwner {
 ​
     /**
      * @Description: 出租房屋
      * @Author: chengjunyu
      */
     void rentHouse();
 ​
     /**
      * @Description: 收回房屋
      * @Author: chengjunyu
      */
     void takeBackHouse();
 }
 ​
 ​
 /**
  * @ClassName HouseOwner
  * @Description 房东-具体主题角色
  * @Author chengjunyu
  * @Date 2022/12/8
  * @Version V1.0
  */
 public class HouseOwner implements IHouseOwner {
 ​
     /**
      * @Description: 出租房屋
      * @Author: chengjunyu
      */
     @Override
     public void rentHouse() {
         System.out.println("房屋闲置,房东要出租房屋");
     }
 ​
     /**
      * @Description: 收回房屋
      * @Author: chengjunyu
      */
     @Override
     public void takeBackHouse() {
         System.out.println("房屋到期,房东要收回房屋");
     }
 }
 ​
 ​
 /**
  * @ClassName HouseOwnerProxy
  * @Description 代理角色
  * @Author chengjunyu
  * @Date 2022/12/8
  * @Version V1.0
  */
 public class HouseOwnerProxy implements IHouseOwner {
 ​
     private IHouseOwner houseOwner;
 ​
     public HouseOwnerProxy(IHouseOwner houseOwner) {
         this.houseOwner = houseOwner;
     }
     /**
      * @Description: 出租房屋
      * @Author: chengjunyu
      */
     @Override
     public void rentHouse() {
         this.houseOwner.rentHouse();
         System.out.println("帮房东出租了房子,挣取佣金2000元");
     }
 ​
     /**
      * @Description: 收回房屋
      * @Author: chengjunyu
      */
     @Override
     public void takeBackHouse() {
         this.houseOwner.takeBackHouse();
         System.out.println("帮房东收回了房子");
     }
 }
 ​
 ​
 /**
  * @ClassName Client
  * @Description 业务场景
  * @Author chengjunyu
  * @Date 2022/12/8
  * @Version V1.0
  */
 public class Client {
     public static void main(String[] args) {
         //创建抽象主题角色
         IHouseOwner houseOwner = new HouseOwner();
         //创建代理类,通过代理类来完成具体主题角色需要做的事情
         HouseOwnerProxy proxy = new HouseOwnerProxy(houseOwner);
         proxy.rentHouse();
         proxy.takeBackHouse();
     }
 }

执行结果:

房屋闲置,房东要出租房屋

帮房东出租了房子,挣取佣金2000元

房屋到期,房东要收回房屋

帮房东收回了房子

从上面代码中可以看出业务场景直接访问的是 IHouseOwner 类对象,也就是说 IHouseOwner 作为访问对象和目标对象的中介,同时也对 rentHouse() 和 takeBackHouse() 方法做出了增强。

4、JDK 动态代理

JDK 动态代理要求目标对象实现一个接口。

Java 中提供了一个动态代理类 Proxy,Proxy 不是静态代理中所说的代理对象的类,而是提供了一个创建代理对象的静态方法(newProxyInstance())来获取代理对象。

4.1、案例代码

在JDK动态代理中,不再需要去手动创建一个代理类来完成目标对象的执行方法。

/**
  * @ClassName IHouseOwner
  * @Description 抽象主题角色
  * @Author chengjunyu
  * @Date 2022/12/8
  * @Version V1.0
  */
 public interface IHouseOwner {
     /**
      * @Description: 出租房屋
      * @Author: chengjunyu
      */
     void rentHouse();
 ​
     /**
      * @Description: 收回房屋
      * @Author: chengjunyu
      */
     void takeBackHouse();
 }
 ​
 /**
  * @ClassName HouseOwner
  * @Description 房东-具体主题角色
  * @Author chengjunyu
  * @Date 2022/12/8
  * @Version V1.0
  */
 public class HouseOwner implements IHouseOwner {
     /**
      * @Description: 出租房屋
      * @Author: chengjunyu
      */
     @Override
     public void rentHouse() {
         System.out.println("房屋闲置,房东要出租房屋");
     }
 ​
     /**
      * @Description: 收回房屋
      * @Author: chengjunyu
      */
     @Override
     public void takeBackHouse() {
         System.out.println("房屋到期,房东要收回房屋");
     }
 }
 ​
 /**
  * @ClassName ProxyFactory
  * @Description  获取代理对象的工厂类
  * @Author chengjunyu
  * @Date 2022/12/8
  * @Version V1.0
  */
 public class ProxyFactory {
     //1、声明目标对象
     private HouseOwner houseOwner = new HouseOwner();
 ​
     /**
      * @Description: 获取代理对象的方法
      * @Author: chengjunyu
      */
     public IHouseOwner getHouseOwner() {
         //2、返回代理对象
         /*
          * ClassLoader loader: 类加载器,用于加载代理类(程序运行中动态的在内存中生成的类),通过目标对象获取
          * Class<?>[] interfaces:代理类实现的接口的字节码对象
          * InvocationHandler h:代理对象的调用处理程序
          */
         IHouseOwner proxyObject = (IHouseOwner) Proxy.newProxyInstance(
                 houseOwner.getClass().getClassLoader(),
                 houseOwner.getClass().getInterfaces(),
                 //匿名内部类,重写 invoke 方法
                 new InvocationHandler() {
                     /*
                      * Object proxy:代理对象,和 proxyObject 是同一个对象,在 invoke 方法中基本上不使用
                      * Method method:对接口中的方法进行封装的 method 对象
                      * Objects[] args:调用方法的实际参数
                      */
                     @Override
                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                         Object object =  method.invoke(houseOwner, args);
                         if ("rentHouse".equals(method.getName()))  {
                             System.out.println("帮房东出租了房子,挣取佣金2000元");
                         }
                         if ("takeBackHouse".equals(method.getName())) {
                             System.out.println("帮房东收回了房子");
                         }
                         return object;
                     }
                 }
         );
         return proxyObject;
     }
 }
 ​
 /**
  * @ClassName Client
  * @Description 业务场景
  * @Author chengjunyu
  * @Date 2022/12/8
  * @Version V1.0
  */
 public class Client {
     public static void main(String[] args) {
         //获取代理对象
         //1、创建代理对象工厂
         ProxyFactory proxyFactory = new ProxyFactory();
         //2、使用factory对象的方法获取代理对象
         IHouseOwner houseOwner = proxyFactory.getHouseOwner();
         //调用租房方法
         houseOwner.rentHouse();
         //调用收回房屋方法
         houseOwner.takeBackHouse();
     }
 }

执行结果:

房屋闲置,房东要出租房屋

帮房东出租了房子,挣取佣金2000元

房屋到期,房东要收回房屋

帮房东收回了房子

4.2、底层原理

在学习JDK动态代理的底层原理之前,可以先考虑一个问题:ProxyFactory是代理类吗?

ProxyFactory 不是代理模式中所说的代理类,代理类是程序在运行过程中动态的在内存中生成的类。

我么可以通过阿里巴巴开源的 Java 诊断工具(Arthas【阿尔萨斯】)查看代理类的结构,操作步骤如下:

1、运行指令 java -jar arhtas-boot.jar 启动 Arthas;

2、运行指令 jad com.sun.proxy.$Proxy0 获取该类源码;

3、优化源码,优化后的源码如下:

 
package com.sun.proxy;
 ​
 import com.design.pattern.proxy.jdkProxy.IHouseOwner;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.UndeclaredThrowableException;
 ​
 public final class $Proxy0 extends Proxy implements IHouseOwner {
     private static Method m1;
     private static Method m2;
 ​
     //构造方法提供了有参构造,参数为 InvocationHandler 对象,赋值给了父类 Proxy
     public $Proxy0(InvocationHandler invocationHandler) {
         super(invocationHandler);
     }
 ​
     static {
         //获取IHouseOwner字节码对象后获取方法
         m1 = Class.forName("com.design.pattern.proxy.jdkProxy.IHouseOwner").getMethod("rentHouse", new Class[0]);
         m2 = Class.forName("com.design.pattern.proxy.jdkProxy.IHouseOwner").getMethod("takeBackHouse", new Class[0]);
     }
 ​
     //调用了 InvocationHandler 的子实现类对象的 invoke 方法
     public final void rentHouse() {
         //此处h即为InvocationHandler对象,this表示本类,m1为rentHouse方法
         this.h.invoke(this, m1, null);
     }
 ​
     public final void takeBackHouse() {
         this.h.invoke(this, m2, null);
     }
 }
 ​
 //父类Proxy
 public class Proxy {
     protected InvocationHandler invocationHandler;
 }

从上面的类中,我们可以看到以下几个信息:

1、代理类($Proxy0)实现了 IHouseOwner ,这也就印证了我们之前说的真实类和代理类实现同样的接口;

2、代理类($Proxy0)将我们提供了的匿名内部类对象传递给了父类。

4.3、执行流程说明

根据JDK动态代理示例代码和底层原理代码,我们可以分析出JDK动态代理的执行流程如下:

1、在业务场景中通过代理对象调用 rentHouse() 和 takeBackHouse();

2、根据多态的特性,执行的是代理类($Proxy0)中的 rentHouse() 和 takeBackHouse();

3、代理类($Proxy0)中的 rentHouse() 方法和 takeBackHouse() 方法中又调用了 InvocationHandler 接口的子实现类对象的 invoke() 方法;

4、invoke 方法通过反射执行了真实对象所属类(HouseOwner)中的rentHouse() 和 takeBackHouse()。

5、CGLib 动态代理

有时候目标对象只是一个单独的对象,并没有实现接口,这个时候就可以使用CGLIB代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。

CGLIB动态代理基于继承来实现代理,所以无法对 final 类、private 和 static 方法实现代理。

5.1、案例代码

同样是上面的案例,这里我们使用CGLIB代理实现。

如果没有定义IHouseOwner接口,只定义了HouseOwner(房屋所有者类),很显然JDK动态代理就无法使用了,因为JDK动态代理要求必须定义接口,对接口进行代理。

CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。

CGLIB是第三方提供的包,所以在Spring框架下需要引入jar包的maven坐标。

<!-- https://mvnrepository.com/artifact/cglib/cglib -->

<dependency>

<groupId>cglib</groupId>

<artifactId>cglib</artifactId>

<version>3.3.0</version>

</dependency>

代码如下:

 
/**
  * @ClassName HouseOwner
  * @Description 房东-具体主题角色
  * @Author chengjunyu
  * @Date 2022/12/8
  * @Version V1.0
  */
 public class HouseOwner {
 ​
     /**
      * @Description: 出租房屋
      * @Author: chengjunyu
      */
     public void rentHouse() {
         System.out.println("房屋闲置,房东要出租房屋");
     }
 ​
     /**
      * @Description: 收回房屋
      * @Author: chengjunyu
      */
     public void takeBackHouse() {
         System.out.println("房屋到期,房东要收回房屋");
     }
 }
 ​
 ​
 /**
  * @ClassName ProxyFactory
  * @Description 代理工厂类
  * @Author chengjunyu
  * @Date 2022/12/10
  * @Version V1.0
  */
 public class ProxyFactory implements MethodInterceptor {
 ​
     private HouseOwner houseOwner = new HouseOwner();
 ​
     public HouseOwner getProxyObject() {
         //1、创建Enhancer对象,类似于JDK动态代理中的Proxy类
         Enhancer enhancer = new Enhancer();
         //2、设置父类的字节码对象,指定父类
         enhancer.setSuperclass(HouseOwner.class);
         //3、设置回调函数
         enhancer.setCallback(this);
         //4、创建代理对象,即目标类对象的子对象
         HouseOwner proxyObject = (HouseOwner) enhancer.create();
         return proxyObject;
     }
 ​
     /**
      * @Description: 方法所属类的回调函数
      * @Author: chengjunyu
      */
     @Override
     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
         System.out.println("调用了回调函数");
         Object object = method.invoke(this.houseOwner, objects);
         return object;
     }
 }
 ​
 ​
 /**
  * @ClassName Client
  * @Description 业务场景类
  * @Author chengjunyu
  * @Date 2022/12/10
  * @Version V1.0
  */
 public class Client {
     public static void main(String[] args) {
         //创建代理工厂对象
         ProxyFactory factory = new ProxyFactory();
         //获取代理对象
         HouseOwner proxyObject = factory.getProxyObject();
         //调用代理对象中的方法
         proxyObject.rentHouse();
         proxyObject.takeBackHouse();
     }
 }

6、三种代理的对比

6.1、JDK代理和CGLib代理

使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在JDK1.6之前比使用反射效率要高。唯一需要注意的是,CGLib不能对声明为final的类或者方法进行处理,因为CGLib原理是动态生成被代理的子类。

在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib的代理效率低一些,但是到JDK1.8的时候,JDK代理效率高于CGLib代理,所以如果有接口使用JDK动态代理,如果没有接口则使用CGLib代理。

6.2、动态代理和静态代理

动态代理和静态代理相比较,最大的好处就是接口中声明的所有方法都被转移到调用处理器一个集中的方法处理(InvocationHandler.invoke)。这样,在接口中方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。

如果接口增加了一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现这个方法,增加了代理维护的复杂度,而动态代理不会出现这样的问题。

7、优缺点

7.1、优点

(1)代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;

(2)代理对象可以扩展目标对象的功能;

(3)代理模式能够将客户端与目标对象分离,在一定程度上降低了系统的耦合度。

7.2、缺点

(1)增加了系统的复杂度。

8、使用场景

8.1、远程(Remote)代理

本地服务通过网络请求远程服务。为了实现本地到远程的通信,我们需要实现网络通信,处理其中可能的异常,为良好的代码设计和可维护性,我们将网络通信部分隐藏起来,只暴露给本地服务一个端口,通过该接口即可访问远程服务提供的功能,而不必过多关心通信部分的细节。

8.2、防火墙(Firewall)代理

当你将浏览器配置称为使用代理功能时,防火墙就将你的浏览器的请求转给互联网;当互联网返回响应时,代理服务器再把它转给你的浏览器。

8.3、保护(Project or Access)代理

控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的访问权限。

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

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

相关文章

【Latex排版小记录】latex设置两端对齐

Latex排版的时候遇到了公式/英文过长超出来的情况 解决办法&#xff1a;在\begin{document}里面增加\begin{sloppypar} \begin{document} \begin{sloppypar}\end{sloppypar} \end{document}

Spring Boot - 利用MDC(Mapped Diagnostic Context)实现轻量级同步/异步日志追踪

文章目录 Pre什么是MDC&#xff08;Mapped Diagnostic Context&#xff09;Slf4j 和 MDC基础工程工程结构POMlogback-spring.xmlapplication.yml同步方式方式一&#xff1a; 拦截器自定义日志拦截器添加拦截器 方式二&#xff1a; 自定义注解 AOP自定义注解 TraceLog切面 测试…

Java实现优先级队列(堆)

前言 在学习完二叉树的相关知识后&#xff0c;我们对数据结构有了更多的认识&#xff0c;本文将介绍到优先级队列(堆&#xff09; 1.优先级队列 1.1概念 前面介绍过队列&#xff0c;队列是一种先进先出(FIFO)的数据结构&#xff0c;但有些情况下&#xff0c;操作的数据可能…

【机器学习】探究Q-Learning通过学习最优策略来解决AI序列决策问题

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

学会 Python 后可以做什么副业?

近年来&#xff0c;Python凭借其简洁易入门的特点受到越来越多人群的青睐。 当然这不仅仅是针对程序员来说&#xff0c;对于一些学生、职场人士也是如此。 Python为什么会大受欢迎呢&#xff1f;因为Python还被大家称为“胶水语言&#xff0c;它适用于网站、桌面应用开发、[自…

蓝牙耳机哪个牌子好用?力荐五款实力超群机型,快收藏!

​随着蓝牙耳机的普及&#xff0c;越来越多的年轻人开始追求这种无线的便利。市场上品牌众多&#xff0c;款式多样&#xff0c;选择起来确实让人眼花缭乱。我整理了一份蓝牙耳机品牌排行榜前十名&#xff0c;希望能为你提供一些参考&#xff0c;帮助你找到心仪的耳机。 一、如何…

C语言堆区内存管理

一、C语言编译的内存分配 二、堆区空间的分配 1、malloc函数 功能&#xff1a;从堆区分配内存 #include <stdlib.h> void *malloc(unsigned int size)//size 分配内存的字节数2、free函数 功能&#xff1a;释放内存 #include <stdlib.h> void free(void *ptr)…

L2-045 堆宝塔 - java

L2-045 堆宝塔 Java (javac) 时间限制 500 ms 内存限制 256 MB 其他编译器 时间限制 400 ms 内存限制 64 MB 栈限制 8192 KB 题目描述&#xff1a; 堆宝塔游戏是让小朋友根据抓到的彩虹圈的直径大小&#xff0c;按照从大到小的顺序堆起宝塔。但彩虹圈不一定是按照直径的大小顺…

Java NIO,高效操作I/O流的必备技能

Java IO在工作中其实不常用到&#xff0c;更别提NIO了。但NIO却是高效操作I/O流的必备技能&#xff0c;如顶级开源项目Kafka、Netty、RocketMQ等都采用了NIO技术&#xff0c;NIO也是大多数面试官必考的体系知识。虽然骨头有点难啃&#xff0c;但还是要慢慢消耗知识、学以致用哈…

Spring核心容器总结

2.2 核心容器总结 2.2.1 容器相关 BeanFactory是IoC容器的顶层接口&#xff0c;初始化BeanFactory对象时&#xff0c;加载的bean延迟加载 ApplicationContext接口是Spring容器的核心接口&#xff0c;初始化时bean立即加载 ApplicationContext接口提供基础的bean操作相关方法…

CTFHUB-技能树-Web前置技能-文件上传(无验证,JS前端验证,前端验证)

CTFHUB-技能树-Web前置技能-文件上传&#xff08;无验证&#xff0c;JS前端验证&#xff0c;前端验证—.htaccess&#xff09; 文章目录 CTFHUB-技能树-Web前置技能-文件上传&#xff08;无验证&#xff0c;JS前端验证&#xff0c;前端验证—.htaccess&#xff09;文件上传无验…

基于 LSTM 模型的古诗词自动生成算法实现及系统实现

近年来&#xff0c;研究者在利用循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;进行古诗自动生成方面取得了显著的效果。但 RNN 存在梯度问题&#xff0c;导致处理时间跨度较长的序列时 RNN 并不具备长期记忆存储功能。随后&#xff0c;出现的基…

(十一)C++自制植物大战僵尸游戏客户端更新实现

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/cFP3z 更新检查 游戏启动后会下载服务器中的版本号然后与本地版本号进行对比&#xff0c;如果本地版本号小于服务器版本号就会弹出更新提示。让用户选择是否更新客户端。 在弹出的更新对话框中有显示最新版本更新的内容…

全球化背景下的海外社媒营销战略:趋势洞察与策略调整

随着全球化的不断深入&#xff0c;企业在海外市场的竞争愈发激烈。在这一背景下&#xff0c;海外社交媒体平台成为了企业品牌推广和营销的重要渠道。本文Nox聚星将和大家探讨全球化背景下&#xff0c;企业如何利用海外社交媒体平台进行品牌推广和营销&#xff0c;并分析企业如何…

Git分布式版本控制系统——在IDEA中使用Git(一)

一、在IDEA中配置Git 本质上还是使用的本地安装的Git软件&#xff0c;所以需要在IDEA中配置Git 打开IDEA的设置页面&#xff0c;按照下图操作 二、在IDEA中使用Git获取仓库 1、本地初始化仓库 2、从远程仓库克隆 方法一&#xff1a; 方法二&#xff1a; 三、.gitignore文件…

#陶晶驰串口屏使用

1.陶晶驰串口屏输入要连接的wifi信息实现 &#xff08;1&#xff09;选择文本控件 &#xff08;2&#xff09;给文本控件配置输入键盘&#xff0c;id代表用户名&#xff0c;password代表wifi密码&#xff08;注意wifi的频段需要为2.4GHz&#xff09; &#xff08;3&#xff0…

k8s之etcd

1.特点&#xff1a; etcd 是云原生架构中重要的基础组件。有如下特点&#xff1a; 简单&#xff1a;安装配置简单&#xff0c;而且提供了 HTTP API 进行交互&#xff0c;使用也很简单键值对存储&#xff1a;将数据存储在分层组织的目录中&#xff0c;如同在标准文件系统中监…

RAG (Retrieval Augmented Generation) 结合 LlamaIndex、Elasticsearch 和 Mistral

作者&#xff1a;Srikanth Manvi 在这篇文章中&#xff0c;我们将讨论如何使用 RAG 技术&#xff08;检索增强生成&#xff09;和 Elasticsearch 作为向量数据库来实现问答体验。我们将使用 LlamaIndex 和本地运行的 Mistral LLM。 在开始之前&#xff0c;我们将先了解一些术…

性能工具之emqtt-bench BenchMark 测试示例

文章目录 一、前言二、典型压测场景三、机器准备四、典型压测场景1、并发连接2、消息吞吐量测试2.1 1 对 1&#xff08;示例&#xff09;2.2 多对1&#xff08;示例&#xff09;2.3 1对多&#xff08;示例&#xff09; 五、遇到的问题client(): EXIT for {shutdown,eaddrnotava…

OpenStack镜像管理与制作

一、OpenStack镜像服务 1、什么是镜像 镜像通常是指一系列文件或一个磁盘驱动器的精确副本。虚拟机所使用的虚拟磁盘&#xff0c;实际上是一种特殊格式的镜像文件。云环境下尤其需要镜像。镜像就是一个模板&#xff0c;类似于VMware的虚拟机模板&#xff0c;其预先安装基本的…