返回顶部
首页 > 资讯 > 精选 >f#简易Comet聊天服务实例分析
  • 837
分享到

f#简易Comet聊天服务实例分析

2023-06-17 19:06:55 837人浏览 独家记忆
摘要

f#简易Comet聊天服务实例分析,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Visual Studio 2010中关于F#的部分已经众人皆知,那么具体该怎么开发呢?这里

f#简易Comet聊天服务实例分析,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

Visual Studio 2010中关于F#的部分已经众人皆知,那么具体该怎么开发呢?这里作者将本来可以用C#开发的实例,改用F#来进行,也是为大家开阔眼界。

普通的WEB应用程序,都是靠大量Http短连接维持的。如实现一个聊天服务时,客户端会不断轮询服务器端索要新消息。这种做法的优势在于简单有效,因此广为目前的聊天服务所采用。不过Comet技术与之不同,简单地说,Comet便是指服务器推(Server-Push)技术。它的实现方式是(这里只讨论基于浏览器的Web平台)在浏览器与服务器之间建立一个长连接,待获得消息之后立即返回。否则持续等待,直至超时。客户端得到消息或超时之后,又会立即建立另一个长连接。Comet技术的***优势,自然就是很高的即使性。

如果要在asp.net平台上实现Comet技术,那么自然需要在服务器端使用异步请求处理。如果是普通处理方式的话,每个请求都会占用一个工作线程,要知道Comet是“长连接”,因此不需要多少客户端便会占用大量的线程,这对资源消耗是巨大的。如果是异步请求的话,虽然客户端和服务器端之间一直保持着连接,但是客户端在等待消息的时候是不占用线程的,直到“超时”或“消息到达”时才继续执行。

以前也有人实现过基于ASP.net的Comet服务原型,不过是使用C#的。而现在我们用F#来实现这个功能。您会发现F#对于此类异步场景有其独特的优势。

F#常用的工作单元是“模块”,其中定义了大量函数或字段。例如我们要打造一个聊天服务的话,我便定义了一个Chat模块:

#light  module internal Comet.Chating.Chat  open System  open System.Collections.Concurrent   type ChatMsg = {      From: string;      Text: string;  }   let private agentCache = new ConcurrentDictionary>()   let private agentFactory = new Func>(fun _ ->       MailboxProcessor.Start(fun o -> async { o |> ignore }))   let private GetAgent name = agentCache.GetOrAdd(name, agentFactory)

在这里我构建了一个名为ChatMsg的Record类型,一个ChatMsg对象便是一条消息。然后,我使用一个名为agentCache的ConcurrentDictionary对象来保存每个用户所对应的聊天队列——MailboxProcessor。它是F#核心库中内置的,用于实现消息传递式并发的组件,非常轻量级,因此我为每个用户分配一个也只使用很少的资源。GetAgent函数的作用是根据用户的名称获取对应的MailboxProcessor对象,自不必多说。

Chat模块中还定义了send和receive两个公开方法,如下:

let send fromName toName msg =       let agent = GetAgent toName      { From = fromName; Text = msg; } |> agent.Post   let receive name =       let rec receive' (agent: MailboxProcessor) messages =           async {              let! msg = agent.TryReceive 0              match msg with              | None -> return messages              | Some s -> return! receive' agent (s :: messages)          }       let agent = GetAgent name       async {          let! messages = receive' agent List.empty          if (not messages.IsEmpty) then return messages          else             let! msg = agent.TryReceive 3000              match msg with              | None -> return []              | Some s -> return [s]      }

send方法接受3个参数,没有返回值,它的实现只是简单地构造一个ChatMsg对象,并塞入对应的MailboxProcessor。不过receive方法是这里最关键的部分(没有之一)。receive函数的作用是接受并返回MailboxProcessor中已有的对象,或者等待3秒钟后超时——这么说其实不太妥当,因为receive方法其实只是构造了一个“做这件事情”的Async Workflow,而还没有真正执行它。至于它是如何执行的,我们稍候再谈。

receive函数的逻辑是这样的:首先我们构造一个辅助函数receive’来“尝试获取”队列中已有的所有消息。receive’是一个递归函数,每次获取一个,并递归获取剩余的消息。agent.TryReceive函数接受0,表示查询队列,并立即返回一个Option结果,如果这个结果为None,则表示队列已为空。于是在receive这个主函数中,便先使用receive’函数获取已有消息,如果存在则立即返回,否则便接收3秒钟内获得的***个消息,如果3秒结束还没有收到则返回None。

在receive和receive’函数中都使用了let!获取agent.TryReceive函数的结果。let!是F#中构造Workflow的关键字,它起到了“语法糖”的作用。例如,以下的Async Workflow:

async {      let req = WebRequest.Create("http://moma.org/")      let! resp = req.GetResponseAsync()      let stream = resp.GetResponseStream()      let reader = new StreamReader(stream)      let! html = reader.ReadToEndAsync()      html  }

事实上在“解糖”后就变成了:

async.Delay(fun () ->      async.Let(WebRequest.Create("http://moma.org/"), (fun req ->          async.Bind(req.GetResponseAsync(), (fun resp ->              async.Let(resp.GetResponseStream(), (fun stream ->                  async.Let(new StreamReader(stream), (fun reader ->                      async.Bind(reader.ReadToEndAsync(), (fun html ->                          async.Return(html))))))))))

let!关键字则会转化为Bind函数调用,Bind调用有两个参数,***个参数为Async<’a>类型,它便负责一个“回调”,待回调后才执行一个匿名函数——也就是Bind函数的第二个参数。可见,let!关键字的一个重要作用,便是将流程的“控制权”转交给“系统”,待合适的时候再继续执行下去。这便是关键,因为这样的话,在接受一个消息的时候,这等待的3秒钟是不占用任何线程的,也就是真正的纯异步。但是如果观察代码——难道不是纯粹的顺序型写法吗?

这就是F#的神奇之处。

在ASP.NET处理时需要Handler,于是在Send阶段便是简单的IHttpHandler:

#light   namespace Comet.Chating   open Comet  open System  open System.Web   type SendHandler() =       interface IHttpHandler with          member h.IsReusable = false         member h.ProcessRequest(context) =               let fromName = context.Request.FORM.Item("from");              let toName = context.Request.Form.Item("to")              let msg = context.Request.Form.Item("msg")              Chat.send fromName toName msg              context.Response.Write "sent"

而Receive阶段则是个异步的IHttpAsyncHandler:

#light   namespace Comet.Chating   open Comet  open System  open System.Collections.Generic  open System.Web  open System.Web.Script.Serialization   type ReceiveHandler() =       let mutable m_context = null     let mutable m_endReceive = null      interface IHttpAsyncHandler with          member h.IsReusable = false         member h.ProcessRequest(context) = failwith "not supported"          member h.BeginProcessRequest(c, cb, state) =              m_context <- c               let name = c.Request.QueryString.Item("name")              let receive = Chat.receive name              let beginReceive, e, _ = Async.AsBeginEnd receive              m_endReceive <- new Func<_, _>(e)               beginWork (cb, state)           member h.EndProcessRequest(ar) =              let convert (m: Chat.ChatMsg) =                  let o = new Dictionary<_, _>();                  o.Add("from", m.From)                  o.Add("text", m.Text)                  o               let result = m_endReceive.Invoke ar              let serializer = new javascriptSerializer()              result              |> List.map convert              |> serializer.Serialize              |> m_context.Response.Write

这里的关键是Async.AsBeginEnd函数,它将Chat.receive函数生成的Async Workflow转化成一组标准APM形式的begin/end对,然后我们只要把BeginProcessRequest和EndProcessReqeust的职责直接交给即可。剩下的,便是一些序列化成JSON的工作了。

于是我们可以新建一个Web项目,引用F#工程,在Web.config里配置两个Handler,再准备一个Chat.aspx页面即可。您可以在文末的链接中查看该页面的代码,也可以在这里试用其效果。作为演示页面,您其实只能“自己给自己”发送消息,其主要目的是查看其响应时间而已。例如,以下便是使用效果一例:

2 - receiving...  3026 - received nothing (3024ms)  3026 - receiving...  6055 - received nothing (3028ms)  6055 - receiving...  7256 - sending 123654...  7268 - received: 123654 (1213ms)  7268 - receiving...  10281 - received nothing (3013ms)  10281 - receiving...  13298 - received nothing (3017ms)  13298 - receiving...  13679 - sending 123456...  13698 - received: 123456 (400ms)  13698 - receiving...  16716 - received nothing (3018ms)  16716 - receiving...  18256 - sending hello world...  18265 - received: hello world (1549ms)  18266 - receiving...  21281 - received nothing (3015ms)  21281 - receiving...

可见,如果没有收到消息,那么receive操作会在3秒钟后返回。当send一条消息后,先前的receive操作便会立即获得消息了,即无需等待3秒便可提前返回。这便是Comet的效果。

至于性能,我写了一个客户端小程序,用于模拟大量用户同时聊天,每个用户每隔1秒便给另外5个用户发送一条消息,然后查看这条消息收到时产生多少的延迟。经过本机测试(2.4GHz双核,2G内存),当超过2K个在线用户时(即2000个长连接)延迟便超过了1秒——到20K还差不多。这个性能其实并不理想。不过,我这个测试也很一般。因为测试环境相当马虎,大量程序(如N个VS)基本上已经完全用满了所有的物理内存,测试客户端和服务器也是同一台机器,甚至代码也是Debug编译的……而根据监视,测试用的客户端小程序CPU占用超过50%,而服务器进程对应的w3wp.exe的CPU占用却小于10%。因此,我们可以这样推断,其实服务器端的性能并没有用足,也有可能是MailboxProcessor的调度方式不甚理想。至于具体是什么原因,我还在调查之中。

***我想说的是,这个Comet实现只是一个原型,我最想说明的问题其实是F#在异步编程中的优势。目前我写的一些程序,例如一些网络爬虫,都已经使用F#进行开发了,因为它的Async Workflow实在是过于好用,为我省了太多力气。同时我还想证明,“语言特性”并非不重要,它对于编程的简化也是至关重要的。在我看来,“类库”也好,“框架”也罢都是可以补充的,但是语言特性是个无法突破的“限制”。例如,异步编程对于F#来说简化了不少,这是因为我们可以使用顺序的方式编写异步程序。在C#中略有不足,但还有yield可以起到相当作用,因此我们可以使用CCR和AsyncEnumerator简化异步操作。但如果您使用的是Java这种劣质语言……因此,放弃Java,使用Scala吧。

值得一提的是,Async Workflow并不是F#的语言特性,F#的语言特性是Workflow,而Async Workflow其实只是实现了一个Workflow Builder,也就是那个async { ... },以此来简化异步编程而已。PDC 09上关于F#对异步编程的支持也有相应的介绍。

关于f#简易Comet聊天服务实例分析问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网精选频道了解更多相关知识。

--结束END--

本文标题: f#简易Comet聊天服务实例分析

本文链接: https://lsjlt.com/news/291937.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • f#简易Comet聊天服务实例分析
    f#简易Comet聊天服务实例分析,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Visual Studio 2010中关于F#的部分已经众人皆知,那么具体该怎么开发呢?这里...
    99+
    2023-06-17
  • C#用websocket实现简易聊天功能(服务端)
    C# 利用websocket实现简易聊天功能——服务端,供大家参考,具体内容如下 前言 使用C#语言进行开发,基于.NET FrameWork4功能包含群聊,...
    99+
    2024-04-02
  • Linux下Select多路复用实现简易聊天室示例
    目录前言多路复用的原理基本概念selectfd_set服务器Code客户端Code效果演示select服务器客户端Ⅰ客户端Ⅱ前言 和之前的udp聊天室有异曲同工之处,这次我们客户端s...
    99+
    2024-04-02
  • Node.js实现简单聊天服务器
    使用Nodejs是如此简单的实现了一个简单的聊天服务器 实现代码如下: var net = require('net'); var chatServer = net.createServer(),c...
    99+
    2022-06-04
    简单 服务器 Node
  • WPF+ASP.NET SignalR实现简易在线聊天功能的示例代码
    目录涉及知识点什么是ASP.NET SignalR在线聊天整体架构ASP.NET SignalR在线聊天服务端1. 创建ASP.NET Web API项目2. 创建消息通知中心Hub...
    99+
    2024-04-02
  • Socket编程简单示例(聊天服务器)
    目录传统Socket基于BIO实现一个简单的聊天服务器Socket编写一个简单的Http服务器基于NIO的非阻塞简单服务器实现基于NIO的Selector的简单服务器实现总结传统So...
    99+
    2023-02-18
    socket编程java java socket网络编程 Java socket
  • python socket多线程通讯实例分析(聊天室)
    本文实例讲述了python socket多线程通讯方法。分享给大家供大家参考,具体如下: #!/usr/bin/evn python """ 这是一个Socket+多进程的例子(聊天服务端) """ i...
    99+
    2022-06-04
    多线程 实例 聊天室
  • 基于统计的交易策略简易实现VNPY的示例分析
    这篇文章将为大家详细讲解有关基于统计的交易策略简易实现VNPY的示例分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。交易思维是基于历史数据中,一组数据比如100天中,K线中最高点或者最低点...
    99+
    2023-06-02
  • Shell脚本实现的一个简易Web服务器例子分享
    假设你想测试网页和一些CGI,而你又不想麻烦Apache安装完整的包。这个快速的shell脚本可能只是你所需要的东西。 简而言之,一个web服务器是一个应用程序,该应用程序将本地文本文件通过网络发送给客户的...
    99+
    2022-06-04
    脚本 简易 例子
  • Pygame库200行代码实现简易飞机大战的示例分析
    这篇文章将为大家详细讲解有关Pygame库200行代码实现简易飞机大战的示例分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。写在开头,因为这个小游戏的实验主要是帮助我熟悉pygame库的使...
    99+
    2023-06-22
  • js,jq,css多方面实现简易下拉菜单功能的示例分析
    这篇文章主要介绍js,jq,css多方面实现简易下拉菜单功能的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!效果图预览一 、css实现html代码部分<!DOCTYPE...
    99+
    2024-04-02
  • 服务器故障实例分析
    这篇文章主要介绍“服务器故障实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“服务器故障实例分析”文章能帮助大家解决问题。1. 出故障了没办法,干it这一行,就得天天面对故障,大家就是传说中的消...
    99+
    2023-06-17
  • nodejs创建简易web服务器与文件读写的实例
    web服务器至少有以下几个特点: 1、24小时不停止的工作,也就是说这个进程要常驻在内存中 2、24小时在某一端口监听,如: http://localhost:8080, www服务器默认端口80 3、要能...
    99+
    2022-06-04
    简易 实例 服务器
  • 使用nodejs搭建一个简易HTTP服务的实现示例
    目录先搭建一个简单的HTTP服务根据不同的请求地址返回不同的信息获取请求参数实现服务端设置(跨域)cookie和读取客户端发送的cookie本文只使用nodejs的http模块搭建一...
    99+
    2024-04-02
  • 云服务器实例类型分析
    云服务器实例类型是指云服务提供商所提供的不同配置的虚拟服务器实例。不同的实例类型具有不同的计算、存储和网络资源,以满足不同的应用需求。云服务器实例类型通常根据 CPU、内存、存储和网络带宽等参数进行分类。云服务器实例类型分析">通用型实例是...
    99+
    2023-10-26
    实例 类型 服务器
  • 腾讯云服务器实例分析
    腾讯云服务器实例分析主要关注于服务器的配置、部署和使用,以及服务的性能和可靠性等方面。下面是一个简要的介绍: 服务简介: 腾讯云服务器提供了多种服务,如云主机、云存储、云监控、负载均衡等。它们可以帮助用户构建高可用、可靠的云基础设施,同...
    99+
    2023-10-26
    腾讯 实例 服务器
  • 亚马逊服务器实例分析
    亚马逊服务器的特点包括: 高可靠性:亚马逊服务器提供冗余机制,包括备用节点和热备份。这些机制可以确保在主节点故障时,应用程序仍能正常运行。 高可用性:亚马逊服务器采用集群技术,将多个节点组合在一起,以提高系统的可用性和可靠性。 高性能:...
    99+
    2023-10-27
    亚马逊 实例 服务器
  • 创建简单的node服务器实例(分享)
    话不多说直接上代码: var http = require('http') //对URL 解析为对象 //1.导入模块 URl模块 var url = require('url') var fs ...
    99+
    2022-06-04
    实例 简单 服务器
  • 分布式微服务云架构实例分析
    今天就跟大家聊聊有关分布式微服务云架构实例分析,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。源码结构JEESZ驱动式项目构建内置高效可靠的代码生成器支持多种数据模型,根据数据库表生成...
    99+
    2023-06-05
  • Docker部署服务的坑实例分析
    这篇文章主要讲解了“Docker部署服务的坑实例分析 ”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Docker部署服务的坑实例分析 ”吧!本项目采用的是s...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作