RabbitMQ先容以及基本使用

打印 上一主题 下一主题

主题 1016|帖子 1016|积分 3048

文章目次

一、什么是消息队列?
二、消息队列的作用(长处)
1、解耦
2、流量削峰
3、异步
4、顺序性
三、RabbitMQ基本结构
四、RabbitMQ队列模式
1、简单队列模式
2、工作队列模式
3、发布/订阅模式
4、路由模式
5、主题模式
6、RPC模式
7、发布者确认模式
五、RabbitMQ相干属性描述
总结

一、什么是消息队列?

消息队列是一种用于在分布式系统中举行通信的技术。它是一种存储和转发消息的中间件,可以用
于将应用步伐之间的通信解耦,从而实现高效的异步通信。消息队列答应发送者将消息发送到队列
中,而接收者则可以从队列中获取消息并举行处理惩罚。这种方式可以资助系统实现高可用性、高性
能、松耦合和可伸缩性。消息队列通常包罗生产者(发送消息的应用步伐)、斲丧者(接收消息的
应用步伐)和队列(存储消息的缓冲区)。
RabbitMQ:是由erlang语言开发,基于AMQP(高级消息队列协议)协议实现的一种消息队列。市面
上还有很多消息队列,比如Kafka、RocketMQ、Redis等,各有优劣,本文主要先容RabbitMQ。
官方文档:RabbitMQ Tutorials | RabbitMQ
二、消息队列的作用(长处)

1、解耦

应用步伐解耦,通过引入消息队列,不同的应用步伐之间可以通过消息队列举行通信,而无需直接
调用对方的接口或方法。这样可以降低系统中各个应用步伐之间的耦合度,使得它们可以独立演化
和扩展,而不会因为对方的变化而受到影响。
2、流量削峰

消息队列可以作为一个缓冲区,临时存储流入的消息,直到系统有足够的资源来处理惩罚它们。当系统
出现流量高峰时,消息队列可以临时存储过多的消息,以平滑处理惩罚流量的颠簸,避免系统被突发的
高负载压垮。
3、异步

发送者在发送消息后可以立即继续实行其他操作,而不需要等候接收者的响应。这样可以进步系统
的并发性和响应速度。也可以资助进步系统的吞吐量,特殊是在面对大量请求或处理惩罚复杂计算时。
发送者可以并行地向多个接收者发送消息,而不会因为等候接收者的响应而阻塞。
4、顺序性

固然并不是所有消息队列都能保证消息的绝对顺序性,但是在很多情况下,消息队列可以保证消息
的相对顺序性。即按照发送顺序举行处理惩罚,对某些场景要求顺序实行很适合。
三、RabbitMQ基本结构

名称
描述
Connection(毗连 )
毗连是生产者和斲丧者与RabbitMQ之间的毗连。每个生产者和斲丧者都需要与RabbitMQ建立一个毗连,以便发送和接收消息。毗连通常是长毗连,可以重用以进步性能和服从。
Channel(信道)
Channel是毗连(Connection)内的逻辑通道,用于完成大部分 AMQP 操作,如声明队列、发送和接收消息等。在 RabbitMQ 中引入 Channel(信道)的主要目标是为了进步系统的性能、灵活性和服从。使用 Channel 可以避免频仍地创建和烧毁毗连,因为一个毗连可以包含多个 Channel。这样可以减少毗连的开销,节省系统资源,并进步性能。
Exchange(交换机)
交换机是消息的接收和分发中心,负责接收生产者发送的消息,并根据指定的路由规则发送到一个或多个队列中。
Exchange相称于Queue的代理,可以设置不同的写入计谋,写入到对应的队列中。对于队列的写入,更加灵活
交换机的类型有:fanout扇出、topic主题、direct直接
Queue(队列)
队列是消息的缓存区,用于存储交换机发送的消息。生产者发送的消息终极会被存储在队列中,等候斲丧者举行斲丧。队列可以长期化到磁盘,以确保消息不会在RabbitMQ宕机或重启后丢失。
Producer(生产者)
生产者是发送消息到RabbitMQ的应用步伐。生产者负责创建消息并将其发送到RabbitMQ的消息队列中。
Consumer(斲丧者)
斲丧者是从RabbitMQ队列中接收消息并举行处理惩罚的应用步伐。斲丧者可以订阅一个或多个队列,并在消息到达队列时接收并处理惩罚它们。斲丧者负责监听队列中的消息,并将其取出举行处理惩罚。
四、RabbitMQ队列模式

基于Exchange交换机,RabbitMQ截至现在有七种队列模式。
1、简单队列模式

一个消息生产者,一个消息斲丧者,一个队列。也称为点对点模式。

图中P代表生产者,C代表斲丧者,Queue是队列名称。
我们看到是没有Exchange的,但是RabbitMQ也会有一个默认的交换机。这个默认的交换机通常被
称为"amq.default"或者""(空字符串),是RabbitMQ自动创建的,用于在没有指定交换机的情况
下将消息发送到队列。
  1. //生产者
  2. var factory = new ConnectionFactory { HostName = "localhost"}; //初始化连接信息
  3. using var connection = factory.CreateConnection();  //创建连接
  4. using var channel = connection.CreateModel(); //创建信道
  5. //声明一个队列,并将信道与队列绑定
  6. channel.QueueDeclare(queue: "hello",
  7.                      durable: false,
  8.                      exclusive: false,
  9.                      autoDelete: false,
  10.                      arguments: null);
  11. //发送消息的内容
  12. string message = $"Hello World!";
  13. var body = Encoding.UTF8.GetBytes(message);
  14. //信道绑定交换机
  15. channel.BasicPublish(exchange: string.Empty,
  16.                      routingKey: string.Empty,
  17.                      basicProperties: null,
  18.                      body: body);
  19. Console.WriteLine($" [x] Sent {message}");
  20. Console.WriteLine(" Press [enter] to exit.");
  21. //消费者
  22. var factory = new ConnectionFactory { HostName = "localhost" };
  23. using var connection = factory.CreateConnection();
  24. using var channel = connection.CreateModel();
  25. channel.QueueDeclare(queue: "hello",
  26.                      durable: false,
  27.                      exclusive: false,
  28.                      autoDelete: false,
  29.                      arguments: null);
  30. Console.WriteLine(" [*] Waiting for messages.");
  31. var consumer = new EventingBasicConsumer(channel);
  32. consumer.Received += (model, ea) =>
  33. {
  34.     var body = ea.Body.ToArray();
  35.     var message = Encoding.UTF8.GetString(body);
  36.     Console.WriteLine($" [x] Received {message}");
  37. };
  38. channel.BasicConsume(queue: "hello",
  39.                      autoAck: true,
  40.                      consumer: consumer);
  41. Console.WriteLine(" Press [enter] to exit.");
复制代码
此时就会生产者发送一条消息,斲丧者就会接收一条消息。
2、工作队列模式

_工作队列又叫做使命队列,正常_会按顺序把消息发送给每一个订阅的斲丧者,均匀而言,每个斲丧
者将获得雷同数量的消息。(不是P发送一条消息,C1和C2都会收到,而是第一条C1斲丧,第二
条C2斲丧。每个消息只会被一个斲丧者接收和处理惩罚)。
这样的好处是可以进步吞吐量,因为生产者发送了很多消息,但是斲丧者只有一个,斲丧者处理惩罚很
慢,就会造成消息积蓄。

  1. //生产者
  2. var factory = new ConnectionFactory { HostName = "localhost"};
  3. using var connection = factory.CreateConnection();
  4. using var channel = connection.CreateModel();
  5. channel.QueueDeclare(queue: "task_queue",
  6.                      durable: true,
  7.                      exclusive: false,
  8.                      autoDelete: false,
  9.                      arguments: null);
  10. var message = $"work queue";
  11. var body = Encoding.UTF8.GetBytes(message);
  12. channel.BasicPublish(exchange: string.Empty,
  13.                  routingKey: string.Empty,
  14.                  basicProperties: null,
  15.                  body: body);
  16. Console.WriteLine($" [x] Sent {message}");
  17. Console.WriteLine(" Press [enter] to exit.");
  18. //消费者
  19. var factory = new ConnectionFactory { HostName = "localhost" };
  20. using var connection = factory.CreateConnection();
  21. using var channel = connection.CreateModel();
  22. channel.QueueDeclare(queue: "task_queue",
  23.                      durable: true,
  24.                      exclusive: false,
  25.                      autoDelete: false,
  26.                      arguments: null);
  27. Console.WriteLine(" [*] Waiting for messages.");
  28. var consumer = new EventingBasicConsumer(channel);
  29. consumer.Received += (model, ea) =>
  30. {
  31.     byte[] body = ea.Body.ToArray();
  32.     var message = Encoding.UTF8.GetString(body);
  33.     Console.WriteLine($" [x] Received {message}");
  34.     channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
  35. };
  36. channel.BasicConsume(queue: "task_queue",
  37.                      autoAck: false,
  38.                      consumer: consumer);
  39. Console.WriteLine(" Press [enter] to exit.");
复制代码
工作队列与简单队列一致,会有一个默认的交换机。
3、发布/订阅模式

发布/订阅模式是一种消息传递模式,它答应发送者(发布者)将消息发布到多个接收者(订阅
者)。消息传递模型的核心思想是生产者从不直接向队列发送任何消息。实际上,生产者通常根本
不知道消息是否会被传递到任何队列
以是消息传递模式,发布者不需要指定队列。
发布/订阅模式交换机类型为Fanout。

  1. //发布者
  2. var factory = new ConnectionFactory { HostName = "localhost"};
  3. using var connection = factory.CreateConnection();
  4. using var channel = connection.CreateModel();
  5. //声明一个交换机,叫做logs,并且交换机的类型是Fanout
  6. channel.ExchangeDeclare(exchange: "logs", type: ExchangeType.Fanout);
  7. var message = "publish_subscribe";
  8. var body = Encoding.UTF8.GetBytes(message);
  9. channel.BasicPublish(exchange: "logs",
  10.                      routingKey: string.Empty,
  11.                      basicProperties: null,
  12.                      body: body);
  13. Console.WriteLine($" [x] Sent {message}");
  14. Console.WriteLine(" Press [enter] to exit.");
  15. //接收者
  16. var factory = new ConnectionFactory { HostName = "localhost"};
  17. using var connection = factory.CreateConnection();
  18. using var channel = connection.CreateModel();
  19. channel.ExchangeDeclare(exchange: "logs", type: ExchangeType.Fanout);
  20. //创建一个具有生成名称的非持久、独占、自动删除队列
  21. var queueName = channel.QueueDeclare().QueueName;
  22. channel.QueueBind(queue: queueName,
  23.                   exchange: "logs",
  24.                   routingKey: string.Empty);
  25. Console.WriteLine(" [*] Waiting for logs.");
  26. var consumer = new EventingBasicConsumer(channel);
  27. consumer.Received += (model, ea) =>
  28. {
  29.     byte[] body = ea.Body.ToArray();
  30.     var message = Encoding.UTF8.GetString(body);
  31.     Console.WriteLine($" [x] {message}");
  32. };
  33. channel.BasicConsume(queue: queueName,
  34.                      autoAck: false,
  35.                      consumer: consumer);
  36. Console.WriteLine(" Press [enter] to exit.");
复制代码
:如果发布者已经发布消息到交换机,但还没有队列绑定到交换机,消息将会丢失。
4、路由模式

路由模式也是一种消息传递模式,是基于消息的路由键(routing key)来将消息从交换机
(exchange)发送到一个或多个队列中。相比较于发布/订阅模式,路由模式多了一个routing key
的概念。
路由模式交换机类型为Direct。

  1. //生产者
  2. var factory = new ConnectionFactory { HostName = "localhost"};
  3. using var connection = factory.CreateConnection();
  4. using var channel = connection.CreateModel();
  5. //定义交换机名称以及类型为Direct
  6. channel.ExchangeDeclare(exchange: "direct_logs", type: ExchangeType.Direct);
  7. //定义路由键
  8. string routingKey = "direct_test";
  9. //发送消息体
  10. string message  = "direct_message";
  11. var body = Encoding.UTF8.GetBytes(message);
  12. channel.BasicPublish(exchange: "direct_logs",
  13.                      routingKey: routingKey,
  14.                      basicProperties: null,
  15.                      body: body);
  16. Console.WriteLine($" [x] Sent '{routingKey}':'{message}'");
  17. Console.WriteLine(" Press [enter] to exit.");
  18. //消费者
  19. var factory = new ConnectionFactory { HostName = "localhost" };
  20. using var connection = factory.CreateConnection();
  21. using var channel = connection.CreateModel();
  22. channel.ExchangeDeclare(exchange: "direct_logs", type: ExchangeType.Direct);
  23. //创建一个具有生成名称的非持久、独占、自动删除队列
  24. var queueName = channel.QueueDeclare().QueueName;
  25. //路由键集合
  26. var routeKeyArr = new string[] { "direct_test", "direct_test2" };
  27. foreach (var routeKey in routeKeyArr)
  28. {
  29.     channel.QueueBind(queue: queueName,
  30.                       exchange: "direct_logs",
  31.                       routingKey: routeKey);
  32. }
  33. Console.WriteLine(" [*] Waiting for messages.");
  34. var consumer = new EventingBasicConsumer(channel);
  35. consumer.Received += (model, ea) =>
  36. {
  37.     var body = ea.Body.ToArray();
  38.     var message = Encoding.UTF8.GetString(body);
  39.     var routingKey = ea.RoutingKey;
  40.     Console.WriteLine($" [x] Received '{routingKey}':'{message}'");
  41. };
  42. channel.BasicConsume(queue: queueName,
  43.                      autoAck: true,
  44.                      consumer: consumer);
  45. Console.WriteLine(" Press [enter] to exit.");
复制代码
路由模式,斲丧者可以监听多个路由键
5、主题模式

基于路由模式,仍然有范围性——它不能基于多个标准举行路由。也就是一个斲丧者只能接收完全
与routing key相匹配的交换机。主题模式主要解决路由模式的不敷,可以模糊匹配routing key。
路由模式交换机类型为Topic。

在生产者方面,基于 . 作为分隔符,用于routing key。比如“stock.usd.nyse”、“nyse.vmw”、
“quick.orange.rabbit”。可以是任何单词,但最多只有255 个字节。
在斲丧者方面,绑定routing key有两种重要的情况:
(1)*(星号):匹配一个单词。
详细语法:
  1. var routeing_key = "info.debug.error";
  2. //匹配 info
  3. "info.*.*"
  4. //匹配debug
  5. "*.debug.*"
  6. //匹配error
  7. "*.*.error"
复制代码
(2)#(散列):匹配零个或多个单词。
详细语法:
  1. var routeing_key = "info.debug.error";
  2. //匹配 info
  3. "info.#"
  4. //匹配debug
  5. "#.debug.#"
  6. //匹配error
  7. "*.*.error"
复制代码
6、RPC模式

RPC模式又叫"请求/回复模式"。
RPC(Remote Procedure Call,远程过程调用)是一种用于在分布式系统中举行通信的技术。它
答应一个进程(或线程)调用另一个进程(或线程)的过程(函数或方法),就像调用本地函数一
样,而不需要开发者显式处理惩罚底层通信细节。
(就是生产者发送一条消息,斲丧者端实行某个方法,获取值的同时,并返回到生产者。)

  1. //生产者
  2. var factory = new ConnectionFactory { HostName = "localhost"};
  3. using var connection = factory.CreateConnection();
  4. using var channel = connection.CreateModel();
  5. //定义接收返回结果的队列
  6. var replyQueueName = channel.QueueDeclare().QueueName;
  7. var consumer = new EventingBasicConsumer(channel);
  8. consumer.Received += (model, ea) =>
  9. {
  10.     var body = ea.Body.ToArray();
  11.     var response = Encoding.UTF8.GetString(body);
  12.     Console.WriteLine(" [.] Got '{0}'", response);
  13. };
  14. //发送消息
  15. var correlationId = Guid.NewGuid().ToString(); //消息唯一性
  16. var props = channel.CreateBasicProperties();
  17. props.CorrelationId = correlationId;
  18. props.ReplyTo = replyQueueName; //回调队列名称
  19. string message = "30";
  20. var messageBytes = Encoding.UTF8.GetBytes(message);
  21. channel.BasicPublish(
  22.     exchange: "",
  23.     routingKey: "rpc_queue",
  24.     basicProperties: props,
  25.     body: messageBytes);
  26. channel.BasicConsume(
  27.     consumer: consumer,
  28.     queue: replyQueueName,
  29.     autoAck: true);
  30. //消费者
  31. var factory = new ConnectionFactory { HostName = "localhost" };
  32. using var connection = factory.CreateConnection();
  33. using var channel = connection.CreateModel();
  34. channel.QueueDeclare(queue: "rpc_queue",
  35.                              durable: false,
  36.                              exclusive: false,
  37.                              autoDelete: false,
  38.                              arguments: null);
  39. channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
  40. var consumer = new EventingBasicConsumer(channel);
  41. channel.BasicConsume(queue: "rpc_queue",
  42.                      autoAck: false,
  43.                      consumer: consumer);
  44. Console.WriteLine(" [x] Awaiting RPC requests");
  45. consumer.Received += (model, ea) =>
  46. {
  47.     string response = string.Empty;
  48.     var body = ea.Body.ToArray();
  49.     var props = ea.BasicProperties;
  50.     var replyProps = channel.CreateBasicProperties();
  51.     replyProps.CorrelationId = props.CorrelationId;
  52.     try
  53.     {
  54.         var message = Encoding.UTF8.GetString(body);
  55.         int n = int.Parse(message);
  56.         Console.WriteLine($" [.] Fib({message})");
  57.         response = FibHelper.Fib(n).ToString();
  58.     }
  59.     catch (Exception e)
  60.     {
  61.         Console.WriteLine($" [.] {e.Message}");
  62.         response = string.Empty;
  63.     }
  64.     finally
  65.     {
  66.         //回调到生产者队列
  67.         var responseBytes = Encoding.UTF8.GetBytes(response);
  68.         channel.BasicPublish(exchange: string.Empty,
  69.                              routingKey: props.ReplyTo,
  70.                              basicProperties: replyProps,
  71.                              body: responseBytes);
  72.         channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
  73.     }
  74. };
  75. //执行函数,并返回结果
  76. public class FibHelper
  77. {
  78.      public static int Fib(int n)
  79.      {
  80.          if (n == 0 || n == 1)
  81.             return n;
  82.          return Fib(n - 1) + Fib(n - 2);
  83.      }
  84. }
复制代码
RPC模式不是消息传递模式,消息只会被一个斲丧者斲丧。
7、发布者确认模式

发布者确认模式(Publisher Confirmation)是 RabbitMQ 提供的一种机制,用于确保消息被成功
发送到交换机(exchange)并被接收到,以及确保消息被正确地路由到队列中。在传统的消息发
布过程中,发布者发送消息到交换机后,并不知道消息是否已经被正确地处理惩罚。为相识决这个问
题,RabbitMQ 提供了发布者确认模式,答应发布者确认消息是否已经被成功接收到。
  1. //生产者
  2. var factory = new ConnectionFactory() { HostName = "localhost" };
  3. using (var connection = factory.CreateConnection())
  4. using (var channel = connection.CreateModel())
  5. {
  6.     // 设置信道为确认模式
  7.     channel.ConfirmSelect();
  8.     // 声明一个队列
  9.     channel.QueueDeclare(queue: "hello",
  10.                          durable: false,
  11.                          exclusive: false,
  12.                          autoDelete: false,
  13.                          arguments: null);
  14.     // 消息内容
  15.     string message = "Hello World!";
  16.     var body = Encoding.UTF8.GetBytes(message);
  17.     try
  18.     {
  19.         // 发送消息
  20.         channel.BasicPublish(exchange: "",
  21.                              routingKey: "",
  22.                              basicProperties: null,
  23.                              body: body);
  24.         // 等待消息确认
  25.         if (channel.WaitForConfirms())
  26.         {
  27.             Console.WriteLine(" [x] Sent {0}", message);
  28.         }
  29.         else
  30.         {
  31.             Console.WriteLine(" [x] Failed to send {0}", message);
  32.         }
  33.     }
  34.     catch (Exception ex)
  35.     {
  36.         Console.WriteLine($"An error occurred: {ex.Message}");
  37.     }
  38. }
  39. Console.WriteLine(" Press [enter] to exit.");
  40. Console.ReadLine();
  41. //消费者
  42. var factory = new ConnectionFactory() { HostName = "localhost" };
  43. using (var connection = factory.CreateConnection())
  44. using (var channel = connection.CreateModel())
  45. {
  46.     // 声明一个队列
  47.     channel.QueueDeclare(queue: "hello",
  48.                          durable: false,
  49.                          exclusive: false,
  50.                          autoDelete: false,
  51.                          arguments: null);
  52.     // 创建消费者
  53.     var consumer = new EventingBasicConsumer(channel);
  54.     // 消费消息
  55.     consumer.Received += (model, ea) =>
  56.     {
  57.         var body = ea.Body;
  58.         var message = Encoding.UTF8.GetString(body);
  59.         Console.WriteLine(" [x] Received {0}", message);
  60.     };
  61.     // 消费者开始接收消息
  62.     channel.BasicConsume(queue: "hello",
  63.                          autoAck: true,
  64.                          consumer: consumer);
  65.     Console.WriteLine(" Press [enter] to exit.");
  66.     Console.ReadLine();
  67. }
复制代码
五、RabbitMQ相干属性描述

上述代码中,有很多属性的设置,下面解释一下。
名称

描述
durable
布尔
队列是否长期化。如果将队列声明为长期化,那么当 RabbitMQ 服务器重启时,队列将被重新声明。长期化队列中的消息会被存储在磁盘上,因此即使在服务器重启后,消息也不会丢失。
exclusive
布尔
是否为排他队列。当队列被声明为排他时,只有声明该队列的毗连(connection)才能使用它。一旦毗连关闭,排他队列就会被删除。这种队列通常是用于临时使命,只答应声明它的毗连使用,不会被其他毗连访问。
autoDelete
布尔
队列是否自动删除。如果将队列声明为自动删除,则在最后一个毗连订阅它的斲丧者取消订阅后,队列将被自动删除。这种属性通常与临时队列一起使用,以确保在不再需要队列时它会被清算。
arguments

用于设置队列或交换机的额外参数的选项。这些参数可以用于定制化队列或交换机的举动,以满足特定的需求。比如,设置队列的最大长度、消息在队列中的最大存活时间。
autoAck
布尔
消息是否自动确认。它是指在斲丧者从队列中接收到消息后,是否自动确认消息的斲丧。当设置为 true 时,表示斲丧者会自动确认收到的消息,此时队列中表示该消息已被斲丧成功了。当设置为 false 时,表示斲丧者需要显式地调用确认方法来告知 RabbitMQ 已经成功处理惩罚了消息,否则消息将被重新放回队列,等候其他斲丧者处理惩罚。
basicProperties

是指在发布消息时可以携带的消息属性。这些属性包含了有关消息的元数据信息,例如消息的优先级、消息的过期时间、消息的类型等等。

总结

RabbitMQ 是一个消息队列,主要作用就是异步、顺序性、削峰等。
七种队列模式,可以根据不同的场景详细使用。
1. 简单队列模式

最简单的消息模式。一个生产者发送消息到一个队列,一个斲丧者从队列中接收消息并处理惩罚。适用
于单个生产者-单个斲丧者的简单场景。
2. 工作队列模式
多个斲丧者共同斲丧消息。斲丧者从队列中取出消息并处理惩罚,消息会均匀地分配给斲丧者。
是基于简单队列模式的缺点,做了提升。适用于负载均衡和使命分发的场景。
3. 发布/订阅模式
生产者将消息发送到交换机,交换机将消息广播到所有与之绑定的队列。多个斲丧者可以订阅不同
的队列,从而接收消息的副本。适用于消息广播和关照的场景。
4. 路由模式
生产者发送消息到交换机,并使用路由键指定消息的目标队列。交换机根据消息的路由键将消息路
由到与之匹配的队列中。适用于根据消息内容举行精确路由的场景。
5. 主题模式
类似于路由模式,但是路由键可以使用通配符举行匹配。适用于消息的多样化路由和灵活的匹配需
求。
6. RPC模式
客户端(RPC请求者)发送请求消息到队列中,并等候服务器(RPC响应者)返反响应消息。
服务器监听请求队列,处理惩罚请求并将响应发送回客户端指定的队列。适用于需要请求-响应式通信
的场景,类似于远程调用。
7. 发布者确认模式

发布者确认模式是 RabbitMQ 提供的一种机制,用于确保消息在发送到交换机并被路由到队列时的
可靠性。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

去皮卡多

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表