后端开发,统计了一下大概有这些场景需要用到序列化:
C#序列化库有非常非常多了,protobuf,json等等。但是这些序列化库都无法应当所有场景,既要可读又要小。protobuf不支持复杂的对象结构(无法使用继承),做消息合适,做数据库存储和日志格式并不好用。json做日志格式合适,但是做网络消息和数据存储就太大。我们当然希望一个库能满足上面所有场景,理由如下:
简单来说就是减少各种数据转换,减少代码,提高开发效率,提高可维护性。当然,Mongo Bson就能够满足。MongoDB库既可以序列化成文本也可以序列化成BSON的二进制格式,并且MongoDB本身就是一个游戏中使用非常多的数据库。Mongo Bson非常完善,是我见过功能最全使用最强大的序列化库,有些功能十分贴心。其支持功能如下:
简单的介绍下mongo bson库
public sealed class Player { public long Id; public string Account { get; private set; } public long UnitId { get; set; } } Player player1 = new Player() { Id = 1 }; string json = player1.ToJson(); Console.WriteLine($"player1 to json: {json}"); Console.WriteLine($"player to bson: {player.ToBson().ToHex()}"); // output: // player to json: { "_id" : NumberLong(1), "C" : [], "Account" : null, "UnitId" : NumberLong(0) } // player to bson: B000000125F69640001000000000000000A4163636F756E740012556E6974496400000000000000000000
注意mongo的json跟标准的json有点区别,如果想用标准的json,可以传入一个JsonWriterSettings对象,限制使用JsonOutputMode.Strict模式
// 使用标准json Player player2 = new Player() { Id = 1 }; Console.WriteLine($"player to json: {player2.ToJson(new JsonWriterSettings() {OutputMode = JsonOutputMode.Strict})}"); // player to json: { "_id" : 1, "C" : [], "Account" : null, "UnitId" : 0 }
反序列化json:
// 反序列化json Player player11 = BsonSerializer.Deserialize<Player>(json); Console.WriteLine($"player11 to json: {player11.ToJson()}");
反序列化bson:
// 反序列化bson using (MemoryStream memoryStream = new MemoryStream(bson)) { Player player12 = (Player) BsonSerializer.Deserialize(memoryStream, typeof (Player)); Console.WriteLine($"player12 to json: {player12.ToJson()}"); }
[BsonIgnore]该标签用来禁止字段序列化。 public sealed class Player { public long Id; [BsonIgnore] public string Account { get; private set; } public long UnitId { get; set; } } Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"}; Console.WriteLine($"player to json: {player.ToJson()}"); // player to json: { "_id" : 2, "UnitId" : 3 }
[BsonElement] 字段加上该标签,即使是private字段也会序列化(默认只序列化public字段),该标签还可以带一个string参数,给字段序列化指定别名。
public sealed class Player { public long Id; public string Account { get; private set; } [BsonElement("UId")] public long UnitId { get; set; } } Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"}; Console.WriteLine($"player to json: {player.ToJson()}"); // player to json: { "_id" : 2, "Account" : "panda", "UId" : 3 }
[BsonIgnoreExtraElements] 该标签用在class上面,反序列化时用来忽略多余的字段,一般版本兼容需要考虑,低版本的协议需要能够反 序列化高版本的内容,否则新版本加了字段,旧版本结构反序列化会出错
[BsonIgnoreExtraElements] public sealed class Player { public long Id; public string Account { get; private set; } [BsonElement("UId")] public long UnitId { get; set; } }
mongo bson库强大的地方在于完全支持序列化反序列化继承结构。需要注意的是,继承反序列化需要注册所有的父类,有两种方法: a. 你可以在父类上面使用[BsonKnownTypes]标签声明继承的子类,这样mongo会自动注册,例如:
[BsonKnownTypes(typeof(Entity))] public class Component { } [BsonKnownTypes(typeof(Player))] public class Entity: Component { } public sealed class Player: Entity { public long Id; public string Account { get; set; } public long UnitId { get; set; } }
这样有缺陷,因为框架并不知道一个类会有哪些子类,这样做对框架代码有侵入性,我们希望能解除这个耦合 。可以扫描程序集中所有子类父类的类型,将他们注册到mongo驱动中
Type[] types = typeof(Game).Assembly.GetTypes(); foreach (Type type in types) { if (!type.IsSubclassOf(typeof(Component))) { continue; } BsonClassMap.LookupClassMap(type); } BsonSerializer.RegisterSerializer(new EnumSerializer<NumericType>(BsonType.String));
这样完全的自动化注册,使用者也不需要关心类是否注册。
mongo bson反序列化时支持一个ISupportInitialize接口,ISupportInitialize有两个方法
public interface ISupportInitialize { void BeginInit(); void EndInit(); }
BeginInit在反序列化前调用,EndInit在反序列化后调用。这个接口非常有用了,可以在反序列化后执行一些操作。例如
[BsonIgnoreExtraElements] public class InnerConfig: AConfigComponent { [BsonIgnore] public IPEndPoint IPEndPoint { get; private set; } public string Address { get; set; } public override void EndInit() { this.IPEndPoint = NetworkHelper.ToIPEndPoint(this.Address); } }
InnerConfig是ET中进程内网地址的配置,由于IPEndPoint不太好配置,我们可以配置成string形式,然后反序列化的时候在EndInit中把string转换成IPEndPoint。 同样我给protobuf反序列化方法也加上了这个调用,参考ProtobufHelper.cs,ET的protobuf因为要支持ilruntime,所以去掉了map的支持,假如我们想要一个map怎么办呢?这里我给生成的代码都做了手脚,把proto消息都改成了partial class,这样我们可以自己扩展这个class,比如:
message UnitInfo { int64 UnitId = 1; float X = 2; float Y = 3; float Z = 4; } // protobuf message G2C_EnterMap // IResponse { int32 RpcId = 90; int32 Error = 91; string Message = 92; // 自己的unit id int64 UnitId = 1; // 所有的unit repeated UnitInfo Units = 2; }
这个网络消息有个repeated UnitInfo字段,在protobuf中其实是个数组,使用起来不是很方便,我希望转成一个Dictionary<Int64, UnitInfo>的字段,我们可以做这样的操作:
public partial class G2C_EnterMap: ISupportInitialize { public Dictionary<Int64, UnitInfo> unitsDict = new Dictionary<long, UnitInfo>(); public void BeginInit() { } public void EndInit() { foreach (var unit in this.Units) { this.unitsDict.Add(unit.UnitId, unit); } } }
通过这样一段代码把消息进行扩展一下,反序列化出来之后,自动转成了一个Dictionary。
ET开源地址地址:egametang/ET: Unity3D Client And C# Server Framework (github.com) qq群:474643097
智能的真正标志不是知识,而是想象力。作者|GadiSinger编译|王玥编辑|陈彩娴什么知识让我们变得聪明?我们用来理解世界、解释新体验和做出深思熟虑的选择的认知结构是什么?定义一个阐明给人类或人工智能更深入理解和更高认知的知识的框架,将有助于我们对此话题进行结构化的讨论。近日,英特尔实验室副总裁兼紧急人工智能研究主任GadiSinger介绍了这种赋予人工智能更高认知的知识构建(knowledgeconstructs)的数个维度,并指出一条通往更高智能机器的道路。图为英特尔实验室副总裁GadiSinger更高层次的机器智能的核心,可能是让知识构建帮助人工智能系统组织自己的世界观,赋予人工智能理解意义、事件和任务的能力。如此一来,机器认知将从数据扩展到知识结构,包括描述性知识、世界动态模型和起源等维度。在学习语言时,我们要区分形式(form)和意义(meaning):形式指的是用来表达意义的符号,也就是表面的表达。每种形式在特定的语境中都有特定的含义,而形式在不同的语境中可以有不同的含义。正如Bengio和Schölkopf等人在“TowardsCausalRepresentationL
参考链接:pythonjson1-1:使用json.dump/dumps将JSON写入文件/字符串pythonjson编码(dump/dumps:字典转化为json)、解码(load/loads:json转化为字典) 一般接口传输数据的数据类型都是json,本文主要介绍json的编码、解码、读取等 1、json的数据类型(1)数字(int、float): jsondata1=‘{“age”:18}’(2)字符串("") jsondate2=‘{“phone”:“12345654321”}’(3)逻辑值(true/false) jsondata3=‘{“boolValue”:False}’(4)null jsondata4=‘{“nullValue”:None}’(5)对象({}) jsondata5=‘{“name”:“yezi”,“address”:{“country”:“china”,“city”:“HeBei”}}’(6)数组([]) jsondata6=‘{“updatedate”:[22,23,24]}’ 2
在Python之并发请求(上)中详细了介绍了使用多线程的方式来编写一个测试服务端程序的高并发请求的性能测试工具。在这个测试的工具中,依据高并发的请求之后,我们得到很全面的响应时间,吞吐量,错误率,以及其他的相关信息。在性能的测试中,更多的是CPU密集型和IO密集型的,基本很多服务端的程序都是基于IO密集型的,那么这样使用多线程的方式它的效率会更高。我们对前面的代码进行改造,既然我们已经可以很轻松的拿到了我们性能测试的结果信息数据,那么我们是否基于结合Flask-Restful的框架,把它提供成一个API,这样别人直接调用我们的API就可以测试被测试的服务端的程序了,这样的方式更加高效和简单。当然,这只是一种思想,在服务端的测试中,我们需要更多考虑的是服务在高并发下以及连续请求的前提下,是否会出现OOM以及SockedTimeOut以及TimeOut等其他的程序信息,如MQ的消息积压,服务的崩溃以及其他的异常情况。本文章这里就不详细的讨论服务的监控,关于saas化下服务的监控可以参考我的文章saas化架构中服务的监控实现思路和具体的代码。下面我们对具体的被测试的API进行封装,这里就以测
十三发自凹非寺 量子位报道|公众号QbitAI文本转语音,又出了个神器。这个工具来自MIT,并且还是在线、免费,可以用它来生成各种字符的44.1kHz声音。这些声音是使用多种音频合成算法和定制的深层神经网络实时生成的。最惊艳的是,这个工具只需要少量的文本数据,还能保留文本所表达出来的韵律,可以说是相当的“声情并茂”了。使用起来非常简单,主页中的“Source”提供了几部电影或动画,“Character”提供了里面的一些角色。只需要在文本框中输入不多于140字符的文本,点击生成,就能很快输出对应角色、带有韵律的语音。文本转语音,有内味儿了效果怎么样?我们先来看看几个例子。首先是《Portal2》中的GLaDOS的声音。TheEnrichmentCenterwouldliketoannounceanewemployeeinitiative(inishutive)offorcedvoluntaryparticipation.IfanyApertureScienceemployeewouldliketooptoutofthisnewvoluntarytestingprogram,pleaser
一丶python2和python3的区别 1.编码&字符串 字符串: python2: Unicode v= u"root" 本质上用unicode存储(万国码) (str/bytes) v="root" 本质用字节存储 python3: str v="root" 本质上用unicode存储(万国码) bytes v= b"root" 本质上用字节存储 编码: python2: ascii 文件头可以修改#-*- encoding:utf-8-*- python3: utf-8 文件头可以修改#-*- encoding:utf-8-*- 2.继承 python2: 经典类/新式类 python3: 新式类 3.范围 python2: range/xrange python3: range 4.输入 py
下面是一些关于Apachekafka流行的使用场景。这些领域的概述,可查看博客文章。消息kafka更好的替换传统的消息系统,消息系统被用于各种场景(解耦数据生产者,缓存未处理的消息,等),与大多数消息系统比较,kafka有更好的吞吐量,内置分区,副本和故障转移,这有利于处理大规模的消息。根据我们的经验,消息往往用于较低的吞吐量,但需要低的端到端延迟,并需要提供强大的耐用性的保证。在这一领域的kafka比得上传统的消息系统,如的ActiveMQ或RabbitMQ的。网站活动追踪kafka原本的使用场景:用户的活动追踪,网站的活动(网页游览,搜索或其他用户的操作信息)发布到不同的话题中心,这些消息可实时处理,实时监测,也可加载到Hadoop或离线处理数据仓库。每个用户页面视图都会产生非常高的量。指标kafka也常常用于监测数据。分布式应用程序生成的统计数据集中聚合。日志聚合许多人使用Kafka作为日志聚合解决方案的替代品。日志聚合通常从服务器中收集物理日志文件,并将它们放在中央位置(可能是文件服务器或HDFS)进行处理。Kafka抽象出文件的细节,并将日志或事件数据更清晰地抽象为消息流。这
Redis相关:src/redis-serverredis.conf复制redis-clishutdown redis-cli-h127.0.0.1-p6379shutdown复制数据库相关:servicemysqlstart servicemysqlstop servicemysqlrestart servicemysqlstatus mysql-uroot-proot复制Tomcat相关:tomcatsystemctlstarttomcat tomcatsystemctlrestarttomcat tomcatsystemctldisabletomcat复制项目相关:java-jarboot.jar java-jarboot.jar& nohupjava-jarboot.jar&复制
近日,Facebook的虚拟现实部门OculusVR又惨遭诉讼了,吃瓜群众们对此事的态度已经由“我的天”变为“啊,又来了”。这次将Ocuuls告上法庭的苦主是一家位于加州的技术授权公司TechnoViewIPInc,该公司向联邦法院起诉Facebook和Oculus侵犯了VR头显制造商ImmersiON-VRelia拥有的3D成像专利。遥想当年,Facebook一掷千金拿出30亿美元收购了草根出身的Oculus。接下来几年,VR行业的迅速发展也证明Facebook的收购案确实是慧眼识英。而OculusRiftCV1和HTCVive在PCVR界的势均力敌,更是让Facebook对VR愈加看好。但是,Facebook与Oculus如今的处境其实并没有媒体所描述的那么一片坦途。官司缠身,Oculus究竟做了什么?Zenimax的CEORobertAltman 2014年5月,Facebook对外宣布将收购Oculus后的第三个月,Bethesda母公司Zenimax起诉旗下子公司idSoftware的创始人JohnCarmack从原公司盗取“关键技术”,并在OculusRift中使用了该技术
明星的微博粉丝数量一直都是个谜,原因在于无论有多少人质疑明星在微博买粉丝数量,但是苦于一直没有证据,所以也就只是大家知道,粉丝不认而已,这也就是成谜了。备注:我要向我微博为数不多的粉丝先声明,我不追任何一个明星,今天为了采集数据,无意中点赞了一个明星微博,请告诉我,微博可以取消赞吗?但是我觉得这次的李小璐夜宿事件似乎提供了真是了解微博明星流量的机会,因为这次是全民大事件,其真正的粉丝不可能不知道,为了维护所谓偶像,极大可能性会参与其中,有兴趣可以去看看这粉丝怼天怼地怼父母、共青团、新华社、紫光阁、央视的架势和气势,就会知道他们有多积极参与其中了。另外一个分析核心是,在经过几大超级微博大v,例如共青团、新华社、紫光阁、央视批评之后,大部分水军(重点)已经意识到再也洗不白了,也就放弃了洗白,剩下的就是粉丝数量了。我就以事件中的所谓的嘻哈男猪脚微博为例,由于我实在不想打下那个名字,为了省事,我就以SB为代号吧。我们首先来看,这SB货在微博上显示的粉丝数量是476万,当然,这粉丝数量在明星当中也只能算是中规中矩,但是这真的是真是数据吗?接下来看,下图是该微博1月4日发布的微博分布在2018年1
大家好,又见面了,我是你们的朋友全栈君。ByteBufByteBuf需要提供JDKByteBuffer的功能(包含且不限于),主要有以下几类基本功能:7种Java基础类型、byte[]、ByteBuffer(ByteBuf)的等的读写缓冲区自身的copy和slice设置网络字节序构造缓冲区实例操作位置指针扩容原理首先确认ByteBuf是否已经被释放,如果被释放,则抛出IllegalReferenceCountException异常判断写入需要的最小空间,如果该空间小于ByteBuf的可写入空间,直接返回,不进行扩容判断写入需要的最小空间,如果该空间大于ByteBuf的(最大容量-当前的写索引),不进行扩容,抛出IndexOutOfBoundsException异常计算新容量,动态扩容的规则,当新容量大于4MB时,以4MB的方式递增扩容,在小于4MB时,从64字节开始倍增(Double)扩容读写索引Netty提供readIndex和writeIndex用来支持读取和写入操作,两个索引将缓冲区划分为三个区域0~readIndex:已读区域(可丢弃区域)readIndex~writeInde
IngressNginxv0.30Hostnetwork模式安装 apiVersion:v1 kind:Namespace metadata: name:ingress-nginx labels: app.kubernetes.io/name:ingress-nginx app.kubernetes.io/part-of:ingress-nginx --- kind:ConfigMap apiVersion:v1 data: large-client-header-buffers:41024k proxy-body-size:250m client-header-buffer-size:512k client-body-buffer-size:2048k compute-full-forwarded-for:'true' use-forwarded-headers:'true' forwarded-for-header:"X-Forwarded-For" keep-alive-requests:"10000" max-worker-connections:"65536" log-f
1. 202.107.6.88 220230E70000031300000044646B1972E6BAE003507991F43030303030303030E6BAE02AFBB4D198E6BAE02AFBB57931 【Hex】 LI[2bits],VN[3bits],Mode[3bits],Statum[8bits],Poll[8bits],Precision[8bits] RootDelay RootDispersion ReferenceIdentifier ReferenceTimestamp OriginateTimestamp ReceiveTimestamp TransmitTimestamp 根延时 根描述 参考标志 T3参考时间戳 T0起源时间戳[请求时的T2] T1接收时间戳 T2 传输时间戳 0123456789101112131415161718192021222324252627282930313233343536373839 4041424344454647 220230E7 0
本文转载自:http://www.tuicool.com/articles/Jb6ziyJ 这两天搞安装包真心坎坷,一个问题接一个问题,先是为了实现自定义动作现啃vbs,后面又是安装过程老是报错: 各种搜索、各种尝试,总算搞掂,积累了些经验,分享一下。 首先CRforVS2010的所有东东都在这里:http://scn.sap.com/docs/DOC-7824 简单说一说资源下载表的情况,资源表如图(敢情都出到SP10了,我用的是13.0.9.1312): 每列代表一类资源,每行代表一个版本,我仅就我知道的资源类别介绍一下: InstallExecutable:我理解是完整版,包括VS插件、报表设计器、运行环境等所有东西,装上后就可以在VS中创建CR报表了。适用于开发机; MSI32/64Bit:运行环境独立安装包,分别适用于32/64平台。就是装在客户电脑上的东东,下文我所谓的MSI整合法就是指这个; MSM32Bit:也是运行环境,只不过是用来合并在安装包中的,不能独立安装。看起来只有32位版;下文我所谓的MSM合并法就指它 别的什么ClickOnce、WEBX
目录为什么对缓存只删除不更新先更新数据库还是先删除缓存?CacheAsidePatternDouble-DeleteRead/WriteThroughPatternWriteBehind设置缓存过期时间总结 为什么对缓存只删除不更新 不更新缓存是防止并发更新导致的数据不一致。 所以为了降低数据不一致的概率,不应该更新缓存,而是直接将其删除, 然后等待下次发生cachemiss时再把数据库中的数据同步到缓存。 先更新数据库还是先删除缓存? 有两个选择: 1.先删除缓存,再更新数据库 2.先更新数据库,再删除缓存 如果先删除缓存,有一个明显的逻辑错误:考虑两个并发操作,线程A删除缓存后,线程B读该数据时会发生CacheMiss,然后从数据库中读出该数据并同步到缓存中,此时线程A更新了数据库。 结果导致,缓存中是老数据,数据库中是新数据,并且之后的读操作都会直接读取缓存中的脏数据。(直到key过期被删除或者被LRU策略踢出) 如果数据库更新成功后,再删除缓存,就不会有上面这个问题。 可能是由于数据库优先,第二种方式也被称为CacheAsidePattern。 CacheAsidePatter
值得一提: 202203042104;20220309798;202203301606; 202202281601. 最多可达成的换楼请求数目 方法一:DFS枚举 枚举所有换楼请求的选择与不选择两种情况,最后判断是否满足题意,时间复杂度为O(2^m),m为请求个数,依题意m<=16,符合要求。 1#include<cstring> 2 3constintN=25; 4 5classSolution{ 6public: 7inta[N][N],b[N]; 8intdfs(into,intx,intn,intm){ 9intres=0; 10if(o==m){ 11inttot=0; 12for(inti=0;i<n;i++){ 13for(intj=0;j<m;j++) 14tot+=(b[j]?a[j][i]:0); 15if(tot!=0)return0; 16} 17returnx; 18} 19b[o]=1; 20res=dfs(o+1,x+1,n,m); 21b[o]=0; 22res=max(res,dfs(o+1,x,
GAMES101作业解答-作业02-TrianglesandZ-buffering 1.题目要求 在屏幕上画出一个实心三角形,换言之,栅格化一个三角形。上一次作业中,在视口变化之后,我们调用了函数rasterize_wireframe(constTriangle&t)。但这一次,你需要自己填写并调用函数rasterize_triangle(constTriangle&t)。 该函数的内部工作流程如下: 创建三角形的2维boundingbox。 遍历此boundingbox内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。 如果在内部,则将其位置处的插值深度值(interpolateddepthvalue)与深度缓冲区(depthbuffer)中的相应值进行比较。 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区(depthbuffer)。 我们需要修改的函数如下: •rasterize_triangle():执行三角形栅格化算法 •staticboolinsideTriangle():测试点是否
NavisworksManage卸载工具,完全彻底删除干净NavisworksManage各种残留注册表和文件。NavisworksManage安装失败,怎么完全彻底删除清理干净NavisworksManage各种残留注册表和文件呢?有些同学想把NavisworksManage重新安装,但是NavisworksManage安装失败显示失败,有时NavisworksManage安装到一半就显示失败,然后会问NavisworksManage无法重装是怎么回事。那么NavisworksManage安装失败的原因是什么呢?如何才能把NavisworksManage卸载干净呢?有人说只要把NavisworksManage注册表7d2f删除就可以了,但是有的同学找到了NavisworksManage注册表7d2f也删除了,但是NavisworksManage再次安装失败。还有的同学直接重装系统,但是重装系统后NavisworksManage仍然无法安装。还有的同学直接把NavisworksManage的程序文件删除,重装NavisworksManage的时候会显示已安装。那么NavisworksM
@FormUrlEncoded @PUT(Constant.BOSS_HX_CHANGE_PHONE_INTERVIEW) Call<ResponseHxResultBean>handleInterview(@Field("user_id")Stringuser_id,@Field("token")Stringtoken,@Field("id")Stringid,@Field("status")intstatus,@Field("inter_id")Stringinter_id,@Field("schedule_id")Stringschedule_id); }复制 网络请求的接口如下所示,其实与POST请求基本相同只是将注解@POST还成了@PUT至于需要修好的Content-Type网上查询资料可以添加注解@Headers(静态请求头) 其他请求的部分就正常写就行了。
packagecom.fengunion.sf;importorg.junit.platform.commons.util.StringUtils;importjava.util.HashMap;importjava.util.Map;importjava.util.Random;publicclassCountUtil{privatestaticMap<String,Integer>map=null;static{map=newHashMap<String,Integer>();}/****实现一个简单的计数器*@paramstr*@return*/publicstaticintcountNum(Stringstr){if(StringUtils.isNotBlank(str)){Integercount=map.get(str);if(count==null){count=1;}else{count++;}map.put(str,count);}returnmap.get(str);}publicstaticvoidmain(String[]args){fo