首页 文章 精选 留言 我的

精选列表

搜索[网站开发],共10000篇文章
优秀的个人博客,低调大师

每日一博 | BBBUG 音乐聊天室的开发故事和架构设计

办公室有个小伙伴喜欢拿音响放歌,然而总放一些动次打次的广场舞DJ曲目,同事听了都头大,每次让人家切歌又不方便 于是某同事吐槽:“我们点啥你放啥?” 一天之后…… “张三你刚说放啥来着?” “卧槽,我说的是刘德华的17岁,你这放的是谁的版本?” “17岁QQ音乐要会员,放不了。” …… 我实在看不下去了,要不写一个点歌平台? 说干就干,第一版俩小时搞定: 一个点歌端,爬虫爬各大平台,包括一些VIP歌曲也给爬下来,只能搜索,点歌。 一个导播端,可以看大伙点了什么歌,导播端可以插队播放,删除。 好,上线。 两天后…… 老板:“卧槽你们当办公室KTV呢?开音响听歌???” 好吧,所有人默默带上了耳机,但点歌端不能播放音乐,只能点歌,这一版宣告凉凉。 …… 那就升级吧: 这一版,不区分点歌端和导播端,所有端都一样,做一个共享队列,大伙点,大伙自己带耳机听。 一天后…… “你能不能让大家听到的歌进度保持一致?我还在听上一首,他们先进去的都已经自动播下一首了……” OK,继续升级,加歌曲同步。 好家伙这一次,跟KTV没啥区别了。 这一版有点意思,还加上了“送啤酒”这个小互动,是不是更像KTV了? 好吧我承认,这种小互动貌似没什么用,于是后来的版本掐掉了。 然而好景不长: “大厅这群人有毒吧?跟五条人杠上了?” “我实在是不敢恭维在座各位的歌单” “这都点的什么玩意歌啊……” “Mojito我实在是听得要吐了……” 好吧,那就安排,自己开房间听歌。 于是有了多房间的版本,每个人都可以创建自己的房间,听自己的歌。 顺便加上一个二级域名和独立域名的绑定功能,于是有了下面的设计图: 这就很棒了,大伙都可以在大厅一起听,也可以去自己房间里点歌听。 好好好景不长,需求又来了~ “自己的房间,能不能不点歌,自动播放自己点过的歌?” 安安安排! 那就来一个电台模式吧,设置了就自动播放自己的歌,有人点歌的话让他排队吧。 这下可以了吧? 安安稳稳听歌一周后…… “好无聊啊,光听歌,能让我听听相声小品吗?” 安安安安安排! 爬谁呢,就蜻蜓FM吧。 上线了小说故事模式…… 好好好好好景还是不长 “这个UI好丑啊,能换换吗?” 我也这么觉得,那就改吧,于是改成了这样 “白色是不是太晃了,出个暗黑模式呗?” “我TM……” 安安安安安安安排! 感觉还挺棒, 本以为故事到这里就基本结束了,然而,嗯。 “网页好烦人啊,能出个Windows客户端吗?” 安安安安安P,等下,C#我快忘干净了。 花俩小时回忆了下C#,又花俩小时学习了下 WTF…… 安安安安安安安排! 于是有了这个Windows客户端 好家伙,写完了,发布了,实际统计了下使用数据,使用人数不超过3,WTF???? “你这大部分是程序员在用,要不出个vscode的插件呗。” 我…… 故事快结束了吗? 天真。 “出个APP吧!” 我…… 好,安排,就拿Uniapp整一套吧。 体验还挺不错,不过因为音乐版权问题,这里就没上架AppStore。 “App下载好麻烦,整一套小程序吧?” 安安安安安安安安安安安安排! 整! 故事到这里就基本结束了,心累。 一口气把所有端的代码都开源了,欢迎有兴趣的小伙子给点点小星星呀。 Github https://github.com/HammCn Gitee https://gitee.com/bbbug_com 顺便附上整体的架构设计图 体验一下 https://www.bbbug.com

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

从微信小程序到鸿蒙js开发【05】——tabs组件&每日新闻

目录: 1、tabs, tab-bar, tab-content 2、tabs的事件处理 3、tabs实现的每日新闻 1、tabs, tab-bar, tab-content 上章说到,鸿蒙的list组件仅能支持竖向滚动,无法实现横向滚动。如果需要作出可横向滚动的顶部菜单栏,鸿蒙提供了tabs组件。tabs配合两个子元素组件tab-bar和tab-content,即可很轻松地实现顶部菜单+内容切换效果。 tab-bar的子元素为text,tab-content的子元素为div。 hml: <div class="container"> <tabs class="tabs"> <tab-bar> <block for="{{ tabBar }}"> <text> {{ $item.name }} </text> </block> </tab-bar> <tab-content> <block for="{{ tabBar }}"> <div style="background-color: {{ $item.color }}; height: 500px;"> <text style="font-size: 50px;"> {{ $idx }} </text> </div> </block> </tab-content> </tabs> </div> css: .container { width: 100%; height: 1200px; display: flex; flex-direction: column; } .tabs{ width: 100%; height: 100%; } js: export default { data: { tabBar: [{name: '推荐', color: '#1296db'}, {name: '最新', color: '#e20a0b'}, {name: '最热', color: '#cdcdcd'}] } } 由效果可看出,tab-bar组件是顶部的菜单栏,自带了动态样式。tab-content组件是下方的内容部分,会根据点击的菜单切换到对应的内容部分。tab-bar和tab-content是根据索引值一一对应的。这里还发现,哪怕tab-content给了height: 500px的样式,还是会占满整个tabs的高度。 tabs的属性index用于指定默认选中的菜单索引项,vertical指定是否为竖向菜单栏。这里设置为竖向后可以看出,tab-bar变为占满一列,tab-content的height样式起作用,宽度则占满tabs。 <tabs class="tabs" index="1" vertical="true"> 多放几个菜单项,并设置tab-bar的属性mode="fixed",则菜单项无法滑动,均布占满整行。 <tabs class="tabs" index="0" vertical="false"> <tab-bar mode="fixed"> mode="scrollable",就有横向滚动效果了,且点击菜单项后整个菜单栏会有适应移动效果。 <tab-bar mode="scrollable"> tab-content属性scrollable-"true",菜单对应内容就可以竖向滚动了,和list效果类似。 <tab-content scrollable="true"> 2、tabs的事件处理 如想捕获菜单项变化的事件并进行处理,需在tabs标签中通过onchange进行绑定。在传入的事件对象中,index则为选中菜单项的索引值。这里使用prompt模块的showToast()进行调试。 <tabs class="tabs" index="0" vertical="false" onchange="changeTab"> import prompt from '@system.prompt'; export default { data: { tabBar: [ ... ] }, changeTab(event) { prompt.showToast({ message: '你点击了第' + event.index + '项。' }) } } 这个onchange事件捕获的条件比较苛刻,注意: tabs tab-bar text tab-content div 以上组件都必须存在,且tab-bar中text的数量和tab-content中div的数量需一致。 或者也可以在text上通过onclick绑定事件,就没有以上的严苛触发条件。 <div class="container"> <tabs class="tabs" index="0" vertical="false"> <tab-bar mode="scrollable"> <block for="{{ tabBar }}"> <text onclick="clickTab($idx)"> {{ $item.name }} </text> </block> </tab-bar> ...... import prompt from '@system.prompt'; export default { data: { tabBar: [......] }, clickTab(idx) { prompt.showToast({ message: '你点击了第' + idx + '项。' }) } } 当然,弊端也很明显。tab-bar文字下的蓝色线条并没有跟随移动,tab-content也没有随之改变,只是事件可以捕获到。这种用法适用于只希望保留tab-bar的部分,自定义动态样式,可以省略tab-content以及其中的div。哪一种方式更好,还得看需求。 3、tabs实现的每日新闻 这里运用了整套tabs组件,从聚合数据新闻头条接口(https://www.juhe.cn/docs/api/id/235)请求数据,实现了一个简单的每日新闻模块。因为鸿蒙的页面布局如果高度超过手机总高度就会有滑动效果,tab-bar和自定义的底部菜单都是固定的,因此也用不着list组件了。代码如下: hml: <!-- 每日新闻 --> <div> <tabs class="newsTabs" onchange="changeNewsType"> <tab-bar> <block for="{{ newsTitles }}"> <text> <span>{{ $item.name }}</span> </text> </block> </tab-bar> <tab-content> <block for="{{ newsTitles }}"> <div class="newsView"> <block for="{{ newsList }}"> <div class="newsItem"> <image src="{{ $item.thumbnail_pic_s }}" class="newsImg"></image> <div class="newsContent"> <text> {{ $item.title }} </text> <div class="newsDesc"> <text> {{ $item.author_name }} </text> <text> {{ $item.date }} </text> </div> </div> </div> </block> </div> </block> </tab-content> </tabs> </div> <!-- 每日新闻end --> css: /*每日新闻*/ .newsTabs { width: 100%; height: 100%; } .newsView { width: 100%; height: 100%; display: flex; flex-direction: column; } .newsItem { width: 100%; height: 240px; border-bottom: 1px solid #bbbbbb; display: flex; align-items: center; } .newsImg { margin-left: 20px; width: 200px; height: 200px; } .newsContent { display: flex; flex-direction: column; margin-right: 20px; margin-left: 20px; } .newsContent>text { margin-top: 20px; height: 140px; font-size: 34px; color: #333333; } .newsDesc { height: 60px; line-height: 60px; display: flex; justify-content: space-between; } .newsDesc>text { font-size: 28px; color: #777777; } js: data: { newsTitles: [ { name: '头条', value: 'top' }, { name: '社会', value: 'shehui' }, { name: '国内', value: 'guonei' }, { name: '国际', value: 'guoji' }, { name: '娱乐', value: 'yule' }, { name: '体育', value: 'tiyu' }, { name: '军事', value: 'junshi' }, { name: '科技', value: 'keji' }, { name: '财经', value: 'caijing' }, { name: '时尚', value: 'shishang' } ], newsList: [], }, changeNewsType(event) { let type = this.newsTitles[event.index].value; fetch.fetch({ url: 'http://v.juhe.cn/toutiao/index?key=xxxxx&type=' + type, responseType: 'json', success: res => { let data = JSON.parse(res.data); this.newsList = data.result.data; } }) }, 作者:Chris. 想了解更多内容,请访问: 51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com

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

redis分布式锁的这些坑,我怀疑你是假的开发

摘要:用锁遇到过哪些问题? 一、白话分布式 什么是分布式,用最简单的话来说,就是为了较低单个服务器的压力,将功能分布在不同的机器上面;就比如: 本来一个程序员可以完成一个项目:需求->设计->编码->测试 但是项目多的时候,一个人也扛不住,这就需要不同的人进行分工合作了 这就是一个简单的分布式协同工作了; 二、分布式锁 首先看一个问题,如果说某个环节被终止或者别侵占,就会发生不可知的事情 这就会出现,设计好的或者设计的半成品会被破坏,导致后面环节出错; 这时候,我们就需要引入分布式锁的概念; 何为分布式锁? 当在分布式模型下,数据只有一份(或有限制),此时需要利用锁的技术控制某一时刻修改数据的进程数。 用一个状态值表示锁,对锁的占用和释放通过状态值来标识。 分布式锁的条件: 可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行。 这把锁要是一把可重入锁(避免死锁) 这把锁最好是一把阻塞锁 这把锁最好是一把公平锁 有高可用的获取锁和释放锁功能 获取锁和释放锁的性能要好 分布式锁的实现: 分布式锁的实现由很多种,文件锁、数据库、redis等等,比较多,在实践中,还是redis做分布式锁性能会高一些; 三、redis实现分布式锁 首先看两个命令: setnx:将 key 的值设为 value,当且仅当 key 不存在。 若给定的 key 已经存在,则 SETNX 不做任何动作。 SETNX 是SET if Not eXists的简写。 127.0.0.1:6379> set lock "unlock" OK 127.0.0.1:6379> setnx lock "unlock" (integer) 0 127.0.0.1:6379> setnx lock "lock" (integer) 0 127.0.0.1:6379> expire:EXPIRE key seconds 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除 127.0.0.1:6379> expire lock 10 (integer) 1 127.0.0.1:6379> ttl lock 8 127.0.0.1:6379> get lock (nil) 基于分布式锁的流程: 这就是一个简单的分布式锁的实现流程,具体代码实现也很简单,就不赘述了; 四、redis实现分布式锁问题 如果出现了这么一个问题:如果setnx是成功的,但是expire设置失败,那么后面如果出现了释放锁失败的问题,那么这个锁永远也不会被得到,业务将被锁死? 解决的办法:使用set的命令,同时设置锁和过期时间 set参数: set key value [EX seconds] [PX milliseconds] [NX|XX] EX seconds:设置失效时长,单位秒 PX milliseconds:设置失效时长,单位毫秒 NX:key不存在时设置value,成功返回OK,失败返回(nil) XX:key存在时设置value,成功返回OK,失败返回(nil) 实践: 127.0.0.1:6379> set unlock "234" EX 100 NX (nil) 127.0.0.1:6379> 127.0.0.1:6379> set test "111" EX 100 NX OK 这样就完美的解决了分布式锁的原子性。 五、用锁遇到过哪些问题?又是如何解决的? 未关闭资源 由于当前线程 获取到redis 锁,处理完业务后未及时释放锁,导致其它线程会一直尝试获取锁阻塞,例如:用Jedis客户端会报如下的错误信息 1redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool redis线程池已经没有空闲线程来处理客户端命令。使用原生方法记得关闭! 解决的方法也很简单,只要我们细心一点,拿到锁的线程处理完业务及时释放锁 B的锁被A给释放了 我们知道Redis实现锁的原理在于 SETNX命令。当 key不存在时将 key的值设为 value ,返回值为 1;若给定的 key已经存在,则 SETNX不做任何动作,返回值为 0 。 SETNX key value 我们来设想一下这个场景:A、B两个线程来尝试给key myLock加锁,A线程先拿到锁(假如锁3秒后过期),B线程就在等待尝试获取锁,到这一点毛病没有。 那如果此时业务逻辑比较耗时,执行时间已经超过redis锁过期时间,这时A线程的锁自动释放(删除key),B线程检测到myLock这个key不存在,执行 SETNX命令也拿到了锁。 但是,此时A线程执行完业务逻辑之后,还是会去释放锁(删除key),这就导致B线程的锁被A线程给释放了。 为避免上边的情况,一般我们在每个线程加锁时要带上自己独有的value值来标识,只释放指定value的key,否则就会出现释放锁混乱的场景 一般我们可以设置value为业务前缀_当前线程ID或者uuid,只有当前value相同的才可以释放锁 锁过期了,业务还没执行完 redis分布式锁过期,而业务逻辑没执行完的场景,不过,这里换一种思路想问题,把redis锁的过期时间再弄长点不就解决了吗? 那还是有问题,我们可以在加锁的时候,手动调长redis锁的过期时间,可这个时间多长合适?业务逻辑的执行时间是不可控的,调的过长又会影响操作性能。 要是redis锁的过期时间能够自动续期就好了。 为了解决这个问题我们使用redis客户端redisson,redisson很好的解决了redis在分布式环境下的一些棘手问题,它的宗旨就是让使用者减少对Redis的关注,将更多精力用在处理业务逻辑上。 redisson对分布式锁做了很好封装,只需调用API即可。 1 RLock lock = redissonClient.getLock("stockLock"); redisson在加锁成功后,会注册一个定时任务监听这个锁,每隔10秒就去查看这个锁,如果还持有锁,就对过期时间进行续期。默认过期时间30秒。这个机制也被叫做:“看门狗” redis主从复制的坑 redis高可用最常见的方案就是主从复制(master-slave),这种模式也给redis分布式锁挖了一坑。 redis cluster集群环境下,假如现在A客户端想要加锁,它会根据路由规则选择一台master节点写入key mylock,在加锁成功后,master节点会把key异步复制给对应的slave节点。 如果此时redis master节点宕机从节点复制失败,为保证集群可用性,会进行主备切换,slave变为了redis master。B客户端在新的master节点上加锁成功,而A客户端也以为自己还是成功加了锁的。另外如果主从复制延迟同样也会造成加锁和解锁延迟的问题。 此时就会导致同一时间内多个客户端对一个分布式锁完成了加锁,导致各种脏数据的产生。 毕竟redis是保持的AP而非CP,如果要追求强一致性可以使用zookeeper分布式锁。 本文分享自华为云社区《redis分布式锁?易踩得坑》,原文作者:minjie 。 点击关注,第一时间了解华为云新鲜技术~

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

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

WebStorm

WebStorm

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

用户登录
用户注册