认证的各类方式

补充一下认证方式,一般是由三大类方式及其组合实现:

What you are:
比如人的生理特征(指纹、面部识别、虹膜、声纹、DNA等)

What you know:
古代机关开启方式、密码、动态指令、预设安全问题、最近和谁聊天、最近几次变动的密码、最近几次登录地址等

What you have:
身份证,医保卡,银行U盾牌,手机短信/电话语音发送验证码

但有一些就不太会好判断归属:
1、公安局开的身份证明或CA颁发给机构的证书,应该是“你有啥”,但很多时候直接被用于“你是谁”
2、图形动态验证码,用于判断“你是人不是机器”,这个算是“你是谁”的简单版本?

令牌内容
令牌不需要带太多的信息,但要满足一些基本要求:
1、令牌颁发机构标识
2、令牌被授予机构、APP、服务或网站标识
3、令牌类型(资源访问令牌、刷新令牌等)
4、令牌可访问的资源类型(头像、昵称、二维码等)
5、令牌有效时间范围
6、令牌颁布机构签名和时间戳
7、数据组织方式其实用Json 或 编码过的字符串都可以
这样,三方就都可以快速验证令牌有效性。

数据权限
数据权限确实很多时候只能系统自己处理,很难跨行业通用,甚至同一行业不同机构也不尽相同。比如两个看似比较通用的场景:
1、当机构架构分为多级时,上级机构能看到下级哪些数据,平级之间能看到那些数据,下级机构能看到什么数据,各行业各类组织互不相同。如果只用功能权限的思路去解决这个问题,容易将功能权限弄得十分复杂。
2、此外就是用户情况比较复杂,比如除了组织架构、岗位、职级外,还有职业证书相关权限控制、客户业务办理关系、项目组组成、项目组轮班,加上审核、稽核、多级质控等功能,仅是功能权限也是力有不逮。
而且,近些年来,各公司对数据权限和数据安全的要求都越来越高、越来越严格了。

凭证
感觉中文的“凭证”指的内容,比Credentials范围广很多。比如:
有时候,网页提示“请输入登录凭证”,说的就是用户名密码
有时候,请下载云主机“临时凭证”,说的就是登录证书
有时候,请出示入场凭证,说的就是一张狗牌

尤其是证书是否算凭证,感觉有些想不清楚。比如:下面的算凭证吗?
1、域控认证,Windows凭证管理器
2、Linux服务器可以通过ssh证书直接访问
3、一些云厂商可以申请临时ssh证书,允许远程访问
4、IOS程序发布时,需要开发者申请APP证书,并签名才能上传发表
5、接入云SDK时,需要申请AppId 和 SecretKey
6、TCP通讯时,采用TLS双证书双向加密

客户端敏感数据加密
对于客户端敏感数据加密的问题:
1、客户端对敏感信息加密,在通用浏览器、开放网络环境下,其实意义不大。如果一位黑客有能力干掉HTTPS通讯加密或者伪装为服务网站的话,替换掉网站前端代码也不是一件难的事情,这样无论如何加密信息都没有任何用途。同时,在客户端、服务的,这些敏感信息其实都有原文的,如果贡献了这两个地方,黑客可以同时获取明文和密文,所以加密也没啥用。
2、对于安全要求特别高,采用特殊通信协议,特殊信道,特殊设备、特殊浏览器的情况下,对于特别敏感的信息,进行二次加密是有必要的。因为设备和软件都难以得到,即使信道被监听,通信加密被破解,也要尽量拖延敏感信息被破解。

字证书有什么具体应用
1、机构、网站、身份认证
2、APP开发者认证
3、可执行程序开发者认证
4、控件开发者认证
5、API认证
6、文档的电子签章证书
7、大家自己搭建开发环境时有时需要自签名证书
其实还有很多咯。

除了顶级CA之外,其实CA也分等级的,这就涉及到了证书链。
此外,CA还要维护黑名单,吊销一些还在有效期之内的证书。

数据验证
之前只是零零散散有了一些想法,也落地了。但看完今天老师的讲解,感觉落地的很难看。

数据校验这部分,回顾了一下,我们真的做了很多,但十分十分的零散。根据各种要求,包括架构、运维、数据安全、数据合规要求、数据脱敏、敏感数据加密,每年都要折腾个几轮。此类代码横跨了文章中提到的全部层,实现方式从单纯的编码规范,到各层编码,到切面,到流水线扫描,到Jar包,到中间件、到持久化层验证,都有零散涉及。如果能尽量统一到 Bean Validation处理,就不至于每次加个要求,都要一通大改了。

缓存的弊端

1、增加了整个系统的复杂度,增加了系统的故障点,系统的整体可靠性可能会下降。比如Redis遇到bigkey,处理不好会让系统性能明显下降;如果Redis所在服务器CPU或内存资源不足,也会引起严重的性能问题
2、增加缓存后,缓存的预加载、过期失效、击穿、穿透、雪崩等问题,需要付出额外代价去解决
3、增加缓存后,缓存与数据库数据的一致性是个难题,需要付出额外代价去解决
4、分布式环境下,各缓存之间的数据同步及一致性,也是个难题,需要付出额外代价去解决
5、分布式环境下,缓存的高可用及冷启动后快速恢复,需要付出额外代价去解决

各类HTTP性能优化方式

网络性能优化

资源预下载,资源异步加载,资源压缩,dns加速,静态资源内容分发,多机房服务器就近访问,购买多条网络链路,防火墙过滤非法请求,F5负载,反向代理,API网关,多实例部署,缓存加速,一致性Hash分流,DB分库,异步任务队列等等。

其实还有P2P加速,网盘文件特征码上传加速,多网卡带宽绑定等特殊手段。

HTTP3
HTTP3的广泛推广,需要电信运营商的大力支持,同时还需要华为、思科等网络设备厂商的大力支持。以往各大电信运营商对UPD限制较多,各种网络设备对TCP的优化也更多。如果HTTP3广泛使用起来,对设备厂商也是个设备升级换代的好机会。

负载均衡
负载均衡和去医院看医生有些相似的:
1、刚到医院,要去分诊挂号,护士和挂号台的人不会问你一堆信息,只询问最基本信息并确定要看哪个科,就可以了。(四层负载,所需信息简单,工作效率高,重在吞吐量,不要有阻塞)
2、到了科室,尤其是看很繁忙的科室,会有护士和年轻医生,各种询问患者的情况,甚至把一些基础检查都做了,才会让专家去进一步提供服务;(七层负载,所需信息复杂一些,效率低一些,但好在量小了,也就避免了阻塞)
3、然后是资深医生看诊(实际网络服务)

但如果反过来,分诊挂号的时候,就咨询一大堆的问题,并作一些检查,每个患者耗时都长,会造成拥堵,患者排队n小时,而且到了科室就没有二次分诊的必要了。所以,七层代理放到前面,吞吐量就差了,四层代理也就没有存在的必要了。

负载均衡

各种事务实现方式

本地事务
Logging是无奈之举,因为操作系统和硬件的有效性不是100%,所以需要处理系统挂掉的情况。在系统挂掉后重启,数据库要知道需要Redo哪些操作,Rollback哪些操作,一定要有一个地方提前记录下来才行;相当于日志做了提前记录,数据库再做操作,根据日志的事务节点,就知道发生异常时数据要如何处理了。

Shadow Paging处理并发时,应该很难实现MVCC这种花式操作吧,多个客户端多个事务难以并发处理。

全局事务和共享事务
之前尝试过分布式事务,但实现效果实在太差,一个DB的事务卡住了,有可能卡住其他DB上的事务,性能直线下降。后面将强一致的场景进行了识别,尽量改造将其移动到一个库,其他直接通过异步等待方式解决,性能提高了不少。不需要强一致的,直接kafka同步搞了。

可靠消息队列
XA使用少,主要是其实现复杂,吞吐量低,会导致其他业务的阻塞,对于高并发访问,得不偿失。
可靠性队列,要轮询,会导致访问量增加,服务器负担上升,而且设计不良容易雪崩。同时,数据存在一段时间的不一致状态,但在可接受的情况下,性能远远优于XA。其实很多业务在重新调整事务顺序后,短期不一致状态的危害会很小。

本地事务

服务调用发展趋势

几类方法对比
CORBA看起来比较烦,用好了比较难,写起来比较烦,但好在起步早啊,而且贵在不断缓慢进步;
DCOM一心要兼容COM,有些反人类,看起来就烦,用好了很难,写起来很烦;
Java RMI、JAVA EJB,都是犯了相同的错,标准太烦了,对开发人员太不友好了。

WebService是看起来容易,用好了很难,写起来很烦;但同时代其他协议都是看起来就很难,所以胜出了,在一段时间内大行其道;

后来REST出现了,看起来容易,学起来容易,用起来也很容易。轻松将WebService挑落马下。

再看现在的Dubbo、Thrift、gRPC等,使用都相对简单,容易上手,开发者友好。
WCF有些小奇葩,很多想法很好,就是重了些,管得有些多,配置有些烦,如果要更多的占领市场,就应该让其变得更容易使用才对,而且应该跨平台才行。反而倒是WebAPI感觉更有前途,至少使用复杂程度和Flask、SpringBoot在一个水平线。

说到底吧,RPC协议和框架,其最终用户就是开发。
一开始能解决用户需求就行,没有其他替代方案,能解决问题,开发前辈们就很开心。
但用着用着,开发感觉体验太差了,就会有新一代产品出来,替代掉之前的产品。
而且越是新的协议,就不能有大的学习成本,容易上手,功能强大。
不信的话,你看下新的手机,比30年前功能多了不知道多少,但哪里还要一本厚厚的说明书才能上手呢?

REST的成功
REST的成功很大程度上源于HTTP及JSON使用的广泛性,其资源的思维确实很巧妙。

但其实不少REST落地成了HTTP协议收发JSON,直接把“HTTP方法”变成了JSON中的一个KV,进行了扩展;而且进一步封装了HTTP的返回值,将HTTP400/500,封装成了JSON,从而可以反馈更多的信息。换句话说,REST的一些问题,可以通过适当的灵活来解决。但唯独“传输效率”这个问题无法突破HTTP+JSON的极限。

个人认为,微服务之间内部通讯使用RPC提升传输效率,Web及外部通讯使用REST或HTTP+JSON可能是一个比较好的折中性选择。

其他通讯手段
很多啊,天天用。通过操作系统机制(共享内存、发送消息通知、粘贴板、各类内核对象等)、数据库(关系型、非关系型、列数据库),通过中间件(队列,Kafka,ESB),通过缓存,通过文件存储(NAS、NFS、对象存储)、通过UDP/RAW协议、通过网络广播、通过短信、通过邮件、通过自动拨电话、通过平台消息推送。

当然还有很多其他的方式,通过电信号编码,光信号编码、通过声波编码、通过量子通讯、甚至通过存储运输等等等等。

各类系统架构演化

一、整体趋势
天下大势,分久必合合久必分。复杂有复杂的代价,简单有简单的代价。

看这些年的发展,也能看出一定的端倪:
单体应用【一统天下】-》业务复杂后,应用拆分【应用拆分】-》SOA、ESB【接口拆分聚合】-》微服务【服务拆分】-》中台【业务聚合】-》去中台【业务不要瞎聚合,再拆回去】

微服务的简单,其实是将服务功能单一化,确实降低了编码及编码维护难度,也是有很高的代价的。我们不仅要享受简单,也要知道其代价。最直观的几个代价有:
1、需要很高的代价去打造适合自己的微服务治理体系
2、由于需要解决单点问题,每个服务要部署多个实例即使用量达不到也要备着
3、运维复杂度上升,需要工具支持,对人员要求也更高;
4、微服务的调用链,比单体应用的调用链,复杂,效率低,排错难;
使用微服务,对运维能力是有一定要求的,而且非所有软件都应该走微服务的路子。盲目的微服务化,服务拆分过细的结果,往往是又合了回来。

究其原因,人对复杂事务的掌握是很有局限性的,所以我们倾向于将复杂问题分解为简单问题,将问题标准化,然后解决问题。整个人类社会都是这样,才会有社会专业分工。但分工又不能太细,这样才能恰到好处的发挥人的创造力。最终都是多种因素折中的结果。

无论是软件开发,还是CPU制造,还是工厂制造商品,还是粮食的生产,原理是相似的。

二、单体程序还是微服务
个人认为,以后的发展趋势是单体系统、微服务架构,会相互学习相互补足。

很多典型的单体系统如操作系统、数据库、中间件如MQ等,在内部设计时也会根据功能进行拆分模块,并不会全部放到一起实现,会尽力降低某一部分出错后导致的后果;同时在分布式环境下,也会给出自己不错的答案。

而微服务正相反,其本身确实是降低了开放的难度,提升了开放效率,隔离做的很好。但同时要看到,微服务将不少工作扔给了运维,没有强力的运维,微服务拆的越细,最后死的也是越难看。所以在微服务化的过程中,很多项目都是拆细了又合并回来,接受了单体组建的风险,避免了运维工作的指数型增长。

单体程序的问题在于,单体应用如果太复杂了,人是很难考虑到各个环节的,需要用更复杂的手段,来进行保证,如火箭、飞机、航母,这些复杂的软硬件一体的开放工作,需要额外的系统及理论及代价去确保系统的可靠性。而且单体系统最长见的一个问题就是,单体系统的稳定性,小于单体系统中最差部分的稳定性,在很多没有良好管控的软件项目上,单体系统的稳定性,其实就是最差的那段代码决定的。人员水平不足,项目经验不足,人力不足,时间不足,是不是和你的项目很像?这种情况下,复杂的单体系统难以高效稳定的发展。

微服务,通过各种拆分隔离解决的这个问题,但同时在运维的维度引入了新的复杂度。每个项目都应该在单体服务稳定性与运维复杂度、资源耗费之间,取得一个平衡。孰优孰劣,应该按项目来确定,而不是一棍子打死。

此外,单片机给人的感觉是单体吧,但加个设备网关,搞个IoT不香么?

同样,各种复杂的操作,云平台给你封装好了,应用只需要找平台要这要那,对于应用来说,平台把内部实现封成了黑盒子,一定意义上说不是单体么?

三、SOA为何衰败

SOA的想做的事情,方向是对的,到今天,很多思想都在落地。

但那种“干这种改变世界的事情,老子要发明一套让所有人看起来都高大上的技术才行”的想法,让SOA过于复杂。

反而不如务实,用尽了少的代价让想法落地。

不应该为了解决简单的问题,一开始就弄一巨复杂的规范,SOA如此,EJB也是如此。

重点没有去解决问题,而是为了规范而去制定规范。甚至为了制定规范,还要发明一堆名词,发明一种语言。

比如说,SOA什么都要管理,就需要发明WSDL来框定业务。WSDL根本就不是人类容易看懂的,而且十分严格。如果通讯双方namespace有个字母大小写不一致,通讯就失败,报错根本看不懂。查几个小时,发现就一个字母大小写问题。问题是你只想发个字符串过去啊。

成功的规范,往往是先落地用起来,然后一点一点长大的;先出一套巨复杂的规范,然后回去套业务,得到的不是惊喜,往往是惊吓。

四、微服务和DevOPS
一个微服务团队,从头到尾完全管理好自己的服务,其实是很难的一件事情。实际中,不少团队,微服务的技术用了不少,但DevOPS其实都没算做起来,仍然十分的依赖运维团队来完成很多工作,自建的微服务平台也不够好用,有了问题人仰马翻的。

感觉这个阶段,不如直接花钱买云厂商的服务,规避部分技术风险,重点发展业务,会更好一些。等有些技术积累了,再做适合自己的技术解决方案。

五、今天的云原生还有哪些主要矛盾,下一次软件架构的进化将会主要解决什么问题?

个人认为,当前的架构,还远远没有达到最优状态。真正最优的时候,绝大多数开发人员,是不需要知道K8S、ISTIO、Spring、数据库、缓存及各类中间件的存在的。

之前的状态是,硬件按硬件的思路发展,系统及虚拟化按其思路发展,应用架构也是按自己的思路发展。之所以当前需要发明很多新技术,是硬件、系统及虚拟化、应用架构彼此之间都有一条鸿沟,那大家就都需要自己造轮子搭梯子解决问题,统一问题必然有N中解法。后面相互磨合久了,鸿沟会进一步变窄,直至平滑过渡。

应用层开发人员,会在很大的技术视野内专注于业务层,其他层的需求只需要简单调用服务即可。

但不久的将来,一定会面临新的巨坑,需要填。

六、FAAS趋势

个人对FAAS有种直觉上的偏见,感觉有些走向了另一个极端:
1、如果保持F很简单,那平台确实可以直接将功能封装为通用服务,但就算加上DSL,F可以施展的空间感觉也不大
2、如果让F变复杂,支持复杂业务,那FAAS其实就退化成为了功能十分单一的微服务,还不如直接微服务来的爽快
整体感觉就是有些鸡肋,一边有纯图形化工具或标准服务,一边是正常程度的编码,夹在中间。

这个真的很像当年ESB的思维方式,给了一堆的标准组件,给人一看感觉封装的真好,不用编程也能做好多事情啊。但用过就知道,稍微复杂的业务就要自己去扩展组件了,不会编程什么都做不了。最后就是换了一个技术平台写代码而已。

各种类型的调度

硬件层面:
1、操作系统管理进程,分配CPU、内存等资源
2、计算线程与CPU的绑定,也算一种调度吧

网络层面:
1、路由器交换机的配置、包括F5的配置
2、服务器或虚拟机组成Cluster或NLB
3、Nginx反向代理时不同节点分配不同的比重,加上访问策略,也算一种调度吧。
4、类似的Dubbo等框架,也支持不同的调度方式

任务层面:
1、做大数据处理的时候,接触过Yarn
2、Kubernetes中Pod调度及驱逐机制
3、线程池、socket连接池、DB连接池、对象池之类的管理好像也能算

此外,在调度的过程中,有一个规律,那就是调度的时候,大家都倾向于,将代码和数据放到一起进行。
在数据量小的时候,大家倾向于把数据搬运到代码附近,比如CPU的调度。
但数据量大的时候,尤其是到了大数据、云计算时代,我们反而倾向于,把代码搬运到数据附近,因为这样更合算。

各类语言泛型实现方式

1、C
通过void*和强制类型转换来实现,也可以通过宏来实现类似功能,一般只会用作简单的实现

2、C++
模板类的模板具体化,预编译时生成不同的类,分别进行编译,会造成代码膨胀。
但由于不需要在运行时进行任何的类型识别,程序也会变得比较干净,运行效率也更高。

3、JAVA
基于类型擦除,在编译泛型类时将强制消除类型信息;
JVM运行时泛型类并不知道自己处理的具体是何种类型。

4、Go
当前版本不支持泛型,可以通过 interface{}强制转换实现泛型。
也可以通过第三方库,实现模板库类型的泛型。

5、C#
泛型类在编译生成中间码IL时,通用类型T只是一个占位符。
在实例化类时,根据实际数据类型代替T并由即时编译器(JIT)生成本地代码运行,不同封闭类的本地代码是不一样的。

6、JS、Python
动态类型天然支持泛型

使用JDK动态代理时为何必须实现至少一个接口

这个问题,就要去看一下OpenJDK的源码了:

//在Proxy类里中:
//constructorParams的定义如下:
private static final Class<?>[] constructorParams = { InvocationHandler.class };

//newProxyInstance无限精简之后就是:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        throws IllegalArgumentException {
    //通过ProxyClassFactory调用ProxyGenerator生成了代理类
    Class<?> cl = getProxyClass0(loader, interfaces);
    //找到参数为InvocationHandler.class的构造函数
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    //创建代理类实例
    return cons.newInstance(new Object[]{h});
}

//在ProxyGenerator类中:
public static byte[] generateProxyClass(final String name,Class<?>[] interfaces, int accessFlags)){}
private byte[] generateClassFile() {}

//上面两个方法,做的就是:
//将接口全部进行代理
//并生成其他需要的方法,比如上面用到的构造函数、toString、equals、hashCode等
//生成对应的字节码
//其实这也就说明了,为何JDK的动态代理,必须需要至少一个接口

Jetty源码分析01

一、Jetty的ScopedHandler的doStart方法,最后一步是将线程私有变量__outerScope设置成null,为什么需要这样做呢?

protected void doStart() throws Exception
{
    try{
        _outerScope=__outerScope.get();
        if (_outerScope==null){
           //本次处理的第一个scope handler
           //告知后续scope handler,_outerScope选我
            __outerScope.set(this);
        }
        super.doStart();
        _nextScope= getChildHandlerByClass(ScopedHandler.class);
    }
    finally
    {
        if (_outerScope==null){
           //本次处理结束
           //为了下次同一个线程处理是,
           //还能正常的设置第一个scope handler
           //必须把threadlocal变量设为null
            __outerScope.set(null);
        }
    }
}

二、Jetty中,ScopedHandler中nextHandle调用顺序是如何的?

//此外,这一节里有一个non scoped handler X,一开始没太看懂调阅顺序。
//后来发现是这样的:
public final void nextHandle(String target...)...
{
    if (_nextScope!=null && _nextScope==_handler){
        //上面条件可以保证下一个handler是scope handler
        _nextScope.doHandle(target,baseRequest,request, response);
    }
    else if (_handler!=null){
        //non scpoe handler调用下一个handler的
        super.handle(target,baseRequest,request,response);
    }
}

感觉类成员的命名不太合适,
比如__outerScope和_outerScope
比如_handler其实一直指向的是下一个handler,是不是该为_nextHandler更好一些?