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

即时通讯IM技术领域基础篇

转自:https:juejin.impost5a694f216fb9a01cb74e8f74即时通讯IM技术领域基础篇即时通讯IM技术领域提高篇

转自:https://juejin.im/post/5a694f216fb9a01cb74e8f74

即时通讯IM技术领域基础篇

即时通讯IM技术领域提高篇

议题

  1. 准备工作(协议选型)

    • 网络传输协议选择 和 数据通信协议选择
  2. xxx项目架构

    • 架构优缺点
    • 架构改进之路
  3. IM 关键技术点 & 策略机制

    • 如何保证消息不丢/不乱序/不重复
    • 心跳策略
    • 重连策略
  4. 典型IM业务场景

    • 用户A发送消息给用户B
    • 用户A发送消息到群C
  5. 存储结构简析

准备工作(协议选型)

选用什么网络传输协议(TCP/UDP/HTTP) ?

  1. udp协议虽然实时性更好,但是如何处理安全可靠的传输并且处理不同客户端之间的消息交互是个难题,实现起来过于复杂. 目前大部分IM架构都不采用UDP来实现.

  2. 但是为啥还需要HTTP呢?

    • 核心的TCP长连接,用来实时收发消息,其他资源请求不占用此连接,保证实时性

    • http可以用来实现状态协议(可以用php开发)

      • 朋友圈
      • 用户个人信息(好友信息,账号,搜索等..)
      • 离线消息用拉模式,避免 tcp 通道压力过大,影响即时消息下发效率
      • 等等...
    • IM进行图片/语言/大涂鸦聊天的时候: http能够很方便的处理 断点续传和分片上传等功能.

  3. TCP: 维护长连接,保证消息的实时性, 对应数据传输协议.

    • 目的: 及时收发消息

选用什么数据通信协议?

  1. IM协议选择原则一般是:易于拓展,方便覆盖各种业务逻辑,同时又比较节约流量。节约流量这一点的需求在移动端IM上尤其重要 !!!

    • xmpp: 协议开源,可拓展性强,在各个端(包括服务器)有各种语言的实现,开发者接入方便。但是缺点也是不少:XML表现力弱,有太多冗余信息,流量大,实际使用时有大量天坑。

    • MQTT: 协议简单,流量少,但是它并不是一个专门为IM设计的协议,多使用于推送. 需要自己在业务上实现群,好友相关等等(目前公司有用MQTT实现通用IM框架).

    • SIP: 多用于VOIP相关的模块,是一种文本协议. sip信令控制比较复杂

    • 私有协议: 自己实现协议.大部分主流IM APP都是是使用私有协议,一个被良好设计的私有协议一般有如下优点:高效,节约流量(一般使用二进制协议),安全性高,难以破解。 xxx项目基本属于私有定制协议<参考了蘑菇街开源的TeamTalk>, 后期通用IM架构使用MQTT

  2. 协议设计的考量:

    • 网络数据大小 —— 占用带宽,传输效率:虽然对单个用户来说,数据量传输很小,但是对于服务器端要承受众多的高并发数据传输,必须要考虑到数据占用带宽,尽量不要有冗余数据,这样才能够少占用带宽,少占用资源,少网络IO,提高传输效率;

    • 网络数据安全性 —— 敏感数据的网络安全:对于相关业务的部分数据传输都是敏感数据,所以必须考虑对部分传输数据进行加密(xxx项目目前提供C++的加密库给客户端使用)

    • 编码复杂度 —— 序列化和反序列化复杂度,效率,数据结构的可扩展性

    • 协议通用性 —— 大众规范:数据类型必须是跨平台,数据格式是通用的

  3. 常用序列化协议

    • 提供序列化和反序列化库的开源协议: pb,Thrift. 扩展相当方便,序列化和反序列化方便(xxx项目目前使用pb)

    • 文本化协议: xml,json. 序列化,反序列化容易,但是占用体积大(一般http接口采用json格式).

xxx项目系统架构

前期架构

...

改进后架构

...

架构的优缺点

优点

  1. 同时支持TCP 和 HTTP 方式, 关联性不大的业务服务独立开来

    • php server
    • router server
    • user center
    • Access server
    • oracle server
  2. 服务支持平行扩展,平行扩展方便且对用户无感知

  3. cache db层的封装,业务调用方直接调用接口即可.

  4. 除了Access server是有状态的,其他服务无状态

  5. 各个服务之间,通过rpc通信,可以跨机器.

  6. oracle里面都是模块化,有点类似MVC模式, 代码解耦, 功能解耦.

缺点

  1. oracle 太过庞大, 可以把某些业务抽取出来

    • 缺点

      • 业务太庞大,多人开发不方便,容易引起code冲突
      • 如果某个小功能有异常,可能导致整个服务不可用
    • 改进

      • oracle里面耦合了apns server, 可以把apns 单独抽取出来. (xxx项目目前已经开始接入通用push推送系统了,类似把apns抽取出来).
  2. push server 没有业务,仅仅是转发Access和oracle之间的请求

    • 缺点

      • 需要单独维护一个比较鸡肋的服务,增加运维成本
    • 改进

      • 把push server合并到Access中,减少一层rpc调用中间环节.减少运维成本还能提高效率(xxx项目新架构已经把push server干掉融合到Access里面)
  3. Access server和用户紧密连接,维持长连接的同时,还有部分业务

    • 缺点

      • 维持着长连接,如果升级更新的话,势必会影响在线用户的连接状态
      • 偶尔部分业务,降低长连接的稳定性
    • 改进:

      • 把Access server 中维持长连接部分抽取出来一个connd server:
        • 仅仅维持长连接,收发包. 不耦合任何业务(xxx项目目前正在改进这个架构,还未上线)

IM 关键技术点

技术点一之: 如何保证消息可达(不丢)/唯一(不重复)/保序(不乱序)

最简单的保序(不乱序)

  1. 为什么有可能会乱序?

    • 对于在线消息, 一发一收,正常情况当然不会有问题

      • 但是,如果收到消息的时候,突然网络异常了,收不到消息了呢?
        • 服务端就会重发或者转离线存储(xxx项目的机制立即转离线存储)
    • 对于离线消息, 可能有很多条.

      • 拉取的时候,一般会把离线的消息都一次性的拉取过来
        • 多条消息的时候,就要保证收取到的消息的顺序性.
  2. 怎么保证不乱序?

    • 每条消息到服务端后,都会生成一个全局唯一的msgid, 这个msgid一定都是递增增长的(msgid的生成会有同步机制保证并发时的唯一性)

    • 针对每条消息,会有消息的生成时间,精确到毫秒

    • 拉取多条消息的时候,取出数据后,再根据msgid的大小进行排序即可.

保证唯一性(不重复)

  1. 消息为什么可能会重复呢?

    • 移动网络的不稳定性,可能导致某天消息发送不出去,或者发送出去了,回应ack没有收到.
      • 这种情况下,就可能会需要有重发机制. 客户端和服务端都可能需要有这种机制.

      • 既然有重复机制,就有可能收到的消息是重复的.

  2. 怎么解决呢? 保证不重复最好是客户端和服务端相关处理

    • 消息meta结构里面增加一个字段isResend. 客户端重复发送的时候置位此字段,标识这个是重复的,服务端用来后续判断

    • 服务端为每个用户缓存一批最近的msgids(所谓的localMsgId),如缓存50条

    • 服务端收到消息后, 通过判断isResend和此msgid是否在localMsgId list中. 如果重复发送,则服务端不做后续处理.

    • 因为仅仅靠isResend不能够准备判断,因为可能客户端确实resend,但是服务端确实就是没有收到......

保证可达(不丢且不重)

  1. 最简单的就是服务端每传递一条消息到接收方都需要一个ack来确保可达

    • 但是ack也有可能在弱网环境下丢失.
  2. 服务端返回给客户端的数据,有可能客户端没有收到,或者客户端收到了没有回应.

    • 因此,就一定要有完善的确认机制来告知客户端确实收到了. 有且仅有一次.
  3. 考虑一个账号在不同终端登录后的情况.

    • 消息要能够发送到当前登录的终端,而且又不能重复发送或者拉取之前已经拉取过的数据.

技术点二之: msgID机制

这里提供两种方案供参考(本质思想一样,实现方式不同)

序列号msgid机制 & msgid确认机制(方案一):

  • 每个用户的每条消息都一定会分配一个唯一的msgid

  • 服务端会存储每个用户的msgid 列表

  • 客户端存储已经收到的最大msgid

image.png

思考

这两种方式的优缺点?

  1. 方式二中,确认机制都是多一次http请求. 但是能够保证及时淘汰数据

  2. 方式一中,确认机制是等到下一次拉取数据的时候进行确定, 不额外增加请求, 但是淘汰数据不及时.

技术点三之: 心跳策略

心跳功能: 维护TCP长连接,保证长连接稳定性, 对于移动网络, 仅仅只有这个功能吗?

  1. 心跳其实有两个作用

    • 心跳保证客户端和服务端的连接保活功能,服务端以此来判断客户端是否还在线

    • 心跳还需要维持移动网络的GGSN

      • 运营商通过NAT(network adddress translation)来转换移动内网ip和外网ip,从而最终实现连上Internet,其中GGSN(gateway GPRS support Node)模块就是来实现NAT的过程,但是大部分运营商为了减少网关NAT的映射表的负荷,若一个链路有一段时间没有通信就会删除其对应表,造成链路中断,因此运营商采取的是刻意缩短空闲连接的释放超时,来节省信道资源,但是这种刻意释放的行为就可能会导致我们的连接被动断开(xxx项目之前心跳有被运营商断开连接的情况,后面改进了心跳策略,后续还将继续改进心跳策略)

      • NAT方案说白了就是将过去每个宽带用户独立分配公网IP的方式改为分配内网IP给每个用户,运营商再对接入的用户统一部署NAT设备,NAT的作用就是将用户网络连接发起的内网IP,以端口连接的形式翻译成公网IP,再对外网资源进行连接。

      • 从mobile 到GGSN都是一个内网,然后在GGSN上做地址转换NAT/PAT,转换成GGSN公网地址池的地址,所以你的手机在Internet 上呈现的地址就是这个地址池的公网地址

  2. 最常见的就是每隔固定时间(如4分半)发送心跳,但是这样不够智能.

    • 4分半的原因就是综合了各家移动运营商的NAT超时时间

    • 心跳时间太短,消耗流量/电量,增加服务器压力.

    • 心跳时间太长,可能会被因为运营商的策略淘汰NAT表中的对应项而被动断开连接

  3. 智能心跳策略

    • 维护移动网GGSN(网关GPRS支持节点)

      • 大部分移动无线网络运营商都在链路一段时间没有数据通讯时,会淘汰 NAT 表中的对应项,造成链路中断。NAT超时是影响TCP连接寿命的一个重要因素(尤其是国内),所以客户端自动测算NAT超时时间,来动态调整心跳间隔,是一个很重要的优化点。
    • 参考微信的一套自适应心跳算法:

      • 为了保证收消息及时性的体验,当app处于前台活跃状态时,使用固定心跳。

      • app进入后台(或者前台关屏)时,先用几次最小心跳维持长链接。然后进入后台自适应心跳计算。这样做的目的是尽量选择用户不活跃的时间段,来减少心跳计算可能产生的消息不及时收取影响。

  4. 精简心跳包,保证一个心跳包大小在10字节之内, 根据APP前后台状态调整心跳包间隔 (主要是安卓)

技术点四之: 断线重连策略

掉线后,根据不同的状态需要选择不同的重连间隔。如果是本地网络出错,并不需要定时去重连,这时只需要监听网络状态,等到网络恢复后重连即可。如果网络变化非常频繁,特别是 App 处在后台运行时,对于重连也可以加上一定的频率控制,在保证一定消息实时性的同时,避免造成过多的电量消耗。

  1. 断线重连的最短间隔时间按单位秒(s)以4、8、16...(最大不超过30)数列执行,以避免频繁的断线重连,从而减轻服务器负担。当服务端收到正确的包时,此策略重置

  2. 有网络但连接失败的情况下,按单位秒(s)以间隔时间为2、2、4、4、8、8、16、16...(最大不超过120)的数列不断重试

  3. 为了防止雪崩效应的出现,我们在检测到socket失效(服务器异常),并不是立马进行重连,而是让客户端随机Sleep一段时间(或者上述其他策略)再去连接服务端,这样就可以使不同的客户端在服务端重启的时候不会同时去连接,从而造成雪崩效应。

典型IM业务场景流程

  1. 用户A发送消息给用户B

    • A 通过账号密码获取token.
    • A 拿着token进行login
    • 服务端缓存用户信息并维持登录状态
    • A 打包数据发送给服务端
    • 服务端检测A用户是否风险用户
    • 服务端对消息进行敏感词检查(这个重要)
    • 服务端生成msgid
    • 服务端进行好友检测(A/B)
    • 服务端进行重复发送检测
    • 服务端获取B的连接信息,并判断在线状态
    • 如果在线,直接发送给B,并入cache和db
    • 如果不在线,直接存储.如果是ios,则进行apns.
    • 在线的B,收到消息后回应ack进行确认.
  2. 用户A发送消息到群C

存储结构

未读索引列表

  • 未读消息索引存在的意义在于保证消息的可靠性以及作为离线用户获取未读消息列表的一个索引结构。

  • 未读消息索引由两部分构成,都存在redis中:

    • 记录用户每个好友的未读数的hash结构
    • 每个好友对应一个zset结构,里面存着所有未读消息的id。
  • 假设A有三个好友B,C,D。A离线。B给A发了1条消息,C给A发了2条消息,D给A发了3条消息,那么此时A的未读索引结构为:

  • hash结构

    • B-1
    • C-2
    • D-3
  • zset结构

User MsgId 1 MsgId 2 MsgId 3
B 1 - -
C 4 7 -
D 8 9 10
  • 消息上行以及队列更新未读消息索引是指,hash结构对应的field加1,然后将消息id追加到相应好友的zset结构中。

  • 接收ack维护未读消息索引则相反,hash结构对应的field减1,然后将消息id从相应好友中的zset结构中删除。

消息下行(未读消息的获取)

该流程用户在离线状态的未读消息获取。

该流程主要由sessions/recent接口提供服务。流程如下:

  • hgetall读取未读消息索引中的hash结构。
  • 遍历hash结构,若未读数不为0,则读取相应好友的zset结构,取出未读消息id列表。
  • 通过消息id列表到缓存(或穿透到数据库)读取消息内容,下发给客户端。

和在线的流程相同,离线客户端读取了未读消息后也要发送接收ack到业务端,告诉它未读消息已经下发成功,业务端负责维护该用户的未读消息索引。

和在线流程不同的是,这个接收ack是通过调用messages/lastAccessedId接口来实现的。客户端需要传一个hash结构到服务端,key为通过sessions/recent接口下发的好友id,value为sessions/recent接口的未读消息列表中对应好友的最大一条消息id。

服务端收到这个hash结构后,遍历它

  • 清空相应缓存
  • 通过zremrangebyscore操作清空相应好友的zset结构
  • 将未读消息索引中的hash结构减掉zremrangebyscore的返回值

这样就完成了离线流程中未读消息索引的维护。

队列处理流程

  • 如果消息标记为offline,则将消息入库,写缓存(只有离线消息才写缓存),更新未读消息索引,然后调用apns进行推送。

  • 如果消息标记为online,则直接将消息入库即可,因为B已经收到这条消息。

  • 如果消息标记为redeliver,则将消息写入缓存,然后调用apns进行推送。

讨论后的疑问

把连接层Access拆一层connd server出来的考量和目的,到底有没有必要?

  1. 拆分出来的目的:

    • 连接层更稳定
    • 减少重启,方便Access服务升级
  2. 真的能够起到这样的效果么?

    • 连接层更稳定 - - - 需要有硬性指标来判断才能确定更稳定,因为Access的服务不重,目前也不是瓶颈点.

      • 目前Access服务不重, 拆分出来真有必要吗?

      • 真要拆分, 那也不是这么拆分, 是在Oracle上做拆分, 类似微服务的那种概念

      • 稳定性不是这么体现,原来 connd 的设计,更薄不承担业务,而现在的 access 还是有一些业务逻辑,那么它升级的可能性就比较高。

      • access 拆分,目的就是让保持连接的那一层足够薄,薄到怎么改业务,它都不用升级代码(tcp 不会断)。

    • 减少重启,方便Access服务升级 - - - 不能通过增加一层服务来实现重启升级,需要有其他机制来确保服务端进行升级而不影响TCP长连接上的用户

      • 拆分出来的connd server 还是有可能会需要重启的, 这时候怎么办呢 ?关键性问题还是没有解决

      • 加一层服务,是打算通过共享内存的方式,connd 只管理连接。access 更新升级的时候,用户不会掉线。

    • 增加一个服务,就多了一条链路, 就可能会导致服务链路过长,请求经过更多的服务,会导致服务更加不可用. 因为要保证每个服务的可用性都到99.999%(5个9)是很难的,增加一个服务,就会降低整个服务的可用性.

  3. 架构改进一定要有数据支撑, 要确实起到效果, 要有数据输出才能证明这个改进是有效果的,要不然花了二个月时间做改进,结果没有用,浪费人力和时间,还降低开发效率

    • 每个阶段的架构可能都不一样,根据当前阶段的用户量和热度来决定

怎么保证接入层服务重启升级? 服务扩/缩容?

  1. 方案: 增加一条信令交互,服务端如果要重启/缩容, 告知连接在此Access上的所有客户端,服务端要升级了,客户端需要重连其他节点

    • 这其实是属于一种主动迁移的策略,这样客户端虽然还是有重连,比我们直接断连接会好一些.
  2. 等确定当前Access节点上的所有客户端都连接到其他节点后, 当前Access节点再进行重启/下线/缩容.

  3. 怎么扩容? 如果需要扩容,则增加新的节点后,通过etcd进行服务发现注册.客户端通过router server请求数据后,拉取到相关节点.

  4. 如果当前3个节点扛不住了,增加2个节点, 这个时候,要能够马上缓解当前3个节点压力,需要怎么做?

    • 按照之前的方式,客户端重新登录请求router server,然后再进行连接的话,这是不能够马上缓解压力的,因为新增的节点后, 当前压力还是在之前几个节点

    • 所以, 服务端需要有更好的机制,来由服务端控制

      • 服务端发送命令给当前节点上的客户端,让客户端连接到新增节点上.

      • 服务端还需要确定是否有部分连接到其他节点了,然后再有相应的策略.

怎么防止攻击

  1. 线上机器都有防火墙策略(包括硬件防火墙/软件防火墙)

    • 硬件防火墙: 硬件防火墙设备,很贵,目前有采购,但是用的少

    • 软件防火墙: 软件层面上的如iptable, 设置iptable的防火墙策略

  2. TCP 通道层面上

    • socket建连速度的频率控制, 不能让别人一直建立socket连接,要不然socket很容易就爆满了,撑不住了

      • 目前设置的是独立ip建连速度超过100/s,则认为被攻击了,封禁此ip
    • 收发消息频率控制, 不能让别人一直能够发送消息,要不然整个服务就挂掉了

      • 要能够发送消息, 必须要先登录

      • 要登录, 必须有token,有秘钥

      • 收发消息也可以设置频率控制

目前市面上的开源/通用协议的比较选型

  1. 为啥xmpp不适合,仅仅是因为xml数据量大吗 ?

    • 目前也有方案是针对xmpp进行优化处理的. 因此流量大并不是主要缺点

    • 还有一点就是消息不可靠,它的请求及应答机制也是主要为稳定长连网络环境所设计,对于带宽偏窄及长连不稳定的移动网络并不是特别优化

    • 因此设计成支持多终端状态的XMPP在移动领域并不是擅长之地

  2. 为啥mqtt不适合? 为啥xxx项目没有用mqtt ?

    • mqtt 适合推送,不适合IM, 需要业务层面上额外多做处理, 目前已经开始再用

    • xxx项目不用mqtt是历史遗留问题,因为刚开始要迅速开展,迅速搭建架构实现,因此用来蘑菇街的teamtalk.

    • 如果后续选型的话, 如果没有历史遗留问题,那么就会选择使用mqtt

  3. 除了数据量大, 还要考虑协议的复杂度, 客户端和服务端处理协议的复杂度?

    • 协议要考虑容易扩展, 方便后续新增字段, 支持多平台

    • 要考虑客户端和服务端的实现是否简单

    • 编解码的效率

跨机房, 多机房容灾

  1. 服务需要能够跨机房,尤其是有状态的节点.

  2. 需要储备多机房容灾,防止整个机房挂掉.

刚讨论说到接入层有哪些功能的:

  1. 维持TCP长连接,包括心跳/超时检测

  2. 收包解包

  3. 防攻击机制

  4. 等待接收消息回应(这个之前没有说到,就是把消息发送给接收方后还需要接收方回应)

思考点(考核关键点)

  1. 消息为什么可能会乱序? 怎么保证消息不乱序?

    • 考虑离线
    • 考虑网络异常
  2. 对于离线消息,存储方式/存储结构要怎么设计?

    • 考虑会有多个人发送消息
    • 考虑缓存+db的方式
  3. 如何保证消息不丢,不重? 怎么设计消息防丢失机制?

    • 考虑同一账号可能会多终端登录
    • 考虑弱网环境下,ACK也可能会丢失
  4. 对于长连接, 怎管理这些长连接?

    • 考虑快速查找
      • 后端数据来了, 怎么快速找到这个请求对应的连接呢
  5. 接入层节点有多个,而且是有状态的.通过什么机制保证从节点1下发的请求,其对应的响应还是会回到节点1呢?

    • 或者说如果响应不回到节点1,而是回到节点2了会有什么弊端?

作者:吴德宝AllenWu
链接:https://juejin.im/post/5a694f216fb9a01cb74e8f74
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


推荐阅读
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 单点登录原理及实现方案详解
    本文详细介绍了单点登录的原理及实现方案,其中包括共享Session的方式,以及基于Redis的Session共享方案。同时,还分享了作者在应用环境中所遇到的问题和经验,希望对读者有所帮助。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 企业数据应用挑战及元数据管理的重要性
    本文主要介绍了企业在日常经营管理过程中面临的数据应用挑战,包括数据找不到、数据读不懂、数据不可信等问题。针对这些挑战,通过元数据管理可以实现数据的可见、可懂、可用,帮助业务快速获取所需数据。文章提出了“灵魂”三问——元数据是什么、有什么用、又该怎么管,强调了元数据管理在企业数据治理中的基础和前提作用。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 本文介绍了如何使用JSONObiect和Gson相关方法实现json数据与kotlin对象的相互转换。首先解释了JSON的概念和数据格式,然后详细介绍了相关API,包括JSONObject和Gson的使用方法。接着讲解了如何将json格式的字符串转换为kotlin对象或List,以及如何将kotlin对象转换为json字符串。最后提到了使用Map封装json对象的特殊情况。文章还对JSON和XML进行了比较,指出了JSON的优势和缺点。 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
author-avatar
曹莹888淑女
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有