避不开的分布式事务

最后更新于:2022-04-02 04:34:18

## 避不开的分布式事务 https://mp.weixin.qq.com/s?__biz=MzU1MzYwMjQ5MQ==&mid=2247485412&idx=1&sn=66560e9c6f84f0d634acd35527edc70f&chksm=fbf11b30cc8692262f3c438b64bd6b6181787b7eb01b17a2d42d6859def28b10a3c683f0befb&scene=132#wechat_redirect 原创Code综艺圈[Code综艺圈](javascript:void(0);)*昨天* 收录于话题 #分布式事务1 #微服务那些事15 ### 前言 关于前面系列的文章已经说到分布式服务之间的通信,则分布式事务接下来就是我们要一起学习的主题,走起。 数据库事务在现有大大小小的系统中几乎是避免不开的,或多或少总会有一些业务关联在一块;对于单机事务的应用场景和操作,相信小伙伴已经够熟练了;随着分布式、微服务的开发模式普及,分布式事务落地也成为了程序员的必备之技,接下来的几篇一起来学习和实操。 ### 正文 #### 1\. 事务回顾 ##### 1\. 1 事务简介 通俗一点理解就是**将一组对数据的操作(增、删、改、查)看做成一个逻辑单元,要么都执行,要么都不执行,确保数据一致性**。 ##### 1.2 事务特性(ACID) * 原子性(Atomicity) 指事务内所有操作要么一起执行成功,要么都一起失败(或者说是回滚);如事务经典转账案例:A给B转账,A把钱扣了,但B没有收到;可见这种错误是不能接受的,最终会回滚,这也是原子性的重要性。 * 一致性(Consistency) 指事务执行前后的状态一致,如事务经典转账案例:A给B互相转账,不管怎么转,最终两者钱的总和还是不变; * 持久性(Durability) 指事务一旦提交,数据就已经永久保存了,不能再回滚; * 隔离性(Isolation) 指多个并发事务之间的操作互不干扰,但是事务的并发可能会导致数据脏读、不可重复读、幻读问题,根据业务情况,采用事务隔离级别进行对应数据读问题处理。 ##### 1.3 事务隔离级别 * 读未提交(**Read uncommitted**) 指一个事务读取到其他未提交事务的数据。可能导致数据**脏读**。 转账案例:A正在给B转账,本来转的1000,A多输入了个0,变成10000,但此事务还未提交,但此时B查询到转入的是10000,但A取消事务回滚之后,B又查询不到转入的数据。这种情况就是**脏读** * 读已提交(**Read committed**) 指一个事务只能读取到其他事务已提交的数据,从而解决了**脏读**的问题。但可能导致数据**不可重复读**; 转账案例:A要给B转账1000,A先查看了一下余额,有1000,然后开始给B转钱,但此时A家里电费通过开启的自动缴费功能,自动从A账户扣除200缴纳电费,并提交;当A转账准备提交,再次确认余额时,钱少了200。这样就导致同一个事务中多次查询的结果不一致,这种情况就是**不可重复读**; * 可重复读(**Repeatable read**) 指事务只要一开启,就不允许其他事务进行修改操作,从而解决了**不可重复读**问题。但可能导致数据**幻读**; 转账案例:A经常给B转账,到年底了,需要查账,然后开启了一个事务进行查询统计,刚开始查询只是10条转账记录,正准备统计时,因为紧急情况A需要给B转一笔钱应急,从而**新增**了一条新记录,并提交;而查账事务正在统计中,最后发现转账额和看到的10条转账记录不匹配。这种情况就是**幻读** * 序列化(**Serializable**) 指事务之间只能串行话执行,就像队列一样,排队进行,这样就解决了**幻读**的问题,但是这种级别的并发性能不高,非特殊需求,这种级别一般不用。 #### 2\. 分布式事务场景 一个项目对应一个数据库,这种单机业务是平时处理的比较多的;这里主要归纳一下会出现分布式事务的场景。 ##### 2.1 一个项目多个数据库 ![图片](https://mmbiz.qpic.cn/mmbiz_png/qQ1zuvjsChRian5anJs8aK5dCOGQAWpFNKm6iaENbFAHnXaH1Dsz1J3kMEaIexWsFnDrZ3oWKqA1zHqcMQHuRDng/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 这种情况一般是并发量不大,但数据量比较大的情况,就比如一些采集设备数据做实时分析的系统,如传感器数据、电机状态等,经过一段时间,数据量会很多,导致单个数据库效率变低,所以通常会采用分业务存储。 如上图,如果出现需要操作DB1中数据的同时又需要操作DB2数据,确保两次操作要么都成功,要么都失败,这就需要事务,而这种和单一系统(一个项目,一个数据库)的事务处理方式不一样,得分布式事务进行处理。 ##### 2.2 多个项目一个数据库 ![图片](https://mmbiz.qpic.cn/mmbiz_png/qQ1zuvjsChRian5anJs8aK5dCOGQAWpFNf3zASGXcIVmRX93YTpiayVicX33ncGyTPAmnzbCLrRRe3GVx8YpclIQg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 有些系统需要将业务分开开发和部署,便于代码管理和后期维护,在数据库资源允许的情况下可以共用一个数据库,在这种情况下如果有事务操作,同样需要分布式事务进行处理。 ##### 2.3 多个项目多个数据库 ![图片](https://mmbiz.qpic.cn/mmbiz_png/qQ1zuvjsChRian5anJs8aK5dCOGQAWpFN1CAGZ8RGZNTlnvQRgl3XmefjiaXHHJIA8wnMcibCCYv5PicS6MoIO5smQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 这种方式其实就是微服务模式,分业务划分项目,每个业务对应一个数据库,这种场景下项目之间的事务肯定是分布式的啦 #### 3\. CAP了解 ##### 3.1 简介 对于分布式应用场景,有很多因素是不可控的,如网络不通、设备宕机、自然灾害等原因导致服务不可用,这种情况对于分布式而言需要有一定的取舍,不能因为个别服务的不可用,导致整个系统崩掉。通常CAP理论会成为分布式指标的取舍,根据系统业务需求,满足其中两个指标即可。如下图: ![图片](https://mmbiz.qpic.cn/mmbiz_png/qQ1zuvjsChRian5anJs8aK5dCOGQAWpFN85Hfn9gQZSwWju9Iyeic5gs5wz4pWPTwwJ89rbJAPbWpHDm4VoqeZHQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) **CAP是Consistency(一致性)、Availability(可用性)、PartitionTolerance(分区容错性)三个词的缩写**,具体含义如下: * **C(Consistency-一致性):是指在写操作之后,任意节点进行读取时,都能一致获取到最新的数据状态**。为了保证数据一致性,在同步数据时会对资源短时进行锁定,目的为了避免获取到老的数据,导致数据不一致情况,但这样就会导致服务在短时间内不可用。 * **A(Availability-可用性):是指发起任何操作时都可以得到响应结果,不会出现响应超时或响应错误**。就算是数据在同步过程中也要保证可用,即宁可拿到旧数据也不要报错。 * **P(PartitionTolerance-分区容错性):这里的分区是指网络分区**,通常分布式系统,各节点会部署到不同子网,由于网络具有不可控性,可能会导致节点之间的通信失败;但在设计此类系统时,应该考虑这种情况,保证提供正常的服务,这其实可以理解为平时咱们说的高可用;**这个指标是分布式系统必备的,不然就不能叫高可用系统啦**。 ##### 3.2 CAP组合 其实通过上面的概述,**C(一致性)和A(可用性)是互相排斥的**,为了保证一致性,会锁定资源导致短时间不可用,而可用性的要求就是必须对操作有对应的响应结果,就算得到的数据不是最新的也行,目的是保证可用。**而P(分区容错性)是分布式系统中必备指标,所以在分布式系统中经常的组合就是CP和AP**。 * **CP:放弃可用性,注重一致性和分区容错性,其实这就是所谓的强一致性**,可能在银行跨行转账这种强一致业务场景才会用到,具体得根据业务场景做取舍。 * **AP:放弃强一致性,注重可用性和分区容错性**,这是现在绝大多数分布式业务场景的选择,只要最后能保证最终一致性(Base理论)即可。 ##### 3.3 简单理解一下BASE理论 **BASE是Basically Available(基本可用),Soft State(软状态)和Eventually Consistent(最终一致性)三个短语的缩写,是对AP的一种扩展,即当出现故障时允许部分服务不可用,但要保证核心服务正常。对于数据,允许一定时间内不一致,通过中间状态(软状态)过渡,最后保证最终一致即可**。 举例说明: **Basically Available(基本可用)**:比如一个系统注册用户成功时需要发送信息通知,允许发送信息不成功,但注册这个核心功能要保证可用。 **Soft State(软状态)**:平时见得最多的软状态有:“支付中”、“数据处理中”等,这些状态是为了满足可用性和最终一致增加的过渡状态。 **Eventually Consistent(最终一致性)**:比如在购买商品时,支付的过程中会显示“支付中”,最终会显示“支付成功”,这个时候其实就保证最终你的账户和收款账户这个事务最终一致了,这种事务可以理解为“**柔性事务**”。 #### 4\. 分布式事务常用的解决方案 * **2PC(Two-phase commit protocol)**,又称二阶段提交,是一种强一致性解决方案。对其进行补充的还有一个叫**3PC**的解决方案。 * **TCC(Try Confirm  Cancel)**,补偿事务。 * **本地消息表** * **消息事务** * **最大努力通知** 这里先不细说,后续逐个击破,最终还是要用代码落地。 ### 总结 这篇只是一个开端,主要是大概回顾一下分布式事务中常遇到的知识点,后续主要是针对各个解决方案的落地,一起撸码。 一个被程序搞丑的帅小伙,关注"Code综艺圈",和我一起学~~~ ![图片](data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg== "图片") 图片 收录于话题#微服务那些事 15个 下一篇gRPC四种模式、认证和授权实战演示 文章已于2021/07/23修改 喜欢此内容的人还喜欢 [ gRPC趁现在还没大火,抢先了解一下 gRPC趁现在还没大火,抢先了解一下 ... Code综艺圈 不喜欢 不看的原因 确定 * 内容质量低 * 不看此公众号 ](javascript:void(0);)[ gRPC四种模式、认证和授权实战演示 gRPC四种模式、认证和授权实战演示 ... Code综艺圈 不喜欢 不看的原因 确定 * 内容质量低 * 不看此公众号 ](javascript:void(0);)[ Redis 帝国的神秘使者,竟然想改造 C 语言! Redis 帝国的神秘使者,竟然想改造 C 语言! ... 悟空聊架构 不喜欢 不看的原因 确定 * 内容质量低 * 不看此公众号 ](javascript:void(0);) ![](https://mp.weixin.qq.com/mp/qrcode?scene=10000004&size=102&__biz=MzU1MzYwMjQ5MQ==&mid=2247485412&idx=1&sn=66560e9c6f84f0d634acd35527edc70f&send_time=) 微信扫一扫 关注该公众号
';

安全背后: 浏览器是如何校验证书的

最后更新于:2022-04-02 04:34:16

## 安全背后: 浏览器是如何校验证书的 https://cjting.me/2021/03/02/how-to-validate-tls-certificate/ # 安全背后: 浏览器是如何校验证书的 tls certificate https 2021.03.02 现如今的 Web,HTTPS 早已经成为标配,公开的 HTTP 网站已经和 Flash 一样,慢慢在消亡了。 启用 HTTPS 的核心是一个叫做**证书**的东西。不知道大家是否有留意,前几年上 12306 的时候,浏览器都会提示「您的链接不是私密链接」,这其实就是因为 12306 的证书有问题。如果点击「继续前往」,打开 12306 网站,它会提示你下载安装它提供的“根证书”。 ![](http://asset.cjting.cn/FrBOHRLcNkDuAUnDstdn4Sjk4xMr.png) 那么,证书是什么?里面含有什么内容?浏览器为什么会不信任 12306 的证书?为什么下载 12306 提供的根证书就可以解决这个问题?根证书又是什么? ## 证书,公钥和私钥 我们先来简单回顾一下 TLS 和 HTTPS。关于这个话题,我在[从零开始搭建一个 HTTPS 网站](https://cjting.me/2016/09/05/build-a-https-site-from-scratch/)这篇博客中有详细的论述,对细节感兴趣的朋友可以先去读一下这篇博客。 HTTPS 全称是 HTTP Over TLS,也就是使用 TLS 进行 HTTP 通信。 TLS (Transport Layer Security) 是一个安全通信协议,用于建立一个安全通道来交换数据。 看名字可以知道这是一个传输层协议,因此应用层协议对它是没有感知的,HTTP, FTP, SMTP 都可以和 TLS 配合使用。 Tip: 关于网络协议,阮一峰的这篇博客[互联网协议入门(一)](https://www.ruanyifeng.com/blog/2012/05/internet_protocol_suite_part_i.html)写的很好,值得一看。 TLS 创建安全链接的步骤如下: * 双方协商使用的协议版本,加密算法等细节 * 服务器发送 证书 给客户端 * 客户端校验证书有效性 * 双方根据握手的一些参数生成一个对称秘钥,此后所有的内容使用这个秘钥来加密 我们先来看证书,证书是一个文件,里面含有目标网站的各种信息。 例如网站的域名,证书的有效时间,签发机构等,其中最重要的是这两个: * 用于生成对称秘钥的公钥 * 由上级证书签发的签名 证书文件的格式叫做 X.509,由[RFC5280](https://tools.ietf.org/html/rfc5280)规范详细定义。存储上分为两种,一种叫做 DER,是二进制的,还有一种叫做 PEM,是基于 Base64 的。 关于 RSA 的公钥和私钥记住一点就行:**我们可以使用算法生成一对钥匙,他们满足一个性质:公钥加密的私钥可以解开,私钥加密的公钥可以解开**。 Tip: RSA 算法的具体工作原理可以参考我这篇博客[RSA 的原理与实现](https://cjting.me/2020/03/13/rsa/)。 证书,顾名思义,是用来证明自己身份的。因为发送证书的时候是明文的(这一步也没法加密),所以证书内容是可以被中间设备篡改的。 那么要怎样设计一套机制保证当我访问 github.com 的时候,收到的证书确实是 github.com 的证书,而不是某个中间设备随意发来的证书? 大家可以思考一下这个问题🤔。 解决办法是采用「信任链」。 首先,有一批证书颁发机构(Certificate Authority,简称为 CA),由他们生成秘钥对,其中私钥保存好,公钥以证书的格式安装在我们的操作系统中,这就是**根证书**。 我们的手机、电脑、电视机的操作系统中都预装了 CA 的根证书,他们是所有信任构建的基石。当然,我们也可以自己下载任意的根证书进行安装。 接下来,只要设计一个体系,能够证明 A 证书签发了 B 证书即可。这样对于收到的任何一个证书,顺藤摸瓜,只要最上面的根证书在系统中存在,即可证明该证书有效。 比如说,我们收到了服务器发过来的 C 证书,我们验证了 C 是由 B 签发的,然后又验证了 B 是由 A 签发的,而 A 在我们的系统中存在,那也就证明了 C 这个证书的有效性。 这其中,A 是根证书,B 是中间证书,C 是叶证书(类似树中的叶节点)。中间证书可以有很多个,信任的链条可以任意长,只要最终能到根证书即可。 得益于 RSA 的非对称性质,验证 A 是否签发了 B 证书很简单: * 计算 B 的 hash 值(算法随便,比如 SHA1) * 使用 A 的**私钥**对该 hash 进行加密,加密以后的内容叫做「签名(Signature)」 * 将该「签名」附在 B 证书中 A 使用自己的私钥给 B 生成签名的过程也就是「签发证书」,其中 A 叫做 Issuer,B 叫做 Subject。 这样,当我们收到 B 证书时,首先使用 A 证书的公钥(公钥存储在证书中)解开签名获得 hash,然后计算 B 的 hash,如果两个 hash 匹配,说明 B 确实是由 A 签发的。 重复上面的过程,直到根证书,就可以验证某个证书的有效性。 接下来我们来问几个问题。 ### 为什么需要中间证书? 为什么要设计中间证书这个环节?直接使用根证书进行签发不好吗? 这是因为根证书的私钥安全性至关重要,一旦被泄露,将引起巨大的安全问题。 所以,根证书的私钥都是被保存在离线的计算机中,有严格的操作规章,每次需要使用时,会有专人将数据通过 USB 拷贝过去,操作完了以后,再将数据带出来。 在这套流程下,直接将根证书用于签发普通证书是不现实的。想想这个世界上有多少网站,每天对证书的需求量都是巨大的,根证书的操作效率无法满足要求,因为不能批量和自动化。 同时,对根证书私钥的操作越多,泄露的风险也就越大,因此,人们就发明了中间证书。 使用根证书签发一些中间证书,这些中间证书就可以用来签发大量的叶证书,这个过程完全可以是自动化的,就像 Let’s Encrypt 那样。 同时,即便中间证书的私钥泄露了也不要紧,可以使用根证书把它们撤销掉,具体怎么撤销是另外一个话题了,这里不再展开。 通过使用中间证书,我们就可以做到既方便,又安全。 ### 浏览器如何获取中间证书? 一般来说,服务器会将中间证书一并发送过来。也就是说,当我们访问某个网站时,收到的不是一个证书,而是一系列证书。 当然,这不是强制要求,在服务器不发送的情况下,浏览器也会使用一些方法去定位中间证书,比如 * 缓存之前下载过的证书 * 证书文件中的 Authority Information Access (AIA) Extension 里面含有上级证书的相关信息,浏览器可以使用这个信息去下载上级证书 ### 根证书有时效吗?过期了怎么办? 每个证书都有有效时间,根证书自然也不例外。 在 Mac 上,打开 Keychain Access,选择侧边的`System Roots`就可以看到我们系统中安装的所有根证书。 ![](http://asset.cjting.cn/FirK2IaLZGajhfpLAORBz6CL6NiD.png) 其中很明显地显示了一栏叫做「Expires」,过期时间。 那么,根证书过期了怎么办? 答案是不怎么办,在过期前 CA 会生成新的根证书,并和各大产商合作,在操作系统升级的时候安装到我们的设备上。 老的根证书过期以后只是无法再作为信任锚点为其他的证书提供信任而已。 根证书的生命周期一般是 20 年,Let’s Encrypt 之前使用的根证书是`DST Root CA X3`,2021 年九月就要过期了,这个证书是 2000 年创建的。 Let’s Encrypt 在 2015 年创建了一个新的根证书`ISRG Root X1`,在 2018 年被完全认可并逐步安装到各种设备上。 安装新的根证书的唯一方式是通过系统更新,但是这其实是不可控的事情,因为总有一些设备不会更新。 我们可以想象一下,如果一个设备停止更新,同时里面内置的所有根证书都过期了,那么这个设备就无法再进行 HTTPS 通信了。 ### 根证书有什么特征?根证书的签名是什么? 每个证书中都含有当前证书对象 Subject 的信息以及该证书的签发者 Issuer 的信息。 根证书的特征是 Subject 和 Issuer 的信息是一致的,也就是所谓的「自签名」(Self-Signed)。 ![](http://asset.cjting.cn/FuWvD3k8eGdc6j81xg3ZDPd72hfJ.png) 因为证书的签名是由上级证书的私钥来签的,根证书没有上级证书,所有根证书的签名是用自己的私钥签的。 我们可以来验证一下,以 GitHub 使用的根证书 DigiCert High Assurance EV Root CA 为例。 ~~~bash # 首先,打开 Keychain Access,找到上述证书,拖拽出来 # 重命名为 root.cer # 转换 DER 到 PEM 格式 $ openssl x509 -inform der -in root.cer -out root.pem # 查看证书的签名,可以看到签名所使用的的 hash 算法是 sha1 $ openssl x509 -in root.pem -text -noout -certopt ca_default -certopt no_validity -certopt no_serial -certopt no_subject -certopt no_extensions -certopt no_signame Signature Algorithm: sha1WithRSAEncryption 1c:1a:06:97:dc:d7:9c:9f:3c:88:66:06:08:57:21:db:21:47: f8:2a:67:aa:bf:18:32:76:40:10:57:c1:8a:f3:7a:d9:11:65: 8e:35:fa:9e:fc:45:b5:9e:d9:4c:31:4b:b8:91:e8:43:2c:8e: b3:78:ce:db:e3:53:79:71:d6:e5:21:94:01:da:55:87:9a:24: 64:f6:8a:66:cc:de:9c:37:cd:a8:34:b1:69:9b:23:c8:9e:78: 22:2b:70:43:e3:55:47:31:61:19:ef:58:c5:85:2f:4e:30:f6: a0:31:16:23:c8:e7:e2:65:16:33:cb:bf:1a:1b:a0:3d:f8:ca: 5e:8b:31:8b:60:08:89:2d:0c:06:5c:52:b7:c4:f9:0a:98:d1: 15:5f:9f:12:be:7c:36:63:38:bd:44:a4:7f:e4:26:2b:0a:c4: 97:69:0d:e9:8c:e2:c0:10:57:b8:c8:76:12:91:55:f2:48:69: d8:bc:2a:02:5b:0f:44:d4:20:31:db:f4:ba:70:26:5d:90:60: 9e:bc:4b:17:09:2f:b4:cb:1e:43:68:c9:07:27:c1:d2:5c:f7: ea:21:b9:68:12:9c:3c:9c:bf:9e:fc:80:5c:9b:63:cd:ec:47: aa:25:27:67:a0:37:f3:00:82:7d:54:d7:a9:f8:e9:2e:13:a3: 77:e8:1f:4a # 提取签名内容到文件中 $ openssl x509 -in root.pem -text -noout -certopt ca_default -certopt no_validity -certopt no_serial -certopt no_subject -certopt no_extensions -certopt no_signame | grep -v 'Signature Algorithm' | tr -d '[:space:]:' | xxd -r -p > root-signature.bin # 提取根证书中含有的公钥 $ openssl x509 -in root.pem -noout -pubkey > root-pub.pem # 使用公钥解密签名 $ openssl rsautl -verify -inkey root-pub.pem -in root-signature.bin -pubin > root-signature-decrypted.bin # 查看解密后的内容 # 可以看到,签名中存储的 hash 值为 E35E...13A8 $ openssl asn1parse -inform DER -in root-signature-decrypted.bin 0:d=0 hl=2 l= 33 cons: SEQUENCE 2:d=1 hl=2 l= 9 cons: SEQUENCE 4:d=2 hl=2 l= 5 prim: OBJECT :sha1 11:d=2 hl=2 l= 0 prim: NULL 13:d=1 hl=2 l= 20 prim: OCTET STRING [HEX DUMP]:E35EF08D884F0A0ADE2F75E96301CE6230F213A8 # 接下来我们计算证书的 hash 值 # 首先提取证书的 body # 因为证书中含有签名,签名是不包含在 hash 值计算中的 # 所以不能简单地对整个证书文件进行 hash 运算 $ openssl asn1parse -in root.pem -strparse 4 -out root-body.bin &> /dev/null # 计算 sha1 哈希值 $ openssl dgst -sha1 root-body.bin SHA1(root-body.bin)= e35ef08d884f0a0ade2f75e96301ce6230f213a8 ~~~ hash 值匹配,这也就说明根证书确实是自签名的,用自己的私钥给自己签名。 ## 纸上得来终觉浅 理论知识我们已经全部具备了,接下来我们来完整走一遍流程,以 github.com 为例,校验一下它的证书是否有效。 ~~~bash # 新建一个文件夹 github 保存所有的文件 $ mkdir github && cd github # 首先,我们下载 github.com 发送的证书 $ openssl s_client -connect github.com:443 -showcerts 2>/dev/null github.com.crt # github.com.crt 是 PEM 格式的文本文件 # 打开可以发现里面有两段 -----BEGIN CERTIFICATE---- # 这说明有两个证书,也就是 github.com 把中间证书也一并发过来了 # 接下来我们把两个证书提取出来 $ awk '/BEGIN/,/END/{ if(/BEGIN/){a++}; out="cert"a".tmpcrt"; print >out}' < github.com.crt && for cert in *.tmpcrt; do newname=$(openssl x509 -noout -subject -in $cert | sed -n 's/^.*CN=\(.*\)$/\1/; s/[ ,.*]/_/g; s/__/_/g; s/^_//g;p').pem; mv $cert $newname; done # 我们得到了两个证书文件 # github_com.pem 和 DigiCert_SHA2_High_Assurance_Server_CA.pem # 首先,验证 github_com.pem 证书确实 # 是由 DigiCert_SHA2_High_Assurance_Server_CA.pem 签发的 # 提取 DigiCert_SHA2_High_Assurance_Server_CA 的公钥 # 命名为 issuer-pub.pem $ openssl x509 -in DigiCert_SHA2_High_Assurance_Server_CA.pem -noout -pubkey > issuer-pub.pem # 查看 github_com.pem 的签名 # 可以看到 hash 算法是 sha256 $ openssl x509 -in github_com.pem -text -noout -certopt ca_default -certopt no_validity -certopt no_serial -certopt no_subject -certopt no_extensions -certopt no_signame Signature Algorithm: sha256WithRSAEncryption 86:32:8f:9c:15:b8:af:e8:d1:de:08:3a:44:0e:71:20:24:d6: fc:0e:58:31:cc:aa:b4:ad:1c:d5:0c:c5:af:c4:bb:fe:5f:ac: 90:6a:42:c8:21:eb:25:f1:6b:2c:37:b2:2a:a8:1a:6e:f2:d1: 4f:a6:2f:bc:cf:3a:d8:c1:9f:30:c0:ec:93:eb:0a:5a:dc:cb: 6c:32:1c:60:6e:ec:6e:f8:86:a5:4f:a0:b4:6d:6a:07:4a:21: 58:d0:29:7d:65:8a:c8:da:6a:ba:ab:f0:75:21:33:00:40:6f: 85:c5:13:e6:27:73:6c:ae:ea:e3:96:d0:53:db:c1:21:68:10: cf:e3:d8:50:b0:14:ec:a9:98:cf:b8:ce:61:5d:3d:a3:6d:93: 34:c4:13:fa:11:66:a3:dd:be:10:19:70:49:e2:04:4d:81:2c: 1f:2e:59:c6:2c:53:45:3b:ee:f6:13:f4:d0:2c:84:6e:28:6d: e4:e4:ca:e4:48:89:1b:ab:ec:22:1f:ee:12:d4:6c:75:e9:cc: 0b:15:74:e9:6d:9f:db:40:1f:e2:24:85:a3:4b:a4:e9:cd:6b: c8:77:9f:87:4f:05:73:00:38:a5:23:54:68:fc:a2:3d:bf:18: 19:0e:a8:fd:b9:5e:8c:5c:e8:fc:e4:a2:52:70:ee:79:a7:d2: 27:4a:7a:49 # 提取签名到文件中 $ openssl x509 -in github_com.pem -text -noout -certopt ca_default -certopt no_validity -certopt no_serial -certopt no_subject -certopt no_extensions -certopt no_signame | grep -v 'Signature Algorithm' | tr -d '[:space:]:' | xxd -r -p > github_com-signature.bin # 使用上级证书的公钥解密签名 $ openssl rsautl -verify -inkey issuer-pub.pem -in github_com-signature.bin -pubin > github_com-signature-decrypted.bin # 查看解密后的信息 $ openssl asn1parse -inform DER -in github_com-signature-decrypted.bin 0:d=0 hl=2 l= 49 cons: SEQUENCE 2:d=1 hl=2 l= 13 cons: SEQUENCE 4:d=2 hl=2 l= 9 prim: OBJECT :sha256 15:d=2 hl=2 l= 0 prim: NULL 17:d=1 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]:A8AA3F746FE780B1E2E5451CE4383A9633C4399E89AA3637252F38F324DFFD5F # 可以发现,hash 值是 A8AA...FD5F # 接下来计算 github_com.pem 的 hash 值 # 提取证书的 body 部分 $ openssl asn1parse -in github_com.pem -strparse 4 -out github_com-body.bin &> /dev/null # 计算 hash 值 $ openssl dgst -sha256 github_com-body.bin SHA256(github_com-body.bin)= a8aa3f746fe780b1e2e5451ce4383a9633c4399e89aa3637252f38f324dffd5f ~~~ hash 值匹配,我们成功校验了 github.pem 这个证书确实是由 DigiCert\_SHA2\_High\_Assurance\_Server\_CA.pem 这个证书来签发的。 上面的流程比较繁琐,其实也可以直接让 openssl 来帮我们验证。 ~~~bash $ openssl dgst -sha256 -verify issuer-pub.pem -signature github_com-signature.bin github_com-body.bin Verified OK ~~~ 接下来的过程是上面一样,首先,我们获取上级证书的信息。 ~~~bash # 获取上级证书的名字 $ openssl x509 -in DigiCert_SHA2_High_Assurance_Server_CA.pem -text -noout | grep Issuer: Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA ~~~ 然后就是校验 DigiCert\_SHA2\_High\_Assurance\_Server\_CA 是由 DigiCert High Assurance EV Root CA 来签发的,同时这个证书存在于我们系统中。 这一步我就不再重复了,留给大家作为练习~ ## 自签名 最后,我们来看看怎么自己签发证书用于本地测试。 我们可以使用 openssl 这样的底层工具来完成这个任务,但是会很繁琐:生成根证书、生成下级证书,使用根证书签发下级证书、安装根证书。 程序员首先要解放的就是自己的生产力,这里我们使用一个工具[mkcert](https://github.com/FiloSottile/mkcert)。 `brew intall mkcert`进行安装,然后使用`mkcert -install`将它的根证书安装到我们的系统中。 接下来我们就可以任意生成我们想要的证书了,以 localhost 为例,运行`mkcert localhost`,我们得到了两个文件,其中`localhost-key.pem`是私钥,`localhost.pem`是证书,用于发送给客户端。 使用一个 HTTP Server 来验证一下,这里我们使用[local-web-server](https://github.com/lwsjs/local-web-server)。 `yarn global add local-web-server`安装完毕以后,使用`ws --key localhost-key.pem --cert localhost.pem`启动我们的服务器,打开 https://localhost:8000,可以看到地址栏有了一把可爱的小锁🎉。
';

文章保存

最后更新于:2022-04-02 04:34:14

[安全背后: 浏览器是如何校验证书的](%E6%96%87%E7%AB%A0%E4%BF%9D%E5%AD%98/%E5%AE%89%E5%85%A8%E8%83%8C%E5%90%8E%E6%B5%8F%E8%A7%88%E5%99%A8%E6%98%AF%E5%A6%82%E4%BD%95%E6%A0%A1%E9%AA%8C%E8%AF%81%E4%B9%A6%E7%9A%84.md) [避不开的分布式事务](%E6%96%87%E7%AB%A0%E4%BF%9D%E5%AD%98/%E9%81%BF%E4%B8%8D%E5%BC%80%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1.md)
';

今日有感

最后更新于:2022-04-02 04:34:11

## 今日有感 [TOC=3,8] ---- ### 公交篇 坐公交时站在中间,手握扶手,看着前方,随着公交的剧烈颠簸,起起落落,飞驰,闭上眼睛我感觉我此刻就是杰克船长,正在驾驶着我的黑珍珠号海盗船,在大海中乘风劈浪,所向披靡,朝着不老泉的方向全速前进...... di,刷卡成功,学生卡,老年卡... “往里面走,往里面走,...” “哎呀,别挤啊,踩到我脚了...” ... 驾驶员的话却让我记住了:“你往里面挤啊,不挤你总是上不去的!” last updat: 2019-03-27 07:42:18 ***** ### 喜欢这代码 我知道任务进度赶不上,可就是忍不住花时间来优化这代码,想把功能做到最好,忍受不了残缺的功能,我想要体验更好,我一直是在作为一个普通用户去看待每个功能的,我也知道 leader 并不关心这些,不在乎我浪费时间做了什么让用户更喜欢这功能,只在乎进度。可我还是忍不住不去管它,它就在那里,在我看不到的地方呼唤我,不去做好我就没心思去干别的了。 我知道症结所在了,我喜欢这代码。至于进度,我会抽时间赶上的。 这绝不是洁癖和强迫症的借口。 last update: 2019-03-28 07:44:36 ***** ### 珍惜拥有的 我们总想拥有更多,对已经拥有的反而没什么感觉,但当生活残酷的让你一件一件的失去曾经拥有的一切的时候,才悔然发现当前仅有的都已经是恩赐了,怎会奢望更多。 比起不能拥有的遗憾,失去最重要的才让人伤痛,愿我们都能珍惜,不要等到失去才追悔莫及长久痛心。 ---- ### 作业方法论 A/B/C: 某项事 1/2/3: 完成某项事的步骤 完整作业(依次完成某项事的每一部分,不适合大规模生产): A 1,2,3 B 1,2,3 C 1,2,3 流水线作业(每次只完成某项事的一部分,重复做相同的部分,而不区分具体的事,这样可以提高效率): A 1, B 1, C 1 A 2, B 2, C 2 A 3, B 3, C 3 ---- ### 术业有专攻,选择用合适的工具 - php web 应用 轻量的中间件 - python 爬虫,数据处理,数据分析 机器学习,自动化,win32 API,胶水万能语言 - java 大数据,中间件,业界最先进成熟方案 - javascript/node.js 大前端,前端工程化,轻量级高性能 EventLoop webServer - C 数据结构,数据库,底层系统级编程 - C++ 面向对象的C语言 - Go 云计算时代的C语言 - Rust 更安全的C语言 ---- https://173app.com/apps/jiohjechmlngmhkhdcplhkkfimefdgil https://www.processon.com/ 可能使用了 lucidchart diagrams 开源流程图项目,界面很像。 ---- last update: 2019-03-29 07:48:59
';

架构

最后更新于:2022-04-02 04:34:09

## 架构 > 记录一些并发,分布式,架构,运维,网络,底层,操作系统等的一些计算机系统性的知识。 知识是有严密的结构、框架、纲目的,看似各个知识点散落在各处,但其实每个节点相互关联,按照层级关系组合起来就能形成一张完整的网结构,建立完整的知识结构,并逐个击破,然后连贯起来运用就算是完全掌握了。 [APP分层架构设计随想](http://mp.weixin.qq.com/s/YykUzzwEl4pYsRFyAsXlcA) [分层架构,前后端分离有啥坏处?](http://mp.weixin.qq.com/s/ZKjKUkWkNqxiwNEjBDXtiQ) [究竟为什么要引入数据库中间件](http://mp.weixin.qq.com/s/lHrMS-GdKsZYhaOf59magQ) [啊,业务层是否也需要服务化?](http://mp.weixin.qq.com/s/HyvK1c87oQrPM0-C2dQ0kQ) [互联网分层架构的本质](http://mp.weixin.qq.com/s/4UB4AZe2R0lVCT6f5ExEMw) [互联网分层架构之-DAO与服务化](http://mp.weixin.qq.com/s/L3Tu-1DPUhj7i5ZJY1vRDA) [架构师之路,季度精选40篇](http://mp.weixin.qq.com/s/vLebPT-58Jw-Q7afhkgHSg) [分布式ID生成器 | 架构师之路](http://mp.weixin.qq.com/s/AHRCYOjnXAgcy2j6vziukQ) [工作线程数究竟要设置为多少 | 架构师之路](http://mp.weixin.qq.com/s/BRpngTEFHjzpGv8tkdqmPQ) [从"嘿,今晚..."谈消息安全传输中的技术点](https://mp.weixin.qq.com/s/TG2MGcoIm19NNwxSOZ2ZIQ) [远离中国式辩论](http://mp.weixin.qq.com/s/ENDQ3bwQ2BE3Qq3YlEjegA) [互联网分层架构,为啥要前后端分离? - 架构师之路](http://mp.weixin.qq.com/s/Nhyo969WnEwyCWpr34ECcA) [分层架构,前后端分离有啥坏处?](http://mp.weixin.qq.com/s/ZKjKUkWkNqxiwNEjBDXtiQ) [关于RPC以及它与HTTP之间的联系(构建分布式系统必懂的知识)](https://www.toutiao.com/a6485639775209390605/?tt_from=weixin&utm_campaign=client_share&app=news_article&utm_source=weixin&iid=12619555732&utm_medium=toutiao_android&wxshare_count=1) [从金拱门餐厅联想到的分布式系统设计思维](http://mp.weixin.qq.com/s/C7Ln3-EnzB6mEEx9k1VeYQ) [Kafka不只是个消息系统](https://mp.weixin.qq.com/s/lnnCYihhk_wIb5R-V7ZXZg) [从前端到后端,互联网分层架构设计](http://mp.weixin.qq.com/s/tnTwlhn3TfL1p65WYqWPdA) [怎么做 Web API 版本控制?](http://mp.weixin.qq.com/s/7IrquwL_yWShdrPwtNm5Tw) [双11,你猜技术人会买什么](http://mp.weixin.qq.com/s/uH0DfNczjQmDCulMP3jO8A) [库存扣多了,到底怎么整 | 架构师之路](http://mp.weixin.qq.com/s/waGRvyhab2z8b-BIw9bJeA) [分布式唯一ID极简教程](http://mp.weixin.qq.com/s/cqIK5Bv1U0mT97C7EOxmnA) [基础教学 | API 是如何工作的](http://mp.weixin.qq.com/s/FDPEWXW6eB11Bhycy7ZSFg) [什么是观察者模式?](http://mp.weixin.qq.com/s/2AC2tFEYTfpYlt_3BHBUsw) [观察者模式和“发布-订阅”模式有区别吗?](http://mp.weixin.qq.com/s?__biz=MzA5NzkwNDk3MQ==&mid=2650585339&idx=1&sn=d3276dcd3637b6e9ed5491ce3c225214&source=41#wechat_redirect) > 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。 [TCPIP协议栈动画](https://www.ixigua.com/a6493074176830620174/?utm_source=toutiao&utm_medium=feed_stream#mid=2925781813) [Web 开发中,什么级别才算是高并发](http://mp.weixin.qq.com/s/GkCDZtviGbBQZL_cLElMFg) [互联网架构,如何进行容量设计?](http://mp.weixin.qq.com/s/_6P2q98_QAJUdIyYrjtp2A) [淘宝和天猫首页都用到了哪些技巧或者技术?-易店无忧网店转让平台的回答-悟空问答](https://www.wukong.com/answer/6493336918502146318/?iid=12619555732&app=news_article&share_ansid=6493336918502146318&wxshare_count=1&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_android&utm_campaign=client_share) [高并发,高容错性商城项用到什么框架?](https://www.wukong.com/answer/6494494750056382733/?iid=12619555732&app=news_article&share_ansid=6494494750056382733&wxshare_count=1&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_android&utm_campaign=client_share) [【系统架构】大型网站架构演化历程(下)](http://mp.weixin.qq.com/s/U5tH_4Am0h4LSXcIa_WlWg) [什么是Zookeeper?](http://mp.weixin.qq.com/s/J8erMBhiogXoQcn91SJcbw) [看腾讯运维应对“18岁照片全民怀旧”事件的方案,你一定不后悔!](https://www.toutiao.com/a6506981644501516803/?tt_from=weixin&utm_campaign=client_share×tamp=1515048863&app=news_article&utm_source=weixin&iid=22069500288&utm_medium=toutiao_android&wxshare_count=1) [什么是DevOps](http://mp.weixin.qq.com/s/l6mPQIkjLNERrBn41wA3xA) [数据库读写分离架构,为什么我不喜欢](http://mp.weixin.qq.com/s/6mov6Ke3kyAUcWygDj-uaQ) [从混乱到混乱,业务逻辑搬家记](http://mp.weixin.qq.com/s/qxW4P7NeAm0r5FyNOtT5Pg) [【系统架构】亿级Web 系统的容错性实践【中】](http://mp.weixin.qq.com/s/VTJ3--4j6iJQb5IUe9JwHw) > 设置超时:怪不得很多软件都会设置超时时间,比如Nginx和PHP,原来是为了容错和性能。 > > 容错很重要,比如纠错码使得浏览器得以运作。 [携程运维自动化平台,上万服务器变更也可以很轻松](https://www.toutiao.com/a6509452096356483587/?tt_from=weixin&utm_campaign=client_share×tamp=1515662757&app=news_article&utm_source=weixin&iid=22069500288&utm_medium=toutiao_android&wxshare_count=1) [一分钟了解互联网动静分离架构](https://mp.weixin.qq.com/s/Kpsq8MH2TvXQEECyGrmj9A) > 总数据量不大,生成静态页面数量不多的业务,非常适合于“页面静态化”优化。 [必备,前台与后台分离的架构实践](https://mp.weixin.qq.com/s/d65cXGXp_jcEeELyDZxGVA) [基础教学 | 什么是负载均衡?](http://mp.weixin.qq.com/s/NAmXKIT9cHewYBqu66HgYw) [单点登录怎如何实现?详解](https://www.toutiao.com/a6510049349332894215/?tt_from=weixin&utm_campaign=client_share×tamp=1515785663&app=news_article&utm_source=weixin&iid=22069500288&utm_medium=toutiao_android&wxshare_count=1) [Dubbo的IP错乱问题,史上最好方法,强烈建议收藏!](https://www.toutiao.com/a6509974695826162190/?tt_from=weixin&utm_campaign=client_share×tamp=1515785858&app=news_article&utm_source=weixin&iid=22069500288&utm_medium=toutiao_android&wxshare_count=1) [《Android插件化技术——原理篇》](http://mp.weixin.qq.com/s/Uwr6Rimc7Gpnq4wMFZSAag) [当多线程并发遇到Actor](https://mp.weixin.qq.com/s/mzZatZ10Rh19IEgQvbhGUg) > 感觉并发,锁,是计算机里面最难的部分,几乎核心的业务逻辑都和这个有关系,建议刘哥以后把这类文章,集结起来,成为一个专题目录。 > > actor模型可以直接类比生活中的一个个人,相互独立,只能通过消息改变别人的状态,在此基础上设置一个个组织,比比你身旁,就知道怎么编程了 曾经研读Erlang理论多年,已经入坑Erlang,目前觉得是最有意义的决定,从此生活就是编程编程就是生活 有兴趣的朋友多多交流 > > 那和MQ的区别? 使用Actor模型,一个系统是有多个Actor组成的。 肯定不会有多个MQ组成,MQ及其处理虽然和Actor思想类似,但是在系统中的地位和作用和Actor完全不同 [算法分析神器—时间复杂度](http://mp.weixin.qq.com/s/643URs9k_EQIMWGmb35QkQ) [集群信息管理,架构设计中最容易遗漏的一环](http://mp.weixin.qq.com/s/ES9BrWL7Nuv78TQOTzI-qg) [分级告警策略,人性化系统监控?](https://mp.weixin.qq.com/s/Rbx-KLGgd6GF4JZU3CGwcQ) [100行代码,搞定http监控框架](http://mp.weixin.qq.com/s/46y8yIL59vUbEs3OVrSXig) [短网址(short URL)系统的原理及其实现](http://mp.weixin.qq.com/s/cqi9C2tgw9-PSfb3Hbmrkw) [Token 认证的来龙去脉 - 边城客栈 - SegmentFault](https://segmentfault.com/a/1190000013010835?_ea=3248405) >[danger] **实际上传统session认证就相当于是一个超级令牌**,cookie的`PHPSESSID`的值就相当于是token令牌(登录验证密码通过就一次性颁发了),但是某些时候,需要不透露密码给第三方也能实现一些权限/资源的访问,那么就颁发一个令牌,所以其实方式变了,但其实根本核心思想一致都没有变。 [基于 Token 的 WEB 后台认证机制](https://mp.weixin.qq.com/s/QDr4DaMrH-g78l5oXFfbeg) > tp支持session_id参数指定在url上,但是这种其实还是传统的 `cookie - session` 模式,如果简单的将这个cookie叫做token的话,那么没什么意义,不过是session_id换个名字而已。所以 token ≠`cookie - session` ,token和session是不同的技术方案。 [精心整理 | 2018一月文章目录](http://mp.weixin.qq.com/s/h5MW309zVBktZdoyS_T7dg) [左耳朵耗子:分布式系统架构经典资料](http://mp.weixin.qq.com/s/KbNNU246BAeJmLoWbKie6g) [什么是CAP定理?](http://mp.weixin.qq.com/s/jRA2vRqmjs9WRMZWvzesMA) [理解 OAuth 2.0 认证](https://mp.weixin.qq.com/s/1vAamlkxBNN5sH0vlLQ2Rw) [【系统架构】大型网站的灵魂——性能](https://mp.weixin.qq.com/s/w0aq1L6ioO5l-S23BDCnWA) [HDFS的诞生](https://mp.weixin.qq.com/s/3QFQRL708Muxf_QzKmSMlw) [【系统架构】大白话聊聊分布式事务](https://mp.weixin.qq.com/s/yBquVKyKnhpy2yPxY-0eqA) [大型网站架构技术一览](https://mp.weixin.qq.com/s/kBU2qBNY2N3vXbxnCnAKxw) [漫画:什么是服务熔断?](https://mp.weixin.qq.com/s/8wzQEW5NFoayhH8JoFCp2A) [左耳朵耗子:聊聊分布式系统的认知故障和弹力设计](https://mp.weixin.qq.com/s/tRIgZmYiVeBpWsHZtUh9pQ) [Lambda 表达式有何用处?](https://mp.weixin.qq.com/s/-PHOc6p-qKJBktle28AUgA) [Docker 微服务教程 - 阮一峰的网络日志](http://www.ruanyifeng.com/blog/2018/02/docker-wordpress-tutorial.html) [敢和我比“快”吗?法国初创公司Algolia为你提供毫秒级的数据库实时搜索服务_36氪](http://36kr.com/p/209747.html) [【蜜豹资讯】神一样的一个人的互联网公司,值5.75亿美金,中国所有婚恋网站是不是都得膜拜一下!_搜狐科技_搜狐网](http://www.sohu.com/a/129466566_486469) [PlentyOfFish.com .NET网站的又一传奇 - linFen - 博客园](http://www.cnblogs.com/luluping/archive/2011/03/08/1977634.html) [【系统架构】小型电商网站的架构(一)](http://mp.weixin.qq.com/s/EbDlb_GazJX-ORnA-AAjfQ) [全球如何保证区块生成是匀速的?](http://mp.weixin.qq.com/s/Z91B7HVnSmP9XG2XEsMM2A) [单点登录原理与简单实现 - ywlaker - 博客园](http://www.cnblogs.com/ywlaker/p/6113927.html) [美团外卖前端可视化界面组装平台 —— 乐高](https://tech.meituan.com/waimai-lego.html) [C++从零实现神经网络(收藏版:两万字长文)](https://mp.weixin.qq.com/s/FLCmaSU8qlmTWSzs1PEhnw) [1分钟了解协同过滤,pm都懂了](http://mp.weixin.qq.com/s/eJZDqoM6Fz6KeD4MEjVFYQ) [信道编码的前世今生: 一段波澜壮阔的通信史!](https://www.toutiao.com/a6533554021599281671/?tt_from=weixin&utm_campaign=client_share×tamp=1521290007&app=news_article_lite&utm_source=weixin&iid=25315997380&utm_medium=toutiao_android&wxshare_count=1) [【系统架构】什么是负载均衡](http://mp.weixin.qq.com/s/ykL2yj00ZaxhloOd2In9eA) [批处理已死,Kafka 当道](http://mp.weixin.qq.com/s/sf20EWt2E1DE6h0ddPvdjQ) [【协议森林】也许,这样理解HTTPS更容易](http://mp.weixin.qq.com/s/C68icGtwh3IzuUbANaRXEg) [【系统架构】亿级Web 系统的容错性实践【下】](http://mp.weixin.qq.com/s/EM9EVZYeqd5eJ3YbvgiyEw) [【系统架构】分布式ID生成器](http://mp.weixin.qq.com/s/_z0-90xbsCd4Pdi6UlEvnA) [【以面带点】服务器后台开发面试题总结](http://mp.weixin.qq.com/s/nnQT6-CO-ngEGueI4ouNEw) [【协议森林】TCP三次握手和SYN攻击](http://mp.weixin.qq.com/s/zU1Mw3yaNmk4D5pP9vxxaw) [一分钟了解 TCP/IP 模型](http://mp.weixin.qq.com/s/KkLh3Lgukah4wi68HjzK6w) [为什么 SQLite 用 C 编写?](http://mp.weixin.qq.com/s/PvKwyg7nXCcm4X5mmqLaVQ) [小白科普:“无状态”那点事儿](http://mp.weixin.qq.com/s/7KmEOXhefUxv51rR03SarA) [一个故事讲明白线程的私家领地:ThreadLocal](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665514354&idx=1&sn=2e76f515be6ea0806d0e6a30f7ff82ce&chksm=80d67d31b7a1f427daec0b65a8b21d3740c7afc4da719faf77eb14e595afb650476c922411ca&scene=21#wechat_redirect) [【协议森林】互联网协议入门(二)](http://mp.weixin.qq.com/s/lg3PiXzI8J6D2DmSz8aKgA) [从密码到token, 一个授权的故事](http://mp.weixin.qq.com/s/6DjDnsy8wf26N0U-pWtG0Q) [扒一扒那些和编程语言无关的技术](http://mp.weixin.qq.com/s/9C2uZ51Io9rry5BuNqxMZQ) [分布式一致性算法:可能比你想象得更复杂](http://mp.weixin.qq.com/s/ohTXhFFywGHGDOkzO45aaQ) [raft-zh_cn/raft-zh_cn.md at master · maemual/raft-zh_cn](https://github.com/maemual/raft-zh_cn/blob/master/raft-zh_cn.md)(比Paxos更简单的分布式一致性算法) [【系统编程】你所不知道的TIME_WAIT和CLOSE_WAIT(上)](http://mp.weixin.qq.com/s/y-7X7juYhcgnPN4AchtrmA) [【系统编程】你所不知道的TIME_WAIT(下)](http://mp.weixin.qq.com/s/MlyIDf9eWRn5x6V9eOaqWg) [【协议森林】详解TCP之滑动窗口](https://mp.weixin.qq.com/s/3VqdjEK4QkER4Q05JgfjhQ) [【系统编程】并发服务器(一):简介](https://mp.weixin.qq.com/s/de3YqaSFaxUyiBMEW7fsKg) > main 函数里面的外层循环用于监听套接字,以便接受新客户端的连接。一旦有客户端进行连接,就会调用 serve_connection,**这个函数中的代码会一直运行,直到客户端断开连接。** (监听就是用的无限循环;这样的循环对服务器有什么影响啊,多个程序这样循环,怎么争夺CPU资源呢?底层是什么机制呢?) [【系统编程】并发服务器(二):线程](https://mp.weixin.qq.com/s/KkrSxVZhOI22JDk4K20cxg) > 然而,线程不是处理多个客户端并行访问的唯一方法。下篇文章中我们会看看其它的解决方案,可以使用异步处理,或者事件驱动的编程。 [用 JavaScript 编写 MPEG1 解码器](https://mp.weixin.qq.com/s/9qAsZipMKcpKEJUjqealog) [【系统架构】Web系统大规模并发:电商秒杀与抢购](http://mp.weixin.qq.com/s/zDbcV_vJeBOnAYxK0WEJQQ) > 必须尽可能“快”,在最短的时间里返回用户的请求结果。为了实现尽可能快这一点,接口的后端存储使用内存级别的操作会更好一点。仍然直接面向 MySQL之类的存储是不合适的,如果有这种复杂业务的需求,都建议采用异步写入。 > > 当然,也有一些秒杀和抢购采用“滞后反馈”,就是说秒杀当下不知道结果,一段时间后才可以从页面中看到用户是否秒杀成功。但是,这种属于“偷懒”行为,同时给用户的体验也不好,容易被用户认为是“暗箱操作”。(这个没有办法,不异步你就干等啊,异步就是告诉你正在排队啊) > > 更可怕的问题是,是用户的行为特点,系统越是不可用,用户的点击越频繁,恶性循环最终导致“雪崩” > > 我们知道在多线程写入同一个文件的时候,会存现“线程安全”的问题(多个线程同时运行同一段代码,如果每次运行结果和单线程运行的结果是一 样的,结果和预期相同,就是线程安全的)。(即没有并发问题,也可客观的理解为操作的幂等性) > > FIFO队列:这样的话,我们就不会导致某些请求永远获取不到锁。看到这里,是不是有点**强行将多线程变成单线程**的感觉哈。(解决并发问题,锁,队列异步操作,其实都是将并行强制变为串行的解决方案。) [【系统架构】互联网架构为什么要做服务化?](http://mp.weixin.qq.com/s/juA88H8miYYAkxMueSeeAQ) > 解耦,模块化,服务化,分离架构 [在架构师眼里,一份美团外卖是如何做出来的?](https://mp.weixin.qq.com/s/Vvhw0ivsJSAKBiV8ScCYDw) [互联网是如何形成的](https://mp.weixin.qq.com/s/s9dAiTLiYfJTOSwOnDpl0g) [互联网协议入门(下)](https://mp.weixin.qq.com/s/d3-m8ysQQzlFf8G2cvRe2A) [架构实践,1分钟解惑,都在这里](https://mp.weixin.qq.com/s/ydoUYhnNpLuLHqeUFxB_Mg) [程序员需要关注的四大技术趋势](https://mp.weixin.qq.com/s/XjYiEOS2ouwn7x5FR23rrw) [如何不停机分库分表迁移?](https://mp.weixin.qq.com/s/1Vjv7rvrbZVqixzgS_AaMw) [【协议森林】聊一聊那些常见的网络通信的性能指标](https://mp.weixin.qq.com/s/FuaErG9aYyMUcMUEYHuBVQ) [沉思录文章精选](https://mp.weixin.qq.com/s/9L6aCfJDT2mmZxH4WFHdYg) [用信鸽来解释 HTTPS](https://mp.weixin.qq.com/s/GihDmoAY_IAG0_m_v577JQ) [RSA 的原理与实现 - CJ Ting's Blog](https://cjting.me/2020/03/13/rsa/) [用信鸽来解释 HTTPS - OSCHINA](https://www.oschina.net/translate/https-explained-with-carrier-pigeons) [【底层原理】基本内存管理(上)](https://mp.weixin.qq.com/s/MGEMmrCxTfi8K8spebsC_w) > 如果和硬盘之间的数据交换过于频繁,处理速度就会下降,表面上看起来就像卡住了一样,这种现象称为抖动(Thrushing)。相信很多人都有过**计算机停止响应**的经历,而造成死机的主要原因之一就是抖动。 [内存那些事](https://www.toutiao.com/a6552369670400246280/?tt_from=weixin&utm_campaign=client_share×tamp=1525642790&app=news_article_lite&utm_source=weixin&iid=31395168747&utm_medium=toutiao_android&wxshare_count=1) [千万条数据,Stack Overflow是如何实现快速分页的?](https://mp.weixin.qq.com/s/FuTjKJUOn3XdN20mFrAozw) [feed留,单聊群聊,系统通知,状态同步,到底是推还是拉?](https://mp.weixin.qq.com/s/54yEWWet9mFztv1fO_GTqQ) > 大部分webim(特别是同时在线量大的)是http轮询请求,后续撰文 [系统通知,居然有人使用拉取](https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961154&idx=1&sn=277f6ec612555bf5a95585e9a161bb5f&chksm=bd2d029e8a5a8b884c9855b8e315a697a0e8eccf227fb36395334d140dd9eebf2489e99862d3&scene=21#wechat_redirect) [状态同步,究竟是推还是拉?](https://mp.weixin.qq.com/s/oQ4K4zMRCGRtqly412U_TQ) [深入分布式缓存:从原理到实践](https://mp.weixin.qq.com/s/nA8jgpD-8Hsurqp5dS6jtg) [DNS是如何运行的](https://mp.weixin.qq.com/s/-6E7xzf4Ym5cU2AzvT1H5g) [双机热备的原理](https://mp.weixin.qq.com/s/ZK3HURE6M6p3BDKI-r_UKQ) > 刨根究底的弄明白。 [如何设计一个秒杀系统](https://mp.weixin.qq.com/s/tvTWVS3ZUwpnA_rffKWEjg) [【系统架构】如何逐步去构建一个大型网站系统](https://mp.weixin.qq.com/s/oYiQEMelkXHbT3ke2BmHOw) > 大型网站是技术和业务的结合,一个满足某些用户需求的网站只要技术和业务二者有一方难度很大,必然会让企业投入更多的、更优秀的人力成本实现它,那么这样的网站就是所谓的大型网站了。 > > 如搜索业务,我们不可能直接通过数据库的select like来实现…… [适用于分布式唯一标识码的生成算法有哪些?-谢逅架构的回答-悟空问答](https://www.wukong.com/answer/6551729132504350980/?iid=31395168747&app=news_article&share_ansid=6551729132504350980&app_id=35&wxshare_count=1&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_android&utm_campaign=client_share) > 同一机器的进程id也考虑进去了 [精心整理 | 2018年三、四月技术文章目录](https://mp.weixin.qq.com/s/olLbAGRk-ou7GkpwEwUZgw) [关系型数据库为什么能活这么久?](https://mp.weixin.qq.com/s/tvUQPSITJ7mxNuxLUbNPOw) [计算机语言的巨变](https://mp.weixin.qq.com/s/n3PAVhbEemFdpWTtpgoTlg) > 作者认为C之后的语言如果不能完美解决C语言的问题和GC问题,那就不是成功的语言,我不认同这个观点。 [数学有些东西是真的,你却永远无法证明。](https://www.toutiao.com/a6536502259449070088/?tt_from=weixin&utm_campaign=client_share×tamp=1526440318&app=news_article_lite&utm_source=weixin&iid=31395168747&utm_medium=toutiao_android&wxshare_count=1) [【系统架构】关于缓存和数据库强一致的可行方案](https://mp.weixin.qq.com/s/RME5b3plT97nYfUaCl9ePw) [缓存的正确使用方式,你都会了吗?](https://mp.weixin.qq.com/s/pUBa4L1bfF09LgGyq_LSCw) [【原创】分布式之数据库和缓存双写一致性方案解析 - 孤独烟 - 博客园](https://www.cnblogs.com/rjzheng/p/9041659.html) [【原创】分布式之数据库和缓存双写一致性方案解析(二) - 孤独烟 - 博客园](https://www.cnblogs.com/rjzheng/p/9240611.html) [漫画:什么是ZooKeeper?](https://mp.weixin.qq.com/s/Gs4rrF8wwRzF6EvyrF_o4A) [认识一下 Kubernetes](https://mp.weixin.qq.com/s/DOYS2WkU0_hm5pruRqA3TQ) [统一配置中心的设计方案](https://mp.weixin.qq.com/s/ZroHhd6omzCFn5g2QFxaVg) [根域名的知识 - 阮一峰的网络日志](http://www.ruanyifeng.com/blog/2018/05/root-domain.html) [一个网卡的自述](https://mp.weixin.qq.com/s/JGsnWFhYPEwHwy7JsCNyNg) [DNS原理入门](https://mp.weixin.qq.com/s/tROPiINO4Rj1pyNBsNhvlw) [一个路由器的自述](https://mp.weixin.qq.com/s/iKfUj-0bxAKw7eVBLoXjbw) [互联网协议入门(上)](https://mp.weixin.qq.com/s/AM2bxUYuINJBVA1YPBTxJA) [互联网协议入门(下)](https://mp.weixin.qq.com/s/d3-m8ysQQzlFf8G2cvRe2A) [davideuler/architecture.of.internet-product: 互联网公司技术架构,微信/淘宝/微博/腾讯/阿里/美团点评/百度/Google/Facebook/Amazon/eBay的架构,欢迎PR补充](https://github.com/davideuler/architecture.of.internet-product) [只有 13 台 DNS 根域名服务器原因 - Jamin Zhang](https://jaminzhang.github.io/dns/The-Reason-of-There-Is-Only-13-DNS-Root-Servers/#top3) [Java虚拟机的Heap监狱](https://mp.weixin.qq.com/s/BiSESLCKW-NExP4-lyiZUw) [Java虚拟机内存初探](https://mp.weixin.qq.com/s/2yTPw83fP9zRLI1-LiRH0A) [秒杀架构优化,产品折衷](https://mp.weixin.qq.com/s/tz1IhmxaS7_1JY_0cqfClw)(产品设计的折中与妥协) [缓存架构,到底设计些什么?](https://mp.weixin.qq.com/s/DM0dKHyD3FweVSHcjabLaw) [从单一架构到分布式交易架构,网易严选的成功实践](https://mp.weixin.qq.com/s/nv3Ht7OqTYQw31QFDX3gNg) >[danger] **没有完美的架构设计,世上也没有绝对的事情,没有谁能保证绝对可靠、安全和高可用,但我们有补偿和容错(类似还有重试,确认等机制),也是能做到万无一失的。** [谈谈高并发之限流特技](https://mp.weixin.qq.com/s/Ja34xAu6FMH0dJlv6lzmzw)(让系统学会优雅的拒绝请求。) [阿里巴巴为什么不用 ZooKeeper 做服务发现?](https://mp.weixin.qq.com/s/uMj3JEhgyYw4CaIhP1-GTQ) [不懂RPC,休谈微服务](https://mp.weixin.qq.com/s/dohsfOBkl2dGbOyQp1WpnQ) [如何优雅地实现分页查询](https://mp.weixin.qq.com/s/CBhhWIVPvn1I8Um5uDAVCQ) [为什么我建议你这样实现MySQL分页](https://mp.weixin.qq.com/s/_Zy2wDF8cuASJubUq1tyWQ) [十分钟搞懂负载均衡](https://mp.weixin.qq.com/s/1WhRvPaoO7M34ngUIxE7ZQ) [6个月重造“淘宝”:阿里重写Lazada,中国技术海外输出纪实](https://mp.weixin.qq.com/s/vyXchTIQWJ_wQtcGft2h8Q)(用Java改造不是没有道理的,很多搜索引擎,中间件等产品都是Java开发的,这样开发资源更丰富一些,有利于长远发展) [标准Web系统的架构分层 - CSDN博客](https://blog.csdn.net/yinwenjie/article/details/46480485) [一文读懂负载均衡之LVS](https://mp.weixin.qq.com/s/RZgxSQW-n1v6Xa9fe20QGw) [CDN为什么这么快](https://mp.weixin.qq.com/s/F9Qy1jjTWpaREcI98s3Z6Q) [自己动手写一个服务网关](https://mp.weixin.qq.com/s/3oKDP_1r9KuZ7WKrHM2Ahw) [【RPC 专题】深入理解 RPC 之服务注册与发现篇](https://mp.weixin.qq.com/s/EciyQVGXANY9cghOTLwk_Q) [【协议森林】图说TCP之滑动窗口和拥塞窗口](https://mp.weixin.qq.com/s/UtyLffohQ2yOJXvpVALE2Q) [什么场景应该用 MongoDB ?](https://mp.weixin.qq.com/s/nUoKZ4je7zTlbYOvS7lyiw) > 1.*版本,用的确实比较早了,在大公司一般用的都是柔性事务,很少有用强制性事务的了,建议有时间可以写一篇柔性事务的文章分享一下 > > 应用需要大量的地理位置查询、文本查询;应用不需要事务及复杂 join 支持;新应用,需求会变,数据模型无法确定,想快速迭代开发 [传统事务与柔性事务](https://www.jianshu.com/p/ab1a1c6b08a1) > 日志,幂等性,业务弹性,最终一致,重试,补偿 [支付宝运营架构中柔性事务指的是什么? - 知乎](https://www.zhihu.com/question/31813039) > 业务层2PC(两阶段提交),事后校正 [缓存,并发更新的大坑?](https://mp.weixin.qq.com/s/c6TZdfLfpPWXmEdTkGO97g) [外部排序](https://mp.weixin.qq.com/s/lWYfIGGVT_84IBDuGb_iFw) [Redis持久化方案该如何选型](https://mp.weixin.qq.com/s/Tp_ayMnFw5rK4ZZCM4nJ5w) > **我们知道,在web服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999% 等等)。** 但是在Redis语境中,高可用的含义似乎要宽泛一些,除了保证提供正常服务(如主从分离、快速容灾技术),还需要考虑数据容量的扩展、数据安全不会丢失等。 [【底层原理】一次程序crash后的调试之旅 —— GDB调试心得](https://mp.weixin.qq.com/s/UfeUXG0cYYfw9y4-xZGp2g) [究竟先操作缓存,还是数据库?](https://mp.weixin.qq.com/s/CuwTRC8HrMHxWZe3_OX98g) [可能是最漂亮的 Spring 事务管理详解](https://mp.weixin.qq.com/s/_7tKg_oR5mbQWHRMe3PINA) > 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则) [你真的懂Mybatis缓存机制吗](https://mp.weixin.qq.com/s/h2x15k71rClaHjcz7u2dlQ) [高可用性的几个级别](https://mp.weixin.qq.com/s/P33JdtPIGmzpe24BMVo4CA) [【开源组件】简单说明CGI和动态请求是什么](https://mp.weixin.qq.com/s/9oi-zkuUtjv0P6GyBLmatg) [一篇文章理解 Web 缓存](https://mp.weixin.qq.com/s/3SDKNbWZHWenzi34TR38gg) [操作系统和Web服务器那点事儿](https://mp.weixin.qq.com/s/-rzyDpckS9CUQblAUac0FQ) > 这其实就是所谓的zero copy技术, 从内核角度看,除了把文件从硬盘读出来之外,没有任何的额外copy。 [聊聊C10K问题及解决方案 - XINJing的专栏 - CSDN博客](https://blog.csdn.net/wangtaomtk/article/details/51811011) > 这个技术本质上也是异步非阻塞技术,它是将事件回调进行了包装,**让程序员看不到里面的事件循环。** 程序员就像写阻塞代码一样简单。比如调用 client->recv() 等待接收数据时,就像阻塞代码一样写。**实际上是底层库在执行recv时悄悄保存了一个状态,比如代码行数,局部变量的值。然后就跳回到EventLoop中了。什么时候真的数据到来时,它再把刚才保存的代码行数,局部变量值取出来,又开始继续执行。** (看来监听就是一段循环,就算你写的代码不是,但其实只是没让你看到里面的循环而已;底层都是循环,尽管你写的代码不是循环,但这是一种假象啊,里面的循环只是没让你看到而已。) > **这就是协程的本质。协程是异步非阻塞的另外一种展现形式。** Golang,Erlang,Lua协程都是这个模型。 [高性能网络编程(二):上一个10年,著名的C10K并发连接问题 - 小辉辉的博客 - CSDN博客](https://blog.csdn.net/qq_26819733/article/details/52890369) [高性能网络编程(一):单台服务器并发TCP连接数到底可以有多少-网络编程/专项技术区 - 即时通讯开发者社区!](http://www.52im.net/thread-561-1-1.html) > 在linux下编写网络服务器程序的朋友肯定都知道每一个tcp连接都要占一个文件描述符,一旦这个文件描述符使用完了,新的连接到来返回给我们的错误是“Socket/File:Can't open so many files”。 [脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?-网络编程/专项技术区 - 即时通讯开发者社区!](http://www.52im.net/thread-1732-1-1.html) > **我们平时用到的套接字其实只是一个引用(一个对象ID),这个套接字对象实际上是放在操作系统内核中。** 这个套接字对象内部有两个重要的缓冲结构,一个是读缓冲(read buffer),一个是写缓冲(write buffer),它们都是有限大小的数组结构。 [一套高可用、易伸缩、高并发的IM群聊架构方案设计实践-IM开发/专项技术区 - 即时通讯开发者社区!](http://www.52im.net/forum.php?mod=viewthread&tid=2015&highlight=%C8%BA%C1%C4) [PHP多进程初探 --- 进程间通信二三事](https://blog.ti-node.com/blog/6379989346195341312) [端游、手游服务端常用的架构是什么样的?](https://mp.weixin.qq.com/s/ptXTD9JcOdIne7ym_PEhCQ) [Netty高性能之道](https://mp.weixin.qq.com/s/1zDDoNx4ZabLy7o-scPbsQ) > 表面上看,串行化设计似乎CPU利用率不高,并发程度不够。但是,通过调整NIO线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工作线程模型性能更优。 [高并发的那点事儿](https://mp.weixin.qq.com/s/n9FnQq2K93JTTr1kMkwJaw) > 并发的英文单词是 Concurrency,并行是 Parallelism。 **如果一个系统支持两个或多个动作(Action)同时存在,那就是一个并发系统。如果一个系统支持两个或多个动作同时执行,那就是一个并行系统。** 也就是说, **单个CPU永远无法同时执行两个或以上的任务,但是允许任务同时存在。** 所以,只有多核或多个CPU才可能发生并行,如果单核单CPU只能发生并发行为。如果 **有人以为单核单 CPU 的并发就是同时执行很多任务,那么这是个错觉。** [操作系统和Web服务器那点事儿](https://mp.weixin.qq.com/s/-rzyDpckS9CUQblAUac0FQ) > zero copy技术减少了上下文的切换,避免了数据不断地在用户态和核心态搬运,不需要CPU参与数据的复制,提高了系统性能,在ngnix, apache等web 服务器中都引入了zero copy技术。 [Nginx 架构初探](https://mp.weixin.qq.com/s/yabECog4oZRSThXGgJ3s9w) [重新理解响应式编程](https://mp.weixin.qq.com/s/gArB6rO8bzXffukKUrHvWA) [为什么我们做分布式使用 Redis?](https://mp.weixin.qq.com/s/FqMScRBFwdwF2n3QJkNLRA) [深入浅出Unix IO模型](https://mp.weixin.qq.com/s/I_KdtqBwq8IYfmtIJeUlyA) [为什么C语言不会过时?](https://mp.weixin.qq.com/s/y3-7PwgI4Xff1zzTz4xWRw) [你确定你的MySQL足够安全吗?](https://mp.weixin.qq.com/s/8fXOabI4BCPmN7fewZqRNg) [搞懂“分布式锁”,看这篇文章就对了](https://www.toutiao.com/a6611354991913337347/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1&api_ab_group=pyq_1×tamp=1539874485&app=news_article_lite&utm_source=weixin&iid=46408437753&utm_medium=toutiao_android&group_id=6611354991913337347) [后端风云](https://mp.weixin.qq.com/s/4biooG7TAp1Q1SSqW6D2CQ) [MQ在分布式系统中的使用场景](https://mp.weixin.qq.com/s/FJUxsBp_oZ4qiksF9VeWoQ) > 消息中间件和RPC从根本上来说都是为了解决分布式系统的服务间通信问题 > 中间件比RPC要多了一个组件,那就是消息中间件本身 > 相较于使用RPC通信,会有更多的组件运维成本,也会增加一次通信的通信延迟,那么我们为什么要使用消息中间件?一个很重要的原因就是,他为我们增加了消息堆积能力,而这个能力提供给我们了很重要的流量削峰,高可用以及广播等问题的解决方案。 [精读《手写 SQL 编译器 - 语法分析》](https://mp.weixin.qq.com/s/g4soU9kIBdie96abOel7fg) [【漫话】如何给女朋友解释什么是分布式和集群?](https://mp.weixin.qq.com/s/o_t2r5SVlJXnk7ir_Dz2Lg) [程序媛成长纪:从DBA到研发工程师](https://mp.weixin.qq.com/s/3ddLLGisl7_o34Z8dV5kaw) [MySQL:缓存算什么东西?!](https://mp.weixin.qq.com/s/OmnuHDFF6UIB1ieOrUv59Q) [8种常被忽视的SQL错误用法](https://mp.weixin.qq.com/s/glpQlE8UfxOLGPno80wBOw) [Linus 在谷歌介绍 Git,内容很精彩,还顺带骂了一堆人(视频)](https://mp.weixin.qq.com/s/6UGBFYln6r6Ex5JaHjvCvA) > 最近看完了英文原版视频。看完后,他对 Git 的理解更深入了,并且发现在 Linus 大神的谈笑风生背后,有很多关于软件开发的深邃思想。 [Redis如何实现乐观锁](https://mp.weixin.qq.com/s/S0QdA19fp5mDPkO4BmSkNw) [【漫画】两台陌生的主机是如何保证数据正确交付的?](https://mp.weixin.qq.com/s/2Rz-DwsHzzCUEg8SG0gNNw) [RabbitMQ + PHP 教程一:Hello World](https://mp.weixin.qq.com/s/-saYCQCYwmH7QTp_TvVj1g) [php+crontab+shell方案实现的秒级定时发起异步请求回调方案](https://segmentfault.com/a/1190000011814778) [一段程序的奇妙旅行(附送10本码农翻身签名版)](https://mp.weixin.qq.com/s/TsvM40aJensSScvPYh47ZA) [Google图解:Chrome 快是有原因的,科普浏览器架构!](https://mp.weixin.qq.com/s/Yr95fAFgl5pWmXLXSiqMZA) [普通程序员也要懂 C10K 吧](https://mp.weixin.qq.com/s/AqAflwsG3lWEIk8aTrt97A) [API版本控制的几种思路](https://mp.weixin.qq.com/s/AeCJdq7HYNYC10iR-2Xamg) [一人两月,开发一个类似京东的APP ?](https://mp.weixin.qq.com/s/AnX38XPI3oOCJLGbtBPYGQ) [Web端即时通讯技术原理详解](https://www.cnblogs.com/linsanshu/p/5759174.html) [如何优雅实现优雅停机?](https://mp.weixin.qq.com/s/GlMZIA-gyDiQ__CsNvnGFQ) [探索 Java 热部署的奥妙](https://mp.weixin.qq.com/s/ZuaryrfI-Tye9l9kRAoXlg) [编程语言的巅峰](https://mp.weixin.qq.com/s/n70je0j9uJUNr59TFwtsow) [你知道一条 SQL 语句是咋执行的吗?](https://mp.weixin.qq.com/s/R7Egj5be-uMCkgK82io-Dw) [蚂蚁金服自研数据库OceanBase的前世今生:从濒临解散到浴火重生](https://mp.weixin.qq.com/s/g4IPWQ34jl96YTZxhd4jbQ) [我也是一个线程,为什么每天累得像狗一样?](https://mp.weixin.qq.com/s/V568XAQlByIENR2mPx7Myw) [从一笔金币充值去思考分布式事务](https://mp.weixin.qq.com/s/6dO2TMhm8kFH70oCxKpwCg) [基于Redis的分布式锁到底安全吗(下)?](https://mp.weixin.qq.com/s/4CUe7OpM6y1kQRK8TOC_qQ) [淘宝大秒系统设计详解](https://mp.weixin.qq.com/s/zEZ4NsaPhpnnWP8-zDl2WQ) [【底层原理】Linux进程及其调度策略](https://mp.weixin.qq.com/s/OzH_hrxKteJuAcOg8PqcoA) [【网络编程】高性能网络编程之accept建立连接](https://mp.weixin.qq.com/s/Lee4DRNCQuGCTTAsSYR8ng) > 偶然性的小概率事件,会在高吞吐量下变成必然性事件。 > 高吞吐量下,容易触发到一些设计上的边界条件; > 学习套接字编程,关注点主要在:套接字的编程方法有哪些?阻塞套接字的各方法是如何阻塞住当前代码段的?非阻塞套接字上的方法如何不阻塞当前代码段的?IO多路复用机制是怎样与套接字结合的?异步IO是如何实现的?网络协议的各种异常情况、操作系统的各种异常情况是怎么通过套接字传递给应用性程序的? > > 当服务器绑定、监听了某个端口后,这个端口的SYN队列和ACCEPT队列就建立好了。 > > 应用程序可以把listen时设置的套接字设为非阻塞模式(默认为阻塞模式),这两种模式会导致accept(接受)方法有不同的行为。 > > 所以,企业级的服务器进程中,若某一线程既使用accept获取新连接,又继续在这个连接上读、写字符流,那么,这个连接对应的套接字通常要设为非阻塞。原因如上图,调用accept时不会长期占用所属线程的CPU时间片,使得线程能够及时的做其他工作。 [看完这篇文章,你肯定理解什么是浮点数了!](https://mp.weixin.qq.com/s/34Fg9GSqRDoBbGFPzOt_ow) [那些让你起飞的计算机基础知识](https://mp.weixin.qq.com/s/Rt9Kn8BoWVfxdBlYdpFDCQ) (计算机的世界自有它自己的一套规则) [可能是讲分布式系统最到位的一篇文章](https://mp.weixin.qq.com/s/eCpJkydT87U7UwvzvOdcGA) [【解忧】佐佐木希和痴汉大叔被困外星球的科幻短片,结尾细思极恐!_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili](https://www.bilibili.com/video/av24106477/?redirectFrom=h5) (真相就在眼前,但需要你迈出脚步) [你从未听说过的最重要的数据库,人类登月计划的功臣](https://mp.weixin.qq.com/s/Ad9PcuYWBWwKbRrbMTzzwg) > IMS基于层次模型工作,这意味着IMS不会将数据视为可以Join的二维表,而是将数据视为树。 [关系型数据库为什么能活这么久?](https://mp.weixin.qq.com/s/tvUQPSITJ7mxNuxLUbNPOw) (直观,业务数据通常就是关系型结构的) [别吵吵,分布式锁也是锁](https://mp.weixin.qq.com/s/hOdEMgRqjZAg1ND5nqwFQA) > 数据库表实现的锁服务局限性很大,性能低,但事务环境下也会有问题。 > 线程的并发执行导致三个操作交织在了一起,最后数据出现了不一致。 > Redis的处理办法, 对于每个读写缓存的请求,Redis都把他们给排成了队,用一个线程挨个去处理,肯定没有这个并发的问题了。 [漫话:如何给女朋友解释为什么双11无法修改收货地址](https://mp.weixin.qq.com/s/En4Tv_32TLz_WpmWJfuM_g) > 排队中的用户也是并发用户。 如果一个系统能同时服务多个用户,那就是一个支持并发的系统, **这个服务并不是指多个用户的操作要在同时进行。** 多个操作同时进行,称之为并行系统。 [【开源组件】Nginx可以做什么?看完这篇你就懂了](https://mp.weixin.qq.com/s/Yw0Rtb3HoRm0HZ5d7OXdyA) [轻量级配置中心](https://mp.weixin.qq.com/s/EvyjSQqg0prNkCpLXMCg4w) [分布式系统中最容易被忽视的六大“暗流”](https://mp.weixin.qq.com/s/SxSfWOn67uv0hwN81SCroA) [4399构架师曹政:解密不一样的中国互联网](http://m.pcgames.com.cn/x/321/3210140.html) > 用户最重要 [为什么有些“业余”的能赢。](https://mp.weixin.qq.com/s/8mNPCpvtVdjf94qUkXuiTg) (什么是专业) [“无状态”的那点事儿](https://mp.weixin.qq.com/s/urehYcJDH1TDZOx9tNgcew) [大厂与小厂工作的选择](https://mp.weixin.qq.com/s/NcccEU_Cg6v_kyG6AL5ZxA) [一个简单的定时任务 · Thinkphp5.1学习笔记 · 看云](https://www.kancloud.cn/hideblue/thinkphpnotebook/853526) [李沐大神开源中文书《动手学深度学习》上线](https://mp.weixin.qq.com/s/oi_9COJ52jdJpXbUQMX8fw) [从汇编语言到类库框架的随感](https://mp.weixin.qq.com/s/xcKEza5xlb9_clfcannI9w) > ……然后程序员就累死在学习这些类库框架的过程中了。 [负载均衡常用手段解析](https://mp.weixin.qq.com/s/MHMkqW1nhaGQSnJRUkBwzw) [为什么Facebook的API以一个循环作为开头?](https://mp.weixin.qq.com/s/WHh9v3icCc90PwiLyv0Hng) [用Python爬取手机APP](https://mp.weixin.qq.com/s/m6HV3UhAaZol4oCAOa9bkQ) [苏宁的Node.js实践:不低于Java的渲染性能、安全稳定迭代快](https://mp.weixin.qq.com/s/kX23axjtgPRb64jb1HQLOQ) > 使用 Node 模板,去替换 Java 模板,去除了模板文件谁写这样的模糊地带,让后端的 Java 工程师,只写 JSON 服务,实现前后端分离。 [工程事故与现实世界](https://mp.weixin.qq.com/s/bY9HnwehaNQajg6_ysumNg) [工程事故与现实世界(续)](https://mp.weixin.qq.com/s/tuX2TOkgnUL_j6q3EPTbxg) [想了解Kafka,RabbitMQ,ZeroMQ,RocketMQ,ActiveMQ之间的差异?这一篇文章就够了!](https://mp.weixin.qq.com/s/y3CheyPMJpLpD3pB3lTT9g) [图解 | 当我们在读写 Socket 时,我们究竟在读写什么?](https://mp.weixin.qq.com/s/fpqknvZDheR3urC_wHe5Gw) [看京东系统架构师如何让笨重的架构变得灵巧](https://mp.weixin.qq.com/s/LQaMwNOnqDF9RDqVCuRfAw) [在我们这个地方,你必须不停地奔跑,才能留在原地](https://mp.weixin.qq.com/s/R9-gX7Y_NZOcljRGYHHX3A) [“微信支付”勒索病毒制造者被锁定 传播、危害和疫情终极解密](https://mp.weixin.qq.com/s/04idVCQusKp9w96tvcDUxw) [为什么我建议程序员要发展副业](https://mp.weixin.qq.com/s/moonnn92lVnvi8xS5Iyz3g) [Jeff Dean的传奇人生:超级工程师们拯救谷歌](https://mp.weixin.qq.com/s/1Y8tbu0PHdfWYqmsZhQUOg) [如何优雅处理前端异常?](https://mp.weixin.qq.com/s/oEqWsPxQA5vdmsRVLIjhog) [【网络编程】tcp服务端一直sleep,客户端发送数据问题总结](https://mp.weixin.qq.com/s/W_JjnDuinmLrahIpPY8h-g) [小白科普:线程和线程池](https://mp.weixin.qq.com/s/aAvVCksF4G2WtVVdLPswpg) [程序员的创业陷阱:接私活](https://mp.weixin.qq.com/s/vvu-CrfQJ46SZ1AE2S4rpQ) [程序员口述:AI 创业两年,积蓄花光,重新写代码](https://mp.weixin.qq.com/s/UdXdqGgM4qG0ydu9lMsddw) [我那牛逼老公,创业成功但最后被辞退,仍然一毛钱股份没有拿到……](https://mp.weixin.qq.com/s/hiL1E4sf2KZBxKiJDjKvUg) [前端与编译原理——用 JS 写一个 JS 解释器](https://mp.weixin.qq.com/s/Xao9COCLCN7TPGKKatctCA) [聊聊微服务的隔离和熔断(送10本书)](https://mp.weixin.qq.com/s/PmU14UsJOb4IiH_81RlJMA) [网络创世纪](https://mp.weixin.qq.com/s/OPyTpxE5oXdvY1XaKpqVNg) [自我反省一下:我不是大佬](https://mp.weixin.qq.com/s/92yA4puoM1cTsuCHoQ2ozA) [那些让你起飞的计算机基础知识](https://mp.weixin.qq.com/s/Rt9Kn8BoWVfxdBlYdpFDCQ) > 一种更加有效的办法是从工作中用到的知识点出发,从这个知识点向外扩展,由点到线,由线到面,然后让各个层次都连接起来,形成一个立体的网络。  [用一幅漫画趣味解读Linux内核](https://mp.weixin.qq.com/s/Zb6gRDEM-vOoh0KRFGv33g) [ofo剧中人:我不愿谢幕](https://mp.weixin.qq.com/s/sMG0gK0gwyF-l5o-O8kJKw) ~~~ 不过,在其他部门豪放投钱的时候,ofo对硬件部门相对精打细算。“车和锁想去要钱很困难,成本线卡得很死。”硬件部门员工金叶秋(化名)有些沮丧,“整个硬件在ofo的地位是很下面的。” 一位ofo公关部人士解释,这是因为ofo和摩拜是两种模式选择。ofo始终认为自己是互联网公司,商业模式、订单增长和速度为第一位,车和锁不过是完成目标的手段;摩拜从一开始认为自己是物联网公司,因而更看重硬件。 直到2017年下半年,ofo硬件矛盾此起彼伏地爆发,戴威才引起重视。一次,一批150万的智能锁因设计问题无法正常开启,戴威在专项会上发过一次大火。在场人士称,戴威一走进会议室就大声地指名道姓,相关负责人起立。他说了一些类似于“没做好”、“做错了”、“重大问题”、“工作失职”这样的话。 ~~~ [什么是线程安全,你真的了解吗?](https://mp.weixin.qq.com/s/Er5LfxFiCUrbByPjdJ4DNg) [【底层原理】Stack的三种含义](https://mp.weixin.qq.com/s/AYwzSs5_BWRF0TOe8qnqWg) * * * * * last update:2018-12-17 16:52:08
';

从人的思考方式到二叉树

最后更新于:2022-04-02 04:34:07

## 从人的思考方式到二叉树 ![](http://p3.pstatp.com/large/53fc00056ea92c0e1d88) ![](http://p3.pstatp.com/large/567800031f41c81f5873) ![](http://p3.pstatp.com/large/53fc00056de6a69ab52e) > 计算机就是这样,它没有人类直观,所以它永远不可能真正的智能。 * * * * * ### 计算机是蠢的 首先要知道计算机是一个工具,是人创造的,它不是神话,不能为所欲为的想什么就是什么。 从一堆数字中找到一个数,人看一眼就能把要找的那个数字拿出来,可是这么简单的事情,计算机就不懂,它就做不到,它听不懂的话,不知道向人一样直接就拿出来给你,即使这么直白,简单的问题它都不会,你没听错,它就是这么蠢,真的蠢,它只能通过什么二叉树一遍一遍的计算来找到目标数字,你说它蠢不蠢,不过它确实是没有脑子嘛,这也不能怪它。 它没有人一样的大脑,它只有一些指令集,有些事在我们人做看来就是很简单的事情,再直白的事情了,可是计算机却是不懂的。他不能以我们的思维方式去解决问题。 它不是蠢,而是你不要把它当人看呐,你拿一个没有血肉的机器跟人比,你说你是不是芍啊。 * * * * * ### 大脑也在用算法计算,只不过你没有注意到 如果10颗糖,标号为1~10,让你拿出标号为3的糖果,你说你直接拿出3来,但其实你大脑中也是从第一个数3个到第三而已,只不过你习以为常而没有意识到大脑进行了这种计算而已,如果让你找到第8个,你会知道从后数,倒数两个就是的,这也是你大脑进行的一种计算,你知道了技巧,会走捷径,而不是从开头数8个。所以你说的直接拿其实都是一种计算,只不过这种计算我们太习以为常了,以至于你都没发觉。要是这十个数字的排列不是理想的按规律呢,此时就复杂了,你刚才的潜意识计算方法就失效了,这种情况你只能一个一个的看了,好的情况你看一次就能找到,而坏的情况需要看十次才能找到。(足球掉入草坪了,怎么快速找到它,教练: 从一头开始,地毯式的找到另一头。) > 这些技巧在你生活中被大量实践,对你来说最平常不过了,以至于你没发现它的存在。 [图解冒泡排序](http://mp.weixin.qq.com/s/3eW3RMka1dlniLRQp7Sa2w) * * * * * ### 如果没有sql,根本无法直接拿出来,只能一个个比对 钉钉有个前端demo里面就是,好在现代浏览器支持简单的sql了。 * * * * * ### 计算机是怎么思考的 [【数据结构与算法】 通俗易懂讲解 二叉搜索树查找](http://mp.weixin.qq.com/s/rSb1JUxWGV5i0aZz8OMwsw) > 为什么要有二叉树,假如有一个数字列表,我想找出一个数字,直接拿出来就可以了。但是计算机不会,它很笨,这么简单的问题它都不会,它蠢蛋挨个的看,直到最终找出我要的数字,这个蠢办法太费力了,于是它想出一个算法,就是二叉树查找法,这样它就快点了不必挨个的看了(扫表)。不知道我的理解对不对。还有为什么它这么笨,是不是它只有比较数字大小的指令,而没有像人一样看一眼,就知道有没有,直接就拿出目标数字的能力。求解惑。 [数据库为什么要使用二叉树?](https://www.wukong.com/question/6471240176008954125/) > 为什么要有二叉树,假如有一个数字列表,我想找出一个数字,直接拿出来就可以了。但是计算机不会,它很笨,这么简单的问题它都不会,它蠢蛋挨个的看,直到最终找出我要的数字,这个蠢办法太费力了,于是它想出一个算法,就是二叉树查找法,这样它就快点了不必挨个的看了(扫表)。不知道我的理解对不对。还有为什么它这么笨,是不是它只有比较数字大小的指令,而没有像人一样看一眼,就知道有没有,直接就拿出目标数字的能力。求解惑。 mysql的主键索引就是二叉树好像,索引上查询也比较快,原因就是没有索引会扫整个表是吗。 比如,我在一个查询id为1的文章,而这个字段没有索引的话,mysql会扫表,从0开始,直到找到位置。是这样吗?为什么它这么蠢,不能直接拿到我要的行呢? 其实我没有意识到,我直接找到数字,也是一眼扫过去,拿出目标数字,只不过我我一目十行很快,其实也是一行一看用眼睛再看的,本质上计算机也是如此。这么来看的话,计算机还比我还高级一点呢,至少有优化的算法,比如二叉树,索引,而我只能靠眼睛了,唯一的优势就是目光范围,可以一下子看几行,当然大脑也要跟着能反映过来,所以一目十行基本是极限了。 其实数据结构,本来就是人设计出来的,当然也是按照人的逻辑思维设计出来的,比如可以想象,假如有一面墙,上面从左到右由大到小写着1~10000的数字,让你从中找出5200这个数字你会怎么做,显然数字太多了,密密麻麻,你很难一眼扫过去就看到这个数字,并且你也不芍,也不可能从左开始一个一个的数,那么你的思维是怎么样的呢,聪明的你很自然的会把这个墙分为两半,一边各五千个数字,这样就很好找了,因为你知道这些数字是有规律的,由大到小排列的(如果没有规律,那就悲催了,只能一个个的看,什么时候找到真的要看运气了哈),然后5200大于5000,所以你自然会在右边墙上找,于是几秒钟你就找到目标数字了,我相信几乎所有人都会是这种逻辑思维,其实这就已经是二叉树的雏形了。 从这个例子中,我们总结到,所有的算法,其实都来自于人的逻辑思维,人是怎么去想的,计算机就是怎么去设计的。而且很多东西都是来源于生活。任何软件的设计,都能从生活当中找到影子和灵感。 人找某样东西需要检索,计算机找东西也需要检索,而要想更快的检索出目标,就要在内容中找出规律,没有任何规律的数据就像混乱的数字墙一样。所以数据库很重要的就是索引。数据库中的索引就是为存储的数据生成的规律。就像字典的索引,书籍的目录一样。 现在可以回答计算机是怎么思考的这个问题了,那就是,人是怎么思考的,计算机就是怎么思考的,即计算机是按照人的制定的方式思考的。 [数据库的最简单实现 - 阮一峰的网络日志](http://www.ruanyifeng.com/blog/2014/07/database_implementation.html) > 这时为了读取数据,可以一条条比对记录。但是这样做效率太低 (**计算机不是魔法,它也是人设计出来的。要理解它,你先要搞清楚人是怎么想问题的。**) [程序员常说的「哈希表」是个什么鬼?](http://www.toutiao.com/i6471769308983722509/) [【数据结构与算法】 字符串匹配的Boyer-Moore算法](http://mp.weixin.qq.com/s/4m1O5ZHsZTRc-JuBF97_3w) [【数据结构与算法】 通俗易懂讲解 二叉树遍历](https://mp.weixin.qq.com/s/9_d_AjLyia7iNMqxaYdngQ) [【数据结构与算法】 通俗易懂讲解 二叉搜索树](http://mp.weixin.qq.com/s/mhFv4TCSZZzC8lBSobT1Cg) [【数据结构与算法】二分查找](http://mp.weixin.qq.com/s/1qUPyysDYDMkjjYVCMCfow) > 二分查找有着查找速度快,平均性能好等优点,但必须要求待查表为有序表,且插入删除困难 [从抄书到开源之巅:章亦春的程序人生](http://mp.weixin.qq.com/s/R1RXYJty2-J4qJYep9Cq0Q) > 机器解题与人类解题的一大区别是,机器不能运用**人类直观**来辅助解题。比如几何题里作辅助线,或者其他基于视觉的解题方法,这些对于机器都是不适宜的。机器需要精确的推理方式,需要把一切都形式化。 [解谜计算机科学(1)](http://www.yinwang.org/blog-cn/2018/04/13/csbook-chapter1) > 每个人都做过计算,只是他们没有理解自己在做什么。回想一下你幼儿园(大概四岁)的时候,妈妈问你:“帮我算一下,4+3 等于几?” 你掰了一会手指,回答:7。当你掰手指的时候,你自己就是一台简单的计算机。 [MySql的索引原理](https://mp.weixin.qq.com/s/ssyg8zd2pMgEhLYPj5YrcQ) > 主键存在的意义仅仅是唯一性吗?索引的工作流程是什么?聚集索引是必须要走的过程吗?覆盖索引存在的意义是什么?这些问题的答案,都在这里。我们平时建表的时候,都会默认添加一个主键,**因为在查询或者修改的时候比较简单,比如可以直接按照一个User的id直接拿到所有的属性,然后进行数据操作。** [机器学习能革了数据库索引的命吗?](https://mp.weixin.qq.com/s/o115JjjtUzJZ4MQ9yO-WuQ) > 应该不行,索引的选择绝不是 “分级猜” 的问题 [对话式交互技术原理及流程揭秘](https://mp.weixin.qq.com/s/A4r01mV-X8CTJy3vPa9BPw) [【漫画】为什么MySQL数据库要用B+树存储索引?](https://mp.weixin.qq.com/s/d9yNsUVFg9UZN62xuOdxow) [「商品架构day8」京东几百亿的商品怎么搜索](https://open-hl.toutiao.com/a6799562892602507780/?utm_campaign=open&utm_medium=webview&utm_source=smartisan_llq_api&req_id=202005092320140100280270381112B5ED&dt=OC105&gy=71c66a993413d9f688242308b0a55129844c5599769dc917a44c961369a91654135cffe5f5616c7f01aac10a322a21a9e890672554475fb9f0437386a4b312e3d6d2ccca7dabf5c0f197079377e328aa4702b27a33df8bd5a3d859cb163383557d7d476adf03f193dce57f7ec41bdb17bb242e51bcab486162e0f153f46499cdbda67336099d1512c0139d7c88e990b3&crypt=5373&label=related_news&a_t=311593618486693354456437e57&item_id=6799562892602507780&fr=normal&from_gid=6824692362262872590&channel_id=88805669586) * * * * * last update:2018-6-29 10:20:23
';

关于人工智能

最后更新于:2022-04-02 04:34:04

## 人工智能永远不可能实现 ![](http://cdn.aipin100.cn/18-1-4/49025500.jpg) 之前有报道说微软开发出了可以自动编程的软件,将会取代程序猿编程,这是人工智能的方向。 王银写了一篇文章[《王垠:自动编程是不可能的,我为什么不在乎人工智能》](http://mp.weixin.qq.com/s/1lWfmYNdrHWvl6So3yIBAw),说这种人工智能不可能实现,程序猿们大不必担心自己会被机器取代。 我认为真正的人工智能永远不可能存在,如果存在就违背自然法则和伦理/伦常了,所以人工智能不会存在,否则那一天就是人类毁灭的开始,就像终结者里面一样。 我为什么这么说,请听我细细道来。 现阶段的人工智能,什么卷积算法,神经网络,大数据计算,模型训练,算法训练,等等,其实都是一些基础编程加上数学理论,统计学知识,建立大数据模型而已,就算模型再多,数据量再大,这样训练出来的东西真的是人工智能。 呵呵。 别再自欺欺人了。 这些根本就不是人工智能,小冰现在能和我们很畅快的交流了,也好像能懂我们的心思了,甚至猜的很准,甚至会撒娇,有小脾气,甚至像你女朋友一样懂你,你以为这就是人工智能了,你觉得人工智能就是如此? 小冰的确很聪明,但是他背后的一切都是数据模型训练的结果,它所有的情感,都是由人编程预设的,只不过在你跟它对话的时候,它背后能够进行上亿次的计算,非常快,所以让你感觉它很聪明,能懂你,它知道最新的网络语,还会和你无障碍的对话,让你分辨不出它是谁,但是这真的不是聪明,这是用拼计算能力的结果,这本质是一种傻。 它只是看起来聪明,但是它不可能有人一样的思维,人的思维是不可控的,是抓不住的,是发散的,甚至是没有意识的,比如在我和你交谈的过程中,我只是看着你说,很少发言,你怎么猜我在想什么呢,你觉得我讨厌你,或者对这个话题不感兴趣,但这些都是你猜的,你根本不知道我在想什,有可能我的思想早已飞到外太空去了,我只是走神了而已,我自己都不知道在想什么,试问,机器能像我一样的思考,有思想吗? 不可能。 机器永远只能做模仿游戏,它永远不可能取代我们,拥有我们一样的思维。 除非…… 王银在文章中说了,医学,神经学的科学家,和计算机科学家根本没在一起研究过,彼此在各自的领域探索,人工,智能,如果真想研究人工智能那多学科,多领域的科学家应该合作才行,否则不懂人脑如何运作的计算机科学家在小屋子里能做出人工智能,小孩也知道不靠谱吧。 所以如果他们合作,不是没有可能研究出人工智能的,这我不敢说。 如果有一天,我们能实现芯片和大脑细胞的融合,能读懂大脑的运作方式,并能够和机器结合,机器也能像大脑一样运作,一样的思考,那时就是人工智能了。 那时我们人的记忆能够被提取,还能移植,还可以放在U盘里面拷贝,你想一下,那时会出现什么。 如果拥有那样的技术,人类就突破生老病死了,在快要死的时候,把记忆提取出来,再植入到另外一个身体里面,那我们就能不死了。甚至还可把自己传到网络上。 这样技术如果被坏人掌握了,会出现什么,推荐大家去看一部电影《超验骇客》,题材和这个差不多。 所以真正的人工智能应该是有生命的,能读懂生命的奥秘,能与生命结为一体。 但是或者我们都错了,谁说人工智能就必须要和人一样的思考呢,我们创造另外一种东西,他是机器,他以机器的方式思考,他没必要学人脑,我们研究人工智能为什么要和人脑的工作模式一样呢,我们被自己的思维禁锢了。 好吧,对错并没有意义,如果那样,请你不要叫人工智能,你的智能里面不要带一个“人”字。 最后再次推荐大家看看文中提到的两部电影《模仿游戏》、《超验骇客》。 大家有什么想说的,可以留言,我们一起探讨。 (补充:即使有一天实现了真正的脑机接口,人脑可以控制计算机做一些事情(这方面的研究已经有一些进展),那也不算是人工智能,只能说这玩意很棒,但它不是人工智能,原因很简单,计算机不是人,它始终是机器,不会拥有人的思想。) * * * * * ### 资料 [敌人为什么那么蠢?电脑AI在游戏中是怎样运行的](https://www.ixigua.com/a6475230188358599181/?utm_medium=feed_steam&utm_source=toutiao) [【科普读物】一个故事告诉你比特币的原理及运作机制(上)](http://mp.weixin.qq.com/s/4PIgQw7mv_bVHFTsCYBjvA) [图解十大经典的机器学习算法](https://mp.weixin.qq.com/s/YF9QVF88s8U52qSHfk9gnA) [《王垠:自动编程是不可能的,我为什么不在乎人工智能》](http://mp.weixin.qq.com/s/1lWfmYNdrHWvl6So3yIBAw) [AlphaGo Zero 和强人工智能](http://www.yinwang.org/blog-cn/2017/11/04/alphago-zero) [「暧昧骗局」机器人索菲亚](https://mp.weixin.qq.com/s/dzL8VCYwcrvonrPv_2BsFQ) [随着IDE的不断升级,普通开发者的手工编码是否会被完全取代? - 知乎](https://www.zhihu.com/question/46269049) > **程序员的工作是programming,不是coding,更不是typing,谢谢。**(设计程序需要思想,而不只是编码,机器有思想吗?所以开发者不会被取代。) [哪些职业容易被机器算法取代?](https://mp.weixin.qq.com/s?__biz=MjM5ODQ2MDIyMA==&mid=2650713845&idx=1&sn=a977810e1e076724fde295d7c5257b46&chksm=bec060a689b7e9b046df8c77775e10ef0510a61c8285baf396ee2476fc79edd6292d53d932fd&scene=21#wechat_redirect) [你对推荐算法的认知,也许都是错的](https://mp.weixin.qq.com/s/LawqunWKmj5iqOC2IGBJMg) [如何评价19岁少年震惊科技界,发明大脑“意念”控制的机械手臂? - 知乎](https://www.zhihu.com/question/40998028?sort=created) [为什么有人故意夸大外国的美好?(系列二——脑电波控制机械臂)](http://card.weibo.com/article/h5/s#cid=1001603953314604664218&vid=3187890854&extparam=&from=1061095010&wm=2468_1001&ip=183.131.104.140) > 为什么都是说国外好,因为利益驱动,知道这符合满足大众情绪,媒体这样写的内容爆点才有人愿意看(永远不要对媒体的良知抱有希望),而大众/吃瓜群众往往是没有独立分辨能力的,他们从不关心正确性,只在会乎自己的感受。有罪的不是谁,不是某个个人,而是我们所有人。 [前端慌不慌?用深度学习自动生成HTML代码](https://www.toutiao.com/a6510101756171518467/?tt_from=weixin&utm_campaign=client_share×tamp=1515785426&app=news_article&utm_source=weixin&iid=22069500288&utm_medium=toutiao_android&wxshare_count=1) [emilwallner/Screenshot-to-code-in-Keras: A neural network that transforms a screenshot into a static website](https://github.com/emilwallner/Screenshot-to-code-in-Keras) [波士顿动力CEO详解机器人原理,人工智能未来,你关心的都在这里](https://www.ixigua.com/a6495235846826885646/?utm_source=toutiao&utm_medium=feed_stream#mid=11095560466) [通俗例子,清晰思路,6行python代码带你了解什么是机器学习](https://www.ixigua.com/a6515912366637449732/?utm_source=toutiao&utm_medium=feed_stream#mid=83882878410) [别激动!Facebook AI 根本就没创造新语言,它只是在犯傻](https://post.mp.qq.com/kan/article/2713129639-42116234.html?_wv=2147483777&sig=f16454eee8d2a239f38b43331c7045c0&article_id=42116234&time=1501598981&_pflag=1&x5PreFetch=1&from=0&web_ch_id=0&s_id=dfbpb5_p2p3e&share_source=2) [麻省理工学院“类脑芯片”最新突破:可将人脑能力“复制”到芯片](https://www.toutiao.com/a6520188540037366275/?tt_from=weixin&utm_campaign=client_share×tamp=1518192254&app=news_article_lite&utm_source=weixin&iid=25315997380&utm_medium=toutiao_android&wxshare_count=1) [用一个像素攻陷神经网络](http://mp.weixin.qq.com/s/F-zVLhMI5sUhi7PvIU0jmg) [C++从零实现神经网络(收藏版:两万字长文)](https://mp.weixin.qq.com/s/FLCmaSU8qlmTWSzs1PEhnw) [神奇女侠演员下海拍片?都是 ML 算法搞的……这是如何做到的?](http://mp.weixin.qq.com/s/TQ1AZcvo4wHdbPmPs6L4kw) [“疯狂”科学家菲尔·肯尼迪:为了研究“脑机接口”,他切开了自己大脑](http://36kr.com/coop/toutiao/5124494.html?ktm_source=toutiao&tt_from=weixin&tt_group_id=6535603620149199373) [“意识上传”服务,100%致命,却引硅谷富豪排队“求死换永生”](https://www.toutiao.com/a6534987497267528205/?tt_from=weixin&utm_campaign=client_share×tamp=1521970538&app=news_article_lite&utm_source=weixin&iid=25315997380&utm_medium=toutiao_android&wxshare_count=1) [这个超逼真的虚拟小姐姐,可能是腾讯游戏未来的样子](http://mp.weixin.qq.com/s/aisnlgWuROBun3Pl-gdnWw) [从「信息被监听」到「孕妇效应」](http://mp.weixin.qq.com/s/0HW6gg0DGAc3IX0mS_KeQQ) [从抄书到开源之巅:章亦春的程序人生](http://mp.weixin.qq.com/s/R1RXYJty2-J4qJYep9Cq0Q) > 机器解题与人类解题的一大区别是,机器不能运用**人类直观**来辅助解题。比如几何题里作辅助线,或者其他基于视觉的解题方法,这些对于机器都是不适宜的。机器需要精确的推理方式,需要把一切都形式化。 > 计算机就是这样,它没有人类直观,所以它永远不可能真正的智能。 [3100名谷歌员工联名上书,抵制与五角大楼合作:不想做战争生意](http://mp.weixin.qq.com/s/OosUhtZgxLfk9kTsgFvW_Q) [3分钟了解今日头条算法原理(科普版)](https://www.365yg.com/a6530119544323703303) [唐文斌:从清华姚班到Face++创业,我是怎么开始做机器学习的?丨二叉树视频](https://mp.weixin.qq.com/s/s6VBUvPzCahb57l-Xr9YoQ) > 人工智能根本就不成立,都是模拟的。 [如何简单形象又有趣地讲解神经网络是什么?](https://mp.weixin.qq.com/s/oWad4myuiNZeLob_nwLABw) [猝不及防,Google成功“造人”令人胆寒!人类迎来史上最惨失业潮…](https://mp.weixin.qq.com/s/a7hx1MTNxIhXyLNHrDQ50w) [智能合约和形式验证](http://www.yinwang.org/blog-cn/2017/04/23/ai) > Hoare Logic 的系统把所有这些前后条件和代码串接起来,经过逻辑推导验证,就可以作出这样的保证:在前条件满足的情况下,执行代码之后,后条件一定是成立的。如果所有这些条件都满足,**系统就认为这是“正确的程序”。注意这里的所谓“正确”,完全是由人来决定的,系统并不知道“正确”是什么意思。** > > 所以,利用深度学习自动标注 Hoare Logic 的前后条件,跟“自动编程”一样,是在试图实现“读心术”,那显然是不可能的。**作为资深的 PL 和形式验证专家,这些人应该知道这是不可能自动实现的。他们提出这样的想法,并且把它作为相对于其他智能合约项目的优势,当然只是为了忽悠外行,为了发币圈钱 ;)** >[danger] **不能跟风,做技术的要有自己的底线和良知,要有求真的心。** [花旗用AI取代投行部8000员工 可怕的是谁都没准备好](https://www.toutiao.com/i6566835998678843907/?tt_from=mobile_qq&utm_campaign=client_share×tamp=1529033928&app=news_article_lite&utm_source=mobile_qq&iid=33124962994&utm_medium=toutiao_android) [亚马逊的机器炒掉仓管员后,这次轮到年薪六位数的白领员工了](https://www.toutiao.com/a6567129065348661774/?tt_from=weixin&utm_campaign=client_share×tamp=1529059887&app=news_article_lite&utm_source=weixin&iid=33124962994&utm_medium=toutiao_android&wxshare_count=1) [微软帮政府作恶遭抗议,CEO回应反被打脸](https://mp.weixin.qq.com/s/SKY5Oa1aVnjPZnLi6flc_Q) > 近期几大科技公司相继因与政府部门的 AI 合作项目深陷舆论泥潭,遭遇来自各界的强烈抗议和反对。人们对 AI 与政府部门合作将带来的危机而惴惴不安,但出了强烈的抗议和反对之外,似乎还没有法律等层面的任何保障,几家科技公司与政府部门的合作也均以态度暧昧不明的所谓声明而不了了之,这不禁让人越来越担心,普通公民如何才能躲过技术发展背后潜藏的负债,科技公司与政府部门之间的合作未来到底会如何发展,如何才能保持公民与国家机器之间微妙的平衡。 [Facebook算法七宗罪曝光:不仅跟踪监听,还能预测死亡](https://mp.weixin.qq.com/s/M76NOJ6r-UdTiYhL71YN_Q) [IBM医疗AI死于难产:NLP搞不定医学问题!](https://mp.weixin.qq.com/s/y-AfnRYXfymTiK3rk43IJw) [神经网络是怎么工作的? 这是我看过的最好的入门视频!仅需5分钟](https://mp.weixin.qq.com/s/mMsKdNW1HMMHYvOhjtfXsQ) [从开源BigDL和Analytics Zoo,看懂英特尔的AI底气](https://mp.weixin.qq.com/s/B259OR43l2vxVECiVQHD3A) [为什么现在的人工智能助理都像人工智障?](https://mp.weixin.qq.com/s?__biz=Mzg5NDIwODgzMA==&mid=2247484380&idx=1&sn=f3313dfb0dc38141ba46967855ca1845&source=41#wechat_redirect) [人工智障 2 : 你看到的AI与智能无关](https://mp.weixin.qq.com/s/fDT0nRtFx66PPeFPxd3IiA) ~~~ 计算机和软件只是机器,没有生命和思想,算智能,但和人工智能,像人一样智能没关系,人工智能应该是有像人一样的智慧。 人工智能可能会出现吗,可能,但绝对不是计算机科学家实现的。 真正的人工智能可能将来由生物科学家和计算机科学家共同实现,即让大脑细胞和电子芯片结合,这就有点恐怖了,那时机器也会有生命了。 ~~~ [【机器学习】如何简单形象又有趣地讲解神经网络是什么?](https://mp.weixin.qq.com/s/jAcInAWI6cVvsnevoHIShQ) [与世界为敌:五角大楼坚持研发意念武器!](https://mp.weixin.qq.com/s/tUWiradVBRvD_aSQ7-1_Fw) [人脸识别终结者:多伦多大学反人脸识别,身份欺骗成功率达99.5%](http://toutiao.com/group/6562006987528208899/?iid=33124962994&app=news_article_lite×tamp=1527922186&wxshare_count=1&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_android&utm_campaign=client_share) [中国:AI 写稿,享有著作权;欧盟:AI 不是人,专利无效](https://mp.weixin.qq.com/s/YS_0fFPFufcWl_ADA9FBZw) [欧洲专利局](https://www.ipwatchdog.com/2020/01/07/epo-ukipo-refuse-ai-invented-patent-applications/id=117648/) 拒绝一项人工智能产生的塑料容器设计,理由是专利的发明者必须是人,不能是机器。 [漫画:啥是机器学习?](https://mp.weixin.qq.com/s/ZIEC5Sz24H2tRxZndb7vdQ) [Q新闻丨《星际争霸2》成 AI 研究环境;软件开发薪酬调查报告:Go、Scala最赚钱;Linux 子系统登陆 Windows](https://mp.weixin.qq.com/s/IPGfQ1GOAaBS7g5szIFcdg) [朋友送了我一个会编程的机器人,说程序员可以下岗了!!!](https://mp.weixin.qq.com/s/m4lglE0AUCXKPVekKUXQJw) [什么是Low Code ? 居然能威胁到专业程序员?](https://mp.weixin.qq.com/s/tDSDtNO79ASS3JqLc2piYg) * * * * * last update:2018-6-15 12:09:10
';

Nginx https配置

最后更新于:2022-04-02 04:34:02

### Nginx https配置 ```shell [root@iZuf6fvttmu9vkdbnencgpZ vhost]# vi yc-mv.tenpower.club.conf server { # 开启https端口 listen 443 ssl; server_name yc-mv.tenpower.club; access_log off; index index.html index.htm index.php; include /usr/local/nginx/conf/rewrite/thinkphp.conf; root /data/wwwroot/yc-mv.tenpower.club/public; # 证书配置 #ssl on; ssl_certificate /usr/local/nginx/cert/yc-mv.tenpower.club/214076134390354.pem; ssl_certificate_key /usr/local/nginx/cert/yc-mv.tenpower.club/214076134390354.key; ssl_session_timeout 5m; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; location ~ \.php { #fastcgi_pass remote_php_ip:9000; fastcgi_pass unix:/dev/shm/php-cgi.sock; fastcgi_index index.php; include fastcgi_params; set $real_script_name $fastcgi_script_name; if ($fastcgi_script_name ~ "^(.+?\.php)(/.+)$") { set $real_script_name $1; #set $path_info $2; } fastcgi_param SCRIPT_FILENAME $document_root$real_script_name; fastcgi_param SCRIPT_NAME $real_script_name; #fastcgi_param PATH_INFO $path_info; } location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|ico)$ { expires 30d; access_log off; } location ~ .*\.(js|css)?$ { expires 7d; access_log off; } } server { # 支持web 80端口访问 listen 80; # 配置访问域名 不包含协议 server_name yc-mv.tenpower.club; # 使用url重写模块重写url 访问非https的url重定向到http上去 rewrite ^/(.*) https://yc-mv.tenpower.club/$1 permanent; } ``` **所以静态资源服务器的这么配置:** 同时支持 https 和 http 访问,不做重定向。 ```shell [root@iZuf6fvttmu9vkdbnencgpZ vhost]# vi static.tenpower.club.conf server { listen 443 ssl; #listen 80; server_name static.tenpower.club; access_log off; index index.html index.htm index.php; #include /usr/local/nginx/conf/rewrite/none.conf; root /data/wwwroot/static.tenpower.club; ssl_certificate /usr/local/nginx/cert/static.tenpower.club/214076243630354.pem; ssl_certificate_key /usr/local/nginx/cert/static.tenpower.club/214076243630354.key; ssl_session_timeout 5m; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; location ~ [^/]\.php(/|$) { #fastcgi_pass remote_php_ip:9000; fastcgi_pass unix:/dev/shm/php-cgi.sock; fastcgi_index index.php; include fastcgi.conf; } location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|ico)$ { expires 30d; access_log off; } location ~ .*\.(js|css)?$ { expires 7d; access_log off; } } server { listen 80; server_name static.tenpower.club; #rewrite ^/(.*) https://yc-mv.tenpower.club/$1 permanent; } ``` >[tip] 即使配置为两者同时支持,不做跳转处理,在使用360和谷歌等浏览器测试时发现,如果访问过https的地址,再次访问不加https的地址也会跳转到https上去,它竟然会自动帮助用户访问安全的地址,**这是浏览器自己的行为,而不是web服务器这样配置的,** 这可能会给我们测试时带来困惑,所以要多换几个浏览器测试一下。 * * * * * 不加www跳转到加www ``` server { listen 80; server_name www.123.com 123.com; access_log off; index index.html index.htm index.php; root /data/wwwroot/123.com; if ($host != www.123.com) { return 301 $scheme://www.123.com$request_uri; } include /usr/local/nginx/conf/rewrite/none.conf; #error_page 404 /404.html; #error_page 502 /502.html; location ~ [^/]\.php(/|$) { #fastcgi_pass remote_php_ip:9000; fastcgi_pass unix:/dev/shm/php-cgi.sock; fastcgi_index index.php; include fastcgi.conf; } location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ { expires 30d; access_log off; } location ~ .*\.(js|css)?$ { expires 7d; access_log off; } location ~ /\.ht { deny all; } } ``` >[tip] 更多项目配置用法参见:[WebServer配置 · web开发最佳实践 · 看云](https://www.kancloud.cn/xiak/web-dev-best-practice/708059) * * * * * ### 参考 - [微信小程序Server端环境配置详解(SSL……](http://www.myhack58.com/Article/sort099/sort0102/2017/83246.htm) - [Nginx环境下http和https(ssl)共存的方法](http://jingyan.baidu.com/article/b87fe19e9a309b5218356818.html) - [Nginx配置同一个域名同时支持http与https两种方式访问 - 周伯通的麦田 - 博客园](https://www.cnblogs.com/phpper/p/6441475.html) - [Nginx配置实现CORS | youyu岁月](http://www.itzh.org/2017/12/25/CORS_config_for_nginx/) * * * * * update time:2018-8-8 23:02:13
';

命名规范

最后更新于:2022-04-02 04:34:00

### 命名规范 >[danger] 编程领域有两个最难的地方,第一个是缓存,第二个就是命名。可见往往被我们忽视的命名是多么的重要的。 缓存难是因为,在复杂的系统中有一个很重要的指标就是缓存命中率,这个值如果想要提高一个点,付出的代价也是非常巨大的,几乎整个架构上的每一步都可以优化,每一步都非常复杂,在大型是系统中尤为如此,比如淘宝的系统,所以缓存的难,体现的是背后的系统架构。 命名难不是因为名字难起,而是命名的背后反映了更多的程序设计,模式,架构,等等问题,这些体现在命名上面,好的命名反映了程序设计的逻辑结构清晰,易懂,有时候一个贴切的命名使程序一看就懂,可读性大大提高,降低了后期维护成本,而一个不好的命名则使程序看起来比较混乱,即使代码写的再怎么精巧,也称不上是精美,这样的代码无疑会给整个系统的后期维护带来很多麻烦,所以好的命名规范,命名规则是多么的重要。 无论怎样,用你喜欢的风格即可,但是请始终保持一致。 * * * * * ### 命名是抽象的 或者说是抽象与具体结合的,但其实,具体也是抽象的,世间万物都是抽象的。 我们来看人名,比如说,马蓉这个名字: 马为姓名,表示她姓马,从而可知他爸爸姓马,可能还能知道是什么笔派的。 蓉为名,一岁一枯荣,蓉是花的意思,一般只有女孩的名字才会出现这个字。父母给她起这个是希望她健康,像花儿一样成长,成长环境,家庭的熏陶决定了孩子的性格,所以根据这个名字基本可以猜测这个女孩的性格了。 名字是抽象的,你可以说它有意义,也可以说她没意义,在于你怎么理解。 根据上下文关系,情景,我们把一些词归为,动词,名词,形容词,主谓宾,其实这些概念就是人们创造出来的,创造出来的那么它的本质就是抽象的,世间万物本是不存在的,世间万物其实都是抽象的,**为什么要抽象,因为要具体**,人们希望对事物更加了解,具体,所以才抽象出来很多东西,形成共识,这就是我们掌握知识的基础。 命名时首先列出这个事物的几个关键对象,比如侧边,关联,容器,记录,关联操作等等,先找出这些关键对象,在对它进行抽象。 * * * * * ### 工具推荐 **命名这么重要,这么复杂,有没有好的工具推荐呢?** 当然有。 [CODELF](http://unbug.github.io/codelf/):变量命名神器,你值得拥有 [百度翻译](http://fanyi.baidu.com/) [有道翻译](http://fanyi.youdao.com/) [海词词典](http://dict.cn/) > 可以装几个翻译的浏览器插件,实时划词翻译。 * * * * * ### 参考 - [PHP书写规范 匈牙利命名法+驼峰法命名 - lighthouse](https://my.oschina.net/u/2403409/blog/478191) - [php的命名规范 - 毅栈 - 博客园](http://www.cnblogs.com/wolfwows/p/5931181.html) - [PHP 代码简洁之道——函数部分](http://mp.weixin.qq.com/s/0ELHxChjHj7gFdCkhmpFvg) - [JavaScript 的 API 设计原则](https://mp.weixin.qq.com/s/8-0O2jQf5pm7XQWjysJKKQ) > 命名这点事:既要短,又要自描述,最重要的是保持一致性 “在计算机科学界只有两件头疼的事:缓存失效和命名问题” — Phil Karlton 选择一个你喜欢的措辞,然后持续使用。选择一种风格,然后保持这种风格。 - [谈编程 · php笔记 · 看云](https://www.kancloud.cn/xiak/php-node/500811) - [命名成为了程序员最难攻克的关卡](http://mp.weixin.qq.com/s/tvGohn0XPLF43ACbDhF22A) update time:2017-11-27 20:10:35
';

crmeb

最后更新于:2022-04-02 04:33:57

## crmeb [TOC=3,8] ---- ### 订单 与 支付 #### 订单表 ```sql CREATE TABLE IF NOT EXISTS `eb_store_order` ( `paid` tinyint(1) NOT NULL DEFAULT '0' COMMENT '支付状态 0: 未支付 1:已支付', `pay_time` int(11) UNSIGNED NOT NULL DEFAULT '0' COMMENT '支付时间', `pay_type` varchar(32) NOT NULL DEFAULT '' COMMENT '支付方式', `add_time` int(11) UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间', `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '订单状态(-1 : 申请退款 -2 : 退货成功 0:待发货;1:待收货;2:已收货;3:待评价;-1:已退款)', `refund_status` tinyint(1) UNSIGNED NOT NULL DEFAULT '0' COMMENT '0 未退款 1 申请中 2 已退款', `refund_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '退款申请类型', `refund_express` varchar(255) NOT NULL DEFAULT '' COMMENT '退货快递单号', `refund_express_name` varchar(255) NOT NULL DEFAULT '' COMMENT '退货快递名称', `refund_reason_wap_img` varchar(2000) NOT NULL DEFAULT '' COMMENT '退款图片', `refund_reason_wap_explain` varchar(255) NOT NULL DEFAULT '' COMMENT '退款用户说明', `refund_reason_time` int(11) UNSIGNED NOT NULL DEFAULT '0' COMMENT '退款时间', `refund_reason_wap` varchar(255) NOT NULL DEFAULT '' COMMENT '前台退款原因', `refund_reason` varchar(255) NOT NULL DEFAULT '' COMMENT '不退款的理由', `refund_price` decimal(8,2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '退款金额', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单表'; ``` #### 分析 支付回执 AliPayService.php ```php public static function handleNotify() { return self::instance()->notify(function ($notify) { if (isset($notify->out_trade_no)) { if (isset($notify->attach) && $notify->attach) { if (($count = strpos($notify->out_trade_no, '_')) !== false) { $notify->trade_no = $notify->out_trade_no; $notify->out_trade_no = substr($notify->out_trade_no, $count + 1); } return (new Hook(PayNotifyServices::class, 'aliyun'))->listen($notify->attach, $notify->out_trade_no, $notify->trade_no); } return false; } }); } public function notify(callable $notifyFn) { app()->request->filter(['trim']); //商户订单号 $postOrder['out_trade_no'] = $paramInfo['out_trade_no'] ?? ''; //支付宝交易号 $postOrder['trade_no'] = $paramInfo['trade_no'] ?? ''; //交易状态 $postOrder['trade_status'] = $paramInfo['trade_status'] ?? ''; //备注 $postOrder['attach'] = isset($paramInfo['passback_params']) ? urldecode($paramInfo['passback_params']) : ''; if (in_array($paramInfo['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED']) && $this->verifyNotify($paramInfo)) { try { if ($notifyFn((object)$postOrder)) { return 'success'; } } catch (\Exception $e) { Log::error('支付宝异步会回调成功,执行函数错误。错误单号:' . $postOrder['out_trade_no']); } } return 'fail'; } ``` 业务回执处理 PayNotifyServices.php ```php public function aliyunProduct(string $order_id = null, string $trade_no = null) { if (!$order_id || !$trade_no) { return false; } try { /** @var StoreOrderSuccessServices $services */ $services = app()->make(StoreOrderSuccessServices::class); $orderInfo = $services->getOne(['order_id' => $order_id]); if (!$orderInfo) return true; if ($orderInfo->paid) return true; return $services->paySuccess($orderInfo->toArray(), PayServices::ALIAPY_PAY, ['trade_no' => $trade_no]); } catch (\Throwable $e) { return false; } } ``` StoreOrderSuccessServices.php ```php public function paySuccess(array $orderInfo, string $paytype = PayServices::WEIXIN_PAY, array $other = []) { $updata = ['paid' => 1, 'pay_type' => $paytype, 'pay_time' => time()]; if ($other && isset($other['trade_no'])) { $updata['trade_no'] = $other['trade_no']; } $res1 = $this->dao->update($orderInfo['id'], $updata); $resPink = true; if ($orderInfo['combination_id'] && $res1 && !$orderInfo['refund_status']) { /** @var StorePinkServices $pinkServices */ $pinkServices = app()->make(StorePinkServices::class); /** @var StoreOrderServices $orderServices */ $orderServices = app()->make(StoreOrderServices::class); $resPink = $pinkServices->createPink($orderServices->tidyOrder($orderInfo, true));//创建拼团 } //缓存抽奖次数 除过线下支付 if (isset($orderInfo['pay_type']) && $orderInfo['pay_type'] != 'offline') { /** @var LuckLotteryServices $luckLotteryServices */ $luckLotteryServices = app()->make(LuckLotteryServices::class); $luckLotteryServices->setCacheLotteryNum((int)$orderInfo['uid'], 'order'); } //订单支付成功后置事件 event('order.orderPaySuccess', [$orderInfo]); //用户推送消息事件 event('notice.notice', [$orderInfo, 'order_pay_success']); //支付成功给客服发送消息 event('notice.notice', [$orderInfo, 'admin_pay_success_code']); $res = $res1 && $resPink; return false !== $res; } ``` #### 总结 1. 只在 PayNotifyServices 成功 才对 三方 响应 'success' 2. PayNotifyServices 成功 a. 订单不存在 true b. 订单已支付 true c. StoreOrderSuccessServices->paySuccess() true a. 更新订单支付状态 成功 b. 订单支付成功后置事件 结果不关心 1. 只在 PayNotifyServices 成功 才对 三方 响应 'success' 2. PayNotifyServices 成功 a. 订单不存在 true b. 订单已支付 true c. wechatUserRecharge->rechargeSuccess() true a. 更新订单 true b. 更新用户余额 b. 订单支付成功后置事件 结果不关心 不过任何 异常 代码错误,都会使 PayNotifyServices false ,所以如果 代码问题,业务履行失败 都不会 对三方响应 'success' ---- ### 架构 Dao 基于模型对数据获取、操作进行封装,Services 使用 Dao 进行数据操作。调用 Services 上不存在的方法 会调用 到 其 Dao 上,所以 Services 可看作 是 “继承” Dao 的。 优点:将 SQL 拼装 从 业务层拆分到 Dao 上了,Services 层的业务成分就更明显了。 缺点:由于优点,于是 业务也被分散到 Services 和 Dao 上了,有点不清晰,只能说 Dao 的 业务成分 没有 Services 明显,但并不是完全没有。 结论: 1. 所以 Services + Dao = 我们 的 Logic 2. Services > Dao > Model ;我们 Logic > Model PS: ~~我们的 Logic、Service 层确实没什么区别,区分的理由实在太过牵强,没有区分的意义。~~(有待商榷,Logic 注重特定的业务场景 如 User,Service 纯技术方案,无特定业务场景特征,如 JWT) ---- ```php abstract class BaseServices { public function __call($name, $arguments) { // TODO: Implement __call() method. return call_user_func_array([$this->dao, $name], $arguments); } } class StoreOrderServices extends BaseServices { public function setOrderTypePayOffline(string $orderId) { return $this->dao->update($orderId, ['pay_type' => 'offline'], 'order_id'); } public function removeOrder(string $uni, int $uid) { $order = $this->getUserOrderDetail($uni, $uid); if (!$order) { throw new ValidateException('订单不存在!'); } ... } } ``` ```php abstract class BaseDao { abstract protected function setModel(): string; protected function getModel() { return app()->make($this->setModel()); } protected function getPk() { return $this->getModel()->getPk(); } public function update($id, array $data, ?string $key = null) { if (is_array($id)) { $where = $id; } else { $where = [is_null($key) ? $this->getPk() : $key => $id]; } return $this->getModel()::update($data, $where); } } class StoreOrderDao extends BaseDao { protected function setModel(): string { return StoreOrder::class; } public function getUserOrderDetail(string $key, int $uid, $with = []) { return $this->getOne(['order_id|unique' => $key, 'uid' => $uid, 'is_del' => 0], '*', $with); } public function batchUpdateOrder(array $ids, array $data, ?string $key = null) { return $this->getModel()::whereIn(is_null($key) ? $this->getPk() : $key, $ids)->update($data); } } ``` ---- [分层 DAO层,Service层,Controller层、View层 - 简书](https://www.jianshu.com/p/df659d7fbbf7) > DAO完成连接数据库修改删除添加等的实现细节,例如sql语句是怎么写的,怎么把对象放入数据库的。service层是面向功能的,一个个功能模块比如说银行登记并完成一次存款,UI要把请求给service层,然后service曾将这一个case分解成许多步骤调用底层的实现完成这次存款,dao就是下面那层。
';

京东到家库存系统架构设计

最后更新于:2022-04-02 04:33:55

## 京东到家库存系统架构设计 https://tech.imdada.cn/2017/08/23/daojia-inventory-system/ 发表于2017-08-23  |   分类于[京东到家](https://tech.imdada.cn/categories/%E4%BA%AC%E4%B8%9C%E5%88%B0%E5%AE%B6/),[架构](https://tech.imdada.cn/categories/%E4%BA%AC%E4%B8%9C%E5%88%B0%E5%AE%B6/%E6%9E%B6%E6%9E%84/) # [](https://tech.imdada.cn/2017/08/23/daojia-inventory-system/#u4EAC_u4E1C_u5230_u5BB6_u5E93_u5B58_u7CFB_u7EDF_u67B6_u6784_u8BBE_u8BA1 "京东到家库存系统架构设计")京东到家库存系统架构设计 > 目前,京东到家库存系统经历两年多的线上考验与技术迭代,现服务着万级商家十万级店铺的规模,需求的变更与技术演进,我们是如何做到系统的稳定性与高可用呢,下图会给你揭晓答案(通过强大的基础服务平台让应用、JVM、Docker、物理机所有健康指标一目了然,7\*24小时智能监控告警让开发无须一直盯着监控,另外数据与业务相辅相成,用数据验证业务需求,迭代业务需求,让业务需求都尽可能的收益最大化,库存系统的开发同学只需要关注业务需求,大版本上线前相应的测试同学会跟进帮你压测,防止上线后潜在的性能瓶颈)。 附1:库存系统技术架构图 [![附1:库存系统技术架构图](http://img.blog.csdn.net/20170821141229602)](http://img.blog.csdn.net/20170821141229602) 附2:库存系统数据流转图 [![附2:库存系统数据流转图](http://img.blog.csdn.net/20170821140525407)](http://img.blog.csdn.net/20170821140525407) > 库存系统的架构很有意思,从上图来看功能上其实并不复杂,但是他面临的技术复杂度却是相当高的,比如秒杀品在高并发的情况下如何防止超卖,另外库存系统还不是一个纯技术的系统,需要结合用户的行为特点来考虑,比如下文中提到什么时间进行库存的扣减最合适,我们先抛出几个问题和大家一起探讨下,如有有妥不处,欢迎大家拍砖。 ## [](https://tech.imdada.cn/2017/08/23/daojia-inventory-system/#u5E93_u5B58_u4EC0_u4E48_u65F6_u5019_u8FDB_u884C_u9884_u5360_28_u6216_u8005_u6263_u51CF_29_u5462 "库存什么时候进行预占(或者扣减)呢")库存什么时候进行预占(或者扣减)呢 > 商家销售的商品数量是有限的,用户下单后商品会被扣减,我们可以怎么实现呢? **举个例子:** **一件商品有1000个库存,现在有1000个用户,每个用户计划同时购买1000个。** * (实现方案1)如果用户加入购物车时进行库存预占,那么将只能有1个用户将1000个商品加入购物车。 * (实现方案2)如果用户提交订单时进行库存预占,那么将也只能有1个用户将1000个商品提单成功,其它的人均提示“库存不足,提单失败”。 * (实现方案3)如果用户提交订单&支付成功时进行库存预占,那么这1000个人都能生成订单,但是只有1个人可以支付成功,其它的订单均会被自动取消。 **京东到家目前采用的是方案2,理由:** * 用户可能只是暂时加入购物车,并不表示用户最终会提单并支付。 * 所以在购物车进行库存校验并预占,会造成其它真正想买的用户不能加入购物车的情况,但是之前加车的用户一直不付款,最终损失的是公司。 * 方案3会造成生成1000个订单,无论是在支付前校验库存还是在支付成功后再检验库存,都会造成用户准备好支付条件后却会出现99.9%的系统取消订单的概率,也就是说会给99.9%的用户体验到不爽的感觉。 * 数据表明用户提交订单不支付的占比是非常小的(相对于加入购物车不购买的行为),目前京东到家给用户预留的最长支付时间是30分钟,超过30分钟订单自动取消,预占的库存自动释放 > 综上所述,方案2也可能由于用户下单预占库存但最终未支付,造成库存30分钟后才能被其它用户使用的情况,但是相较于方案1,方案3无疑是折中的最好方案。 ## [](https://tech.imdada.cn/2017/08/23/daojia-inventory-system/#u91CD_u590D_u63D0_u4EA4_u8BA2_u5355_u7684_u95EE_u9898_uFF1F "重复提交订单的问题?")重复提交订单的问题? > 重复提交订单造成的库存重复扣减的后果是比较严重的。比如商家设置有1000件商品,而实际情况可能卖了900件就提示用户无货了,给商家造成无形的损失 **可能出现重复提交订单的情况:** * (1、用户善意行为)app上用户单击“提交订单”按钮后由于后端接口没有返回,用户以为没有操作成功会再次单击“提交订单”按钮 * (2、用户恶意行为)黑客直接刷提单接口,绕过App端防重提交功能 * (3、提单系统重试)比如提单系统为了提高系统的可用性,在第一次调用库存系统扣减接口超时后会重试再次提交扣减请求 **好了,既然问题根源缕清楚了,我们一一对症下药** * (1、用户善意行为)app侧在用户第一次单击“提交订单”按钮后对按钮进行置灰,禁止再次提交订单 * (2、用户恶意行为)采用令牌机制,用户每次进入结算页,提单系统会颁发一个令牌ID(全局唯一),当用户点击“提交订单”按钮时发起的网络请求中会带上这个令牌ID,这个时候提单系统会优先进行令牌ID验证,令牌ID存在&令牌ID访问次数=1的话才会放行处理后续逻辑,否则直接返回 * (3、提单系统重试)这种情况则需要后端系统(比如库存系统)来保证接口的幂等性,每次调用库存系统时均带上订单号,库存系统会基于订单号增加一个分布式事务锁,伪代码如下: ```java int ret=redis.incr(orderId); redis.expire(orderId,5,TimeUnit.MINUTES); if(ret==1){//添加成功,说明之前没有处理过这个订单号或者5分钟之前处理过了 boolean alreadySuccess=alreadySuccessDoOrder(orderProductRequest); if(!alreadySuccess){ doOrder(orderProductRequest); }else{ return "操作失败,原因:重复提交"; } }else{ return "操作失败,原因:重复提交"; } ``` ## [](https://tech.imdada.cn/2017/08/23/daojia-inventory-system/#u5E93_u5B58_u6570_u636E_u7684_u56DE_u6EDA_u673A_u5236_u5982_u4F55_u505A "库存数据的回滚机制如何做")库存数据的回滚机制如何做 **需要库存回滚的场景也是比较多的,比如:** * (1、用户未支付)用户下单后后悔了 * (2、用户支付后取消)用户下单&支付后后悔了 * (3、风控取消)风控识别到异常行为,强制取消订单 * (4、耦合系统故障)比如提交订单时提单系统T1同时会调用积分扣减系统X1、库存扣减系统X2、优惠券系统X3,假如X1,X2成功后,调用X3失败,需要回滚用户积分与商家库存。 > 其中场景1,2,3比较类似,都会造成订单取消,订单中心取消后会发送mq出来,各个系统保证自己能够正确消费订单取消MQ即可。而场景4订单其实尚未生成,相对来说要复杂些,如上面提到的,提单系统T1需要主动发起库存系统X2、优惠券系统X3的回滚请求(入参必须带上订单号),X2、X3回滚接口需要支持幂等性。 > > 其实针对场景4,还存在一种极端情况,如果提单系统T1准备回滚时自身也宕机了,那么库存系统X2、优惠券系统X3就必须依靠自己为完成回滚操作了,也就是说具备自我数据健康检查的能力,具体来说怎么实现呢? > > 可以利用当前订单号所属的订单尚未生成的特点,可以通过worker机制,每次捞取40分钟(这里的40一定要大于容忍用户的支付时间)前的订单,调用订单中心查询订单的状态,确保不是已取消的,否则进行自我数据的回滚。 ## [](https://tech.imdada.cn/2017/08/23/daojia-inventory-system/#u591A_u4EBA_u540C_u65F6_u8D2D_u4E701_u4EF6_u5546_u54C1_uFF0C_u5982_u4F55_u5B89_u5168_u5730_u5E93_u5B58_u6263_u51CF "多人同时购买1件商品,如何安全地库存扣减")多人同时购买1件商品,如何安全地库存扣减 > 现实中同一件商品可能会出现多人同时购买的情况,我们可以如何做到并发安全呢? 伪代码片段1: ```java synchronized(this){ long stockNum = getProductStockNum(productId); if(stockNum>requestBuyNum) { int ret=updateSQL("update stock_main set stockNum=stockNum-"+requestBuyNum +" where productId="+productId); if(ret==1){ return "扣减成功"; }else { return "扣减失败"; } } } ``` 伪代码片段1的设计思想是所有的请求过来之后首先加锁,强制其串行化处理,可见其效率一定不高, 伪代码片段2: ```java int ret=updateSQL("update stock_main set stockNum=stockNum-"+requestBuyNum +" where productId="+productId+" and stockNum>="+requestBuyNum ); if(ret==1){ return "扣减成功"; }else { return "扣减失败"; } ``` 这段代码只是在where条件里增加了and stockNum>=”+requestBuyNum即可防止超卖的行为,达到了与上述伪代码1的功能 如果商品是促销品(比如参与了秒杀的商品)并发扣减的机率会更高,那么数据库的压力会更高,这个时候还可以怎么做呢 海量的用户秒杀请求,本质上是一个排序,先到先得.但是如此之多的请求,注定了有些人是抢不到的,可以在进入上述伪代码Dao层之前增加一个计数器进行控制,比如有50%的流量将直接告诉其抢购失败,伪代码如下: ```java public class SeckillServiceImpl{ private long count=0; public String buy(User user,int productId,int productNum){ count++; if(count%2=1){ Thread.sleep(1000); return "抢购失败"; }else{ return doBuy(user,productId,productNum); } } } ``` 另外同一个用户,不允许多次抢购同一件商品,我们又该如何做呢 ```java public String doBuy(user,productId,productNum){ //用户除了第一次进入值为1,其它时候均大于1 int tmp=redis.incr(user.getUid()+productId); if(tmp==1){ redis.expire(user.getUid()+productId,3600); //1小时后key自动销毁 doBuy1(user,productId,productNum); }else{ return "抢购失败"; } } ``` > 怎么样,看了上述的介绍是不是觉得库存系统很有意思,而且总会有你意想不到的高并发问题等你来日挑战,当然了,也非常欢迎你加入我们(目前北京、上海均在招Java高级工程师,可以加微信【北京的同学加***lzc\_java***,上海的同学加***25252937***】进一步了解职位详情,等你来约) ------------- ~~~ updateSQL("update stock_main set stockNum=stockNum-"+requestBuyNum +" where productId="+productId+" and stockNum>="+requestBuyNum ); ~~~ `stockNum>="+requestBuyNum` 这里做乐观锁条件更好,不一定要用版本号,因为本次只关心有库存就行,不需要关系数据版本有没有被人更新。(哪怕再简单的细节,也有可以优化和琢磨的地方)
';

微信登录与绑定

最后更新于:2022-04-02 04:33:53

## 微信登录与绑定 #### 1. PC上微信扫码登陆 PC版,登陆页面 - 第三方登录,点击微信登陆,手机微信扫码(可能有确认授权的提,需要用户同意授权), 获取到openid后,,扫码认证后,提示 注册新账号,或者 绑定已有账号 1. 注册新账号,和普通注册流程一样,在注册后(注册会自动登陆),账号自动关联绑定上一步扫码的微信账号,下次微信扫码就可以直接登陆了。 2. 绑定已经账号,如果用户之前通过手机号码在平台注册过账号,那么就不需要在注册新账号了,而是输入之前已有账号的手机号码,短信验证后,直接登陆,同时关联绑定扫码的微信账号,下次微信扫码就可以直接登陆了。 ![](http://cdn.aipin100.cn/e993acaacd460f355d3d48dbf6e66a89) ![](http://cdn.aipin100.cn/9649d825ef0413c851436ddbd04b86ed) 第三方登录时是否直接创建用户,京东的模式是让用户选,创建新账号并关联,关联已有帐号。如果直接创建新账号,会出现,你在pc上本来已经有一个帐号了,结果你现在微信上又创建一个帐号并绑定,那么你就有两个帐号了,这样会混乱。最好一开始,全部使用微信登录并创建帐号。 加一个注册来源表,uid,第三方a c id ,常规注册,邮箱,手机号码等,desc 通过159注册
';

分类与类型

最后更新于:2022-04-02 04:33:50

### 分类与类型 商品分类与商品类型是一个东西吗,有什么不同呢? [一直不能理解“商品分类”和“商品类型的”区别](http://bbs.ecshop.com/thread-72239-1-1.html) [什么是商品类型](http://www.cnblogs.com/sunxi/articles/2572072.html) 一般用两个单词表示: type(类型) category(类别) > 一般我们所说的分类是指的分类别 商品分类,商品类型(种类),分类重运营,是运营层面上的,种类则是商品品类层面的,对于标准种类产品,可以根据种类设计规范的规格和属性参数,而对于非标产品,就没有标准的规格和属性参数了。标类产品可以利用标准规格和属性进行检索,非标产品就做不到这点,但是非标产品可以自定义规格和属性更加的灵活。 > 非标产品指不是按照国家颁布的统一的行业标准和规格制造的产品或设备,而是根据自己的用途需要,自行设计制造的产品或设备。且外观或性能不在国家设备产品目录内。 [非标产品_百度百科](https://baike.baidu.com/item/%E9%9D%9E%E6%A0%87%E4%BA%A7%E5%93%81/1755418?fr=aladdin) 上冲开发中需要注意的一些问题,比如积分抵扣下单时,如果不注意会出大问题,请看 [优化之前的代码,能满足用户最大程度的使用抵扣掉积分,在保证安全的同时最大限度的满足用户使用出去积分,算是比较完美的解决方案吧](https://github.com/xiasf/gj-shop/commit/19ecc89ca10fe2acf947cc9e1c0ae9eeeff1f988) * * * * * last update:2018-5-30 15:41:09
';

购物车技术

最后更新于:2022-04-02 04:33:48

## 购物车技术 ### 京东Java架构师讲解购物车的原理及Java实现 混在软件圈 2018-05-19 19:35:44 今天来写一下关于购物车的东西, 这里首先抛出四个问题: 1)用户没登陆用户名和密码,添加商品, 关闭浏览器再打开后 不登录用户名和密码 问:购物车商品还在吗? 2)用户登陆了用户名密码,添加商品,关闭浏览器再打开后 不登录用户名和密码 问:购物车商品还在吗? 3)用户登陆了用户名密码,添加商品, 关闭浏览器,然后再打开,登陆用户名和密码 问:购物车商品还在吗? 4)用户登陆了用户名密码,添加商品, 关闭浏览器 外地老家打开浏览器 登陆用户名和密码 问:购物车商品还在吗? 上面四个问题都是以京东为模板, 那么大家猜猜结果是什么呢? 1)在 2)不在了 3)在 4)在 如果你能够猜到答案, 那么说明你真的很棒, 那么关于这四点是怎么实现的呢? (如果有不认可的小伙伴可以用京东实验一下) 下面我们就来讲解下购物车的原理,最后再来说下具体的code实现. 1)用户没有登录, 添加商品, 此时的商品是被添加到了浏览器的Cookie中, 所以当再次访问时(不登录),商品仍然在Cookie中, 所以购物车中的商品还是存在的. 2)用户登录了,添加商品, 此时会将Cookie中和用户选择的商品都添加到购物车中, 然后删除Cookie中的商品. 所以当用户再次访问(不登录),此时Cookie中的购物车商品已经被删除了, 所以此时购物车中的商品不在了. 3)用户登录, 添加商品,此时商品被添加到数据库做了持久化存储, 再次打开登录用户名和密码, 该用户选择的商品肯定还是存在的, 所以购物车中的商品还是存在的. 4)理由3) 这里再说下 没登录 保存商品到Cookie的优点以及保存到Session和数据库的对比: 1:Cookie: 优点: 保存用户浏览器(不用浪费我们公司的服务器) 缺点:Cookie禁用,不提供保存 2:Session:(Redis : 浪费大量服务器内存:实现、禁用Cookie) 速度很快 3:数据库(Mysql、Redis、SOlr) 能持久化的就数据库 速度太慢 那么我今天要讲的就是: * 用户没登陆:购物车添加到Cookie中 * 用户登陆: 保存购物车到Redis中 (不用数据库) 整体的思路图解: ![京东Java架构师讲解购物车的原理及Java实现](http://p3.pstatp.com/large/pgc-image/15267297244796fe643bc4e) 接下来就是代码实例来实现 购物车的功能了: 首先我们看下购物车和购物项两个JavaBean的设计: 购物车: buyerCart.java 1 public class BuyerCart implements Serializable{2 3 /**4 * 购物车5 */6 private static final long serialVersionUID = 1L;7 8 //商品结果集9 private List items = new ArrayList();10 11 //添加购物项到购物车12 public void addItem(BuyerItem item){13 //判断是否包含同款14 if (items.contains(item)) {15 //追加数量16 for (BuyerItem buyerItem : items) {17 if (buyerItem.equals(item)) {18 buyerItem.setAmount(item.getAmount() + buyerItem.getAmount());19 }20 }21 }else {22 items.add(item);23 }24 25 }26 27 public List getItems() {28 return items;29 }30 31 public void setItems(List items) {32 this.items = items;33 }34 35 36 //小计37 //商品数量38 @JsonIgnore39 public Integer getProductAmount(){40 Integer result = 0;41 //计算42 for (BuyerItem buyerItem : items) {43 result += buyerItem.getAmount();44 }45 return result;46 }47 48 //商品金额49 @JsonIgnore50 public Float getProductPrice(){51 Float result = 0f;52 //计算53 for (BuyerItem buyerItem : items) {54 result += buyerItem.getAmount()*buyerItem.getSku().getPrice();55 }56 return result;57 }58 59 //运费60 @JsonIgnore61 public Float getFee(){62 Float result = 0f;63 //计算64 if (getProductPrice() 这里使用了@JsonIgonre注解是因为下面需要将BuyerCart 转换成Json格式, 而这几个字段只有get 方法, 所以不能转换, 需要使用忽略Json. 下面是购物项: buyerItem.java 1 public class BuyerItem implements Serializable{2 3 private static final long serialVersionUID = 1L;4 5 //SKu对象6 private Sku sku;7 8 //是否有货9 private Boolean isHave = true;10 11 //购买的数量12 private Integer amount = 1;13 14 public Sku getSku() {15 return sku;16 }17 18 public void setSku(Sku sku) {19 this.sku = sku;20 }21 22 public Boolean getIsHave() {23 return isHave;24 }25 26 public void setIsHave(Boolean isHave) {27 this.isHave = isHave;28 }29 30 public Integer getAmount() {31 return amount;32 }33 34 public void setAmount(Integer amount) {35 this.amount = amount;36 }37 38 @Override39 public int hashCode() {40 final int prime = 31;41 int result = 1;42 result = prime * result + ((sku == null) ? 0 : sku.hashCode());43 return result;44 }45 46 @Override47 public boolean equals(Object obj) {48 if (this == obj) //比较地址49 return true;50 if (obj == null)51 return false;52 if (getClass() != obj.getClass())53 return false;54 BuyerItem other = (BuyerItem) obj;55 if (sku == null) {56 if (other.sku != null)57 return false;58 } else if (!sku.getId().equals(other.sku.getId()))59 return false;60 return true;61 }62 } 1、将商品加入购物车中 ![京东Java架构师讲解购物车的原理及Java实现](http://p9.pstatp.com/large/pgc-image/1526729724458970ecfcc9e) 1 //加入购物车2 function addCart(){3 // + skuId4 window.location.href="/shopping/buyerCart?skuId="+skuId+"&amount="+$("#buy-num").val();5 } 这里传入的参数是skuId(库存表的主键, 库存表保存的商品id,颜色,尺码,库存等信息), 购买数量amount. 接着我们来看Controller是如何来处理的: 1 //加入购物车2 @RequestMapping(value="/shopping/buyerCart")3 public String buyerCart(Long skuId, Integer amount, HttpServletRequest request,4 HttpServletResponse response) throws JsonParseException, JsonMappingException, IOException{5 //将对象转换成json字符串/json字符串转成对象6 ObjectMapper om = new ObjectMapper();7 om.setSerializationInclusion(Include.NON_NULL);8 BuyerCart buyerCart = null;9 //1,获取Cookie中的购物车10 Cookie[] cookies = request.getCookies();11 if (null != cookies && cookies.length > 0) {12 for (Cookie cookie : cookies) {13 //14 if (Constants.BUYER_CART.equals(cookie.getName())) {15 //购物车 对象 与json字符串互转16 buyerCart = om.readValue(cookie.getValue(), BuyerCart.class);17 break;18 }19 }20 }21 22 //2,Cookie中没有购物车, 创建购物车对象23 if (null == buyerCart) {24 buyerCart = new BuyerCart();25 }26 27 //3, 将当前款商品追加到购物车28 if (null != skuId && null != amount) {29 Sku sku = new Sku();30 sku.setId(skuId);31 BuyerItem buyerItem = new BuyerItem();32 buyerItem.setSku(sku);33 //设置数量34 buyerItem.setAmount(amount);35 //添加购物项到购物车36 buyerCart.addItem(buyerItem);37 }38 39 //排序 倒序40 List items = buyerCart.getItems();41 Collections.sort(items, new Comparator() {42 43 @Override44 public int compare(BuyerItem o1, BuyerItem o2) {45 return -1;46 }47 48 });49 50 //前三点 登录和非登录做的是一样的操作, 在第四点需要判断51 String username = sessionProviderService.getAttributterForUsername(RequestUtils.getCSessionId(request, response));52 if (null != username) {53 //登录了54 //4, 将购物车追加到Redis中55 cartService.insertBuyerCartToRedis(buyerCart, username);56 //5, 清空Cookie 设置存活时间为0, 立马销毁57 Cookie cookie = new Cookie(Constants.BUYER_CART, null);58 cookie.setPath("/");59 cookie.setMaxAge(-0);60 response.addCookie(cookie);61 }else {62 //未登录63 //4, 保存购物车到Cookie中64 //将对象转换成json格式65 Writer w = new StringWriter();66 om.writeValue(w, buyerCart);67 Cookie cookie = new Cookie(Constants.BUYER_CART, w.toString());68 //设置path是可以共享cookie69 cookie.setPath("/");70 //设置Cookie过期时间: -1 表示关闭浏览器失效 0: 立即失效 >0: 单位是秒, 多少秒后失效71 cookie.setMaxAge(24*60*60);72 //5,Cookie写会浏览器73 response.addCookie(cookie);74 }75 76 //6, 重定向77 return "redirect:/shopping/toCart";78 } 这里设计一个知识点: 将对象转换成json字符串/json字符串转成对象 我们在这里先写一个小的Demo来演示json和对象之间的互转, 这里使用到了springmvc中的ObjectMapper类. 1 public class TestJson {2 3 @Test4 public void testAdd() throws Exception {5 TestTb testTb = new TestTb();6 testTb.setName("范冰冰");7 ObjectMapper om = new ObjectMapper();8 om.setSerializationInclusion(Include.NON_NULL);9 //将对象转换成json字符串10 Writer wr = new StringWriter();11 om.writeValue(wr, testTb);12 System.out.println(wr.toString());13 14 //转回对象15 TestTb r = om.readValue(wr.toString(), TestTb.class);16 System.out.println(r.toString());17 }18 19 } 执行结果: ![京东Java架构师讲解购物车的原理及Java实现](http://p3.pstatp.com/large/pgc-image/15267297242755ce8f89479) 这里我们使用了Include.NON_NULL, 如果TestTb 中属性为null 的就不给转换成Json, 从对象-->Json字符串 用的是 objectMapper.writeValue(). 从Json字符串-->对象使用的是objectMapper.readValue(). 回归上面我们项目中的代码, 只有未登录 添加商品时才会将此商品添加到Cookie中. 1 //未登录2 //4, 保存购物车到Cookie中3 //将对象转换成json格式4 Writer w = new StringWriter();5 om.writeValue(w, buyerCart);6 Cookie cookie = new Cookie(Constants.BUYER_CART, w.toString());7 //设置path是可以共享cookie8 cookie.setPath("/");9 //设置Cookie过期时间: -1 表示关闭浏览器失效 0: 立即失效 >0: 单位是秒, 多少秒后失效10 cookie.setMaxAge(24*60*60);11 //5,Cookie写会浏览器12 response.addCookie(cookie); 我们debug 可以看到: ![京东Java架构师讲解购物车的原理及Java实现](http://p1.pstatp.com/large/pgc-image/1526729724258ef7cb1f337) 这里已经将对象购物车对象buyerCart转换成了Json格式. 将商品添加到购物车, 不管是登录还是未登录, 都要先取出Cookie中的购物车, 然后将当前选择的商品追加到购物车中. 然后登录的话 就把Cookie中的购物车清空, 并将购物车的内容添加到Redis中做持久化保存. 如果未登录, 将选择的商品追加到Cookie中. 将购物车追加到Redis中的代码:insertBuyerCartToRedis(这里面包含了判断添加的是否是同款) 1 //保存购物车到Redis中2 public void insertBuyerCartToRedis(BuyerCart buyerCart, String username){3 List items = buyerCart.getItems();4 if (items.size() > 0) {5 //redis中保存的是skuId 为key , amount 为value的Map集合6 Map hash = new HashMap();7 for (BuyerItem item : items) {8 //判断是否有同款9 if (jedis.hexists("buyerCart:"+username, String.valueOf(item.getSku().getId()))) {10 jedis.hincrBy("buyerCart:"+username, String.valueOf(item.getSku().getId()), item.getAmount());11 }else {12 hash.put(String.valueOf(item.getSku().getId()), String.valueOf(item.getAmount()));13 }14 }15 if (hash.size() > 0) {16 jedis.hmset("buyerCart:"+username, hash);17 }18 }19 20 } 判断用户是否登录: String username = sessionProviderService.getAttributterForUsername(RequestUtils.getCSessionId(request, response)); 1 public class RequestUtils {2 3 //获取CSessionID4 public static String getCSessionId(HttpServletRequest request, HttpServletResponse response){5 //1, 从Request中取Cookie6 Cookie[] cookies = request.getCookies();7 //2, 从Cookie数据中遍历查找, 并取CSessionID8 if (null != cookies && cookies.length > 0) {9 for (Cookie cookie : cookies) {10 if ("CSESSIONID".equals(cookie.getName())) {11 //有, 直接返回12 return cookie.getValue();13 }14 }15 }16 //没有, 创建一个CSessionId, 并且放到Cookie再返回浏览器.返回新的CSessionID17 String csessionid = UUID.randomUUID().toString().replaceAll("-", "");18 //并且放到Cookie中19 Cookie cookie = new Cookie("CSESSIONID", csessionid);20 //cookie 每次都带来, 设置路径21 cookie.setPath("/");22 //0:关闭浏览器 销毁cookie. 0:立即消失. >0 存活时间,秒23 cookie.setMaxAge(-1);24 25 return csessionid;26 }27 } 1 //获取2 public String getAttributterForUsername(String jessionId){3 String value = jedis.get(jessionId + ":USER_NAME");4 if(null != value){5 //计算session过期时间是 用户最后一次请求开始计时.6 jedis.expire(jessionId + ":USER_NAME", 60*exp);7 return value;8 }9 return null;10 } 2、购物车展示页面 最后 重定向到购物车展示页: return "redirect:/shopping/toCart"; 这里进入结算页有两种方式: 1) 在商品详情页 点击加入购物车. 2) 直接点击购物车按钮 进入购物车结算页. 下面来看下结算页的代码: 1 @Autowired2 private CartService cartService;3 //去购物车结算, 这里有两个地方可以直达: 1,在商品详情页 中点击加入购物车按钮 2, 直接点击购物车按钮4 @RequestMapping(value="/shopping/toCart")5 public String toCart(Model model, HttpServletRequest request,6 HttpServletResponse response) throws JsonParseException, JsonMappingException, IOException{ 7 //将对象转换成json字符串/json字符串转成对象8 ObjectMapper om = new ObjectMapper();9 om.setSerializationInclusion(Include.NON_NULL);10 BuyerCart buyerCart = null;11 //1,获取Cookie中的购物车12 Cookie[] cookies = request.getCookies();13 if (null != cookies && cookies.length > 0) {14 for (Cookie cookie : cookies) {15 //16 if (Constants.BUYER_CART.equals(cookie.getName())) {17 //购物车 对象 与json字符串互转18 buyerCart = om.readValue(cookie.getValue(), BuyerCart.class);19 break;20 }21 }22 }23 24 //判断是否登录25 String username = sessionProviderService.getAttributterForUsername(RequestUtils.getCSessionId(request, response));26 if (null != username) {27 //登录了28 //2, 购物车 有东西, 则将购物车的东西保存到Redis中29 if (null == buyerCart) {30 cartService.insertBuyerCartToRedis(buyerCart, username);31 //清空Cookie 设置存活时间为0, 立马销毁32 Cookie cookie = new Cookie(Constants.BUYER_CART, null);33 cookie.setPath("/");34 cookie.setMaxAge(-0);35 response.addCookie(cookie);36 }37 //3, 取出Redis中的购物车38 buyerCart = cartService.selectBuyerCartFromRedis(username);39 }40 41 42 //4, 没有 则创建购物车43 if (null == buyerCart) {44 buyerCart = new BuyerCart();45 }46 47 //5, 将购物车装满, 前面只是将skuId装进购物车, 这里还需要查出sku详情48 List items = buyerCart.getItems();49 if(items.size() > 0){50 //只有购物车中有购物项, 才可以将sku相关信息加入到购物项中51 for (BuyerItem buyerItem : items) {52 buyerItem.setSku(cartService.selectSkuById(buyerItem.getSku().getId()));53 }54 }55 56 //5,上面已经将购物车装满了, 这里直接回显页面57 model.addAttribute("buyerCart", buyerCart);58 59 //跳转购物页面60 return "cart";61 } 这里 就是 购物车详情展示页面, 这里需要注意, 如果是同一件商品连续添加, 是需要合并的. 购物车详情展示页面就包括两大块, 1) 商品详情 2)总计(商品总额,运费) 其中1)商品详情又包括 商品尺码,商品颜色, 商品购买数量, 是否有货. ![京东Java架构师讲解购物车的原理及Java实现](http://p1.pstatp.com/large/pgc-image/152672972427926245d2e8f) 取出Redis中的购物车: buyerCart = cartService.selectBuyerCartFromRedis(username); 1 //取出Redis中购物车2 public BuyerCart selectBuyerCartFromRedis(String username){3 BuyerCart buyerCart = new BuyerCart();4 //获取所有商品, redis中保存的是skuId 为key , amount 为value的Map集合5 Map hgetAll = jedis.hgetAll("buyerCart:"+username);6 Set> entrySet = hgetAll.entrySet();7 for (Entry entry : entrySet) {8 //entry.getKey(): skuId9 Sku sku = new Sku();10 sku.setId(Long.parseLong(entry.getKey()));11 BuyerItem buyerItem = new BuyerItem();12 buyerItem.setSku(sku);13 //entry.getValue(): amount14 buyerItem.setAmount(Integer.parseInt(entry.getValue()));15 //添加到购物车中16 buyerCart.addItem(buyerItem);17 }18 19 return buyerCart;20 } 将购物车装满, 前面只是将skuId装进购物车, 这里还需要查出sku详情: List items = buyerCart.getItems(); buyerItem.setSku(cartService.selectSkuById(buyerItem.getSku().getId())); 1 //向购物车中的购物项 添加相应的数据, 通过skuId 查询sku对象, 颜色对象, 商品对象2 public Sku selectSkuById(Long skuId){3 Sku sku = skuDao.selectByPrimaryKey(skuId);4 //颜色5 sku.setColor(colorDao.selectByPrimaryKey(sku.getColorId()));6 //添加商品信息7 sku.setProduct(productDao.selectByPrimaryKey(sku.getProductId()));8 return sku;9 } 接着就返回"cart.jsp", 这个就是购物车详情展示页面了. 3、去结算页面 到了这里就说明用户必须要 登录, 而且购物车中必须要有商品. 所以这里我么你需要利用springmvc的过滤功能, 用户点击结算的时候必须要先登录, 如果没有登录的话就提示用户需要登录. 1 //去结算2 @RequestMapping(value="/buyer/trueBuy")3 public String trueBuy(String[] skuIds, Model model, HttpServletRequest request, HttpServletResponse response){4 //1, 购物车必须有商品, 5 //取出用户名 再取出购物车6 String username = sessionProviderService.getAttributterForUsername(RequestUtils.getCSessionId(request, response));7 //取出所有购物车8 BuyerCart buyerCart = cartService.selectBuyerCartFromRedisBySkuIds(skuIds, username);9 List items = buyerCart.getItems();10 if (items.size() > 0) {11 //购物车中有商品12 //判断所勾选的商品是否都有货, 如果有一件无货, 那么就刷新页面.13 Boolean flag = true;14 //2, 购物车中商品必须有库存 且购买大于库存数量时视为无货. 提示: 购物车原页面不动. 有货改为无货, 加红提醒.15 for (BuyerItem buyerItem : items) {16 //装满购物车的购物项, 当前购物项只有skuId这一个东西, 我们还需要购物项的数量去判断是否有货17 buyerItem.setSku(cartService.selectSkuById(buyerItem.getSku().getId()));18 //校验库存19 if (buyerItem.getAmount() > buyerItem.getSku().getStock()) {20 //无货21 buyerItem.setIsHave(false);22 flag = false;23 }24 if (!flag) {25 //无货, 原页面不动, 有货改成无货, 刷新页面.26 model.addAttribute("buyerCart", buyerCart);27 return "cart";28 }29 }30 }else {31 //购物车没有商品32 //没有商品: 1>原购物车页面刷新(购物车页面提示没有商品)33 return "redirect:/shopping/toCart";34 }35 36 37 //3, 正常进入下一个页面38 return "order";39 } 取出 所指定的购物车, 因为我们结算之前在购物车详情页面会勾选 我们 需要购买的商品, 所以这里是根据所勾选的商品去结算的. BuyerCart buyerCart = cartService.selectBuyerCartFromRedisBySkuIds(skuIds, username); 从购物车中取出指定商品: 1 //从购物车中取出指定商品2 public BuyerCart selectBuyerCartFromRedisBySkuIds(String[] skuIds, String username){3 BuyerCart buyerCart = new BuyerCart();4 //获取所有商品, redis中保存的是skuId 为key , amount 为value的Map集合5 Map hgetAll = jedis.hgetAll("buyerCart:"+username);6 if (null != hgetAll && hgetAll.size() > 0) {7 Set> entrySet = hgetAll.entrySet();8 for (Entry entry : entrySet) {9 for (String skuId : skuIds) {10 if (skuId.equals(entry.getKey())) {11 //entry.getKey(): skuId12 Sku sku = new Sku();13 sku.setId(Long.parseLong(entry.getKey()));14 BuyerItem buyerItem = new BuyerItem();15 buyerItem.setSku(sku);16 //entry.getValue(): amount17 buyerItem.setAmount(Integer.parseInt(entry.getValue()));18 //添加到购物车中19 buyerCart.addItem(buyerItem);20 }21 }22 }23 }24 25 return buyerCart;26 } 1) 当我们购买的商品只要有一件是无货的状态, 那么刷新购物车详情页面, 回显无货的商品状态. 2)当购物车中午商品时, 刷新当前页面. 购物车就这么多东西, 可能有讲解不到或者错误的地方, 欢迎大家指出来.如果对你有帮助的话也请点个赞支持一下,谢谢~ 原文:[京东Java架构师讲解购物车的原理及Java实现](https://www.toutiao.com/a6557254324370539011/?tt_from=weixin&utm_campaign=client_share×tamp=1526746168&app=news_article_lite&utm_source=weixin&iid=31395168747&utm_medium=toutiao_android&wxshare_count=1) * * * * * last update:2018-5-23 23:11:33
';

生成唯一订单号

最后更新于:2022-04-02 04:33:46

## 生成唯一订单号 确保生产唯一的订单号,字段建立唯一索引。 * * * * * ### 一般方案 ```php for ($i = 0; $i < $data['createnum']; $i++) { do { // $code = strtolower(get_rand_str(8, 0, 1)); // 获取随机8位字符串 // 零填充导致Excel显示数字不正确 $code = zerofill(mt_rand(0, 99999999), 8, 9); $password = mt_rand(1000, 9999); // 获取随机4位数字 $check_exist = M('exchange_list')->where(array('code' => $code))->find(); } while ($check_exist); $add['code'] = $code; $add['password'] = $password; $newId = M('exchange_list')->add($add); if (!$newId) { exit('重复!'); } else { } } ``` https://coding.net/u/xiasf/p/gj-shop/git/blob/master/Application/Admin/Controller/ExchangeController.class.php 这里只知道插入失败就表示重复,其实这是不准确的,有可能是别的原因导致插入失败呢,不一定是重复原因,除非能得到sql失败的错误码,每种情况都有一个系统确定的错误码,比如重复有一个错误码。 重复不能失败,而是要重试。 * * * * * ### 终极方案 ```php /* 插入订单表 */ $error_no = 0; do { $order['order_sn'] = get_order_sn(); //获取新订单号 $GLOBALS['db']->autoExecute($GLOBALS['ecs']->table('order_info'), $order, 'INSERT'); $error_no = $GLOBALS['db']->errno(); if ($error_no > 0 && $error_no != 1062) { die($GLOBALS['db']->errorMsg()); } } while ($error_no == 1062); //如果是订单号重复则重新提交数据 $new_order_id = $db->insert_id(); $order['order_id'] = $new_order_id; ``` 这里更加完善就是能够识别到重复插入(唯一索引限制)报错的错误码:1062 不过这个错误码可能每个数据库系统都不一样。 这种方式应该是最完美安全的终极方案了。 [电商网站订单号怎么生成呢?我目前用订单ID来做感觉不行,从1开始这样太不好看了,像淘宝那样的订单ID怎么生成的呢? - apple的回答 - SegmentFault 思否](https://segmentfault.com/q/1010000004104517/a-1020000014209988) * * * * * ```php // tpDb操作出错要抛出异常,只能这么处理了。 // 如果这步骤出错,基本就是重复的问题,那么一直重试,再次生成邀请码更新进去,直至成功 do { try { // 生成唯一邀请码 Db::name('user')->where('id', $newId)->update([ 'invitation_code' => createInvitationCode(), ]); $upStatus = 1; } catch (\Exception $e) { trace('createInvitationCode: [' . $e->getCode() . '] ' . $e->getMessage(), 'error'); $upStatus = 0; } } while ($upStatus == 0); ``` * * * * * ### 扩展 [分布式ID生成器 | 架构师之路](http://mp.weixin.qq.com/s/AHRCYOjnXAgcy2j6vziukQ) [如何做一个靠谱的发号器](https://tech.youzan.com/id_generator/) * * * * * last update:2018-4-10 04:45:12
';

优惠券

最后更新于:2022-04-02 04:33:43

## 优惠券 :-: ![](http://pic.qiantucdn.com/58pic/27/34/34/74458PICwIf_1024.jpg!/fw/580/watermark/url/L3dhdGVybWFyay12MS4zLnBuZw==/align/center) 优惠券,代金券,各种红包,积分,其实就是免费送给用户的财产,和用户余额没有本质区别(不能提现,只能消费,毕竟发放它就是为了刺激消费的),只不过有使用条件而已(比如满多少可用,不设找零,不退款等等),不能像余额那样提现。这些券之类的资产看似是免费补贴给用户的,其实都是一种营销,补贴就是营销的成本。 所以问题来了,这个成本谁出,商家还是平台。很简单,**当然是这个营销对谁有益,则由谁来承担。** 基于此,所以有一下规则: 平台优惠券,所有店铺可用;商家优惠券,商家发放,只在当前店铺消费时可用。 * * * * * ### 结算问题 **平台优惠券不参与结算**,所以商家并没有收到核销的优惠券来和平台抵扣的说法,比如订单实付100,用户使用平台优惠券抵扣了50,但是结算时还是看实付100的,并不会管这个抵扣的,商家还是要结算100走的。但是如果用户是使用的商家优惠券,那么结算时只能结算给商家50了,而不是实付的100,另外一种做法是,商家发放优惠券时就先把商家用于补贴的钱扣掉,比如商家往活动账户充值100,就可以发两张50的优惠券了。(这种方式商家发放优惠券需要先充值,一般在电商中不适用) * * * * * ### 下单支付 #### 联合支付 shopnc采用,余额、线上联合支付的方式。比如订单实付100,余额只有50,那么扣50余额(下单,冻结余额为不可用,字段 `freeze_predeposit` ),剩下50由在线渠道支付。 联合支付方式,这也是最便捷的方式,即一个订单,可能由多部分共同组合支付完成,比如线上,余额,积分,白条,优惠券等等组合支付。(每个订单需要支付记录 `pay_order` 来记录这些) 这样虽然便捷,但是也有缺点,当退货,取消订单时的退款比较麻烦,当然优惠券是不能退款,这个在优惠券的规则里面有规定。 通常一个商家生成一个订单,平台优惠券是可以跨订单的,所以得平分到每个订单,当然并不是用 `除法` ,而是用 `减除法` 计算。否则可能有小数除不尽的情况。 ~~~ 余额支付和在线支付联合支付 先冻结余额部分,在线如果成功就扣,不成功那就要恢复余额。 ~~~ #### 多笔订单合并支付 ![这里也可以多选合并支付](http://cdn.aipin100.cn/18-3-30/92942662.jpg) 合并|2笔订单 ![多订单提交直接合并支付](http://cdn.aipin100.cn/18-3-30/78936861.jpg) (所以设计时还是要有一个支付订单表,`pay_order`:记录所有的发起的支付请求,为每个支付请求生成一个支付ID,通过`pay_order`与`order`以及其他业务关联,而不是直接用订单表来记录支付以及支付结果的回调) * * * * * #### 分期支付 白条支付额度不够,仅支付部分,剩下的可以选择银行卡支付。 银行卡支付 + 白条支付 ![](http://cdn.aipin100.cn/18-4-3/50018462.jpg) https://pcashier.jd.com/success/payResult.action?amount=347.37&orderType=0&passKey=A7A5CE9F16DBD6276FF64A1DC80AC8EB1875BC4368016D4FD084AE7517BE23D8&companyId=6&toType=10&cashierId=1&orderId=73794632808 * * * * * ### 转转的组合支付(高效灵活的支付) 1 ![](http://cdn.aipin100.cn/18-3-4/2952719.jpg) 2 ![](http://cdn.aipin100.cn/18-3-4/94250787.jpg) 3 ![](http://cdn.aipin100.cn/18-3-4/86235073.jpg) 4 ![](http://cdn.aipin100.cn/18-3-4/81259862.jpg) 5 ![](http://cdn.aipin100.cn/18-3-4/59144510.jpg) 6 ![](http://cdn.aipin100.cn/18-3-4/46499714.jpg) 7 ![](http://cdn.aipin100.cn/18-3-4/81385444.jpg) 8 ![](http://cdn.aipin100.cn/18-3-4/44675786.jpg) * * * * * ### 扩展 [电商基本功:被小瞧的促销设计,并没有想得那么简单](http://toutiao.com/group/6527414499681501699/?iid=25315997380&app=news_article_lite×tamp=1519833724&wxshare_count=1&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_android&utm_campaign=client_share) ![](http://cdn.aipin100.cn/6f22980e4740c709f5063f4a109f61ba) > 很多人不明白商家为什么设置内部优惠券,而不直接在店铺里做优惠,可以看看 ---- last update:2018-3-4 17:39:18
';

设计记录

最后更新于:2022-04-02 04:33:41

## 设计记录 [聊聊对账系统的设计方案](https://mp.weixin.qq.com/s/47s0YdRM6u1JNngNe6yghg) ---- ### 库存设计 淘宝可以自动补库存,还是可以设置分批库存,这库存最后一件售完,又显示几百件,估计手机分批库存,让人感觉库存不多的假象,刺激购买。 last update:2018-1-16 03:59:03
';

电商表设计

最后更新于:2022-04-02 04:33:39

## 电商表设计 1. 商品表 2. 商品属性表 3. 商品规格表 4. 商品SKU表 5. 商品组合关联表 6. 优惠活动的表 7. 商品分类表 8. 商品品牌表 9. 商品行业类目表 10. 商品行业模型 * * * * * ### 扩展 [非小型电子商务系统设计经验分享 - 活雷锋 - 博客园](http://www.cnblogs.com/mmmjiang13/archive/2012/07/05/2575538.html#!comments) [从淘宝数据结构来看电子商务中商品属性设计 - 活雷锋 - 博客园](http://www.cnblogs.com/mmmjiang13/archive/2011/04/21/1983079.html) [再从淘宝数据结构来看电子商务中商品属性设计 - 活雷锋 - 博客园](http://www.cnblogs.com/mmmjiang13/archive/2011/08/03/2125640.html) [电商系统设计之商品 (上)](https://mp.weixin.qq.com/s/c23QAz2lGIN4VNRbgUKPsQ) ---- last update:2018-3-1 14:46:25
';

商品属性分析

最后更新于:2022-04-02 04:33:37

';

商品规格分析

最后更新于:2022-04-02 04:33:34

商品规格的设计还需要考虑,当U规格更新,添加,删除时,会对商品,购物车有什么影响。 更新规格和商品时修改了SpecGoodsPrice表。 tpshop更新规格时并没有修改购物车,更新商品时只修改购物车spec_key等于''的数据,也就是删除规格暂时不会影响购物车,购物车还看得到原先的旧的商品数据。 但是当下单时,由于购物车数据有spec_key,由于使用 ~~~ getGoodNum($val['goods_id'], $val['spec_key']); /** * 获取商品库存 * @param type $goods_id 商品id * @param type $key 库存 key */ function getGoodNum($goods_id, $key) { if (!empty($key)) { return M("SpecGoodsPrice")->where("goods_id = $goods_id and `key` = '$key'")->getField('store_count'); } else { return M("Goods")->where("goods_id = $goods_id")->getField('store_count'); } } ~~~ 获取商品,所以SpecGoodsPrice表中没有,那就没有库存了,这个逻辑也很合理。(这种没有库存其实是没有这种规格了商品了,可以检测出来,提示用户商品失效) 规格直接更改可以改变商品,SKU这样有点侵入式的感觉,感觉最好是规格信息变动不会影响线上业务,需要更新商品才会变动其他信息才比较好。 实际上电商这种规格表的设计,每次更新商品,哪怕没有信息变化,就点击保存,都会引起SpecGoodsPrice(相当于产品表SKU表)变化,因为删除了旧数据,重新添加一遍,这样就导致了每次id都会变化,所以这也就是天猫商品销量只能统计某个商品,而不能统计某个SKU了,一个商品的生命周期可能有很多SKU生命周期,这也是一款产品下面有的人买的是上面没有的规格的原因。参考:[为什么在京东的商品详情页每点击一次商品参数就要刷新一次页面?](https://www.zhihu.com/question/27859589) 既然这个SKU id是变化了,那就不能用在业务逻辑中,可以忽略它,商品购买后订单记录指向的是商品的ID,SPU id,然后有一个规格组 套餐: 套餐一…… 这样的字段,这个只是起展示作用的,没有其他意义了。 购物车中其实也是这样的规格信息,起展示作用。 但是tpshop的设计有一点新颖。 那就是规格用了两张表:tb_spec和tb_spec_item 这样就就能得到一个key(spec_key):其实就是spec_item_id 那么真的key_key…… 就能代表 一组规格了,就是spec_id:spec_name 套餐: 套餐一 >[danger] 注意:tpshop将key的排序转变为小到大的排序,这很重要,前台也遵循这个排序,只有这样才能标识唯一的SKU。另外还要注意,key_name没有根据item_key的顺序,不然可能造成SPU下的不同SKU规格顺序不相同。 这涉及到规格项排序问题(规格项顺序就是规格的顺序,如:颜色,尺码,套餐) 而specInput的排序只是让规格值小的放在前面,便于合并单元格好看,应该不影响任何业务逻辑。 另外规格值也要有顺序,便于展示时自由调整。 所以设计到业务相关的顺序应该有规格(名)排序和规格值排序。其他的都没有排序,SKU也没有(只是逻辑数据而已)。 tpshop的设计是: 首先为同类商品建立商品规格。可以建立多个规格,每个规格可以有多个规格项。 然后这组规格就可以被该类型的商品选用了。注意这里是选用,而不是全部非得都要用。 当选择了多个规格的多个规格项之后,就要得出选中的规格的规格项的笛卡尔乘积,乘积的值就是该商品SKU数量,也叫做规格组数量,一组规格的商品是不可再分的完整的商品。 ### 名词解释: 颜色:`红色` `黄色` 尺码: `M码` `L码` `XL码` 套餐: `套餐一` ---- 上面说明了,这件商品规格的信息: 该商品有**3种类型的规格**,**规格名称**分别为:`颜色`,`尺码`,`套餐` 其中颜色规格的规格值有:`红色`,`黄色` SKU就是笛卡尔乘积 T = R * S 这些规格共有六种交集。也就是说有六个SKU。SKU的数量登录规格的笛卡尔积,并且每个SKU都是完整的规格组,规格组由最小单元规格的键值对所组成,规格名 : 规格值。键值对组成了一个规格项。 笛卡尔的每个交集都是唯一的,所以一个SPU下的SKU(规格组)也是唯一的。 一个规格组的键值对的数量就是刚才选中的规格种类的数量,一个SPU下的每个SKU的规格规格项数量是一样的,规格的顺序也都是一样的,只是规格值不同。也就是说“键”都是一样的,只是“值”不同而已。如果不是这样,那就不合法。 每一个规格组都包含某个规格的某个规格项。 `颜色:红色,尺码:M码,套餐:套餐一` **(规格组 SKU)** `颜色:红色` —— **规格项(键值对)** * * * * * ### 商品规格展示(两处) 规格展示有两处,一处是后台,这个要注意,不要忘记了,这儿是选择,并不是类型下的规格都是的,也可以不选的。所以这个规格展示必须从规格表中读出来。 第二处就是商品详情页的规格展示,这个是商品的全部SKU规格,也就是后台所有选中的规格,这个规格要从SKU表中读出来,thshop就是这么做的(纠错,tpshop其实还是从规格表中读出的,只不过用SKU中的数据从规格表中读而已,所以它要特别小心它的规格排序了,它也分别在前端后端做统一转换的了),但是iwebshop就多此一举,在商品表里面有个字段spec_array,他把所有规格sku放在这里存一份了,商品详情页面展示规格就是使用的这个字段的数据。这种方式太冗余了(其实这样也没什么不可,适当的时候反三范式也是好方法,免得每次计算这个数据麻烦),从SKU表中很容易得到所需要的数据。拿到商品所有的SKU,然后处理下数据就可以了。 这里要注意一个问题,那就是规格(名)排序的问题,有的算法需要考虑“顺序”一致的问题,请注意。 ### 扩展 tpshop的笛卡尔乘积实在服务端php计算的,采用的设计思想是通过将多个数组不断的转换为两个数组来进行计算,因为只有两个数组时才可以很简单的计算出笛卡尔乘积。 ~~~ /** * 多个数组的笛卡尔积 * * @param unknown_type $data */ function combineDika() { $data = func_get_args(); $data = current($data); $cnt = count($data); $result = array(); $arr1 = array_shift($data); foreach ($arr1 as $key => $item) { $result[] = array($item); } foreach ($data as $key => $item) { $result = combineArray($result, $item); } return $result; } /** * 两个数组的笛卡尔积 * @param unknown_type $arr1 * @param unknown_type $arr2 */ function combineArray($arr1, $arr2) { $result = array(); foreach ($arr1 as $item1) { foreach ($arr2 as $item2) { $temp = $item1; $temp[] = $item2; $result[] = $temp; } } return $result; } ~~~ 不过感觉这两个函数没有设计好,功能有融合,因为第二个函数的第一个参数只能是二维数组才可以。显然这一步又在第一个函数里面做了。 ### 参考: - [笛卡尔乘积](http://baike.baidu.com/link?url=ZC7bEfQVmex1SrG5U8LMZU1MAcxfSTItoKk17H3nVv0WcbvzKgiqDNHUHxOUnc_DWsBZV5cLUOhRHbA573ikZCLuwq4PAieVKYoNXx-83YafC7K3VtST34goTIuOOL03nHkaB1s1_n1HONPQU8QWtq2xiTylvbxaxEPmVyPTCzQbtvrYh2Lk74ld62BRh_ExhAzDHnen4JDnHn6B8bfMF4EIWWbM26z8XEPuGw2XbBW) - [js实现的笛卡尔乘积-商品发布](http://www.cnblogs.com/xumanbu/p/4552290.html) - [js 笛卡尔积算法与多重数组笛卡尔积的例子](http://blog.csdn.net/a473554142/article/details/48970225) - [淘宝、京东 多规格组合商品 的数据库设计](https://segmentfault.com/q/1010000005955316) - [非小型电子商务系统设计经验分享](http://www.cnblogs.com/mmmjiang13/archive/2012/07/05/2575538.html) - [再从淘宝数据结构来看电子商务中商品属性设计](http://www.cnblogs.com/mmmjiang13/archive/2011/08/03/2125640.html) ### 其他 商品类型对应的规格和属性,这样的方式跟淘宝一样,其实不一定要这样,这很适用于相对标准化的商品,不太灵活,比如商品不能单独随意的添加规格和属性,比如外卖餐品的大份,小份,这只需要一个简单的规格就可以了,而且不是每个品类都有这样的规格,这样能够随意自定义就灵活多了。 还有需要考虑如果修改类型的规格,规格值会对给类型的商品带来什么影响呢?这些都需要考虑的。 还有商品变动,会对用户的购物车产生什么影响,等等要素。
';