RabbitMQ .NET消息队列使用入门(三)【MVC实现RPC例子】
来源:互联网 发布:简益通讯淘宝店怎么样 编辑:程序博客网 时间:2024/05/05 00:02
每一个孤独的灵魂都需要陪伴
RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
有多种 RPC模式和执行。最初由 Sun 公司提出。IETF ONC 宪章重新修订了 Sun 版本,使得 ONC RPC 协议成为 IETF 标准协议。现在使用最普遍的模式和执行是开放式软件基础的分布式计算环境(DCE)。
工作原理(以Windows下为例)
运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:
1.调用客户端句柄;执行传送参数
2.调用本地系统内核发送网络消息
3.消息传送到远程主机
4.服务器句柄得到消息并取得参数
5.执行远程过程
6.执行的过程将结果返回服务器句柄
7.服务器句柄返回结果,调用远程系统内核
8.消息传回本地主机
9.客户句柄由内核接收消息
10.客户接收句柄返回的数据
RPC OVER HTTP
Microsoft RPC-over-HTTP 部署(RPC over HTTP)允许RPC客户端安全和有效地通过Internet 连接到RPC 服务器程序并执行远程过程调用。这是在一个名称为RPC-over-HTTP 代理,或简称为RPC 代理的中间件的帮助下完成的。
RPC 代理运行在IIS计算机上。它接受来自Internet 的RPC 请求,在这些请求上执行认证,检验和访问检查,如果请求通过所有的测试,RPC 代理将请求转发给执行真正处理的RPC 服务器。通过RPC over HTTP,RPC客户端不和服务器直接通信,它们使用RPC 代理作为中间件。
新建一个控制台程序,Program.cs
using System;using System.Text;using System.Threading;using System.Threading.Tasks;using Common;using RabbitMQ.Client;using RabbitMQ.Client.Events;using RabbitMQ.Client.Exceptions;namespace RPCServer{ class Program { private static IConnection _recvConn; //接收消息的连接 private static IConnection _senderConn; //返回结果的连接 private static IModel _recvChannel; //接收消息的信道 private static IModel _sendChannel; //返回结果的信道 private static bool isExit; private static void Main(string[] args) { Setup(); Console.WriteLine("开始使用RPC消息:"); WaitCommand(); } private static void Setup() { var factory = new ConnectionFactory { HostName = "localhost", UserName = "guest", Password = "guest", //VirtualHost = "test", AutomaticRecoveryEnabled = true }; try { _recvConn = factory.CreateConnection(); _recvChannel = _recvConn.CreateModel(); _recvChannel.QueueDeclare("rpcQueue", false, false, false, null); _recvChannel.BasicQos(0, 10, false); var consumer = new EventingBasicConsumer(_recvChannel); consumer.Received += consumer_Received; _recvChannel.BasicConsume("rpcQueue", false, consumer); _senderConn = factory.CreateConnection(); //_sendChannel = _senderConn.CreateModel(); } catch (BrokerUnreachableException ex) { Console.WriteLine("RabbitMQ服务器尚未启动!"); Thread.Sleep(2000); isExit = true; } } /// <summary> /// 等待接收指令 /// </summary> private static void WaitCommand() { while (!isExit) { string line = Console.ReadLine().ToLower().Trim(); switch (line) { case "exit": Close(); isExit = true; break; case "clear": Console.Clear(); break; default: break; } } Console.WriteLine("Goodbye!"); } private static void Close() { if (_recvChannel != null && _recvChannel.IsOpen) { _recvChannel.Close(); } if (_recvConn != null && _recvConn.IsOpen) { _recvConn.Close(); } if (_senderConn != null && _senderConn.IsOpen) { _senderConn.Close(); } } private static void consumer_Received(object sender, BasicDeliverEventArgs e) { byte[] body = e.Body; Task.Run(() => HandlingMessage(body, e)); } /// <summary> /// 消息处理 /// </summary> /// <param name="msgModel"></param> /// <param name="e"></param> private static async void HandlingMessage(byte[] body, BasicDeliverEventArgs e) { bool isSuccess = false; bool hasRejected = false; string message = Encoding.UTF8.GetString(body); string replyMsg = ""; IModel _senderChannel = null; try { _senderChannel = _senderConn.CreateModel(); //多线程中每个线程使用独立的信道 replyMsg = message + " 处理成功"; var random = new Random(); int num = random.Next(0, 4); //模拟处理失败 /*if (random.Next(0, 11) == 4) { throw new Exception("处理失败", null); }*/ //模拟解析失败 if (random.Next(0, 11) == 8) { throw new MessageException("消息解析失败"); } //await Task.Delay(num * 1000); //模拟消息处理 //这里简单处理,仅格式化输出消息内容 Console.WriteLine("Time:" + DateTime.Now + " ThreadID:" + Thread.CurrentThread.ManagedThreadId + " Used: " + num + "s MSG:" + message); isSuccess = true; } catch (MessageException msgEx) { Console.WriteLine("Time:" + DateTime.Now + " ThreadID:" + Thread.CurrentThread.ManagedThreadId + " ERROR:" + msgEx.Message + " MSG:" + message); _recvChannel.BasicReject(e.DeliveryTag, false); //不再重新分发 hasRejected = true; replyMsg = message + "解析失败"; isSuccess = true; } catch (Exception ex) { Console.WriteLine("Time:" + DateTime.Now + " ThreadID:" + Thread.CurrentThread.ManagedThreadId + " ERROR:" + ex.Message + " MSG:" + message); replyMsg = "处理失败"; } if (isSuccess) { try { IBasicProperties props = e.BasicProperties; IBasicProperties replyProps = _senderChannel.CreateBasicProperties(); replyProps.CorrelationId = props.CorrelationId; _senderChannel.BasicPublish("", e.BasicProperties.ReplyTo, replyProps, Encoding.UTF8.GetBytes(replyMsg)); //发送消息到内容检查队列 if (!hasRejected) { _recvChannel.BasicAck(e.DeliveryTag, false); //确认处理成功 此处与不再重新分发,只能出现一次 } } catch (AlreadyClosedException acEx) { Console.WriteLine("ERROR:连接已关闭"); } } else { _recvChannel.BasicReject(e.DeliveryTag, true); //处理失败,重新分发 } _senderChannel.Close(); } }}
MVC站点
TestViewModel.cs代码:
public class TestViewModel { public string ReplyMessage { get; set; } }
TestController.cs代码:
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using System.Text;using RabbitMQ.Client;using RabbitMQ.Client.Exceptions;using Common;using WebApp.Models;using System.Threading.Tasks;namespace WebApp.Controllers{ public class TestController : AsyncController { // GET: /Test/ public async Task<ActionResult> Index() { TestViewModel viewModel = new TestViewModel() { ReplyMessage = "交换机关闭着." }; return View(viewModel); } [HttpPost] public async Task<MvcHtmlString> DoWork() { string message = "消息:" + new Random().Next(1, 10000).ToString(); string replyMsg = ""; RPCClient client = null; try { client = RPCClient.GetInstance(); replyMsg = await client.CallAsync(message); } catch (AlreadyClosedException e) { replyMsg = "交换机关闭着."; } catch (NoRPCConsumeException ex) { replyMsg = ex.Message; } catch (Exception e) { replyMsg = e.Message; } return new MvcHtmlString("<p>" + replyMsg + "</p>"); } public async Task<ActionResult> Serv() { string message = "消息:" + new Random().Next(1, 10000).ToString(); var client = new ServiceReference1.WebService1SoapClient(); string reply = (await client.HandlerMessageAsync(message)).Body.HandlerMessageResult; TestViewModel viewModel = new TestViewModel() { ReplyMessage = reply }; return View("Index", viewModel); } }}
Index.cshtml
@model WebApp.Models.TestViewModel@{ Layout = null;}<!DOCTYPE html><html><head> <meta name="viewport" content="width=device-width" /> <title>Index</title></head><body> <input type="button" name="sendBtn" id="sendBtn" value="发一条消息到队列" /> <div id="receiveArea"> <p>@Model.ReplyMessage</p> </div> <script src="~/Scripts/jquery-1.10.2.min.js"></script> <script> $("#sendBtn").on("click", function () { $.ajax({ url: "/Test/DoWork", dataType: "html", type: "POST", success: function (result) { console.log(result); $("#receiveArea").append($(result)); } }); }); </script></body></html>
运行结果如图:
- RabbitMQ .NET消息队列使用入门(三)【MVC实现RPC例子】
- RabbitMQ .NET消息队列使用入门(一)【简单示例】
- RabbitMQ .NET消息队列使用入门(二)【多个队列间消息传输】
- RabbitMQ消息队列(三)
- .Net下RabbitMQ消息队列的使用
- C#.NET使用消息队列RabbitMQ
- 基于PHP使用rabbitmq实现消息队列
- RabbitMQ消息队列(五):RPC远程调用
- 使用rabbitmq消息队列
- 消息队列RabbitMQ入门介绍
- 消息队列RabbitMQ入门介绍
- 消息队列RabbitMQ入门介绍
- 消息队列RabbitMQ入门介绍
- 消息队列RabbitMQ入门介绍
- 消息队列RabbitMQ入门介绍
- 消息队列RabbitMQ入门介绍
- RabbitMQ消息队列(1):RabbitMQ入门
- 消息队列系列(三):Rabbitmq Trace的使用
- HashMap中实现原理及hashcode方法
- mac下 mysql卸载 安装 重置密码
- SteamVR Unity工具包(VRTK)控制器交互
- Postman chrome浏览器插件下载地址和安装方式,解决“只能通过Chrome网上应用商店安装该程序”的方法
- Opencv判断是否加载图片的两种方法
- RabbitMQ .NET消息队列使用入门(三)【MVC实现RPC例子】
- ffmpeg提取YUV和PCM
- USACO-Factorials(阶乘最后一个非0数)
- 区块链学堂(5):Geth 安装
- SpannableString常见用法总结
- Linux常用命令大全
- 程序员看过来:二十个让你泪流满面的瞬间
- 【原创】一维线段树的建树、插入、遍历和删除
- eclipse常用快捷键