activemq--MASTER SLAVE+BROKER CLUSTER 实践(二)

2023-05-16


鱼与熊掌兼得法-完美解决方案


我们知道:


  • master/slave模式下,消息会被逐个复制
  • 而cluster模式下,请求会被自动派发


那么可不可以把两者集成起来呢?答案是有的,网上所谓的独创。。。统统是错的!!对,因为我全试验过了我敢这么説,写得都是些个啥呀。。。一个个还COPY不走样,全错了而且。


我这个才叫独创,来看原理。




MASTER SLAVE+BROKER CLUSTER的搭建


  • 我们使用ZK搭建MASTER SLAVE
  • 我们使用BROKER CLUSTER把两个“组”合并在一起


先来看下面的集群规划




MASTER SLAVE+BROKER CLUSTER的搭建-Group1的配置


由于这涉及到两个组6个ActiveMQ的实例配置,如果把6个配置全写出来是完全没有必要的,因此我就把配置分成两组来写吧。每个组的配置对于其组内各个节点都为一致的,除去那些个端口号。Group1的配置(保持6个实例中brokerName全部为一致)

[html] view plain copy print ?
  1. <networkConnectors>   
  2.      <networkConnector uri="static:(tcp://ymklinux:61619,tcp://ymklinux:61620,tcp://ymklinux:61621)" duplex=“false"/>    
  3.  </networkConnectors>   
<networkConnectors> 
	 <networkConnector uri="static:(tcp://ymklinux:61619,tcp://ymklinux:61620,tcp://ymklinux:61621)" duplex=“false"/>  
 </networkConnectors> 

可以看到这边的broker cluster的配置是用来确保每一台都可以和Group2中的各个节点保持同步


[html] view plain copy print ?
  1. <persistenceAdapter>    
  2.     <replicatedLevelDB     
  3.                           directory="${activemq.data}/leveldb"    
  4.                           replicas="3"    
  5.                           bind="tcp://0.0.0.0:0"    
  6.                           zkAddress="192.168.0.101:2181"    
  7.                           zkPassword=""    
  8.                           hostname="ymklinux"  
  9.                           sync="local_disk"  
  10.               zkPath="/activemq/leveldb-stores/group1"   
  11.                 />    
  12. </persistenceAdapter>  
<persistenceAdapter>  
	<replicatedLevelDB   
              	          directory="${activemq.data}/leveldb"  
              	          replicas="3"  
               	          bind="tcp://0.0.0.0:0"  
              	          zkAddress="192.168.0.101:2181"  
              	          zkPassword=""  
              	          hostname="ymklinux"
              	          sync="local_disk"
	          zkPath="/activemq/leveldb-stores/group1" 
              	/>  
</persistenceAdapter>


MASTER SLAVE+BROKER CLUSTER的搭建-Group2的配置


[html] view plain copy print ?
  1. <networkConnectors>   
  2.     <networkConnector uri="static:(tcp://ymklinux:61616,tcp://ymklinux:61617,tcp://ymklinux:61618)" duplex=“false"/>    
  3. </networkConnectors>  
 <networkConnectors> 
 	<networkConnector uri="static:(tcp://ymklinux:61616,tcp://ymklinux:61617,tcp://ymklinux:61618)" duplex=“false"/>  
 </networkConnectors>

可以看到这边的broker cluster的配置是用来确保每一台都可以和Group1中的各个节点保持同步


[html] view plain copy print ?
  1. <persistenceAdapter>    
  2.     <replicatedLevelDB     
  3.                           directory="${activemq.data}/leveldb"    
  4.                           replicas="3"    
  5.                           bind="tcp://0.0.0.0:0"    
  6.                           zkAddress="192.168.0.101:2182"    
  7.                           zkPassword=""    
  8.                           hostname="ymklinux"  
  9.                           sync="local_disk"  
  10.               zkPath="/activemq/leveldb-stores/group2"   
  11.                 />    
  12. </persistenceAdapter>  
<persistenceAdapter>  
	<replicatedLevelDB   
              	          directory="${activemq.data}/leveldb"  
              	          replicas="3"  
               	          bind="tcp://0.0.0.0:0"  
              	          zkAddress="192.168.0.101:2182"  
              	          zkPassword=""  
              	          hostname="ymklinux"
              	          sync="local_disk"
	          zkPath="/activemq/leveldb-stores/group2" 
              	/>  
</persistenceAdapter>


MASTER SLAVE+BROKER CLUSTER的搭建-客户端


客户端:

[html] view plain copy print ?
  1. <property name="brokerURL" value="failover:(tcp://192.168.0.101:61616,  
  2.                                       tcp://192.168.0.101:61617,  
  3.                                       tcp://192.168.0.101:61618,  
  4.                                       tcp://192.168.0.101:61619,  
  5.                                       tcp://192.168.0.101:61620,  
  6.                                       tcp://192.168.0.101:61621)" />  
<property name="brokerURL" value="failover:(tcp://192.168.0.101:61616,
                                      tcp://192.168.0.101:61617,
                                      tcp://192.168.0.101:61618,
                                      tcp://192.168.0.101:61619,
                                      tcp://192.168.0.101:61620,
                                      tcp://192.168.0.101:61621)" />


把6台实例全部启动起来(乖乖,好家伙)
把客户端写上全部6台实例(乖乖,好长一陀)





MASTER SLAVE+BROKER CLUSTER的搭建-实验


  • 使用生产端任意发送3条消息。


  • 生产端连上了61616,发送了3条消息,然后我们把61616所属的activemq1的进程直接杀了


  • 然后运行消费端,消费端连上了61618,消费成功3条消息


搞定了!


哈 哈 ,不要急,再重复一遍该实验,来:


  • 先把所有实例进程全部杀掉
  • 把6台实例全部启动起来(乖乖,好家伙)
  • 把客户端写上全部6台实例(乖乖,好长一陀)
  • 使用生产端任意发送3条消息。
  • 生产端连上了61616,发送了3条消息,然后我们把61616所属的activemq1的进程直接杀了
  • 然后运行消费端,消费端连上了61620,控台显示无任何消息消费


哈 哈 ,死惨了。。。作者滚粗。。。


为什么 ?WHY?


所谓的完美方案并不是真正的完美方案-情况A


是的,以上的方案存在着这一漏洞!来看原理!

对于上述情况,当61616宕机后,如果此时请求被failover到了group1中任意一个节点时,此时消费端完全可以拿到因为宕机而未被消费掉的消费。


所谓的完美方案并不是真正的完美方案-情况B


对于上述情况,当61616宕机后,如果此时请求被failover到了group2中任意一个节点时,此时因为group2中并未保存group1中的任何消息,因此只要你的请求被转发到另一个group中,消费端是决无可能去消费本不存在的消息的。这不是有可能而是一定会发生的情况。因为在生产环境中,请求是会被自由随机的派发给不同的节点的。


我为什么要提这个缺点、要否认之前的篇章? 我就是为了让大家感受一下网上COPY复制还是错误信息的博文的严重性,那。。。怎么做是真正完美的方案呢。。。



如何让方案完美




我们来看,如果我们把两个group间可以打通,是不是就可以在上述情况发生时做到failover到group2上时也能消费掉group1中的消息了呢?


怎么做?很简单,其实就是一个参数




duplex=true



什么叫duplex=true?


它就是用于mq节点间双向传输用的一个功能,看下面的例子


这就是duplex的使用方法,我很奇怪,竟然这么多人COPY 那错的1-2篇博文,但是就是没有提这个duplex的值,而且都设的是false。

duplex的作用就是mq节点1收到消息后会变成一个sender把消息发给节点2。

此时客户如果连的是节点2,也可以消息节点1中的消息,因此我给它起了一个比英译中更有现实意义的名称我管它叫“穿透”。

下面来看用“穿透机制”改造的真正完美方案吧。


最终完美解决方案


考虑到消费端可能会发生:


当Group1中有未消费的数据时时,消费端此时被转派到了Group2中的任意一个节点。


因此,在配置时需要进行如下的穿透:


按照这个思路我们在各Group中的各节点中作如下配置即可:

Group1中的配置


[html] view plain copy print ?
  1. <networkConnectors>   
  2.     <networkConnector uri="static:(tcp://ymklinux:61619,tcp://ymklinux:61620,tcp://ymklinux:61621)" duplex="true"/>    
  3. </networkConnectors>   
	 <networkConnectors> 
	 	<networkConnector uri="static:(tcp://ymklinux:61619,tcp://ymklinux:61620,tcp://ymklinux:61621)" duplex="true"/>  
	 </networkConnectors> 



Group2中的配置

[html] view plain copy print ?
  1. <networkConnectors>   
  2.     <networkConnector uri="static:(tcp://ymklinux:61616,tcp://ymklinux:61617,tcp://ymklinux:61618)" duplex="true"/>    
  3.  </networkConnectors>  
 <networkConnectors> 
	 	<networkConnector uri="static:(tcp://ymklinux:61616,tcp://ymklinux:61617,tcp://ymklinux:61618)" duplex="true"/>  
	 </networkConnectors>



因为,你设的是duplex=true,它相当于下面几行的效果:


[html] view plain copy print ?
  1. <!-- 在group1中 -->    
  2. <networkConnector name="group1-broker1" uri="static:(tcp://broker1:61619)" duplex="false" />    
  3.     
  4. <!-- 在group1中 -->    
  5. <networkConnector name="group1-broker2" uri="static:(tcp://broker0:61620)" duplex="false" />    
  6.   
  7. <!-- 在group1中 -->    
  8. <networkConnector name="group1-broker3" uri="static:(tcp://broker0:61621)" duplex="false" />   
  9.   
  10.   
  11. <!-- 在group2中 -->    
  12. <networkConnector name="group2-broker1" uri="static:(tcp://broker1:61616)" duplex="false" />    
  13.     
  14. <!-- 在group2中 -->    
  15. <networkConnector name="group2-broker2" uri="static:(tcp://broker0:61617)" duplex="false" />    
  16.   
  17. <!-- 在group2中 -->    
  18. <networkConnector name="group2-broker3" uri="static:(tcp://broker0:61618)" duplex="false" />   
<!-- 在group1中 -->  
<networkConnector name="group1-broker1" uri="static:(tcp://broker1:61619)" duplex="false" />  
  
<!-- 在group1中 -->  
<networkConnector name="group1-broker2" uri="static:(tcp://broker0:61620)" duplex="false" />  

<!-- 在group1中 -->  
<networkConnector name="group1-broker3" uri="static:(tcp://broker0:61621)" duplex="false" /> 


<!-- 在group2中 -->  
<networkConnector name="group2-broker1" uri="static:(tcp://broker1:61616)" duplex="false" />  
  
<!-- 在group2中 -->  
<networkConnector name="group2-broker2" uri="static:(tcp://broker0:61617)" duplex="false" />  

<!-- 在group2中 -->  
<networkConnector name="group2-broker3" uri="static:(tcp://broker0:61618)" duplex="false" /> 


现在知道为什么要duplex=true了吧!

验证


  1. 先把所有实例进程全部杀掉
  2. 把6台实例全部启动起来(乖乖,好家伙)
  3. 把客户端写上全部6台实例(乖乖,好长一陀)
  4. 使用生产端任意发送3条消息。
  5. 生产端连上了61619(属于Group2),发送了3条消息,然后我们把61619所属的activemq4的进程直接杀了
  6. 然后运行消费端,消费端连上了61616,控台显示消费成功3条消息
大功告成!!!


结束语


从生产环境的高可用性来説,如果需要使用完美解决方案的话我们至少需要以下这些实体机


2台实体机

  • 每台虚出3个子节点来(用VM),供3个MQ实例运行使用,2*3共为6个
  • 并且一台实体机必须承载一个GROUP,同一个GROUP最好不要跨实体机或虚拟
2台实体机
  • 每台虚出3个子节点来(用VM),供3个ZK 节点使用,2*3共为6个
  • 并且一台实体机必须承载一个ZK GROUP,同一个ZK GROUP最好不要跨实体机或虚拟
同一台实体机上不得又安装ZK又安装MQ


上述是ActiveMQ Master Slave + Broker Cluster的最小化配置,为了得到更高的高可用性,建议6个MQ实例间全部需要有物理机承载。


但是,上述情况是用于应对 百万级并发消息的生产环境而言才需要如此大动干戈,对于常规环境笔者建议:


搞两台高配置的VM分散在两台不同的实体机,做MASTER SLAVE即可,当然笔者强烈建议使用ZK做ActiveMQ的Master Slave,ZK可以用3个节点,分别来自3个不同的虚机(不是指在一个VM里启3个不同端口的ZK实例,而是来自于3台虚机),至于3台虚机是否在不同实体机上,这倒不是太大要求,因为在MASTER SLAVE中的ZK只作节点信息、消息同步使用,不会受太多并发访问之苦。

===activemq--MASTER SLAVE+BROKER CLUSTER 实践(三)=====



还有一种集群方式留给大家自己去练习,很简单,就是几个节点间无MASTER SLAVE也只做static Broker Cluster, 用的就是这个“穿透原理”。

比如说,我有5个节点,其中生产连着A和B,消费连着C、D、E,然后在A上对C、D、E做穿透,B也对C、D、E做穿透,客户端只对A和B做failover,这就构成了一个spoker机制。它也可以制作出一个MASTER SLAVE + Broker Cluster的模型来,然后producer对a,b进行监听,而consumer failerover至c,d,e即可,这个架构不难,动一下手就会了。







本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

activemq--MASTER SLAVE+BROKER CLUSTER 实践(二) 的相关文章

  • 如何访问.net中的activemq统计插件

    我正在尝试访问 activemq 统计信息http activemq apache org statisticsplugin html in c 这就是我到目前为止所拥有的 我无法得到消费者的回复 我可以在监控网站上查看队列的计数增加 pu
  • Linux下activemq的安装与安装成功确认

    一 下载 apache activemq 5 14 0 bin tar gz 二 安装 将压缩包拷入linux内 进行解压 tar zxvf apache activemq 5 14 0 bin tar gz 与redis nginx不同的
  • Raspberry ALSA 声音输出/输入从机

    我正在尝试设置一台设备用于播放 另一台设备用于捕获 我的nano etc asound conf有这个 pcm default type asym playback pcm plughw 1 1 capture pcm plughw 1 0
  • 当两个应用程序都使用嵌入式 activemq 时,如何将 Jms 消息从一个 spring-boot 应用程序发送到另一个应用程序

    我有两个 spring boot 应用程序 在接收器应用程序的 Application java 中我有 Bean public JmsListenerContainerFactory
  • ActiveMQ:通过JMX获取连接列表?

    如何获取ActiveMQ OpenWire 连接器的连接列表 JConsole 能够列出连接 但我看不到可以使用哪个 视图 来获取列表 连接的对象名称示例 org apache activemq BrokerName localhost T
  • 是否可以镜像 ActiveMQ 中的单个队列?

    我在生产系统中运行 ActiveMQ 我们的一些队列数量非常多 有些队列数量非常少 我有兴趣镜像低容量队列之一 以便我可以围绕收到的消息构建非正式的监视服务 很遗憾 我能找到的唯一文档似乎暗示镜像队列是全有或全无 您要么为您拥有的每个队列创
  • 找不到元素“beans”的声明

    我有弹簧罐spring 3 2 0 RC1 jar并试图实施Apache ActiveMQ helloWorld给出的教程中的程序here http icodingclub blogspot com 2011 07 introduction
  • Apache Camel AMQP - ActiveMQ AMQP 标头不匹配值 1,期望 0

    我正在尝试创建一个通过 AMQP 与 ActiveMQ 集成的 Apache Camel 应用程序 我一直在按照提供的 骆驼示例 spring jms 项目 它通过标准 TCP 连接 但我已修改为使用我的独立 ActiveMQ 5 8 安装
  • RabbitMQ 和 ActiveMQ 在同一台机器上运行

    出于测试目的 我需要在同一台 Windows 计算机上运行 ActiveMQ 和 RabbitMQ 我已经安装了两者 但无法一起运行它们 我需要停止一项服务才能运行另一项服务 这是我尝试启动运行 ActiveMQ 的 RabbitMQ 时遇
  • 从 activemq 主题获取消息时,logstash 输入速度非常慢

    我已经在logstash中配置了JMS输入来订阅JMS主题消息并将消息推送到弹性搜索 input jms id gt my first jms yaml file gt D softwares logstash 6 4 0 config j
  • 如何在activemq中杀死消费者

    我试图摆脱某个队列中的所有 消费者数量 每当我清除 删除队列时 如果我再次创建同名的队列 消费者的数量仍然保留 即使有 0 条待处理消息 仍然有 6 个消费者 我的问题可能源于我的 java 代码 但没有关闭会话或连接 我已经尝试过重新启动
  • 无法使用 Node.js 连接到 Apache ActiveMQ

    背景 尝试使用 AMQP 1 0 协议通过以下方式连接到我的 Apache ActiveMQ 代理amqp10 https www npmjs com package amqp10 我使用以下代码 改编自自述文件中的原始示例 const A
  • 网络故障后重新连接ActiveMQ服务器

    我们正在使用 ActiveMQ 5 8 0 通过 TCP 将 Java 应用程序连接到另一个系统 请求 回复 与对临时队列的回复同步 与我们的客户端及其相应部分配合良好 但我们不确定如何处理 异常 情况 例如短暂的网络故障 我们正在测试应用
  • ActiveMQ 简单身份验证插件 - 用户管理员无权创建:topic://ActiveMQ.Advisory.Connection

    我修改了我们的 ActiveMQ 配置以使用 ActiveMQ 简单身份验证插件以及具有代理所有权限的单个用户和密码
  • 使用 Gattle 向 ActiveMQ 发布消息

    我一直在使用Gatling将消息发布到 ActiveMq 服务器 我明白了 java lang SecurityException 无效的用户名 null 或空 不过我使用有效的用户名和密码 这是我的测试代码 抛出了异常 任何有关如何解决此
  • 如何从 MQTT 生产并在 ActiveMQ 中作为 MQTT 和 JMS 消费

    我有一个设置 其中消息作为 MQTT 生成到 ActiveMQ 我有两个消费者 一个作为 JMS 另一个作为 MQTT 当我将消息作为 JMS 消息发布到主题 foo 时 我在 JMS 和 MQTT 消费者处都收到消息 但是当我在同一主题上
  • MongoDB 架构设计 - 实时聊天

    我正在启动一个项目 我认为该项目特别适合 MongoDB 因为它提供的速度和可扩展性 我目前感兴趣的模块是与实时聊天有关的 如果我要在传统的 RDBMS 中执行此操作 我会将其分为 频道 一个频道有很多用户 用户 一个用户有一个频道但有多条
  • 主题消息可以在activemq中持久化吗?

    我对 JMS 和 ESB 非常陌生 我使用 activemq 作为 JMS 使用 mule 作为 ESB 当我将消息从一个队列转发到另一个队列时 jms 连接器参数 persistentDelivery 为 true 它会在 activem
  • Apache Camel/ActiveMQ 优先级路由

    我有两个具有相同消费者的 AMQ 队列 第一个队列 Q1 处理 97 的消息 另一个队列 Q2 仅处理 3 问题是 Q2 中的消息需要在排队后立即处理 所以我的问题是 当第二季度有消息可用时 我需要以某种方式暂停第一条路线以吸引其消费者 a
  • 如何使用 C 客户端通过 ActiveMQ 启用 SSL

    我已经配置了 ActiveMQ http activemq apache org http activemq apache org 通过以下方式使用 ssl 上下文RedHat 的 SSL TLS 教程 https access redha

随机推荐

  • Fragment中使用viewLifecycleOwner/getActivity/this

    观察liveData使用生命周期 如图 xff1a 当liveData想在fragment里观察的时候 xff0c 可以使用getActivity this getViewLifecycleOwner getActivity不必说 xff0
  • Android的FileProvider使用解释

    前言 从Android7 0 xff08 N xff09 开始 xff0c 严格执行 StrictMode 模式 xff0c 也就是说 xff0c 将对安全做更严格的校验 不允许在 App 间 xff0c 使用 file 的方式 xff0c
  • STM32G0 串口编程

    利用USB转串口调试 xff0c 烧写单片机程序 1 打开STM32 CubeProgrammer软件设置启动配置 2 让程序从system Flash启动 xff08 1 xff09 勾选 DTR 勾选RTS 取消勾选RTS 2 取消勾选
  • Android使用SystemProperties基础了解

    安卓系统属性是以键值对的形式存在 xff0c 一般放在system prop xff0c build prop xff0c default prop等文件中 这些属性可能是进程状态 xff0c 资源使用情况 xff0c 系统特有属性等等 创
  • 使用Android Studio打包Module成jar包

    现在假设我们想打包一个module成jar包的形式给其它应用调用 xff1a vrservice 1 0 jar 步骤1 在Module项目的build gradle文件中做如下配置 xff1a 生成jar包的配置如下 xff1a def
  • C++无符号整型与有符号整型变量的运算-不简单

    示例分析 xff1a include lt iostream gt include lt stdio h gt struct Result char c char d unsigned char e Result getChar int x
  • C++虚继承下的类大小

    前言 带有虚函数的虚继承的对象模型比较复杂 xff0c 所以单独整理一下 其实关于计算类大小是C 43 43 的一大易错点之一 即便是我在这儿理了半天 xff0c 也不一定就是正确的 xff0c 如果大佬看到本文 xff0c 发现我很多错误
  • 解决android的跑马灯频繁刷新的问题

    先贴一下跑马灯效果的代码 xff0c 这里我是继承的TextView xff1a public class MarqueeTextView extends AppCompatTextView public MarqueeTextView C
  • 关于第一次将STM32与电脑连接情况

    安装了Keil xff08 ARM xff09 版本之后 xff0c 不管是自己编程 xff0c 还是配套的程序运行 我们都想把它下载到STM32芯片里面 xff0c 在板子上运行 这里介绍几种方法 1 用J LINK下载调试 这个工具 x
  • [java语言]——InetAddress类的getByName()方法

    InetAddress 表示互联网协议 xff08 IP xff09 地址 InetAddress getByName 34 www 163 com 34 在给定主机名的情况下确定主机的IP地址 如果参数为null 获得的是本机的IP地址
  • Java中Calendar.DAY_OF_WEEK、DAY_OF_MONTH需要减一的原因

    Java中对日期的处理需要用到Calendar类 xff0c 其中有几个方法在使用时需要新手注意 1 在获取月份时 xff0c Calendar MONTH 43 1 的原因 xff08 Java中Calendar MONTH返回的数值其实
  • C语言学习笔记(三)(头文件的结构顺序)(类型不完整的解决方法)

    目录 前言原则结构推荐结构图方便的结构 类型不完整的原因和解决方法 xff1a 定义了不定长数组结构体的定义放在了头文件里面解决方法1 xff08 当采用使用时包含特定头文件时 xff09 xff1a 解决方法2 xff08 当一个头文件包
  • S2

    int count 61 0 double lat 61 55 8241 double lng 61 137 8347 double radius 61 900 半径 double capHeight 61 2 S2 M PI radius
  • 从STM32F407到AT32F407(一)

    雅特力公司的MCU有着性能超群 xff0c 价格优越的巨大优势 xff0c 缺点是相关资料少一些 xff0c 我们可以充分利用ST的现有资源来开发它 我用雅特力的STM32F437开发板 xff0c 使用原子 stm32f407的开发板自带
  • vue-java分离

    import com google gson Gson import io renren common utils HttpContextUtils import io renren common utils R import org ap
  • java学习

    莫求全 有效努力 定时出结果 架构设计DDD 微服务介绍 https www kancloud cn qingshou aaa1 2667225 https www cnblogs com chencan p 16042197 html h
  • CentOS6.5添加虚拟IP(VIP)

    使用keepalived 实现Nginx高可用时 需要用到这项技术 虚拟ip在高可用中的作用后续再说 今天看看怎么给服务器配置虚拟IP xff0c 其实也就是多分配个IP地址 首先查看一下现有网卡的IP地址 xff0c 用root特权运行下
  • 微服务Spring Cloud例子

    Spring Cloud简介 Spring Cloud是一个基于Spring Boot实现的云应用开发工具 xff0c 为开发者提供了在分布式系统 xff08 配置管理 xff0c 服务发现 xff0c 熔断 xff0c 路由 xff0c
  • 美邦威集成呼吸墙饰

    http www mbwqs cn 湖北光大新型环保装饰材料有限公司 美邦威集成呼吸墙饰 生产销售中心 xff1a 湖北汉川经济开发区光大工业园 光大材料 210590 上海股交所挂牌
  • activemq--MASTER SLAVE+BROKER CLUSTER 实践(二)

    鱼与熊掌兼得法 完美解决方案 我们知道 xff1a master slave模式下 xff0c 消息会被逐个复制而cluster模式下 xff0c 请求会被自动派发 那么可不可以把两者集成起来呢 xff1f 答案是有的 xff0c 网上所谓