首页 文章 精选 留言 我的

精选列表

搜索[轻量级mvc框架],共10000篇文章
优秀的个人博客,低调大师

orcache —— 轻量级本地内存缓存

orcache 是一款极简设计、高性能、并发安全、支持分布式一致性的内存缓存。 特性 代码量<300行、30s完成接入 高性能、极简设计、并发安全 支持LRU和LRU-2两种模式 额外小组件支持分布式一致性 基准性能 点此查看用例 点此查看结果 如何使用 下载包(预计5秒) 非go modules模式: sh>go get -u github.com/orca-zhang/orcache go modules模式: sh>go mod tidy && go mod download 引入包(预计5秒) import ( "time" "github.com/orca-zhang/orcache" ) 定义实例(预计5秒) 可以放置在任意位置(全局也可以),建议就近定义 var c = orcache.NewLRUCache(16, 200, 10 * time.Second) 设置缓存(预计5秒) c.Put("uid1", o) // o可以是任意变量,一般是对象指针,存放固定的信息,比如*UserInfo 查询缓存(预计5秒) if v, ok := c.Get("uid1"); ok { return v.(*UserInfo) // 不用类型断言,咱们自己控制类型 } // 如果内存缓存没有查询到,下面再回源查redis/db 删除缓存(预计5秒) 在信息发生变化的地方 c.Del("uid1") 运行吧 🎉完美搞定🚀性能直接提升X倍! sh>go run <你的main.go文件> 参数说明 NewLRUCache 第一个参数是桶的个数,用来分散锁的粒度,每个桶都会使用独立的锁 不用担心,随意设置一个就好,orcache会找一个等于或者略大于输入大小的2的幂次的数字,后面便于掩码计算 第二个参数是每个桶所能容纳的item个数上限 意味着orcache全部写满的情况下,应该有第一个参数✖️第二个参数个item 第三个参数是每个item的过期时间 orcache使用内部定时器提升性能,默认100ms精度,每秒校准 最佳实践 复杂对象优先存放指针(注意⚠️一旦放进去不要再修改其字段,即使再拿出来也是,item有可能被其他人同时访问) 如果需要修改,解决方案:取出字段每个单独赋值,或者用copier做一次深拷贝后在副本上修改 也可以存放对象(相对于上一个性能差一些,因为拿出去有拷贝) 缓存的对象尽可能越往业务上层越大越好(节省内存拼装和组织时间) 如果不想因为类似遍历的请求把热数据刷掉,可以改用LRU-2模式,虽然可能有很少的损耗(💬什么是LRU-2) 一个实例可以存储多种类型的对象,试试key格式化的时候加上前缀,用冒号分割 并发访问量大的场景,试试256、1024个桶,甚至更多 特别场景 LRU-2模式 💬什么是LRU-2 直接在NewLRUCache()后面跟.LRU2(<num>)就好,参数<num>代表LRU-2热队列的item上限个数(每个桶) var c = orcache.NewLRUCache(16, 200, 10 * time.Second).LRU2(1024) 空缓存哨兵(不存在的对象不用再回源) // 设置的时候直接给`nil`就好 c.Put("uid1", nil) // 读取的时候,也和正常差不多 if v, ok := c.Get("uid1"); ok { if v == nil { // 注意⚠️这里需要判断是不是空缓存哨兵 return nil // 是空缓存哨兵,那就返回没有信息或者也可以让`uid1`不出现在待回源列表里 } return v.(*UserInfo) } // 如果内存缓存没有查询到,下面再回源查redis/db 需要修改部分数据,且用对象指针方式存储时 比如,我们从orcache中获取了*UserInfo类型的用户信息缓存v,需要修改其状态字段 import ( "github.com/jinzhu/copier" ) o := &UserInfo{} copier.Copy(o, v) // 从v复制到o o.Status = 1 // 修改副本的数据 统计缓存使用情况 实现超级简单,注入inspector后,每个操作只多了一次原子操作,具体看代码 引入stats包 import ( "github.com/orca-zhang/orcache/stats" ) 绑定缓存实例(名称为自定义的池子名称,内部会按名称聚合) var _ = stats.Bind("user", c) var _ = stats.Bind("user", c, c1, c2) var _ = stats.Bind("room", caches...) 打印统计信息 stats.Stats().Range(func(k, v interface{}) bool { fmt.Printf("stats: %s %+v\n", k, v) return true }) 分布式一致性组件 💬原理说明 引入dist包 import ( "github.com/orca-zhang/orcache/dist" ) 绑定缓存实例 名称为自定义的池子名称,内部会按名称聚合 注意⚠️绑定可以放在全局,不依赖初始化 var _ = dist.Bind("user", c) var _ = dist.Bind("user", c, c1, c2) var _ = dist.Bind("token", caches...) 绑定redis client 目前支持redigo和goredis,其他库可以自行实现dist.RedisCli接口,或者提issue给我 go-redis v7及以下版本 import ( "github.com/orca-zhang/orcache/dist/goredis/v7" ) dist.Init(goredis.Take(redisCli)) // redisCli是*redis.RedisClient类型 dist.Init(goredis.Take(redisCli, 100000)) // 第二个参数是channel缓冲区大小,不传默认100 go-redis v8及以上版本 import ( "github.com/orca-zhang/orcache/dist/goredis" ) dist.Init(goredis.Take(redisCli)) // redisCli是*redis.RedisClient类型 dist.Init(goredis.Take(redisCli, 100000)) // 第二个参数是channel缓冲区大小,不传默认100 redigo 注意⚠️github.com/gomodule/redigo要求最低版本go 1.14 import ( "github.com/orca-zhang/orcache/dist/redigo" ) dist.Init(redigo.Take(pool)) // pool是*redis.Pool类型 主动通知所有节点、所有实例删除(包括本机) 当db的数据发生变化或者删除时调用 发生错误时会降级成只处理本机所有实例(比如未初始化或者网络错误) dist.OnDel("user", "uid1") 不希望你白来 客官,既然来了,学点东西再走吧! 我想尽力让你明白orcache做了啥,以及为什么要这么做 什么是本地内存缓存 L1 缓存引用 .................... 0.5 ns 分支错误预测 ...................... 5 ns L2 缓存引用 ...................... 7 ns 互斥锁/解锁 ...................... 25 ns 主存储器引用 .................... 100 ns 使用 Zippy 压缩 1K 字节 ........3,000 ns = 3 µs 通过 1 Gbps 网络发送 2K 字节... 20,000 ns = 20 µs 从内存中顺序读取 1 MB ........ 250,000 ns = 250 µs 同一数据中心内的往返........... 500,000 ns = 0.5 ms 发送数据包 加州<->荷兰 .... 150,000,000 ns = 150 ms 从上表可以看出,内存访问和网络访问(同数据中心)差不多是一千到一万倍的差距! 曾经遇到不止一个工程师:“缓存?上redis”,但我想说,redis不是万金油,某些程度上讲,用它还是噩梦(当然我说的是缓存一致性问题...😄) 因为内存操作非常快,相对于redis/db你基本可以忽略不计,比如现在有一个查询接口,我们把结果缓存1秒,也就是1秒内不会请求redis/db,如果接口的QPS是1000,那回源次数降低到了1/1000(理想情况),意味着访问redis/db部分的性能提升了1000倍,听上去是不是很棒? 继续看,你会爱上她的!(当然也可能是他,亦或者是牠,ahaha) 使用场景,解决什么问题 高并发大流量场景 缓存热点数据(比如人气比较高的直播间) 突发QPS削峰(比如信息流中突发新闻) 节省成本 单机场景(不部署redis、memcache也能快速提升QPS上限) redis和db实例降配(能拦截大部分请求) 不怎么会变化的数据(写少读多) 比如配置等(这类数据使用地方多,会有放大效应,很多时候可能会因为这些配置热key对redis实例的规格误判,需要单独为它们升配) 可以容忍短暂不一致的数据 信息查询(用户头像、昵称、商品库存(实际下单会在db再次检查)等) 配置延迟生效(过期时间10秒,那最多10秒生效) 设计思路 orcache是lrucache库的升级版本 最下层是用原生map和存双链表的node实现的最基础LRU(最久未访问) PS:我实现的其他版本(go/C++/js)在leetcode都是超越100%的解法 第2层包了分桶策略、并发控制、过期控制(会自动适配等于或者略大于输入大小的2的幂次个桶,便于掩码计算) 第2.5层用很简单的方式实现了LRU-2能力,代码不超过20行,直接看源码(搜关键词LRU-2) 什么是LRU 最久未访问的优先驱逐 每次被访问,item会被刷新到队列的最前面 队列满后再次写入新item,优先驱逐队列最后面、也就是最久未访问的item 什么是LRU-2 LRU-K是少于K次访问的用单独的LRU队列存放,超过K次的另外存放 主要优化的场景是比如一些遍历类型的查询,批量刷缓存以后,很容易把一些本来较热的item给驱逐掉 为了实现简单,我们这里实现的是LRU-2,也就是第2次访问就放到热队列里,并不记录访问次数 主要优化的是热key的缓存命中率 分布式一致性组件原理 其实简单的利用了redis的pubsub功能 主动告知被缓存的信息有更新,广播到其他所有节点 某种意义上说,它只是缩小不一致时间窗口的一个方式(有网络延迟且不保证一定完成) 需要注意⚠️: 尽量减少使用,适合用在写少读多WORM(Write-Once-Read-Many)的场景 redis性能毕竟不如内存,而且有广播类通信(写放大) 以下场景会降级(时间窗口变大),但至少会保证当前节点的强一致性 redis不可用、网络错误 消费goroutine panic 存在未生效节点(灰度canary发布,或者发布过程中)的情况下,比如 已使用orcache但首次添加此插件 新加入缓存的数据或者新加的删除操作 关于性能 释放锁不用defer(单接口性能差20倍,看到有宣称高性能还用defer的,直接pass吧) 不用异步清理(没意义,分散到写时驱逐更合理,不易抖动) 没有用内存容量来控制(单个item的大小一般都有预估大小,简单控制个数即可) 分桶策略,自动选择2的幂次个桶(分散锁竞争,2的幂次掩码操作更快) key用string类型(可扩展性强;语言内建支持引用,更省内存) 不用虚表头(虽然绕脑一些,但是有20%左右提升) 选择LRU-2实现LRU-K(实现简单,近乎没有额外损耗) 没用整块内存(写满后复用以前的内存效果也很好,整块方式尝试过提升不大、但可读性大大降低) 可以直接存指针(不用序列化,如果使用[]byte那优势大大降低) 使用内部定时器计时(默认100ms精度,每秒校准,剖析发现time.Now()产生临时对象导致GC耗时增加) 失败的优化尝试 key由string改为reflect.StringHeader,结果:负优化 node预分配连续空间,通过游标和freelist决定新申请(是否满)还是复用,结果:不明显 互斥锁改为读写锁,Get请求也会修改数据,访问违例,即使不改数据,结果:读写混合场景负优化 time.Timer实现内部定时器,结果:触发不稳定,后直接用Sleep实现定时器 分布式一致性组件挂inspector自动同步更新和删除,结果:性能影响较大且需要特殊处理循环调用问题 关于GC优化 就像我在C++版性能剖析器里提到的性能优化的几个层次,单从一个层次考虑性能并不高明 《第三层次》里有一句“没有比不存在的东西性能更快的了”(类似奥卡姆剃刀),能砍掉一定不要想着优化 比如为了减少GC大块分配内存,却提供[]byte的值存储,意味着必须序列化、拷贝(虽不在库的性能指标里,人家用还是要算,包括:GC、内存、CPU) 如果序列化的部分可以复用用在协议层拼接,能做到ZeroCopy,那也无可厚非,而orcache存储指针直接省了额外的部分 我想表达的并不是GC优化不重要,而更多应该结合场景,使用者额外损耗也需要考虑,而非宣称gc-free,结果用起来并非那样 我所崇尚的“暴力美学”是极简,缺陷率和代码量成正比,复杂的东西早晚会被淘汰,KISS才是王道 orcache一共只有不到300行,千行bug率一定的情况下,它的bug不会多 常见问题 问:一个实例可以存储多种对象吗? 答:可以呀,比如加前缀格式化key就可以了(像用redis那样冒号分割),注意⚠️别搞错类型。 问:如何给不同item设置不同过期时间? 答:用多个缓存实例。(😄没想到吧) 问:如果有热热热热key问题怎么解决? 答:本身【本地内存缓存】就是用来抗热key的,这里可以理解成是非常非常热的key(单节点几十万QPS),它们最大的问题是对单一bucket锁定次数过多,影响在同一个bucket的其他数据。那么可以这样:一是改用LRU-2不让类似遍历的请求把热数据刷掉,二是除了增加bucket,可以用多实例(同时写入相同的item)+读随机访问某一个的方式,让热key有多个副本,不过删除(反写)的时候要注意多实例全部删除,适用于“写少读多WORM(Write-Once-Read-Many)”的场景,或者“写多读多”的场景可以把有变化的diff部分单独摘出来转化为“写少读多WORM(Write-Once-Read-Many)”的场景。 问:为什么不用虚表头方式处理双链表?太弱了吧! 答:2019-04-22泄漏的【lrucache】被人在V站上扒出来喷过,还真不是不会,现在的写法,虽然比pointer-to-pointer方式读起来绕脑,但是有20%左右的提升哈!(😄没想到吧) 问:为什么不提供int类型的key的接口? 答:考虑过,但是为了分布式一致性处理的简单,只提供string的接口看着也不错,用fmt.Sprint(i)也不麻烦。

优秀的个人博客,低调大师

Filebeat轻量级日志采集工具

Beats 平台集合了多种单一用途数据采集器。这些采集器安装后可用作轻量型代理,从成百上千或成千上万台机器向 Logstash 或 Elasticsearch 发送数据。 一、架构图 此次试验基于前几篇文章,需要先基于前几篇文章搭建基础环境。 二、安装Filebeat 下载并安装Filebeat wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.0.1-x86_64.rpm yum install ./filebeat-6.0.1-x86_64.rpm 修改Filebeat配置文件 vim /etc/filebeat/filebeat.yml # 主配置文件 \- type: log # 文档类型 paths: \- /var/log/httpd/access.log* # 从哪里读入数据 # 输出在elasticsearch与logstash二选一即可 output.elasticsearch: #将数据输出到Elasticsearch。与下面的logstash二者选一 hosts: ["localhost:9200"] output.logstash: # 将数据传送到logstash,要配置logstash使用beats接收 hosts: ["172.18.68.14:5044"] 启动Filebeat systemctl start filebeat 三、配置Filebeat 配置Logstash接收来自Filebeat采集的数据 vim /etc/logstash/conf.d/test.conf input { beats { port => 5044 # 监听5044用于接收Filebeat传来数据 } } filter { grok { match => { "message" => "%{COMBINEDAPACHELOG}" # 匹配HTTP的日志 } remove_field => "message" # 不显示原信息,仅显示匹配后 } } output { elasticsearch { hosts => ["http://172.18.68.11:9200","http://172.18.68.12:9200","http://172.18.68.13:9200"] # 集群IP index => "logstash-%{+YYYY.MM.dd}" action => "index" document_type => "apache_logs" } } 启动Logstash /usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/test.conf 四、模拟日志访问 通过curl命令来模拟客户访问,生成访问日志 curl 127.0.0.1 curl 172.18.68.51 curl 172.18.68.52 curl 172.18.68.53 五、验证信息 清除之前实验的旧数据(删除时要在对话框中输入删除),然后可以看到filebeat采集数据经过Logtash过滤再送给Elasticsearch的数据。 扩展 随着ELK日志系统逐渐升级,现在已经能基于Filebeat采集各节点日志,Logstash过滤、修剪数据,最后到ELasticsearch中进行索引构建、分词、构建搜索引擎。现在可以基于Elasticsearch的Head查看在浏览器中查看,但是Head仅仅能简单查看并不能有效进行数据分析、有好展示。要想进行数据分析、有好展示那就需要用到Kibana,Kibana依然放在下一篇文章中讲解,这里先放上架构图。

优秀的个人博客,低调大师

Retrofit2.0+ .Net MVC4(WebApi) 上传多张图片

最近在做Android项目,使用的是MVP+Retrofit+rxjava+dagger的项目架构,RestFul 服务使用的.Net平台的WebApi。由于业务中需要有多文件上传的功能。所以在这里记载一下,便于以后查阅。 Android端Retrofit api 定义 在这次上传中,我们将实体类数据和文件数据一起上传到服务端。所以只写一个api就好 1 2 () Observable<HttpResult<String>>addEvent(()Evententity,()List<MultipartBody.Part>parts); 读取文件并将其转换成MuiltipartBody.Part 列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 List<MultipartBody.Part>getFilesMap(){ =ArrayList<MultipartBody.Part>(); Filefile=; { (!=&&.size()>){ (Stringpath:){ file=File(path); RequestBodyrequestBody=RequestBody.(MediaType.(),file); MultipartBody.Partpart=MultipartBody.Part.(,file.getName(),requestBody); .add(part); } } }(Exceptione){ e.printStackTrace(); } ; } 调用Retrofit 进行网络请求,并将实体类数据和文件数据一并发送到 服务端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 addEvent(){ Evententity=.getEntity(); List<MultipartBody.Part>fileMap=.getFilesMap(); .clear(); { Subscriptionsubscription=.getRetrofitInstance() .create(EventInterface.) .addEvent(entity,fileMap) .subscribeOn(.io()).observeOn(.ui()).subscribe(Subscriber<HttpResult<String>>(){ onCompleted(){ } onError(Throwablee){ .setLoadingIndicator(); e.printStackTrace(); } onNext(HttpResult<String>httpResult){ (httpResult.getResultCode()!=ConstData.HttpResult.){ .setLoadingIndicator(); }{ .setLoadingIndicator(); } } }); .add(subscription); }(Exceptione){ e.printStackTrace(); } } 在webapi 端对文件以及数据进行接收。由上面转化MultipartBody 那一个环节我们可以看到,传到服务端的数据是以HTTP形式传送的。数据类型都是multipart/form-data类型的。所以我们在服务端要使用http相关协议来进行接收。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [HttpPost] public HttpResult<string>AddEvent() { //获取EventET的属性 HttpRequestrequest=HttpContext.Current.Request; stringentity=request.Form[ 0 ]; BaseBNbaseBN= new BaseBN(); EventETet=baseBN.CTJsonSerializer<EventET>(entity); SystemBNbn= new SystemBN(); et.EVENT_ID=bn.CreateID( "EVENT" ,HaiQu.N, "EVENT_ID" , "EVENT" ,EventType.YiYou); List<string>addr= new List<string>(); //获取上传的文件 HttpFileCollectioncoll=request.Files; for ( int i= 0 ;i<coll.Count;i++) { HttpPostedFilefile=coll.Get(i); Stringfilename=file.FileName.Substring(file.FileName.LastIndexOf( "\\" )+ 1 ); if (!Directory.Exists(System.Web.HttpContext.Current.Server.MapPath( "~/Upload/" +et.EVENT_ID))) //如果不存在就创建file文件夹 { Directory.CreateDirectory(System.Web.HttpContext.Current.Server.MapPath( "~/Upload/" +et.EVENT_ID)); } stringfileSavePath=string.Format( "~/Upload/" +et.EVENT_ID+ "/{0}" ,filename); file.SaveAs(System.Web.HttpContext.Current.Server.MapPath(fileSavePath)); addr.Add(fileSavePath); } EventBNeventBN= new EventBN(); HttpResult<string>result=eventBN.AddEvent(et,addr,HaiQu.N); return result; } 这里有几点需要注意,实体类的数据,传输到服务端的时候,会以JSON字符串的形式传输到后台。这样的话,在后台使用的时候,需要对其进行反序列化。 另外,上传的文件,在HTTP中是以集合的形式存在,直接取出来,存储到本地就好。有一点需要注意的是,由于IIS对上传文件的限制,需要提前修改一下允许上传文件的上限。要不然,会上传不成功(这些都是已经踩过的坑)。 说在最后,其实上传文件很简单,网上有很多的大神已经讲解的非常详细。之所又自己啰嗦了一下,是因为,在做的过程中,踩了很多的坑,而且网上大多数人都只写了Android端,没有写服务端。再加上,.NET WebApi 用的人比较小众,所以就记录了一下。以后有时间会补上Spring 作为服务端的写法。以及Struts作为服务端的写法。 今天就到这里吧。 本文转自Eumenides_s 51CTO博客,原文链接:http://blog.51cto.com/xiaoshuaigege/1878634 ,如需转载请自行联系原作者

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册