• 在学习中我们如何快速准确的读懂单片机程序?

    在学习中我们如何快速准确的读懂单片机程序?

    在现实中,我相信有很多刚刚接触单片机的同学,简直是无从下手,打开一个程序,更会被复杂的结构和密密麻麻的代码吓到,产生退缩的想法,这篇文章带你了解一下单片机程序。 我对单片机的总结:“单片机其实就是一个芯片,内部有若干寄存器,外部有若干引脚,我们可以通过程序控制内部的寄存器使得引脚与外部世界保持联系!”就这几句话,道出了单片机的真谛!有没有感觉到单片机是多么的简单! 1.单片机程序执行流程 这是我们首先必须要知道的。单片机程序一般就有两种,一种是汇编程序,一种是c语言程序。这里我们讲c语言程序。 单片机程序都有一个包含主函数的文件,包含主函数的文件都有一个统一的结构,如下所示: #include "xxx.h" int main() // 这是主函数的函数名 { ......; // 若干条语句 ......; while(1) // while括号中是1,说明程序进入后将在while里面无线循环,不会出来了,不懂的去看c语言基础之while篇 { ......; // 若干条语句 ......; } } 重点:单片机一上电,从主函数main的第一条语句开始执行,是一条语句接着一条语句从上而下执行,直到进入while后,再从while的第一条语句执行到最后一条语句,由于是死循环,会再从while的第一条语句执行到最后一条语句,如此反复执行,永不停止!直到断电! 这些语句当中,有些是函数的调用,遇到函数的调用,进入到函数,再从函数的第一条语句执行到最后一条语句,然后跳出函数,再从刚才主函数中那条函数的下一条语句开始执行。如果实在搞不明白函数是怎么一回事,你可以用函数里面的所有语句代替函数在主函数中的位置。例如: #include "LPC11XX.H" #define LED1_ON LPC_GPIO1->DATA &= ~(1<<0) #define LED1_OFF LPC_GPIO1->DATA |= (1<<0) #define LED2_ON LPC_GPIO1->DATA &= ~(1<<1) #define LED2_OFF LPC_GPIO1->DATA |= (1<<1) /***********************************/ /* 延时函数 */ /***********************************/ void delay() { uint16_t i,j; for(i=0;i<5000;i++) for(j=0;j<200;j++); } /***********************************/ /* LED初始化函数 */ /***********************************/ void led_init() { LPC_SYSCON->SYSAHBCLKCTRL |= (1<<16); LPC_IOCON->R_PIO1_0 &= ~0x07; LPC_IOCON->R_PIO1_0 |= 0x01; LPC_IOCON->R_PIO1_1 &= ~0x07; LPC_IOCON->R_PIO1_1 |= 0x01; LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<16); LPC_GPIO1->DIR |= (1<<0); LPC_GPIO1->DATA |= (1<<0); LPC_GPIO1->DIR |= (1<<1); LPC_GPIO1->DATA |= (1<<1); } /***********************************/ /* 主函数 */ /***********************************/ int main() { led_init(); while(1) { delay(); LED1_ON; LED2_OFF; delay(); LED1_OFF; LED2_ON; } } 上面这个例子中,单片机一上电,会执行主函数的第一条语句,也就是led_init(),这个是一个函数的调用语句,程序会从led_init函数中的第一条语句开始执行,直到执行完最后一条语句后,回到主函数,进入while,从while的第一条语句delay()开始执行,delay()又是一个函数,程序会从delay()的第一条语句开始执行,delay()函数中有两个for循环,执行完for循环后,就跳出delay()函数,执行LED1_ON,由于LED1_ON是个用#define定义的宏定义,由c语言基础知识之#define宏定义篇,我们知道,LED1_ON就是LPC_GPIO1->DATA &= ~(1<<0),如此继续执行下去……。 如果不用define宏定义,也不用函数,上面的例子就可以写为如下形式: #include "LPC11XX.H" /***********************************/ /* 主函数 */ /***********************************/ int main() { //LED初始化 LPC_SYSCON->SYSAHBCLKCTRL |= (1<<16); LPC_IOCON->R_PIO1_0 &= ~0x07; LPC_IOCON->R_PIO1_0 |= 0x01; LPC_IOCON->R_PIO1_1 &= ~0x07; LPC_IOCON->R_PIO1_1 |= 0x01; LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<16); LPC_GPIO1->DIR |= (1<<0); LPC_GPIO1->DATA |= (1<<0); LPC_GPIO1->DIR |= (1<<1); LPC_GPIO1->DATA |= (1<<1); while(1) { for(i=0;i<5000;i++) for(j=0;j<200;j++); LPC_GPIO1->DATA &= ~(1<<0); LPC_GPIO1->DATA |= (1<<1); for(i=0;i<5000;i++) for(j=0;j<200;j++); LPC_GPIO1->DATA |= (1<<0); LPC_GPIO1->DATA &= ~(1<<1); } } 有没有发现,第二种表示方法,虽然不涉及函数和宏定义了,对于c语言掌握不是很好的人来说,看的比较爽。如果你掌握了c语言的这些宏定义和函数的小技巧,第一种表示方法是不是更有利于阅读程序的功能呢? 2.读懂程序需要c语言基础知识,当然,也可以边看程序,边学习c语言基础知识。 3.读懂程序需要会看单片机的寄存器定义,在程序中,大都是在给单片机的寄存器进行配置或是获取单片机寄存器的数据。看哪种单片机程序,就要学会看哪种单片机的寄存器定义。知道了寄存器的定义,就知道如何配置寄存器或是获取的寄存器数据代表的意义了。 例如我们要看LPC1114的程序,那么LPC1114的用户手册是必须要打开的。例如LPC_SYSCON->SYSAHBCLKCTRL |= (1<<16);这条语句,就是在给SYSCON模块中的SYSAHBCLKCTRL寄存器进行配置,所以我们要找到这个寄存器的定义。首先,打开用户手册,找到SYSCON这一章,然后找到寄存器描述这一节,就可以找到这个寄存器的定义了。至于(|=(1<<16))这些,都是写基本的逻辑运算,也是些c语言的基础知识而已。例如(|=(1<<16)) 这个就是把1左移16个位,然后把左移后的数据与SYSAHBCLKCTRL寄存器进行或运算,运算后的结果再放入SYSAHBCLKCTRL寄存器当中。1左移16个位,就是bit16为1,其它位为0。与寄存器SYSAHBCLKCTRL进行或运算,我们不管这个寄存器原来的值是多少,我们现在只知道,1或任何数,都等于1;0或任何数,都等于任何数。所以,1左移16位后,再与寄存器进行或运算,实际上是把寄存器的bit16置1,剩下的位原来是多少,还是多少。 总结一句话,学习单片机主要是把程序里面的“或”“和”“进制转换”搞清楚,就很容易搞懂单片机程序了。

    单片机 单片机 程序

  • 新手在做单片机时是如何快速搞通NRF24L01通信的

    新手在做单片机时是如何快速搞通NRF24L01通信的

    我相信很多同学在刚开始学习单片机的时候,在做NRF24L01通信时会遇到很多棘手的问题,有时候一个星期也搞不明白,还有可能越搞越不懂,越搞越不会,最后无奈只好想放弃。今天在这里给大家讲一下新手是如何快速搞通的。 其实NRF24L01是一个非常容易操控的东西(读者:“站着说话不腰疼,会了当然不难”。瑞生:“真的很简单,请看下文”)。 大部分人使用的NRF24L01模块都如上图所示,上面模块的原理图如下: 看不清楚?去datasheet上去看。这里唠叨几句,很多人懒的要命,连datasheet也没有看过,就到处求爷爷求奶奶的帮他搞NRF24L01。还有一部分人,“我的datasheet上怎么没有?” “亲,能给个datasheet吗?”。datasheet是一个免费的东西,你要看哪个芯片的datasheet,去哪个芯片的官网去下载就可以了。瑞生说:你连芯片的datasheet也拿不到,我劝你还是改行吧! 为了节省大家的时间,这里放出NRF24L01的datasheet(^_^),点击下载:nRF24L01 回到正题,大家看NRF24L01模块的原理图,实际上就是一个芯片,连了一些必要的电容电阻电感和一个天线,然后留出了和单片机通信的SPI口和IRQ中断引脚。 NRF24L01,任何单片机可以驱动,带硬件SPI口的单片机,可以配置好SPI外设以后驱动,没有硬件SPI口的单片机,可以用IO口模拟SPI时序通信。 重点:NRF24L01是一个数字芯片,内部有若干寄存器,什么数据寄存器、配置寄存器、状态寄存器等。单片机通过SPI口,首先配置好NRF24L01的配置寄存器,诸如频道,通道,地址,接收还是发送模式等等。然后分两种情况,一、如果配置为了发送模式,就可以发送数据了,发送完数据以后,IRQ引脚会拉低,所以观察IRQ引脚就可以知道有没有发送成功;二、如果配置为了接收模式,就需要不断的观察IRQ引脚,IRQ引脚正常是高电平,如果接收到数据,就会变成低电平,所以观察这个引脚就知道有没有接收到数据。 新手在做NRF24L01的通信程序时,最好拿两个相同的单片机,做相同的程序(除了一个配置未发送,一个配置为接收)。 重中之重:首先要确保单片机和NRF24L01能够正常的SPI通信,这个就好比你和一个阿拉伯人说话,你告它先去家乐福买瓶牛奶,再去沃尔玛买个鸡腿,然后….,但是如果阿拉伯人根本听不懂你说话,那么…就没有然后了。你用单片机给NRF24L01写配置寄存器,输入地址,输入频道…稍等,你配置了半天,NRF24L01的寄存器真的如你所想配置好了吗?不确定?这就需要验证。验证方法?太容易了,找一个可读可写的寄存器,你先写进去,然后再读出来,如果数据一样,那么你的SPI通信就正常,你大可放心的配置了,如果读出来的数据和写进去的数据不一样,恭喜你,你再和不懂汉语的阿拉伯人说了半天废话,你还是先搞通SPI再说吧。这里我给你一个寄存器,就是地址寄存器,可读可写,所以你可以用地址寄存器验证一个SPI通信是否正常,下面是我写的验证函数: /********************************************/ /* 函数功能:检测24L01是否存在 */ /* 返回值; 0 存在 */ /* 1 不存在 */ /********************************************/ uchar NRF24L01_Check(void) { uchar check_in_buf[5]={0x11,0x22,0x33,0x44,0x55}; uchar check_out_buf[5]={0x00}; NRF_CE=0; NRF24L01_Write_Buf(WRITE_REG+TX_ADDR, check_in_buf, 5); NRF24L01_Read_Buf(READ_REG+TX_ADDR, check_out_buf, 5); if((check_out_buf[0] == 0x11)&&\ (check_out_buf[1] == 0x22)&&\ (check_out_buf[2] == 0x33)&&\ (check_out_buf[3] == 0x44)&&\ (check_out_buf[4] == 0x55))return 0; else return 1; } 如果你不确保你的SPI通信正常,请先确定以后再进行下面的内容,否则就可能要对牛弹琴了。 收发数据的程序,要先判断“数据是否发送出去了”和“是否接收到了”,而不要直接判断发送和接收的数据是否一致。 例如:接收模块,if(IRQ==0)点亮LED1,当程序执行后,当你看到LED1亮了,哇,接收到了。 当你确定接收到数据以后,你再判断接收到的数据和你发送的数据是否一致。 不难看出,以后我们在做程序的时候一定要循序渐进,不要一步求成。我们要相信一步一步的做,最后一定会完成的。

    单片机 NRF24L01通信 单片机

  • 学习单片机之前一定要先学会看懂数字世界

    学习单片机之前一定要先学会看懂数字世界

    如果我们在做单片机无线通信的时候,没有很好地理解“数字”的概念,我们就会遇到很多的问题,例如:可以传送文字吗?,可以传送声音和图片吗?......这些问题虽然听上去让人觉得很奇葩,但是正是这样的问题更反映出在学单片机之前,我们并没有做好功课。 数字与模拟 自从有了计算机,我们的生活就和数字息息相关了。 “数字”和“模拟”和的感性认识,最直接的就是电脑显示器的接口:VGA接口和DVI接口。VGA接口就是模拟接口,DVI接口就是数字接口。 (左边是DVI数字接口,右边是VGA模拟接口) 我们先来理解一下“数字”与“模拟”的区别。 在一个5V的系统中,“模拟”电压可以是0~5V中的任何一个值,例如2.36V,而“数字”电压,只有0V和5V两种,一般情况下,我们把0V称作低电平(0),把5V称作高电平(1)。 在数字世界中,只有0和1 我们每天使用的电脑就是一个数字器件,各种声音、图片、视频、文字等数据在电脑上存储,最根本都是01组成的。 这里就不得不提到一个我们学习单片机必须要掌握的两个概念:位和字节。 我们刚才说的01就是位,也就是说,一个位不是0就是1,这就是刚才说的那些声音、图片等最终的存储方法。 8个位就是一个字节。字节就是为了计算方便和显示方便而故意搞出来的一个东西。由此还涉及到了BCD码、二进制、十六进制、十进制等概念和算法。 回到最初的那些奇葩问题 还是举栗子吧,像单片机这种实践课程,有了栗子才好理解。 英文传输:比如传输“hello”,hello每个字母对应的十六进制字节为:0x68 0x65 0x6c 0x6c 0x6F,那么,我们只需要把这几个字节传输过去,在那边再进行ascii与字母的转换,即可显示hello。 中文传输:比如传输“老顽童”,1个汉字对应的2个十六进制码,我们一般用gbk码。“老顽童”对应的gbk码字节为:0xc0 0xcf 0xcd 0xe7 0xcd 0xaf。我们只需要把这几个字节传输出去,在那边再进行gbk与十六进制的转换即可。 声音、图片、视频,文件都比较大,它们是由很多个字节组成的,并且按照一定的格式存储。例如前面的英文用的而是ascii码,中文用的是gbk码,这些都被称作“协议”。“协议”就是用来编码和解码的。声音也有很多“协议”,比如mp3格式,wma格式等等。比如图片有jpg,png,bmp等格式,比如视频有avi,mp4等格式。这些格式都有官方统一的协议规定,最终都是字节的排列方式不同。 所以,不管传输什么,都是在传输字节。我们一般以十六进制形式写出来一个字节,其实一个字节就是由8个位组成的。 关键的问题,就是在传输之前,你得知道你要传输的东西的协议,然后借助各种软件把这些要传输的东西转换成字节,然后就可以发送了。 介绍到这里,大家应该有了基本了解,希望大家在学习单片机的时候把基础知识把握好。

    单片机 数字世界 单片机

  • 选择Kong作为你的API网关

    Kong(https://github.com/Kong/kong)是一个云原生,高效,可扩展的分布式 API 网关。自 2015 年在 github 开源后,广泛受到关注,目前已收获 1.68w+ 的 star,其核心价值在于高性能和可扩展性。 为什么需要 API 网关 在微服务架构之下,服务被拆的非常零散,降低了耦合度的同时也给服务的统一管理增加了难度。如上图左所示,在旧的服务治理体系之下,鉴权,限流,日志,监控等通用功能需要在每个服务中单独实现,这使得系统维护者没有一个全局的视图来统一管理这些功能。API 网关致力于解决的问题便是为微服务纳管这些通用的功能,在此基础上提高系统的可扩展性。如右图所示,微服务搭配上 API 网关,可以使得服务本身更专注于自己的领域,很好地对服务调用者和服务提供者做了隔离。 为什么是 Kong SpringCloud 玩家肯定都听说过 Zuul 这个路由组件,包括 Zuul2 和 Springcloud Gateway 等框架,在国内的知名度都不低。没错,我称呼这些为组件 Or 框架,而 Kong 则更衬的上产品这个词。在此我们可以简单对比下 Zuul 和 Kong。 举例而言,如果选择使用 Zuul,当需要为应用添加限流功能,由于 Zuul 只提供了基本的路由功能,开发者需要自己研发 Zuul Filter,可能你觉得一个功能还并不麻烦,但如果在此基础上对 Zuul 提出更多的要求,很遗憾,Zuul 使用者需要自行承担这些复杂性。而对于 Kong 来说,限流功能就是一个插件,只需要简单的配置,即可开箱即用。 Kong 的插件机制是其高可扩展性的根源,Kong 可以很方便地为路由和服务提供各种插件,网关所需要的基本特性,Kong 都如数支持: 云原生: 与平台无关,Kong可以从裸机运行到Kubernetes 动态路由:Kong 的背后是 OpenResty+Lua,所以从 OpenResty 继承了动态路由的特性 熔断 健康检查 日志: 可以记录通过 Kong 的 HTTP,TCP,UDP 请求和响应。 鉴权: 权限控制,IP 黑白名单,同样是 OpenResty 的特性 SSL: Setup a Specific SSL Certificate for an underlying service or API. 监控: Kong 提供了实时监控插件 认证: 如数支持 HMAC, JWT, Basic, OAuth2.0 等常用协议 限流 REST API: 通过 Rest API 进行配置管理,从繁琐的配置文件中解放 可用性: 天然支持分布式 高性能: 背靠非阻塞通信的 nginx,性能自不用说 插件机制: 提供众多开箱即用的插件,且有易于扩展的自定义插件接口,用户可以使用 Lua 自行开发插件 上面这些特性中,反复提及了 Kong 背后的 OpenResty,实际上,使用 Kong 之后,Nginx 可以完全摒弃,Kong 的功能是 Nginx 的父集。 而 Zuul 除了基础的路由特性以及其本身和 SpringCloud 结合较为紧密之外,并无任何优势。 Kong 的架构 image-20180712184740981 从技术的角度讲,Kong 可以认为是一个 OpenResty 应用程序。OpenResty 运行在 Nginx 之上,使用 Lua 扩展了 Nginx。Lua 是一种非常容易使用的脚本语言,可以让你在 Nginx 中编写一些逻辑操作。之前我们提到过一个概念 Kong = OpenResty + Nginx + Lua,但想要从全局视角了解 Kong 的工作原理,还是直接看源码比较直接。我们定位到本地的 Kong 文件夹,按照上图中的目录层级来识识 Kong 的庐山真面目。 Kong 文件下包含了全部源码和必要组件,分析他们,我们便得到了 Kong 的架构。0.13.x 是目前 Kong 的最新版本。 从 2 号块中可以看到 nginx.conf ,这其实便是一个标准的 Nginx 目录结构,这也揭示了 Kong 其实就是运行在 Nginx 的基础之上,而进行的二次封装。由 share 文件夹向下展开下一次分析。 share 文件夹中包含了 OpenResty 的相关内容,其实背后就是一堆 Lua 脚本,例如 lapis 包含了数据库操作,Nginx 生命周期,缓存控制等必要的 Lua 脚本,logging 包含了日志相关的 Lua 脚本,resty 包含了 dns,健康检查等相关功能的 Lua 脚本…而其中的 kong 目录值得我们重点分析,他包含了 Kong 的核心对象。 api 和 core 文件夹,封装了 Kong 对 service,route,upstream,target 等核心对象的操作代码(这四个核心对象将会在下面的小节重点介绍),而 plugins 文件夹则是 Kong 高可扩展性的根源,存放了 kong 的诸多扩展功能。 plugins 文件夹包含了上一节提到的 Kong 的诸多插件功能,如权限控制插件,跨域插件,jwt 插件,oauth2 插件…如果需要自定义插件,则需要将代码置于此处。 从上述文件夹浏览下来,大概可以看到它和 Nginx 的相似之处,并在此基础之上借助于 Lua 对自身的功能进行了拓展,除了 nginx.conf 中的配置,和相对固定的文件层级,Kong 还需要连接一个数据库来管理路由配置,服务配置,upstream 配置等信息,是的,由于 Kong 支持动态路由的特性,所以几乎所有动态的配置都不是配置在文件中,而是借助于 Postgres 或者 Cassandra 进行管理。 postgres Kong 对外暴露了 Restful API,最终的配置便是落地在了数据库之中。 Kong 的管理方式 通过文件夹结构的分析,以及数据库中的表结构,我们已经对 Kong 的整体架构有了一个基本的认识,但肯定还存在一个疑问:我会配置 Nginx 来控制路由,但这个 Kong 应当怎么配置才能达到相同的目的呢?莫急,下面来看看 Kong 如何管理配置。 Kong 简单易用的背后,便是因为其所有的操作都是基于 HTTP Restful API 来进行的。 kong端点 其中 8000/8443 分别是 Http 和 Https 的转发端口,等价于 Nginx 默认的 80 端口,而 8001 端口便是默认的管理端口,我们可以通过 HTTP Restful API 来动态管理 Kong 的配置。 一个典型的 Nginx 配置 upstream helloUpstream { server localhost:3000 weight=100; } server { listen 80; location /hello { proxy_pass http://helloUpstream;     } } 如上这个简单的 Nginx 配置,便可以转换为如下的 Http 请求。 对应的 Kong 配置 # 配置 upstream curl -X POST http://localhost:8001/upstreams --data "name=helloUpstream" # 配置 target curl -X POST http://localhost:8001/upstreams/hello/targets --data "target=localhost:3000" --data "weight=100" # 配置 service curl -X POST http://localhost:8001/services --data "name=hello" --data "host=helloUpstream" # 配置 route curl -X POST http://localhost:8001/routes --data "paths[]=/hello" --data "service.id=8695cc65-16c1-43b1-95a1-5d30d0a50409" 这一切都是动态的,无需手动 reload nginx.conf。 我们为 Kong 新增路由信息时涉及到了 upstream,target,service,route 等概念,他们便是 Kong 最最核心的四个对象。(你可能在其他 Kong 的文章中见到了 api 这个对象,在最新版本 0.13 中已经被弃用,api 已经由 service 和 route 替代) 从上面的配置以及他们的字面含义大概能够推测出他们的职责,upstream 是对上游服务器的抽象;target 代表了一个物理服务,是 ip + port 的抽象;service 是抽象层面的服务,他可以直接映射到一个物理服务(host 指向 ip + port),也可以指向一个 upstream 来做到负载均衡;route 是路由的抽象,他负责将实际的 request 映射到 service。 他们的关系如下 upstream 和 target :1 对 n service 和 upstream :1 对 1 或 1 对 0 (service 也可以直接指向具体的 target,相当于不做负载均衡) service 和 route:1 对 n 高可扩展性的背后—插件机制 Kong 的另一大特色便是其插件机制,这也是我认为的 Kong 最优雅的一个设计。 文章开始时我们便提到一点,微服务架构中,网关应当承担所有服务共同需要的那部分功能,这一节我们便来介绍下,Kong 如何添加 jwt 插件,限流插件。 插件(Plugins)装在哪儿?对于部分插件,可能是全局的,影响范围是整个 Kong 服务;大多数插件都是装在 service 或者 route 之上。这使得插件的影响范围非常灵活,我们可能只需要对核心接口进行限流控制,只需要对部分接口进行权限控制,这时候,对特定的 service 和 route 进行定向的配置即可。 为 hello 服务添加50次/秒的限流 curl -X POST http://localhost:8001/services/hello/plugins \ --data "name=rate-limiting" \ --data "config.second=50" 为 hello 服务添加 jwt 插件 curl -X POST http://localhost:8001/services/login/plugins \ --data "name=jwt" 同理,插件也可以安装在 route 之上 curl -X POST http://localhost:8001/routes/{routeId}/plugins \ --data "name=rate-limiting" \ --data "config.second=50" curl -X POST http://localhost:8001/routes/{routeId}/plugins \ --data "name=jwt" 在官方文档中,我们可以获取全部的插件 https://konghq.com/plugins/,部分插件需要收费的企业版才可使用。 kong插件 总结 Kong 是目前市场上相对较为成熟的开源 API 网关产品,无论是性能,扩展性,还是功能特性,都决定了它是一款优秀的产品,对 OpenResty 和 Lua 感兴趣的同学,Kong 也是一个优秀的学习参考对象。基于 OpenResty,可以在现有 Kong 的基础上进行一些扩展,从而实现更复杂的特性,比如我司内部的 ABTest 插件和定制化的认证插件,开发成本都相对较低。Kong 系列的文章将会在以后持续连载。 Kirito 有话说:感谢阅读,为了博主更好地创作,提供更多的原创文章,请大家务必!务必!务必!帮我点击阅读一下明天即将发出来的公众号文章!!!感谢老铁~ 阅读扩展 初识 Kong 之负载均衡 https://www.cnkirito.moe/kong-loadbalance/ Kong 集成 Jwt 插件 https://www.cnkirito.moe/kong-jwt/

    架构师社区 网关 API Kong

  • 秒杀系统设计~亿级用户

    从事电商行业十几年,经历过大大小小的促销活动和秒杀上百次,每次做秒杀瞬时访问量会翻数十倍,甚至数百倍。经历过雪崩那么我们怎么设计秒杀系统,才能保证秒杀系统的高性能和稳定性,同时还要保证日常业务不受影响呢? 先看看秒杀场景特点。秒杀开始前几分钟,大量用户开始进入秒杀商品详情页面,很多人开始频繁刷新秒杀商品详情页,这时秒杀商品详情页访问量会猛增。秒杀开始,大量用户开始抢购,这时创建订单,扣库存压力会显著增大。实际上,秒杀场景基本都是秒杀参与人多,秒杀成功的人却寥寥无几,经常是几十万人或者更多人抢几百个商品库存。 那么我们曾经是怎么设计秒杀系统的呢?主要涉及以下几个方面: 秒杀业务流程上的考虑: 由于参加秒杀的商品售卖价格非常低,基本都是“抢到即赚到”,成功下单后却不付款的情况非常少。所以我们采用下单减库存的方案,下单时扣减库存,然后再进行支付。假如真有个别订单不付款怎么办?没关系,秒杀好活动最主要的目的是吸引流量,个别订单不支付对秒杀活动本身影响不大。况且,没支付剩下的库存还可以做为普通商品继续售卖。不过要注意对机器人和自动脚本的防御,后面会详细介绍。 。如果请求全部打到后端服务,那后端服务的压力会非常大(后端服务要处理业务逻辑,而且还要访问数据库,吞吐量比较低)考虑到秒杀是运营同学提前安排的活动,要秒杀哪些商品、商品价格等信息在秒杀活动开始前已经确定下来,所以我们可以把秒杀商品详情页做成静态页面把商评价等信息全部放在这个,然后把这个静态页面上传到CDN上预热(CDN是内容分发网络,可以简单理解成互联网上的巨大的缓存,用于存,可以显著提高访问速度),用CDN扛流量,样商品详情页的访问请求就不用访问自己的网站(源站)。这样既可以提高访问速度,也没有给网站增加压力,同时也减少了网站带宽压力。 请求拦截: 前端页面,相关按钮点击后置灰,防止重复提交 网关(zuul,nginx)层,为了避免前端恶意请求,比如一些攻击脚本,在网关层要对下单等接口按userID限流,几秒钟只能访问一次。考虑到秒杀场景参与人多,秒杀成功的人极少,我们可以把绝大部分抢购下单请求在网关层直接拒掉,按秒杀失败处理。这样就极大减少了后端服务的压力。 假设秒杀库存是200个,我们可以只放行200个请求到后端服务。要注意,为了尽量避免库存被机器人和自动脚本抢走,200个请求不能在秒杀开始瞬间同时放行,可以分段放行,比如秒杀开始后随机选取100ms内的5个请求放行(这100ms内的其他请求直接拒掉,按秒杀失败处理),之后每隔100ms放行5个请求,4秒钟可以放行完200个请求。分段放行,除了限制了机器人和自动脚本,把请求分散在各个时间段,还进一步缓解了后端服务的压力。 分段放行总时间不能太长,假如每100ms放行1个请求,放行完所有200个请求需要20秒时间,这样用户就会明显感知到下单早的人没秒杀成功,下单晚的人反而秒杀成功了,用户体验会变差。 另外,秒杀过程网关压力会比较大,网关可以做成集群,多节点分摊访问压力。 后端服务设计: 如果秒杀库存只有200,经过网关拦截,再加上采用分段放行的方式,对于后端服务基本没什么压力了,日常的后端服务就完全可以支撑秒杀活动了。不用再做更复杂的设计。不过,假如秒杀库存有几万个,放行的下单请求就有几万个,为了用户体验放行总时间也不能太长,这时后端服务该怎么设计呢? 这时主要压力就在数据库了,扣减库存压力,创建订单压力。 库存可以放到Reids缓存中,来提高扣减库存吞吐能力。对于热点商品的库存可以利用Redis分片存储。 创建订单可以走异步消息队列。后端服务接到下单请求,直接放进消息队列,监听服务取出消息后,先将订单信息写入Redis,每隔100ms或者积攒100条订单,批量写入数据库一次。前端页面下单后定时向后端拉取订单信息,获取到订单信息后跳转到支付页面。用这种批量异步写入数据库的方式大幅减少了数据库写入频次,从而明显降低了订单数据库写入压力。 隔离: 1,业务隔离。从业务上把秒杀和日常的售卖区分开来,把秒杀做为营销活动,要参与秒杀的商品需要提前报名参加活动,这样我们就能提前知道哪些商家哪些商品要参与秒杀,可以根据提报的商品提前生成静态页面并上传到CDN预热,提报的商品库存也需要提前预热,可以将商品库存在活动开始前预热到Redis,避免秒杀开始后大量的缓存穿透。 2,部署隔离。秒杀相关服务和日常服务要分组部署,不能因为秒杀出问题影响日常售卖业务。可以申请单独的秒杀域名,从网络入口层就开始分流。网关也单独部署,秒杀走自己单独的网关,从而避免日常网关受到影响。秒杀可以复用订单,库存,支付等日常服务,只是需要一些小的改造(比如下单流程走消息队列,批量写入订单库,以及在Redis中扣减库存)。 3,数据隔离。为了避免秒杀活动影响到日常售卖业务,Redis缓存需要单独部署,甚至数据库也需要单独部署!数据隔离后,秒杀剩余的库存怎么办?秒杀活动结束后,剩余库存可以归还到日常库存继续做为普通商品售卖。数据隔离后,秒杀订单和日常订单不在相同的数据库,之后的订单查询怎么展示?可以在创建秒杀订单后发消息到消息队列,日常订单服务采取拉的方式消费消息,这时日常订单服务是主动方,可以采用线程池的方式,根据机器的性能来增加或缩小线程池的大小,控制拉取消息的速度,来控制订单数据库的写入压力。 网络: 接口防刷,前面已经提到过,在网关层对下单等接口按userID限流。 在实际访问量超过预估访问量时,整体限流可以起到保护作用,避免系统被压垮。 假如限制同一个用户10分钟能下一次单,一般情况下10分钟内,商品早已经被抢光了,用户也就没有再次下单的机会了。 可以在网关层上面再加一层防火墙或者高防服务,来防御DDos等分布式网络攻击。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    架构师社区 秒杀系统 亿级用户 后端服务

  • GitHub 5W 星:一行命令下载全网视频

    大多数视频网站没有提供下载的功能,如果你想下载 B站、优酷、YouTube、腾讯视频等网站上的优质视频,该怎么办? 如果你去网上搜索,如何下载 B站 视频。会有很多答案,不是让你下载一些不知名的软件就是装各种插件。 有没有其他安全可靠且方便的方法,一键下载全网视频?答案是:有的,今天推荐两个可以一键下载全网视频的 GitHub 项目 01 You-Get 第一个 GitHub 项目可完美解决下载问题,标星 39.2k,一行命令下载全网视频。 支持的国内网站: 如何使用 安装方法一: 02 Windows 用户可以使用 Scoop 安装。直接在 Windows powershell 输入以下命令即可。 scoop install annie 直接下载视频 一段1080p 视频几秒钟就下载完成。 -i 命令:显示视频信息但不下载 根据刚才我们得到的视频信息,我们可以选择下载360p画质的视频: 这个方法非常实用,比如在 B站上面可能会有一些优质的视频,但是过不了多久就会被下降,利用这个功能,你就能批量保存 B站或者其它视频网站的视频。 a nnie -F C:\Users\10550\menu.txt 大功告成,批量下载成功!

    架构师社区 GitHub You-Get

  • 设计RPC接口时,你有考虑过这些吗?

    RPC 框架的讨论一直是各个技术交流群中的热点话题,阿里的 dubbo,新浪微博的 motan,谷歌的 grpc,以及不久前蚂蚁金服开源的 sofa,都是比较出名的 RPC 框架。RPC 框架,或者一部分人习惯称之为服务治理框架,更多的讨论是存在于其技术架构,比如 RPC 的实现原理,RPC 各个分层的意义,具体 RPC 框架的源码分析…但却并没有太多话题和“如何设计 RPC 接口”这样的业务架构相关。 段子 可能很多小公司程序员还是比较关心这个问题的,这篇文章主要分享下一些个人眼中 RPC 接口设计的最佳实践。 初识 RPC 接口设计 由于 RPC 中的术语每个程序员的理解可能不同,所以文章开始,先统一下 RPC 术语,方便后续阐述。 大家都知道共享接口是 RPC 最典型的一个特点,每个服务对外暴露自己的接口,该模块一般称之为 api;外部模块想要实现对该模块的远程调用,则需要依赖其 api;每个服务都需要有一个应用来负责实现自己的 api,一般体现为一个独立的进程,该模块一般称之为 app。 api 和 app 是构建微服务项目的最简单组成部分,如果使用 maven 的多 module 组织代码,则体现为如下的形式。 serviceA 服务 serviceA/pom.xml 定义父 pom 文件 <modules> <module>serviceA-apimodule> <module>serviceA-appmodule> modules> <packaging>pompackaging> <groupId>moe.cnkiritogroupId> <artifactId>serviceAartifactId> <version>1.0.0-SNAPSHOTversion> serviceA/serviceA-api/pom.xml 定义对外暴露的接口,最终会被打成 jar 包供外部服务依赖 <parent> <artifactId>serviceAartifactId> <groupId>moe.cnkiritogroupId> <version>1.0.0-SNAPSHOTversion> parent> <packaging>jarpackaging> <artifactId>serviceA-apiartifactId> serviceA/serviceA-app/pom.xml 定义了服务的实现,一般是 springboot 应用,所以下面的配置文件中,我配置了 springboot 应用打包的插件,最终会被打成 jar 包,作为独立的进程运行。 <parent> <artifactId>serviceAartifactId> <groupId>moe.cnkiritogroupId> <version>1.0.0-SNAPSHOTversion> parent> <packaging>jarpackaging> <artifactId>serviceA-appartifactId> <build> <plugins> <plugin> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-maven-pluginartifactId> plugin> plugins> build> 麻雀虽小,五脏俱全,这样一个微服务模块就实现了。 旧 RPC 接口的痛点 统一好术语,这一节来描述下我曾经遭遇过的 RPC 接口设计的痛点,相信不少人有过相同的遭遇。 查询接口过多。各种 findBy 方法,加上各自的重载,几乎占据了一个接口 80% 的代码量。这也符合一般人的开发习惯,因为页面需要各式各样的数据格式,加上查询条件差异很大,便造成了:一个查询条件,一个方法的尴尬场景。这样会导致另外一个问题,需要使用某个查询方法时,直接新增了方法,但实际上可能这个方法已经出现过了,隐藏在了令人眼花缭乱的方法中。 难以扩展。接口的任何改动,比如新增一个入参,都会导致调用者被迫升级,这也通常是 RPC 设计被诟病的一点,不合理的 RPC 接口设计会放大这个缺点。 升级困难。 在之前的 “初识 RPC 接口设计”一节中,版本管理的粒度是 project,而不是 module,这意味着:api 即使没有发生变化,app 版本演进,也会造成 api 的被迫升级,因为 project 是一个整体。问题又和上一条一样了,api 一旦发生变化,调用者也得被迫升级,牵一发而动全身。 难以测试。接口一多,职责随之变得繁杂,业务场景各异,测试用例难以维护。特别是对于那些有良好习惯编写单元测试的程序员而言,简直是噩梦,用例也得跟着改。 异常设计不合理。在既往的工作经历中曾经有一次会议,就 RPC 调用中的异常设计引发了争议,一派人觉得需要有一个业务 CommonResponse,封装异常,每次调用后,优先判断调用结果是否 success,在进行业务逻辑处理;另一派人觉得这比较麻烦,由于 RPC 框架是可以封装异常调用的,所以应当直接 try catch 异常,不需要进行业务包裹。在没有明确规范时,这两种风格的代码同时存在于项目中,十分难看! 在千米网的三个月中,看了不少最佳实践。加上一次公司内部易永健老师的分享,涉及到了相同的话题,耳濡目染,这些曾经我发觉的痛点也逐渐有了解决之道。 单参数接口 如果你使用过 springcloud ,可能会不适应 http 通信的限制,因为 @RequestBody 只能使用单一的参数,也就意味着,springcloud 构建的微服务架构下,接口天然是单参数的。而 RPC 方法入参的个数在语法层面是不会受到限制的,但如果强制要求入参为单参数,会解决一部分的痛点。 使用 Specification 模式解决查询接口过多的问题 public interface StudentApi{ Student findByName(String name); ListfindAllByName(String name); Student findByNameAndNo(String name,String no); Student findByIdcard(String Idcard); } 如上的多个查询方法目的都是同一个:根据条件查询出 Student,只不过查询条件有所差异。试想一下,Student 对象假设有 10 个属性,最坏的情况下它们的排列组合都可能作为查询条件,这便是查询接口过多的根源。 public interface StudentApi{ Student findBySpec(StudentSpec spec); ListfindListBySpec(StudentListSpec spec); PagefindPageBySpec(StudentPageSpec spec); } 上述接口便是最通用的单参接口,三个方法几乎囊括了 99% 的查询条件。所有的查询条件都被封装在了 StudentSpec,StudentListSpec,StudentPageSpec 之中,分别满足了单对象查询,批量查询,分页查询的需求。如果你了解领域驱动设计,会发现这里借鉴了其中 Specification 模式的思想。 单参数易于做统一管理 public interface SomeProvider { void opA(ARequest request); void opB(BRequest request); CommonResponseopC(CRequest request); } 入参中的入参虽然形态各异,但由于是单个入参,所以可以统一继承 AbstractBaseRequest,即上述的 ARequest,BRequest,CRequest 都是 AbstractBaseRequest 的子类。在千米内部项目中,AbstractBaseRequest 定义了 traceId、clientIp、clientType、operationType 等公共入参,减少了重复命名,我们一致认为,这更加的 OO。 有了 AbstractBaseRequest,我们可以更加轻松地在其之上做 AOP,千米的实践中,大概做了如下的操作: 请求入参统一校验(request.checkParam(); param.checkParam();) 实体变更统一加锁,降低锁粒度 请求分类统一处理(if (request instanceof XxxRequest)) 请求报文统一记日志(log.setRequest(JsonUtil.getJsonString(request))) 操作成功统一发消息 如果不遵守单参数的约定,上述这些功能也并不是无法实现,但所需花费的精力远大于单参数,一个简单的约定带来的优势,我们认为是值得的。 单参数入参兼容性强 还记得前面的小节中,我提到了 SpringCloud,在 SpringCloud Feign 中,接口的入参通常会被 @RequestBody 修饰,强制做单参数的限制。千米内部使用了 Dubbo 作为 Rpc 框架,一般而言,为 Dubbo 服务设计的接口是不能直接用作 Feign 接口的(主要是因为 @RequestBody 的限制),但有了单参数的限制,便使之成为了可能。为什么我好端端的 Dubbo 接口需要兼容 Feign 接口?可能会有人发出这样的疑问,莫急,这样做的初衷当然不是为了单纯做接口兼容,而是想充分利用 HTTP 丰富的技术栈以及一些自动化工具。 自动生成 HTTP 接口实现(让服务端同时支持 Dubbo 和 HTTP 两种服务接口) 看过我之前文章的朋友应该了解过一个设计:千米内部支持的是 Dubbo 协议和 HTTP 协议族(如 JSON RPC 协议,Restful 协议),这并不意味着程序员需要写两份代码,我们可以通过 Dubbo 接口自动生成 HTTP 接口,体现了单参数设计的兼容性之强。 通过 Swagger UI 实现对 Dubbo 接口的可视化便捷测试 又是一个兼容 HTTP 技术栈带来的便利,在 Restful 接口的测试中,Swagger 一直是备受青睐的一个工具,但可惜的是其无法对 Dubbo 接口进行测试。兼容 HTTP 后,我们只需要做一些微小的工作,便可以实现 Swagger 对 Dubbo 接口的可视化测试。 有利于 TestNg 集成测试 自动生成 TestNG 集成测试代码和缺省测试用例,这使得服务端接口集成测试变得异常简单,程序员更能集中精力设计业务用例,结合缺省用例、JPA 自动建表和 PowerMock 模拟外部依赖接口实现本机环境。 TestNg 自动化测试 这块涉及到了公司内部的代码,只做下简单介绍,我们一般通过内部项目 com.qianmi.codegenerator:api-dubbo-2-restful ,com.qianmi.codegenerator:api-request-json 生成自动化的测试用例,方便测试。而这些自动化工具中大量使用了反射,而由于单参数的设计,反射用起来比较方便。 接口异常设计 首先肯定一点,RPC 框架是可以封装异常的,Exception 也是返回值的一部分。在 go 语言中可能更习惯于返回 err,res 的组合,但 JAVA 中我个人更偏向于 try catch 的方法捕获异常。RPC 接口设计中的异常设计也是一个注意点。 初始方案 public interface ModuleAProvider { void opA(ARequest request); void opB(BRequest request); CommonResponseopC(CRequest request); } 我们假设模块 A 存在上述的 ModuleAProvider 接口,ModuleAProvider 的实现中或多或少都会出现异常,例如可能存在的异常 ModuleAException,调用者实际上并不知道 ModuleAException 的存在,只有当出现异常时,才会知晓。对于 ModuleAException 这种业务异常,我们更希望调用方能够显示的处理,所以 ModuleAException 应该被设计成 Checked Excepition。 正确的异常设计姿势 public interface ModuleAProvider { void opA(ARequest request) throws ModuleAException; void opB(BRequest request) throws ModuleAException; CommonResponseopC(CRequest request) throws ModuleAException; } 上述接口中定义的异常实际上也是一种契约,契约的好处便是不需要叙述,调用方自然会想到要去处理 Checked Exception,否则连编译都过不了。 调用方的处理方式 在 ModuleB 中,应当如下处理异常: public class ModuleBService implements ModuleBProvider { @Reference ModuleAProvider moduleAProvider; @Override public void someOp() throws ModuleBexception{ try{             moduleAProvider.opA(...);         }catch(ModuleAException e){ throw new ModuleBException(e.getMessage());         }     } @Override public void anotherOp(){ try{             moduleAProvider.opB(...);         }catch(ModuleAException e){ // 业务逻辑处理 }     } } someOp 演示了一个异常流的传递,ModuleB 暴露出去的异常应当是 ModuleB 的 api 模块中异常类,虽然其依赖了 ModuleA ,但需要将异常进行转换,或者对于那些意料之中的业务异常可以像 anotherOp() 一样进行处理,不再传递。这时如果新增 ModuleC 依赖 ModuleB,那么 ModuleC 完全不需要关心 ModuleA 的异常。 异常与熔断 作为系统设计者,我们应该认识到一点:RPC 调用,失败是常态。通常我们需要对 RPC 接口做熔断处理,比如千米内部便集成了 Netflix 提供的熔断组件 Hystrix。Hystrix 需要知道什么样的异常需要进行熔断,什么样的异常不能够进行熔断。在没有上述的异常设计之前,回答这个问题可能还有些难度,但有了 Checked Exception 的契约,一切都变得明了清晰了。 public class ModuleAProviderProxy { @Reference private ModuleAProvider moduleAProvider; @HystrixCommand(ignoreExceptions = {ModuleAException.class}) public void opA(ARequest request) throws ModuleAException {         moduleAProvider.opA(request);     } @HystrixCommand(ignoreExceptions = {ModuleAException.class}) public void opB(BRequest request) throws ModuleAException {         moduleAProvider.oBB(request);     } @HystrixCommand(ignoreExceptions = {ModuleAException.class}) public CommonResponseopC(CRequest request) throws ModuleAException { return moduleAProvider.opC(request);     } } 如服务不可用等原因引发的多次接口调用超时异常,会触发 Hystrix 的熔断;而对于业务异常,我们则认为不需要进行熔断,因为对于接口 throws 出的业务异常,我们也认为是正常响应的一部分,只不过借助于 JAVA 的异常机制来表达。实际上,和生成自动化测试类的工具一样,我们使用了另一套自动化的工具,可以由 Dubbo 接口自动生成对应的 Hystrix Proxy。我们坚定的认为开发体验和用户体验一样重要,所以公司内部会有非常多的自动化工具。 API 版本单独演进 引用一段公司内部的真实对话: A:我下载了你们的代码库怎么编译不通过啊,依赖中 xxx-api-1.1.3 版本的 jar 包找不到了,那可都是 RELEASE 版本啊。 B:你不知道我们 nexus 容量有限,只能保存最新的 20 个 RELEASE 版本吗?那个 API 现在最新的版本是 1.1.31 啦。 A:啊,这才几个月就几十个 RELEASE 版本啦?这接口太不稳定啦。 B:其实接口一行代码没改,我们业务分析是很牛逼的,一直很稳定。但是这个 API 是和我们项目一起打包的,我们需求更新一次,就发布一次,API 就被迫一起升级版本。发生这种事,大家都不想的。 在单体式架构中,版本演进的单位是整个项目。微服务解决的一个关键的痛点便是其做到了每个服务的单独演进,这大大降低了服务间的耦合。正如我文章开始时举得那个例子一样:serviceA 是一个演进的单位,serviceA-api 和 serviceA-app 这两个 Module 从属于 serviceA,这意味着 app 的一次升级,将会引发 api 的升级,因为他们是共生的!而从微服务的使用角度来看,调用者关心的是 api 的结构,而对其实现压根不在乎。所以对于 api 定义未发生变化,其 app 发生变化的那些升级,其实可以做到对调用者无感知。在实践中也是如此 api 版本的演进应该是缓慢的,而 app 版本的演进应该是频繁的。 所以,对于这两个演进速度不一致的模块,我们应该单独做版本管理,他们有自己的版本号。 问题回归 查询接口过多。各种 findBy 方法,加上各自的重载,几乎占据了一个接口 80% 的代码量。这也符合一般人的开发习惯,因为页面需要各式各样的数据格式,加上查询条件差异很大,便造成了:一个查询条件,一个方法的尴尬场景。这样会导致另外一个问题,需要使用某个查询方法时,直接新增了方法,但实际上可能这个方法已经出现过了,隐藏在了令人眼花缭乱的方法中。 解决方案:使用单参+Specification 模式,降低重复的查询方法,大大降低接口中的方法数量。 难以扩展。接口的任何改动,比如新增一个入参,都会导致调用者被迫升级,这也通常是 RPC 设计被诟病的一点,不合理的 RPC 接口设计会放大这个缺点。 解决方案:单参设计其实无形中包含了所有的查询条件的排列组合,可以直接在 app 实现逻辑的新增,而不需要对 api 进行改动(如果是参数的新增则必须进行 api 的升级,参数的废弃可以用 @Deprecated 标准)。 升级困难。 在之前的 “初识 RPC 接口设计”一节中,版本管理的粒度是 project,而不是 module,这意味着:api 即使没有发生变化,app 版本演进,也会造成 api 的被迫升级,因为 project 是一个整体。问题又和上一条一样了,api 一旦发生变化,调用者也得被迫升级,牵一发而动全身。 解决方案:以 module 为版本演进的粒度。api 和 app 单独演进,减少调用者的不必要升级次数。 难以测试。接口一多,职责随之变得繁杂,业务场景各异,测试用例难以维护。特别是对于那些有良好习惯编写单元测试的程序员而言,简直是噩梦,用例也得跟着改。 解决方案:单参数设计+自动化测试工具,打造良好的开发体验。 异常设计不合理。在既往的工作经历中曾经有一次会议,就 RPC 调用中的异常设计引发了争议,一派人觉得需要有一个业务 CommonResponse,封装异常,每次调用后,优先判断调用结果是否 success,在进行业务逻辑处理;另一派人觉得这比较麻烦,由于 RPC 框架是可以封装异常调用的,所以应当直接 try catch 异常,不需要进行业务包裹。在没有明确规范时,这两种风格的代码同时存在于项目中,十分难看! 解决方案:Checked Exception+正确异常处理姿势,使得代码更加优雅,降低了调用方不处理异常带来的风险。

    架构师社区 RPC RPC接口 dubbo

  • 阿里P7背调红灯:被前前公司说坏话,修改领导名被查

    一位面试阿里P7的求职大佬,所有流程都顺利通过了。眼看阿里p7offer到手,年薪百万在即,没想到在背调环节出了问题,被亮了红灯。 人生的反转来的太快,就像龙卷风,可把这位大佬郁闷坏了。 原因竟是,这位大佬,因和前前老板有一段不愉快的故事,被其在单位里到处恶意造谣,造成了楼主的口碑极差。 互联网圈子就那么大,阿里人事自然就直接查到了。已经被搅黄了两个offer。害,到底发生了什么吗?能闹到这地步? 又又又是背调的问题,让打工人一提起便义愤填膺的背调。有人指出,背调属于利用个人信息获利,难道不违法吗? 也有人指出,互联网公司太畸形,没有人情味,有些矫枉过正。既然招聘,就该用人不疑,疑人不用,何必过分执着于100%真实性。再说,一般没有关系冲突,也不会随便离职。 还有人提出了对付这位难缠老板的方法:实在不行喝两瓶酒蹲点,额,这样兄dei,你喝酒肯定没有花生米。 还有一部分网友比较理性,喜欢就事论事,提解决方案。有的人劝网友忘了阿里,躲过了阿里,值得庆祝。没准是大好事呢。 还有人指出楼主实在不行换个城市工作,或者找个外企以及小公司暂时过度半年,毕竟只背调前两段工作经历。 还有人劝楼主再努力争取一下,澄清误会,就算不成功,也不留遗憾。 关于背调,几乎成了每一个打工人深恶痛绝的一次体验,毕竟对求职者太不友好了。谁没在年轻时碰到过几个渣公司,碰见过几个渣领导,如果背调随便就成为了公司的杀伤性武器,那么将错过多少人才呢?希望企业能找到使用背调的平衡点,不要矫枉过正,让真正的人才流失。 但作为打工人,我们始终改变不了环境,只能改进自己。 如果感觉有问题,还是提前和HR做沟通,在背调前坦诚相告,毕竟HR每招成功一个人,他才有绩效哦。 同时,尽可能维护圈子,留一个好口碑。和老板作对,就算占了上风,也失了风度。和光同尘,是维护人际关系的秘诀,适用于任何关系。

    架构师社区 阿里 阿里P7 背调

  • 《如何与面试官处朋友》系列-缓存击穿、穿透、雪崩场景原理大调解

    前面我们提到分布式多级缓存架构的全貌,但总感觉少了些什么东西。在这样大的场景下面,如果遇到缓存使用问题那可咋办?但自古英雄出少年,相信此刻你已踏马西去,正走在寻找答案上得夕阳西下。每每面谈Redis大家肯定不陌生,反正就是各种被社会得毒打。上来就缓存问题(击穿、穿透、雪崩)三板斧,直接就是开门红,险些让我们招架不住。在这里我又日思夜想: 它们之间是如何造成的? 项目业务开发中应该注意些什么才能防范它们? 一眼望去你的简历,就 缓存击穿 我心房 面试官: 嗯,不错,那使用缓存常会遇到缓存的3大问题,你先给说下缓存击穿是什么? 吒吒辉: 其实,缓存使用问题主要就集中在数据为脏数据、使用时缓存失效、缓存实例不可用等问题上。 而我们一切的方案都折中于满足于主要问题。而这“缓存击穿”就是缓存失效的情景。 什么是缓存击穿呢? 缓存击穿就是当你请求访问缓存Key时,恰巧它失效了。而这时数据还没更新到缓存中,外加上高频访问请求。相当于把缓存都给打穿了。 从而让这些透穿的请求分分钟把数据库给打得怀疑人生,甚至不能自理。犹如下面这感觉,表面风平浪静,实则早以暗藏杀机 为什么有这么大的杀伤力? 因为你原来的请求走的是缓存,现在它没了。所以数据库就需要多承受几十、上百倍的数据访问压力。 假设当时 10000/s 个请求,缓存能扛住9000/s 个请求,缓存一失效。此时10000 /s个请求全部落到数据库,那自然是招架不住。 这时一般数据库的CPU会飙到100%,服务器会卡死。可能DBA会直接重启一波。那不用多说,会直接再次陷入僵局。 当然这种规模访问,一般都有监控系统,在得出数据库负载过大的情况时,会发送报警的消息通知。然后再针对性想办法。 面试官:那你觉得这种情况要怎么做? 吒吒辉: 解铃还须系铃人,既然是在使用过程中遇到突变情况,那么就需要在业务&缓存上来提前进行搭桥。 1.1 加锁 如果缓存失效没拿到数据,就先拿到互斥锁然后去数据库查询数据,然后在返回结果,并重新设置缓存。 这样高频的请求访问就会被限制为1个请求。数据库的压力自然也会变小,后面相同请求继续走缓存。 public static getData(string $key) { //从缓存读取数据 $result = $redis->get($key); //缓存中不存在数据 if ($result ==null ) { //获取swoole互斥锁 $lock = new Swoole\Lock(SWOOLE_MUTEX); if ($lock->lock()) { $result = getDataFormMysql($key); //获取到数据库 if ($result != null)) setDataFromRedis($key, $result)); return $lock->unlock(); }else { //获取锁失败,则重试 usleep(10000); $result = getData($key;) } } return $result; } 但这加锁方案是不是会阻塞请求,影响用户体验呢? 还是一个递归的调用。没办法,你没抢到锁的请求只能等待或重试。不然你难道要打人爆力抢锁呀!!! 有什么方式可以改进? 用队列。 其实上面的锁还可改为最大重试的次数,不然如果遇到网络波动而导致请求阻塞,这样不断的进行重试获取锁,就会把机器给拖垮。当然网站请求量少,基本上没啥问题。但万事还得小心。 二眼回眸你的简历,那 缓存穿透 我心脏 面试官: 那你在谈谈缓存穿透吧? 缓存穿透就是当你请求访问缓存Key时,缓存本身的内容就没有它,数据库更无它的身影。这样的请求就会穿透缓存直达数据库。 如果这是一个高频请求,数据库就可能被恶意的打垮。就比如一种大网,请求就通过中间的缝隙穿透过去。好像漏网之鱼 为什么说被恶意的打垮呢? 原因就在,数据库本身就无这个key对应的数据,而缓存又依照数据库生存,那它就更没得了。 比如 拿商品id=-1的条件来查询;你说这不是恶意是啥?明明就没有,结果还是要来查询。明摆着给你穿小鞋。欲加之罪,何患无辞。 面试官: 那你面对这种情况会怎么来解决?咳咳,这个还需要大致分析下才有出路。 1.1.访问限制黑名单 如果不想业务上加入太多不可控因子。那么可以在服务端加入访问限定黑名单机制。咋个意思? 用户请求发送到服务端时,服务端调用Redis-client先拿 EXISTS 判断缓存数据是否存在,如果发现没得值,就把当前key记录到访问限制黑名单列表,访问次数+1,并设置合适的过期时间。然后再去数据库读取数据。 如果访问的key是我们缓存中的内容,那么自然可以去数据库拿到数据,如果同一个key或几个key都一二再再而三拿不到数据,那么就肯定有问题。对应黑名单限制列表的访问次数自然也就上去了。 这样后面请求获取key时,就先去访问控制列表看看有没得它(key)这个刺儿头,有,我直接头都不回的给你拒绝掉,还顺手给你一大嘴巴子(给黑名单里面的key重置过期时间),这样后面恶意请求在过来,可以保证这个黑名单还有key的名字。直接让它面壁思过,给我唱征服。 三眼再看你的简历,那 缓存雪崩 让我彻底沦陷 面试官: 那缓存雪崩要怎么解决? 吒吒辉: 缓存雪崩其实和缓存击穿有点类似,都是缓存失效。只不过它们各自针对缓存失效的情况不一样。 缓存雪崩是缓存同时大面积失效而导致请求像雪崩式的一拥而下,直捣黄龙(数据库)。而缓存击穿针对的是单个key访问恰巧失效的问题。 由此可见缓存雪崩的破坏力更大。关键要点在于同时会有大面积的key失效。那怎么解决呢? 思考题 缓存三大问题的本质是什么造成的? 设计的方案很多,具体实践拿捏看什么? 你网站的热点数据发现规则会怎么考虑呢?要不要单独出业务? 如有感悟,欢迎关注。 点赞、分享、留言都可走一走。本来文章还是可以写很多东西,不过我感觉像微服务、优化、分布式等场景在这里不大合适,就准备单独出技术知识分享专题来做,不然大家就不好理解了。 也可以后面来一个高级版的缓存问题防御。这样大家可能很好理解。具体怎么安排大家可留言告知。除此之外以下相关的内容帮助大家提升

    架构师社区 缓存 nosql 缓存击穿

  • Spring揭秘--寻找遗失的web.xml

    今天我们来放松下心情,不聊分布式,云原生,来聊一聊初学者接触的最多的 java web 基础。几乎所有人都是从 servlet,jsp,filter 开始编写自己的第一个 hello world 工程。那时,还离不开 web.xml 的配置,在 xml 文件中编写繁琐的 servlet 和 filter 的配置。随着 spring 的普及,配置逐渐演变成了两种方式—java configuration 和 xml 配置共存。现如今,springboot 的普及,java configuration 成了主流,xml 配置似乎已经“灭绝”了。不知道你有没有好奇过,这中间都发生了哪些改变,web.xml 中的配置项又是被什么替代项取代了? servlet servlet3.0 以前的时代 为了体现出整个演进过程,还是来回顾下 n 年前我们是怎么写 servlet 和 filter 代码的。 项目结构(本文都采用 maven 项目结构) . ├── pom.xml ├── src    ├── main    │   ├── java    │   │   └── moe    │   │       └── cnkirito    │   │           ├── filter    │   │           │   └── HelloWorldFilter.java    │   │           └── servlet    │   │               └── HelloWorldServlet.java    │   └── resources    │       └── WEB-INF    │           └── web.xml    └── test        └── java public class HelloWorldServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        resp.setContentType("text/plain");        PrintWriter out = resp.getWriter();        out.println("hello world");    } } public class HelloWorldFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {    } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        System.out.println("触发 hello world 过滤器...");        filterChain.doFilter(servletRequest,servletResponse);    } @Override public void destroy() {    } } 别忘了在 web.xml 中配置 servlet 和 filter <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee        http://java.sun.com/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>HelloWorldServletservlet-name> <servlet-class>moe.cnkirito.servlet.HelloWorldServletservlet-class> servlet> <servlet-mapping> <servlet-name>HelloWorldServletservlet-name> <url-pattern>/hellourl-pattern> servlet-mapping> <filter> <filter-name>HelloWorldFilterfilter-name> <filter-class>moe.cnkirito.filter.HelloWorldFilterfilter-class> filter> <filter-mapping> <filter-name>HelloWorldFilterfilter-name> <url-pattern>/hellourl-pattern> filter-mapping> web-app> 这样,一个 java web hello world 就完成了。当然,本文不是 servlet 的入门教程,只是为了对比。 servlet3.0 新特性 servlet_3.0 Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布。该版本在前一版本(Servlet 2.5)的基础上提供了若干新特性用于简化 Web 应用的开发和部署。其中一项新特性便是提供了无 xml 配置的特性。 servlet3.0 首先提供了 @WebServlet,@WebFilter 等注解,这样便有了抛弃 web.xml 的第一个途径,凭借注解声明 servlet 和 filter 来做到这一点。 除了这种方式,servlet3.0 规范还提供了更强大的功能,可以在运行时动态注册 servlet ,filter,listener。以 servlet 为例,过滤器与监听器与之类似。ServletContext 为动态配置 Servlet 增加了如下方法: ServletRegistration.Dynamic addServlet(String servletName,Class servletClass) ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) ServletRegistration.Dynamic addServlet(String servletName, String className) T createServlet(Classclazz) ServletRegistration getServletRegistration(String servletName) Map 其中前三个方法的作用是相同的,只是参数类型不同而已;通过 createServlet() 方法创建的 Servlet,通常需要做一些自定义的配置,然后使用 addServlet() 方法来将其动态注册为一个可以用于服务的 Servlet。两个 getServletRegistration() 方法主要用于动态为 Servlet 增加映射信息,这等价于在 web.xml 中使用标签为存在的 Servlet 增加映射信息。 以上 ServletContext 新增的方法要么是在 ServletContextListener 的 contexInitialized 方法中调用,要么是在 ServletContainerInitializer 的 onStartup() 方法中调用。 ServletContainerInitializer 也是 Servlet 3.0 新增的一个接口,容器在启动时使用 JAR 服务 API(JAR Service API) 来发现 ServletContainerInitializer 的实现类,并且容器将 WEB-INF/lib 目录下 JAR 包中的类都交给该类的 onStartup() 方法处理,我们通常需要在该实现类上使用 @HandlesTypes 注解来指定希望被处理的类,过滤掉不希望给 onStartup() 处理的类。 一个典型的 servlet3.0+ 的 web 项目结构如下: . ├── pom.xml └── src    ├── main    │   ├── java    │   │   └── moe    │   │       └── cnkirito    │   │           ├── CustomServletContainerInitializer.java    │   │           ├── filter    │   │           │   └── HelloWorldFilter.java    │   │           └── servlet    │   │               └── HelloWorldServlet.java    │   └── resources    │       └── META-INF    │           └── services    │               └── javax.servlet.ServletContainerInitializer    └── test        └── java 我并未对 HelloWorldServlet 和 HelloWorldFilter 做任何改动,而是新增了一个 CustomServletContainerInitializer ,它实现了javax.servlet.ServletContainerInitializer接口,用来在 web 容器启动时加载指定的 servlet 和 filter,代码如下: public class CustomServletContainerInitializer implements ServletContainerInitializer { private final static String JAR_HELLO_URL = "/hello"; @Override public void onStartup(Set> webAppInitializerClasses, ServletContext servletContext)

    架构师社区 java xml Spring

  • 30岁+程序员转行做高校老师,无奈发现学历要求博士起

    众所周知,30岁是大厂程序员的魔咒。到了30岁后,精力体力不足,互联网公司,开始计划着招聘更年轻更具性价比的工具人,而老猿们也开始自觉地规划起未来的走向。 这不,这位老哥已经萌生了转行做高校老师的想法。结果,偷偷查了后才发现,高校老师至少博士学历起步,且严卡论文数量。一时有些惆怅,感叹起转行不易,发帖探讨。 一百度网友调侃,现在23岁,能不能活到35岁不好说。一时惹人哄堂大笑,有网友还在底下回复:那你烫头吗? 关于高校老师,大家纷纷指出,听说现在大学老师没编制了,kpi考核一样很严格。 关于30岁去向,大家一时兴致满满,说起了打算。没计划都是骗人的嘛。果然互联网圈子焦虑感太严重。谁能想到,除了日常的考公,银行等进编制,外卖和滴滴也成了程序员们时髦的职业。 听听他们怎么形容外卖员?饭来伸手+黄袍加身,哈哈~而这位滴滴的程序员更是爆出,我根本不用辞职,反手注册个司机就行了。真是合理利用资源,近水楼台先得月嘛~ 考公,银行,国企等编制内的单位更是成为大家首选青睐的职业。不过也有人指出,国企也没那么好混,难啊。 还有程序员指出,自然应该是35岁实现财务自由,之后想干啥干啥,创业投资,实在不行卖个鸡蛋饼也行吧。 最后,讨论了一圈,大家还是没找到什么好路子可以退守程序员岗位。还有人趁机指责互联网人贩卖焦虑,没必要。而且圈外人早就想进去了,你们作啥? 总之,关于35岁之后去哪儿,大家其实并没有很好的思路。 话说,现在哪行哪业赚钱容易呢?可能是因为程序员们工资太高,快钱赚久了,到转行时不能忍受一块一块赚钱的落差吧,所以才感觉做什么都会成为将就。 其实,要是能摆正心态一切就都ok了。要知道,无论是做个街边的小贩,还是成为成功的投资者,亦或是高收入的副业创业者,大家都是靠努力赚钱,没有高低贵贱之分。赚钱多少只能说明赚钱能力,难道还能说明人生意义吗? 加油,30+程序员,愿你们都能找到属于自己的人生路。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    架构师社区 互联网 程序员 工具人

  • 23种设计模式的通俗解释,看完秒懂

    01 工厂方法 追 MM 少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是 MM 爱吃的东西,虽然口味有所不同,但不管你带 MM 去麦当劳或肯德基,只管向服务员说「来四个鸡翅」就行了。麦当劳和肯德基就是生产鸡翅的 Factory 工厂模式:客户类和工厂类分开。 消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。 02 建造者模式 MM 最爱听的就是「我爱你」这句话了,见到不同地方的 MM,要能够用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到 MM 我只要按对应的键,它就能够用相应的语言说出「我爱你」这句话了,国外的 MM 也可以轻松搞掂,这就是我的「我爱你」builder。 建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。 03 抽象工厂 请 MM 去麦当劳吃汉堡,不同的 MM 有不同的口味,要每个都记住是一件烦人的事情,我一般采用 Factory Method 模式,带着 MM 到服务员那儿,说「要一个汉堡」,具体要什么样的汉堡呢,让 MM 直接跟服务员说就行了。 工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。 04 原型模式 跟 MM 用 QQ 聊天,一定要说些深情的话语了,我搜集了好多肉麻的情话,需要时只要 copy 出来放到 QQ 里面就行了,这就是我的情话 prototype 了。(100 块钱一份,你要不要) 原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。 05 单态模式 俺有 6 个漂亮的老婆,她们的老公都是我,我就是我们家里的老公 Sigleton,她们只要说道「老公」,都是指的同一个人,那就是我 (刚才做了个梦啦,哪有这么好的事) 单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的 “单一实例” 的需求时才可使用。 06 适配器模式 在朋友聚会上碰到了一个美女 Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友 kent 了,他作为我和 Sarah 之间的 Adapter,让我和 Sarah 可以相互交谈了 (也不知道他会不会耍我) 适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。 07 桥梁模式 早上碰到 MM,要说早上好,晚上碰到 MM,要说晚上好;碰到 MM 穿了件新衣服,要说你的衣服好漂亮哦,碰到 MM 新做的发型,要说你的头发好漂亮哦。不要问我 “早上碰到 MM 新做了个发型怎么说” 这种问题,自己用 BRIDGE 组合一下不就行了 桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合 / 聚合关系而不是继承关系,从而使两者可以独立的变化。 08 合成模式 Mary 今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店,你自己挑。”“这件 T 恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”“喂,买了三件了呀,我只答应送一件礼物的哦。”“什么呀,T 恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。”“……”,MM 都会用 Composite 模式了,你会了没有? 合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。 09 装饰模式 Mary 过完轮到 Sarly 过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上 “最好的的礼物,就是爱你的 Fita”,再到街上礼品店买了个像框(卖礼品的 MM 也很漂亮哦),再找隔壁搞美术设计的 Mike 设计了一个漂亮的盒子装起来……,我们都是 Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗? 装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。 10 门面模式 我有一个专业的 Nikon 相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但 MM 可不懂这些,教了半天也不会。幸好相机有 Facade 设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整,这样 MM 也可以用这个相机给我拍张照片了。门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。 门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。 11 享元模式 每天跟 MM 发短信,手指都累死了,最近买了个新手机,可以把一些常用的句子存在手机里,要用的时候,直接拿出来,在前面加上 MM 的名字就可以发送了,再不用一个字一个字敲了。共享的句子就是 Flyweight,MM 的名字就是提取出来的外部特征,根据上下文情况使用。享元模式:FLYWEIGHT 在拳击比赛中指最轻量级。 享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。 将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。 12 代理模式 跟 MM 在网上聊天,一开头总是 “hi, 你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?” 这些话,真烦人,写个程序做为我的 Proxy 吧,凡是接收到这些话都设置好了自己的回答,接收到其他的话时再通知我回答,怎么样,酷吧。 代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。 在公众号:C语言中文社区,回复“C语言”免费领取百本编程类电子书 客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。 13 责任链模式 晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的 MM 哎,找张纸条,写上 “Hi, 可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的 MM 把纸条传给老师了,听说是个老处女呀,快跑! 责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。 14 命令模式 俺有一个 MM 家里管得特别严,没法见面,只好借助于她弟弟在我们俩之间传送信息,她对我有什么指示,就写一张纸条让她弟弟带给我。这不,她弟弟又传送过来一个 COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐三个男朋友送 COMMAND,就数你最小气,才请我吃面。” 命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。 15 解释器模式 俺有一个《泡 MM 真经》,上面有各种泡 MM 的攻略,比如说去吃西餐的步骤、去看电影的方法等等,跟 MM 约会时,只要做一个 Interpreter,照着上面的脚本执行就可以了。 解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。 在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。 16 迭代模式 我爱上了 Mary,不顾一切的向她求婚。Mary:“想要我跟你结婚,得答应我的条件” 我:“什么条件我都答应,你说吧” Mary:“我看上了那个一克拉的钻石” 我:“我买,我买,还有吗?” Mary:“我看上了湖边的那栋别墅” 我:“我买,我买,还有吗?” Mary:“我看上那辆法拉利跑车” 我脑袋嗡的一声,坐在椅子上,一咬牙:“我买,我买,还有吗?” 迭代模式:迭代模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。 迭代模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。 17 调停者模式 四个 MM 打麻将,相互之间谁应该给谁多少钱算不清楚了,幸亏当时我在旁边,按照各自的筹码数算钱,赚了钱的从我这里拿,赔了钱的也付给我,一切就 OK 啦,俺得到了四个 MM 的电话。调停者模式:调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。 当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。 18 备忘录模式 同时跟几个 MM 聊天时,一定要记清楚刚才跟 MM 说了些什么话,不然 MM 发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个 MM 说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦。 备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。 19 观察者模式 想知道咱们公司最新 MM 情报吗?加入公司的 MM 情报邮件组就行了,tom 负责搜集情报,他发现的新情报不用一个一个通知我们,直接发布给邮件组,我们作为订阅者(观察者)就可以及时收到情报啦。 观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。 20 状态模式 跟 MM 交往时,一定要注意她的状态哦,在不同的状态时她的行为会有不同,比如你约她今天晚上去看电影,对你没兴趣的 MM 就会说 “有事情啦”,对你不讨厌但还没喜欢上的 MM 就会说 “好啊,不过可以带上我同事么?”,已经喜欢上你的 MM 就会说 “几点钟?看完电影再去泡吧怎么样?”,当然你看电影过程中表现良好的话,也可以把 MM 的状态从不讨厌不喜欢变成喜欢哦。 状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。 状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。 21 策略模式 跟不同类型的 MM 约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,单目的都是为了得到 MM 的芳心,我的追 MM 锦囊中有好多 Strategy 哦。策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。 策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。 22 模板方法模式 看过《如何说服女生上床》这部经典文章吗?女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤 (Template method),但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦 (具体实现); 模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。 23 访问者模式 情人节到了,要给每个 MM 送一束鲜花和一张卡片,可是每个 MM 送的花都要针对她个人的特点,每张卡片也要根据个人的特点来挑,我一个人哪搞得清楚,还是找花店老板和礼品店老板做一下 Visitor,让花店老板根据 MM 的特点选一束花,让礼品店老板也根据每个人特点选一张卡,这样就轻松多了; 访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。 访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    C语言与CPP编程 设计模式 模板方法 策略模式

  • RabbitMQ消息路由失败的处理方案(回调与备份交换机AE)

    我们知道,消息在RabbitMQ的整个生命周期是生产者投递消息到Exchange,Exchange根据路由键将消息路由到合适的Queue,Queue再将消息推(或消费者主动拉)给消费者。 在这个过程当中,Exchange根据路由键将消息路由到合适的Queue的过程,可能发生诸如 Exchange没有任何Queue与其绑定, 或者根据消息的路由键,没有任何一个合适的Queue来投递消息, 从而导致消息路由失败。对于这些路由失败的消息应该如何处理呢?有两种方式: 将消息返回给投递该条消息的生产者。 使用备份交换机 alternate-exchange(AE)。 方式1:将消息返回给投递该条消息的生产者 配置 rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() { @Override public void returnedMessage(ReturnedMessage returnedMessage) {         log.error("消息被退回:{}", returnedMessage);     } }); 消息被退回:且可以看到原因是无法路由 方式2:使用备份交换机 使用方式1需要我们在程序中进行编码设置回调函数监听,增加了生产者代码的复杂性,那么为了消息不丢失还有没有其他方式来处理路由失败的消息呢:答案是使用备份交换机。 相较于使用回调函数,使用备份交换机只需要给交换机绑定一个备份交换机即可,当消息路由失败之后,消息将投递到备份交换机,再由备份交换机路由消息到备份队列。这样我们只需要关注这个备份队列就能知道/获取到路由失败的消息。通常情况下备份交换的Type应该设置为 fanout。 配置 /**  * 备份队列  *  * @return */ @Bean public Queue alternateQueue() { return QueueBuilder             .durable("Q_ALTERNATE")             .build(); } /**  * 备份交换机  *  * @return */ @Bean public Exchange alternateExchange() { return ExchangeBuilder             .fanoutExchange(X_ALTERNATE)             .durable(true)             .build(); } /**  * 备份绑定  *  * @param alternateExchange  * @param alternateQueue  * @return */ @Bean public Binding alternateBinding(Exchange alternateExchange, Queue alternateQueue) { return BindingBuilder             .bind(alternateQueue)             .to(alternateExchange)             .with("")             .noargs(); } 消息投递

    架构师社区 交换机 RabbitMQ 消息路由

  • 万字长文带你图解计算机网络(超全)!!

    Connection:keep-alive 在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。 TCP—长连接 所谓长连接,指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般需要自己做在线维持(不发生RST包和四次挥手)。 连接→数据传输→保持连接(心跳)→数据传输→保持连接(心跳)→……→关闭连接(一个TCP连接通道多个读写通信);这就要求长连接在没有数据通信时,定时发送数据包(心跳),以维持连接状态; TCP保活功能,保活功能主要为服务器应用提供,服务器应用希望知道客户主机是否崩溃,从而可以代表客户使用资源。如果客户已经消失,使得服务器上保留一个半开放的连接,而服务器又在等待来自客户端的数据,则服务器将应远等待客户端的数据,保活功能就是试图在服务器端检测到这种半开放的连接。 TCP—短连接 短连接是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接(管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段); 连接→数据传输→关闭连接; 应用场景 长连接: 多用于操作频繁(读写),点对点的通讯,而且连接数不能太多情况,。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用建立TCP连接。 例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。 短连接: 像WEB网站的http服务一般都用短链接(http1.0只支持短连接,1.1keep alive 带时间,操作次数限制的长连接),因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好; HTTP 原理 HTTP 是一个无状态的协议。无状态是指客户机(Web 浏览器)和服务器之间不需要建立持久的连接,这意味着当一个客户端向服务器端发出请求,然后服务器返回响应(response),连接就被关闭了,在服务器端不保留连接的有关信息.HTTP 遵循请求(Request)/应答(Response)模型。客户机(浏览器)向服务器发送请求,服务器处理请求并返回适当的应答。所有 HTTP 连接都被构造成一套请求和应答。 地址解析 如用客户端浏览器请求这个页面:http://www.lydms.com:8080/index.htm 从中分解出协议名、主机名、端口、对象路径等部分,对于我们的这个地址,解析得到的结果如下:协议名:http 主机名:www.lydms.com 端口:8080 对象路径:/index.htm 在这一步,需要域名系统 DNS 解析域名 localhost.com,得主机的 IP 地址。 封装 HTTP 请求数据包 把以上部分结合本机自己的信息,封装成一个 HTTP 请求数据包 封装成 TCP 包并建立连接 封装成 TCP 包,建立 TCP 连接(TCP 的三次握手) 客户机发送请求命 客户机发送请求命令:建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是 MIME 信息包括请求修饰符、客户机信息和可内容。 服务器响应 服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是 MIME 信息包括服务器信息、实体信息和可能的内容。 服务器关闭 TCP 连接 一般情况下,一旦 Web 服务器向浏览器发送了请求数据,它就要关闭 TCP 连接,然后如果浏览器或者服务器在其头信息加入了这行代码 Connection:keep-alive,TCP 连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。 HTTPS HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP 通道,简单讲是 HTTP 的安全版。即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL。其所用的端口号是 443。过程大致如下: SSL/TLS之间的关系 SSL 是英文“Secure Sockets Layer”的缩写,中文叫做“安全套接层”。它是在上世纪90年代中期,由网景公司设计的。为啥要发明 SSL 这个协议呢?因为原先互联网上使用的 HTTP 协议是明文的,存在很多缺点——比如传输内容会被偷窥(嗅探)和篡改。发明 SSL 协议,就是为了解决这些问题。 到了1999年,SSL 因为应用广泛,已经成为互联网上的事实标准。IETF 就在那年把 SSL 标准化。标准化之后的名称改为 TLS(是“Transport Layer Security”的缩写),中文叫做“传输层安全协议”。 很多人都把这两者并列称呼(SSL/TLS),因为这两者可以视作同一个东西的不同阶段。 建立连接获取证书 SSL 客户端通过 TCP 和服务器建立连接之后(443 端口),并且在一般的 tcp 连接协商(握手)过程中请求证书。即客户端发出一个消息给服务器,这个消息里面包含了自己可实现的算法列表和其它一些需要的消息,SSL 的服务器端会回应一个数据包,这里面确定了这次通信所需要的算法,然后服务器向客户端返回证书。(证书里面包含了服务器信息:域名。申请证书的公司,公共秘钥)。 证书验证 Client 在收到服务器返回的证书后,判断签发这个证书的公共签发机构,并使用这个机构的公共秘钥确认签名是否有效,客户端还会确保证书中列出的域名就是它正在连接的域名。 数据加密和传输 如果确认证书有效,那么生成对称秘钥并使用服务器的公共秘钥进行加密。然后发送给服务器,服务器使用它的私钥对它进行解密,这样两台计算机可以开始进行对称加密进行通信。 CDN 原理 CND 一般包含分发服务系统、负载均衡系统和管理系统。 分发服务系统 其基本的工作单元就是各个 Cache 服务器。负责直接响应用户请求,将内容快速分发到用户;同时还负责内容更新,保证和源站内容的同步。根据内容类型和服务种类的不同,分发服务系统分为多个子服务系统,如:网页加速服务、流媒体加速服务、应用加速服务等。每个子服务系统都是一个分布式的服务集群,由功能类似、地域接近的分布部署的 Cache 集群组成。在承担内容同步、更新和响应用户请求之外,分发服务系统还需要向上层的管理调度系统反馈各个Cache 设备的健康状况、响应情况、内容缓存状况等,以便管理调度系统能够根据设定的策略决定由哪个 Cache 设备来响应用户的请求。 负载均衡系统 负载均衡系统是整个 CDN 系统的中枢。负责对所有的用户请求进行调度,确定提供给用户的最终访问地址。使用分级实现。最基本的两极调度体系包括全局负载均衡(GSLB)和本地负载均衡(SLB)。GSLB 根据用户地址和用户请求的内容,主要根据就近性原则,确定向用户服务的节点。一般通过 DNS解析或者应用层重定向(Http 3XX 重定向)的方式实现。SLB 主要负责节点内部的负载均衡。当用户请求从 GSLB 调度到 SLB 时,SLB 会根据节点内各个Cache 设备的工作状况和内容分布情况等对用户请求重定向。SLB 的实现有四层调度(LVS)、七层调度(Nginx)和链路负载调度等。 管理系统 分为运营管理和网络管理子系统。网络管理系统实现对 CDN 系统的设备管理、拓扑管理、链路监控和故障管理,为管理员提供对全网资源的可视化的集中管理,通常用 web 方式实现。运营管理是对 CDN 系统的业务管理,负责处理业务层面的与外界系统交互所必须的一些收集、整理、交付工作。 包括用户管理、产品管理、计费管理、统计分析等。 TCP/IP协议族 TCP/IP只提供无连接、不可靠的服务。 传输之前需要进行三次握手。 IP的主要功能包括将上层数据(如TCP、UDP数据)或者同层的其它数据(如ICMP数据)封装到IP数据报中,将IP数据报传送到最终目的地;为了使数据能够在链路层上进行传输,对数据进行分段,确定数据报到达其它网络中的目的地的路径。 应用层协议—文件传输服务(FTP) 用来在计算机之间传输文件。 实际Internet的FTP服务是一种匿名(anonymous)FTP服务,设置一个特殊的用户名-anonymous,供公众使用。 匿名登录到FTP服务器后,其工作方式与常规FTP相同。通常处于安全目的,大多数匿名FTP服务器只允许下载,不允许上传文件。 FTP在客户端到服务器建立2条TCP连接,一条是控制连接,主要用于传输命令和参数(端口21);另一条是数据连接,主要用于传输文件(端口号20)。 应用层协议—远程登录协议(Telnet) 远程登录服务实在Telnet协议的支持下,将用户计算机和远程主机连接起来,在远程计算机上运行程序,用户输入的信息通过Telnet协议发送给远程主机,主机在TCP端口监听用户请求,并处理后,将结果通过Telnet协议返回给客户。客户再经过适当的转换显示在计算机屏幕上。因为使用Telnet命令进行远程登录,所有称为Telnet远程登录。 由客户端软件、服务器软件以及Telnet通用协议三部分组成。 应用层协议—电子邮件协议(SMTP) 电子邮件(E-mail)利用计算机进行信息交换的电子媒体信件。 基于客户端/服务器模式,有E-mail客户端软件、E-mail服务器、通信协议三部分组成。发送邮件,首先到达自己注册的邮件服务器主机,再在网络传输中经过多个计算机和路由中转到达目的地的邮件服务器主机,进入收件人的电子邮箱,最后邮件的接收者上网并启动电子邮件管理程序,会自动下载到自己计算机,完成接受邮件。 SMTP:简单邮件传输协议 MIME:Internet邮件扩充协议 PEM:增强私密邮件保护协议 POP:来保管用户未能及时取走的邮件,简单的纯文本协议,每次传输以正规E-mail为单位,不提供部分传输。 传输层协议—TCP 在IP提供的不可靠数据服务的基础上为应用程序提供一个可靠的、面向连接的、全双工的数据传输服务。TCP在源主机和目的之间建立和关闭连接操作是,均需要通过三次握手来确认建立和关闭是否成功。TCP虽然提供了一个可靠的数据传输服务,但是以牺牲通信量来实现的。 **TCP采用了重发技术:**发送数据时,启动定时器,在规定时间内没有收到确认信息,就重新发送数据包。 传输层协议—UDP 用户数据报协议是一种不可靠、无连接的协议,与同层面向连接的TCP相比,UDP是一种无连接的协议(无错误检测功能)。TCP有助于提供可靠的连接,UDP有助于提高传输的高速率性。不负责重新发送丢失的包,不对接收的数据进行排序,不消除重复的IP数据报,不负责建立和终止连接。(都是UDP应用程序负责的) TCP:交互式会话应用程序(FTP等)。 UDP:自己进行错误检测、不需要检测错误(DNS、SNMP)。 网际层协议—IP IP只提供无连接、不可靠的服务,把差错检测和流量控制之类的服务授权给了其他层的协议。IP的主要功能: 将上层数据(TCP、UDP数据)或同层其他数据(ICMP数据)封装到IP数据报中; 将IP数据报传送到最终目的地; 为了使数据能够在链路层上进行传输,对数据进行分段; 确定数据报到达其它网络中的目的地的路径。 网际层协议—ICMP Internet控制信息协议,用于发送查错报文的协议。ICMP让IP更加稳固。也是利用IP来传送报文的。ping工具就是利用ICMP报文进行目标是否可达测试。 5种差错报文:(源抑制、超时、目的不可达、重定向和要求分段) 4种信息报文:回应请求、回应应答、地址屏蔽码请求和地址屏蔽码应答。 网际层协议—ARP和RARP 地址解析协议(ARP)和反地址解析协议(RARP) ARP的作用是将IP地址转为物理地址,RARP的作用是将物理地址转为IP地址。每台设备都有唯一的物理地址(通过网卡给出),为了屏蔽底层协议及物理地址上的差异,IP协议又使用了IP地址。因此,在传输过程中,必须对IP地址和物理地址进行相互转换。 网络接口层协议—以太网(Ethernet IEEE 802.3 Ethernet IEEE 802.3:标准局域网,速度10Mps,传输介质为铜缆。Ethernet IEEE 802.3u:快速以太网,速度100Mps,传输介质为双绞线。Ethernet IEEE 802.3z:千兆以太网,速度1000Mps,传输介质为光纤或双绞线。 网络接口层协议—令牌环网(Ethernet IEEE 802.5) 只有拥有令牌才可以发送数据。 发送数据时,需要捕获一个令牌。 令牌不为空,需要等待。 网络接口层协议—光纤分布式数据接口(FDDI) 以光纤为传输介质。 采用双环体系结构,两环上的信息反方向流动。双环中一环称为主环,另一个环称为次环。正常情况下,主环传输数据,次环处于空闲状态。 双环设计的目的是提供高可靠性和稳定性。FDDI定义的传输介质有单模光纤和多模光纤两种。 网络接口层协议—点对点协议(PPP) 主要用于”拨号上网“这种广域连接模式。优点简单、具备用户验证功能、可以解决IP分配等。为各种主机、网桥和路由器之间简单连接的一种通用解决方案。 利用以太网(Ethernet)资源在以太网上运行PPP来进行用户认证接入的方式成为PPPoE。是目前ADSL接入方式中最广泛的技术标准。 ATM网络撒花姑娘运行PPP来管理用户再认证的方式成为PPPoA。 PPPoA和PPPoE运行原理相同,不同的是运行环境的不同。 其他—ADSL(非对称用户数据线) ADSL Modem上网拨号方式有3中,即专线方式(静态IP)、PPPoA和PPPoE。 ADSL独享带宽,安全可靠。费用低廉,使用过去的电话线路,可以分离电话机和ADSL Modem(上网)。 其他—IPv4和IPv6 IPv4:32位二进制,能表示IP地址个数:2^32=42亿。 IPv6:128位二级制,能表示IP地址个数:2^128=3.4 * 10^38。

    架构师社区 程序员 计算机网络 TCP

  • Microchip 发布首款 IEEE® 802.3bt 以太网供电 USB Type-C®电源和数据适配器

    Microchip 发布首款 IEEE® 802.3bt 以太网供电 USB Type-C®电源和数据适配器

    如今,许多消费类、企业和工业设备将USB Type-C端口作为唯一的输入电源选项。虽然USB-C®技术可以提供高功率和高数据速率,但其安装范围必需在离AC插座最远3米的距离内。随着以太网供电(PoE)日益普遍,通过标准以太网电缆供电成为一种更普遍、更方便、同时也是最实用的解决方案,可以在最远100米的距离内提供电源和数据。虽然市场上大多数适配器只提供电源,但提供的功率有限,最高只有25W。Microchip Technology Inc.(美国微芯科技公司)今日发布一款具有最高功率的PoE转USB-C电源和数据适配器,能通过PoE基础设施支持的以太网电缆提供高达60W的USB输出功率。 新推出的适配器(部件编号PD-USB-DP60)可接受高达90W的PoE,并通过USB-C转换为60W输出,能为大多数相机、笔记本电脑、平板电脑和其他使用USB-C输入电源的设备供电。该适配器通过减少对AC基础设施的依赖来简化安装。由于无需依赖AC插座,不再受3米的距离限制,新款适配器可将电力输送到100米以外。新款适配器还增强了USB-C电源设备的远程电源管理能力。由PoE源提供的远程电源复位功能,可以通过Web界面或简单网络管理协议(SNMP)进行电源上电复位,无需在设备所在地手动拔掉插头并重新启动。 Microchip 推出的 PoE 转 USB-C 适配器可连接到部署了各种标准的PoE 源,支持较新的 IEEE® 802.3af/at/bt 标准以及传统的 PoE 标准。由于已经安装的PoE有各种不同的实施方案,因此拥有一个多功能的适配器至关重要。 Microchip PoE业务部总监Iris Shuker表示:“这款新器件是轻松部署USB-C设备并为其提供长距离电源和数据连接的理想选择。适配器采用Microchip的USB电源传输IC和PoE芯片组,可与我们最新的PoE供电器和中继器完美搭配。” 新款适配器具有将90W输入转换为60W输出的能力,使需要更高功率充电的设备能够使用以前无法使用的PoE,同时可与Microchip的高性价比单端口和多端口(最多24个)PoE供电器/中继器以及符合IEEE 802.3af/at/bt行业标准的交换机配对使用,每个端口可提供高达90W的功率。如果需要更低的功率为USB-C设备供电,可以使用IEEE802.3af(15.4W)或IEEE802.3at(30W)PoE源。 Microchip还提供支持PoE转USB-C适配器的关键集成电路解决方案,包括PD70xxx系列PoE IC和LAN7800 USB转以太网桥接器。该功能由Microchip的供电软件框架(PSF)实现。PSF是一个开源的供电(PD)协议栈,运行在UPD301C PD控制器上,为Microchip的PD控制器、单片机和USB集线器提供完整的PD 3.0功能和定制功能。 供货与定价 PoE 至 USB-C 适配器现已上市,单位售价 100 美元。如需了解更多信息,请联系 Microchip 销售代表、全球授权分销商或访问 Microchip 网站。如需购买本文提及的产品,请访问我们的直销网站或联系 Microchip 授权分销商。

    Microchip 以太网 Microchip 适配器

发布文章