热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

在线直播源码评论弹幕是如何“练”成的?

在线直播源码评论弹幕是如何“练”成的?提起弹幕(dnm),大家都会想到「视频弹幕」。视频弹幕是指网友们在观看视频的同时参与评论,即所谓“

在线直播源码评论弹幕是如何“练”成的?

提起弹幕(dànmù),大家都会想到「视频弹幕」。视频弹幕是指网友们在观看视频的同时参与评论,即所谓“即时反馈”, 评论以飞行形式横穿屏幕,视觉效果类似多发密集的子弹飞速而过,故称之为“弹幕”。“弹幕”最大的特点就是允许受众在观看直播的同时将评论内容发送到服务器与直播同步播放,这可以让观众的反馈瞬间产生,与主播发生即时互动,甚至可以形成隔空对话

随着手机竖屏直播时代的到来,弹幕也从原本的“横穿飞行”,衍生出一种新的模式——在屏幕左下方竖向滚动。实际效果如下图所示。

从效果图上我们还看到有几点重要信息:


  • 历史弹幕上屏:为了活跃气氛,观众初次进入直播间可以观看前 30 条历史弹幕。
  • 即时弹幕消息上屏:即时收到的弹幕消息从底部上屏,并实现自动滚动。
  • 个人评论消息上屏:个人评论的消息“优先”上屏,不受即时消息堆积影响。
  • 系统提示消息上屏:系统提示消息分两种,一种是固定提示,固定在弹幕列表头部,不会消失。另外一种是主播操作产生的临时提示,跟弹幕消息一起,超过数量限制时,就会被清理。

看似简单,实现的过程中却需考虑如下几点:


  • 即时消息堆积问题: 在李佳琦、薇娅等超级主播的直播间,每秒接收到的消息数以千计,如果不做“屏控”处理,且有计划地过滤掉“低质”弹幕,而是一股脑地将消息上屏,弹幕便会飞速滚动,那么观众和主播都无法有效地获取内容信息。
  • 上屏弹幕堆积问题: 随着时间的推移,上屏弹幕数量逐渐累积,如果不及时清理陈旧的DOM节点,会导致系统卡顿,甚至程序崩溃。
  • 用户互动体验优化问题: 当用户滚动弹幕时,需要暂停弹幕上屏逻辑,便于用户进行操作;当弹幕滚动到底部时,自动恢复弹幕上屏逻辑;长期暂停的弹幕需要设有自动恢复机制,等等体验问题都是需要优化的内容。
  • 历史弹幕上屏问题: 如若 30 条历史弹幕一窝蜂上屏,弹幕会飞速滚动,给用户极差的体验观感,需要制定策略实现分布式上屏。

2 千里之行,始于布局


2.1 组件的 data 与 properties

一口吃不成胖子,先从效果图显示的布局入手,思考封装这个组件,需要传入哪些参数,哪些是通过properties由父组件传入,哪些通过data来维护,与视图进行通信。

data = {toLast: 'item0', // 弹幕滚动索引realBarrage: [], // 实际上屏的弹幕};properties = {barrageHeight: {type: Number,value: 500,}, // 弹幕容器高度config: {type: Object,value: DEFAULT_CONFIG,}, // 弹幕配置tagConfig: {type: Array,value: TAG_DEFAULT_CONFIG,}, // 标签配置信息systemHint: {type: String,value: DEFAULT_SYSTEM_HINT,}, // 系统提示信息};

先来谈谈properties属性:


  • barrageHeight: Number类型,弹幕容器高度,可动态调节,如下图所示,超级弹幕出现时,需要缩小弹幕高度。

弹幕高度变化图片示例


  • systemHint:String类型,系统提示信息内容,长期存在于弹幕列表头部,默认配置信息如下。

const DEFAULT_SYSTEM_HINT ='系统提示:欢迎来到直播间!直享倡导绿色直播,文明互动,购买直播推荐商品时,请确认购买链接描述与实际商品一致,避免上当受骗。如遇违法违规现象,请立即举报!';

  • config:Object类型,弹幕配置信息, 可配置参数如下:

const DEFAULT_CONFIG = {INTERVAL: 300, // 刷新频率,默认300msBARRAGE_MAX_COUNT: 50, // 上屏弹幕的最大数量POOL_MAX_COUNT: 50, // 弹幕池(未上屏)弹幕上限BARRAGE_MAX_FRAME: 6, // 屏控处理,每次同时上屏弹幕的数量SLEEP_TIME: 5000, // 弹幕休眠时间,默认5000msCHECK_SLEEP: true, // 是否休眠,休眠超过 SLEEP_TIME ,则开启自动滚动
};

  • tagConfig:Array类型,标签配置信息,主要用于自定义化标签的样式和内容,当前的默认配置如下:

const TAG_DEFAULT_CONFIG = [{bgColor: 'linear-gradient(to right, #fb3e3e, #ff834a)',tagName: '主播',},{bgColor: 'linear-gradient(to top, #ffb365, #ff8c17)',tagName: '号主',},{bgColor: 'linear-gradient(to left, #8bb1ff, #5195ff)',tagName: '粉丝',},
];

然后,再谈谈data属性:


  • realBarrage: Array类型,实际上屏的弹幕列表,分两种,一种是评论消息,一种是系统消息。弹幕列表中每一项包含的属性类型说明如下:

interface queueI {name?: string; // 用户昵称comment?: string; // 评论内容isMe?: boolean; // 是否自己发送的弹幕tagIndex?: number; // 标签索引,可自定义,默认情况 0-主播,1-号主,2-粉丝systemInfo?: string; // 系统提示消息,如果存在这个属性,则前面四个属性无需存在
}

其中,namecomment为普通评论消息的必须属性,systemInfo为系统消息的必须属性,两者互斥。 弹幕数据tagIndex属性与标签配置信息中TAG_DEFAULT_CONFIG数组的索引一一对应,默认情况 0-主播,1-号主,2-粉丝。如果修改标签配置信息,那么tagIndex属性值的对应关系也要重新梳理。


  • toLast: String类型,弹幕索引,随着新弹幕不断上屏,弹幕列表需要实现自动滚动,小程序提供的组件的scroll-into-view属性支持滚动到对应的子元素 id,所以需要维护toLast来指定。

2.2 开始布局

知道了每个dataproperties的含义,可以根据设计稿,对布局一把梭了。为了实现可滚动视图区域,组件外层使用包裹。用到的属性如下表所示:


属性类型说明默认值必填
scroll-yboolean允许纵向滚动false
scroll-with-animationboolean在设置滚动条位置时使用动画过渡false
scroll-into-viewstring值应为某子元素 id(id 不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素-
lower-thresholdnumber/string距底部/右边多远时,触发 scrolltolower 事件50
bindscrolltolowereventhandle滚动到底部/右边时触发-
bindscrolleventhandle滚动时触发,event.detail = {scrollLeft, scrollTop, scrollHeight, scrollWidth, deltaX, deltaY}-
bindtouchstarteventhandle手指触摸动作开始-
bindtouchendeventhandle手指触摸动作开始-

容器内的子元素包含两部分,一部分的直播间提示消息,放在滚动列表的头部,永远不会被清理。另外一部分是即时消息,分为评论消息和系统消息,放在realBarrage中,达到数量最大限制时会被清理。

视图布局代码如下所示:

{{systemHint}}= 0 && tagConfig[item.tagIndex]}}" style="background-image: {{tagConfig[item.tagIndex].bgColor}}">{{tagConfig[item.tagIndex].tagName}}{{item.name}}:\t{{item.comment}}{{item.systemInfo}}


3 封装一个类

根据需求,对功能进行思考和拆分,抽象出一个通用类,此处定义为QueueBarrage。这个类维护两个优先级队列,一个是弹幕池,一个是上屏弹幕。服务器推送的弹幕消息进入弹幕池,池子里的弹幕需要进行过滤重排、溢出处理。设置轮询机制,对弹幕进行分批次上屏,同时上屏弹幕也设有溢出处理策略。自己发送的弹幕不走弹幕池,直接上屏。


3.1 类的属性定义和构造器

下列代码是类的属性定义和构造器。


  • queueList:弹幕池,包括所有未上屏的弹幕。
  • barrageList:上屏的弹幕列表。
  • changeCallback:弹幕上屏回调,弹幕上屏逻辑是业务逻辑,需要以回调的形式传入。
  • config: 弹幕配置信息,通过创建类实例传参,可以覆盖默认配置信息。

export default class QueueBarrage {queueList: queueI[]; // 弹幕池,包括所有弹幕barrageList: queueI[]; // 上屏弹幕changeCallback; // 弹幕上屏回调config; // 配置信息isPaused: boolean; // 弹幕是否暂停isActive: boolean; // 弹幕是否休眠timer;private checkActiveTimer;constructor(config = {}) {this.config = { ...defaultConFig, ...config };this.queueList = [];this.barrageList = [];this.isPaused = false;this.isActive = false;this.flush();}
}

3.2 接收到的消息进入弹幕池

前面提到过,在李佳琦、薇娅等超级主播的直播间,每秒接收到的消息数以千计,如果直接一股脑地将消息上屏,弹幕便会飞速滚动,那么观众和主播都无法有效地获取内容信息。因此,需要维护一个「弹幕池」,能对弹幕进行优先级排序,有效地过滤掉“低质”弹幕,同时对池子的容量做限制,添加“溢出”处理策略。

代码实现如下所示,将消息放入弹幕池,如果弹幕数量超出最大数量限制,则对弹幕进行过滤,同时删除超出的那部分弹幕。当然,历史弹幕信息为了保证上下文逻辑的严谨性,是无需进行优先级排序的,所以只需要截取超出的部分。为了区分两种弹幕类型,本函数的第二个参数isFilter用来控制是否进行过滤。

barrageEnterQueue(queue: queueI[], isFilter = true) {queue.forEach(v => {this.queueList.push(v);});// 进入直播间历史弹幕不进行过滤if (this.queueList.length > this.config.POOL_MAX_COUNT) {if (isFilter)this.queueList = filter(this.queueList, this.config.POOL_MAX_COUNT);elsethis.queueList.splice(0,this.queueList.length - this.config.POOL_MAX_COUNT,);}if (!this.isPaused) {!this.timer && this.flush();}}

那么过滤规则是怎么样的呢?这得视业务情况而定,下面贴出本业务的弹幕过滤逻辑:


  • 无意义弹幕的权重降低 0.5
  • 短弹幕的权重降低 0.2
  • 自己发送的弹幕的权重提高 1.0

这里需要注意的是,自己发送的弹幕的权重优先级是最高的,可以走弹幕池,通过过滤提高优先级,但这需要一定的时间消耗。不走弹幕池,直接上屏是比较合理的实现方案。

const CONTENT_FIELD = 'comment';
const REG_MEANINGLESS = /^[\d\s\!\@\#\$\%\^\&\*\(\)\-\=]+$/;
// 权重计算规则
const rules &#61; [function meaningless(this: any, weight) {return this[CONTENT_FIELD] && REG_MEANINGLESS.test(this[CONTENT_FIELD])? weight - 0.5: weight;},function barrage2short(this: any, weight) {return this[CONTENT_FIELD] && this[CONTENT_FIELD].length <3? weight - 0.2: weight;},
];

接下来&#xff0c;便可根据权重计算规则对弹幕进行排序且筛选。第一个参数是弹幕列表&#xff0c;第二个参数是最大数量限制。

export function filter(barrages, limit) &#61;> {barrages.forEach(barrage &#61;> {barrage.weight &#61; rules.reduce((weight, rule) &#61;> {return rule.call(barrage, weight);}, 1);});return barrages.sort((a, b) &#61;> b.weight - a.weight).slice(barrages.length - limit);
};

3.3 轮询上屏

将消息放入弹幕池后&#xff0c;接下来的操作便是轮询从弹幕池里取固定数量的弹幕上屏。实现逻辑如下所示&#xff1a;


  • 第一步&#xff0c;从弹幕池里取出固定数量为BARRAGE_MAX_FRAME&#xff08;默认为 6&#xff09;的弹幕&#xff0c;添加到barrageList&#xff08;上屏弹幕列表&#xff09;中。如上效果图所示&#xff0c;6 条弹幕同时上屏&#xff0c;正好在一个屏幕高度内&#xff0c;不影响用户获取内容信息。
  • 第二步&#xff0c;对barrageList列表做溢出处理&#xff0c;超出最大数量限制BARRAGE_MAX_COUNT&#xff08;默认为 50&#xff09;&#xff0c;删除列表头部超出数量的弹幕。这样可以将上屏弹幕的数量维持在一个最大阈值内。如上效果图所示&#xff0c;弹幕列表数量超出阈值&#xff0c;头部的弹幕已经被清理&#xff0c;用户只能获取最新的 50 条弹幕。
  • 第三步&#xff0c;barrageList得到更新后&#xff0c;需要执行changeCallback回调做真正的上屏处理&#xff0c;这个回调涉及业务逻辑操作&#xff0c;需要在类实例化的时候进行赋值。
  • 使用setTimeout实现轮询机制&#xff0c;每INTERVALms&#xff08;默认为 300&#xff09;执行如上 3 步操作&#xff0c;如果上屏弹幕列表为空&#xff0c;表示没有新的弹幕消息&#xff0c;则不执行上屏回调处理。需要注意的是&#xff0c;这里用setTimeout模拟setInterval实现轮询&#xff0c;这是因为当回调函数的执行被阻塞时&#xff0c;setInterval会产生回调堆积。

private flush() {this.timer &#61; setTimeout(() &#61;> {if (this.queueList.length > 0) {// 从弹幕池中取弹幕this.barrageList &#61; [...this.barrageList,...this.queueList.splice(0, this.config.BARRAGE_MAX_FRAME),];// 判断上屏弹幕是否超过最大限制&#xff0c;如超过&#xff0c;删除旧弹幕if (this.barrageList.length > this.config.BARRAGE_MAX_COUNT) {this.barrageList.splice(0,this.barrageList.length - this.config.BARRAGE_MAX_COUNT,);}// 弹幕上屏this.barrageList.length > 0 &&this.changeCallback &&this.changeCallback(this.barrageList);}this.flush();}, this.config.INTERVAL);}// 弹幕上屏回调函数赋值emitQueueChange(cb) {this.changeCallback &#61; cb;}

3.4 自己发的弹幕直出

用户自己发送的弹幕&#xff0c;不走弹幕池过滤&#xff0c;无视网络质量&#xff0c;无条件优先上屏。代码如下所示&#xff1a;

// 自己的弹幕直出barrageEnterQueueSelf(queue: queueI) {this.barrageList.push(queue);this.changeCallback && this.changeCallback(this.barrageList);}

3.5 轮询控制系统


  • 当用户滑动弹幕列表时&#xff0c;为了方便用户执行截图、赋值、搜索等操作&#xff0c;弹幕上屏逻辑需要暂停。
  • 当用户将列表滚动到底部时&#xff0c;恢复轮询。
  • 同时需要设置重启机制&#xff0c;当弹幕暂停时间超过SLEEP_TIMEms&#xff08;默认为 5000&#xff09;&#xff0c;且用户不在执行其它操作&#xff0c;弹幕恢复轮询。

具体的处理逻辑如下所示&#xff1a;

// 检查弹幕是否激活setActiveAndAutoRestart() {// 如果没有开启if (!this.config.CHECK_SLEEP) return;if (this.checkActiveTimer) {clearTimeout(this.checkActiveTimer);}this.checkActiveTimer &#61; setTimeout(() &#61;> {this.restart();}, this.config.SLEEP_TIME);}// 弹幕暂停滚动pause() {if (this.isPaused) return;this.isPaused &#61; true;if (this.timer) {clearTimeout(this.timer);this.timer &#61; null;}this.setActiveAndAutoRestart();}// 弹幕重新开始滚动restart() {if (!this.isPaused) return;this.isPaused &#61; false;this.queueList.length && this.flush();}

4 组件方法详解


4.1 组件生命周期操作


  • attached&#xff0c;组件挂载时&#xff0c;创建Barrage类实例&#xff0c;设置弹幕上屏的实际回调方法&#xff0c;通过toLast值指定列表滚动到该元素&#xff0c;通过更新realBarrage数据&#xff0c;来更新实际上屏的数据。
  • detached&#xff0c;组件销毁时&#xff0c;清除Barrage类实例,同时对定时器进行清理。

attached() {// 创建Barrage实例this.barrage &#61; new Barrage(this.properties.config);// 初始化轮询回调方法this.barrage.emitQueueChange(data &#61;> {this.setData({toLast: &#96;item${data.length}&#96;,realBarrage: data,});});// 校准弹幕容器高度this.checkContainerClientHeight();}detached() {this.barrage && this.barrage.destroy();}

4.2 三种角色的弹幕消息进入弹幕池

父组件可以通过 id 获取弹幕组件的实例&#xff0c;从而调用其封装的方法。用法如下所示&#xff1a;


// 获取弹幕组件实例getBarrageContext() {if (!this.barrageContext) {(this.barrageContext as any) &#61; this.selectComponent(&#39;#wr-live-barrage&#39;);}return this.barrageContext;}// 调用方法示例handleSendBarrage() {(this.getBarrageContext() as any).sendBarrageBySelf(data);}

弹幕组件暴露的三个方法如下表格所示&#xff1a;


API说明参数默认值
multiPushBarrage初始化直播间时&#xff0c;填充数十条历史弹幕数据&#xff0c;组件已经优化&#xff0c;实现分布式上屏queueI[]-
sendBarrageEnterQueue将接收到的弹幕消息放入弹幕池, 第二个参数表示是否执行弹幕过滤规则<queueI[], Boolean><-, true>
sendBarrageBySelf自己发送的弹幕消息直接上屏queueI-

三个方法的代码如下所示&#xff0c;都是将弹幕放入弹幕池&#xff0c;唯一的区别是自己发送的弹幕时&#xff0c;需要恢复轮询机制。

// 自己发弹幕sendBarrageBySelf(barrage) {this.barrage.barrageEnterQueueSelf(barrage);this.barrage.restart();}// 即时消息弹幕填充sendBarrageEnterQueue(list, isFilter &#61; true) {this.barrage.barrageEnterQueue(list, isFilter);}// init直播间的时候&#xff0c;历史弹幕填充multiPushBarrage(list) {this.sendBarrageEnterQueue(list, false);}

4.3 用户行为处理


4.3.1 监听滚动到底部

实现监听滚到到底部&#xff0c;需要先来了解一下浏览器的scrollHegihtscrollTopclientHegiht三个属性。


  • scrollHegiht: 文档内容实际高度&#xff0c;包括超出视窗的溢出部分。
  • scrollTop: 滚动条滚动距离。
  • clientHeight: 窗口可视范围高度。

clientHeight &#43; scrollTop >&#61; scrollHeight 时&#xff0c;表示已经抵达内容的底部了&#xff0c;可以加载更多内容。


4.3.2 滚动事件监听

列表滚动时&#xff0c;由于当前实际上屏的弹幕列表数量和内容&#xff08;内容长度不一&#xff0c;有的 1 行&#xff0c;有的 2 行&#xff0c;有的 3 行&#xff09;发生变化&#xff0c;需要先校准容器的实际高度&#xff0c;方便后续做滚动到底部判断。同时设置弹幕激活策略&#xff0c;静止 n 秒后恢复滚动。

// 滚动容器会计算高度,并激活弹幕自动恢复handleScrollBarrageContainer({ detail }) {this.containerScrollHeight &#61; detail.scrollHeight;this.checkContainerClientHeight();this.barrage.setActiveAndAutoRestart();}// 校准弹幕容器高度checkContainerClientHeight() {if (!this.containerClientHieghtChecked) {wx.createSelectorQuery().in(this as TrivialInstance).select(&#39;.live-barrage&#39;).fields({size: true,},res &#61;> {if (res) {const { height &#61; 0 } &#61; res;if (height &#61;&#61;&#61; this.containerClientHeight) {this.containerClientHieghtChecked &#61; true;}this.containerClientHeight &#61; height;}},).exec();}}

列表滚动到底部&#xff0c;激活弹幕上屏逻辑。

// 滚动到底部&#xff0c;重新开启刷新策略handleScrollBottom() {if (!this.barrageTouch) {this.barrage.restart();}}

4.3.3 touch 事件监听

用户触摸屏幕动作开始时&#xff0c;暂停弹幕上屏逻辑。

barrageTouchStart() {this.barrageTouch &#61; true;this.barrage.pause();}

用户触摸动作结束时&#xff0c;判断是否滚动到底部&#xff0c;如果是的话&#xff0c;恢复弹幕上屏逻辑

// 判断弹幕是否到底barrageTouchEnd() {this.barrageTouch &#61; false;wx.createSelectorQuery().in(this as TrivialInstance).select(&#39;.live-barrage&#39;).fields({scrollOffset: true,},({ scrollTop }) &#61;> {if (this.containerClientHeight &#43; scrollTop &#43; 5 >&#61;this.containerScrollHeight) {this.begainScroll &#61; true;this.barrage.restart();}},).exec();}

在线直播源码评论弹幕是如何“练”成的&#xff1f;
本文转载自网络&#xff0c;感谢&#xff08;蔡小真&#xff09;的分享&#xff0c;转载仅为分享干货知识&#xff0c;如有侵权欢迎联系云豹科技进行删除处理


推荐阅读
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • WPF开发心率检测大数据曲线图的高性能实现方法
    本文介绍了在WPF开发中实现心率检测大数据曲线图的高性能方法。作者尝试过使用Canvas和第三方开源库,但性能和功能都不理想。最终作者选择使用DrawingVisual对象,并结合局部显示的方式实现了自己想要的效果。文章详细介绍了实现思路和具体代码,对于不熟悉DrawingVisual的读者可以去微软官网了解更多细节。 ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • Centos下安装memcached+memcached教程
    本文介绍了在Centos下安装memcached和使用memcached的教程,详细解释了memcached的工作原理,包括缓存数据和对象、减少数据库读取次数、提高网站速度等。同时,还对memcached的快速和高效率进行了解释,与传统的文件型数据库相比,memcached作为一个内存型数据库,具有更高的读取速度。 ... [详细]
author-avatar
跳花妹535
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有