无服务器架构解析:开发人员如何专注于业务代码本身?
**本文来自腾讯云技术沙龙,本次沙龙主题为Serverless架构开发与SCF部署实践 **
演讲嘉宾:黄文俊,曾负责企业级存储、企业级容器平台等产品的架构与开发,目前主要负责SCF腾讯无服务器云函数产品相关。对容器平台、微服务架构、无服务器架构以及DevOps等多种热门技术领域均有涉猎。
大家好,自我介绍一下,目前我是腾讯云无服务器云函数产品负责人。我做了很多年后端开发。今天是从一个程序员角度讲解一下我们怎么样用Serverless架构。
我将本次讲解分为几块:第一,Serverless架构介绍;第二,对云函数产品介绍;第三,Serverless使用场景。
讲Serverless架构之前我们可以来看一下整个云的发展过程,在没有云之前大家可能都是用的物理服务器,早期时候大家都用的物理机托管方式,采购一些服务器在机房里托管,这个时候大家前期要选择物理机型号,要做好IDC网络;如果出了问题还要请IDC人员帮你操作。这些设备的投入和运维成本还是很高的。
云时代到来之后,由于虚拟化技术的运用,我们用上了云主机。云主机是大家直接在云上做虚拟机购买,开通就可以使用。这时候我们称之为IaaS(基础设施即服务),这种情况下就无需物理机运营,直接放到云平台来做。而之后随着容器技术的发展,我们有了容器平台,或者叫PaaS(平台即服务)。在容器平台到来之后实际上还存在一部分基础设施运维问题,但是这时候基础设施逐渐下沉到运维人员进行操作;而从应用开发者角度来看,他们已经不用再去关心虚拟机,或者操作系统。在这种情况下,应用开发人员更多的去关注应用所需要的计算资源或者存储资源的使用。继续向前发展,我们到了FaaS(函数即服务)。这时候运维人员不需要关注底层的运维,而是按需运行的能力。业务开发人员能够进一步做与业务相关的事情。
接下来我们来看一下Serverless架构是什么。Serverless从物理机或虚拟机的使用上进行了分离,更关注上层业务的运行情况。Serverless架构包含两块:函数即服务和后端即服务。函数即服务提供的是计算能力。原有的计算能力,无论是容器也好,虚拟机也好都承载在一定的操作系统之上,函数即服务把计算能力进行了进一步抽象,我们在后文再继续进行展开。另外,Serverless还有后端即服务,比如对象存储,数据库应用,缓存服务,我们也可以称之为Serverless,因为这些服务也能够在云上提供开通即服务,开通即使用的能力。在使用这些产品时同样不需要关注它的服务器是什么样的,它的服务器部署在哪里,而是服务开通就可以使用了,后面的运维工作都交给了云,所以不用感知它的最底层服务器,因此我们也可以把它称之为Serverless。这种服务就称之为Serverless后端即服务。这两个合起来可以称为Serverless架构。
函数即服务的工作原理是什么样的?在Serverless上是怎样提供计算能力的?大家原来使用容器或者虚拟机的时候都可以知道,我们把代码上传到容器或者上传到虚拟机,然后启动一个进程,代码就可以运行,它就可以接受外部的请求,做一些实时的响应。Serverless和原有的容器或虚拟机不同,实现的是计算托管服务,Serverless用户首先要做的,是把我们称为云函数的代码,提交到平台上进行代码托管;然后要做的是配置触发器。为什么需要配置触发器?因为云函数的运行方式是触发式运行,有触发的时候,代码才会真正运行起来。所以配置触发器意味着我们给它设置了一个触发源,也就是定义了在什么事件下代码才真正运行起来。用户代码托管到平台之后,事件没有到来之前,它仅仅是代码文件和配置存储,代码并没有运行。什么情况下运行?是当事件触发真正到来的时候,云函数才会真正启动一个实例,这个实例就意味着一个计算单元。计算单元被拉起后,这个事件就被传到这个计算单元中进行计算处理。如果这个触发源的事件很多,并发很高的情况下,平台会根据事件的堆积情况,或者事件到达的速度,自动把同一份代码和配置拉起多个实例进行并发处理。因此可以看到Serverless的运行是按需运行,意味着只有在事件到来的情况下,代码才会被拉起,才会运行起来。
自动并发,是指云函数平台会根据事件堆积情况自动的进行并发,自动拉起多个实例进行处理。而原有的容器或者虚拟机如果要进行并发的话还是要有一定的手工参与,比如启动更多的容器,或者加入更多的虚拟机来承载高并发的请求。而函数即服务是完全自动的运行。
按需运行带来的另外一个特点,是代码在运行起来之后上才会占用计算资源。函数即服务的费用也是根据按需运行来的,也就是函数运行的时候才进行计费;而没有使用的情况下不会计费。实际上大多数互联网业务只有白天的时候,甚至六点之后大家下班之后业务才会迎来高峰,而到凌晨之后实际上没有多少请求的,因此函数即服务能够很好的满足波峰波谷来削峰填谷的能力。
从上面的原理可以看出函数即服务的一些特点,比如说代码托管,云函数平台所提供的直接就是运行环境,也就是支持各种开发语言的环境;对于开发者或者函数服务使用者来说,并没有感知到它下面的服务器在哪里,而是由函数平台完成了函数运行的调度。因此实际来讲不需要运维,包括操作系统优化,服务器维护等等这些都是由平台进行承载。
而秒级部署意味着函数在真正的被请求的时候才运行。而这个请求才运行代表着当请求到达平台的时候函数才会被实时拉起并运行。运行完成后如果没有后续请求,实例也会退还。
由于函数运行是事件触发的,而事件其实包含很多种类,有各种触发器都可以对接云函数。有越多的触发器对接,云函数所能提供的场景也就越多。
对于开发者来说,使用云函数的情况下,他真正关注的是应该业务,是使用代码去聚焦他的业务逻辑,例如是拿到这个事件后该进行什么样的逻辑操作,进行什么样的业务存储,而不需要去关注怎么使用业务代码实现高并发,怎么样实现高请求的承载能力。因此这里看到函数即服务能够为应用开发者带来一些便利,而自动并发本身也是函数即服务所具有的特点。
而对于腾讯云无服务器云函数,在最开始开发产品的时候,我们目标也是一样,就是把计算进行托管。在计算托管的情况下,我们使用计算就像我们使用腾讯云对象存储一样,在使用的时候不用关心最底层的运维,不用关心虚拟机或者物理机是否安全。和对象存储进行对比也能看到,我们计算也是按照实际使用情况进行计费。当然现在云函数还处于免费期,大家可以随时使用。
从使用方法来说,云函数本身,或者说函数即服务这种产品本身的使用方法都是很简单的。我们在开发的时候更多的关注于核心代码的编写。核心代码的意思实际上就是真正的业务逻辑。而且业务逻辑里不需要考虑高并发,因为由刚才给出来的函数即服务这种计算特点来看的话,在高并发请求的时候是通过多个实例处理进行,因此业务代码在编写的时候,就关注单个事件的处理就行。因此,第一步的核心的就是编写核心业务代码,就是用代码要实现什么样的业务。后续就是配置触发方式。配置触发方式就是把函数代码和触发源对接起来。和云平台上其他的产品进行对接,需要什么样的事件,处理什么样的事件,进行什么样的逻辑处理,做好这样的触发源对接后,函数就能够在事件产生的情况下运行。
因此,从整个使用方法来看的话,大家真正要做的是两步:第一,编写代码,第二,配置好触发。而对于底层的基础设施,环境配置这块都不需要大家操心的。
目前,腾讯云函数从运行环境来说目前已经支持了Python、Nodejs、PHP、Golang、Java等语言的开发运行环境。
接下来是触发器,因为触发器越多,云函数所能去使用的场景其实也越多,我们已经实现的触发器有定时触发器;腾讯云对象存储服务,包括文件的上传、删除等时间;CMQ 消息队列服务;API 网关服务,这个是通过serverless 架构实现 API 服务的一款重要触发器;另外,还有ckafka,这个是腾讯云提供的kafka能力。目前kafka算是一个开源产品,我们腾讯云把它包装后放到云上来,也是兼容标准的kafka协议。因此在很多情况下直接迁移到腾讯云不需要任何修改。因为kafka本身作为消息传递的载体,跟腾讯原有的消息队列类似,由消息来执行云函数。
下面介绍一下在什么场景下Serverless可以落地?第一,在Serverless场景中最常用到的就是API服务。大家知道实现一个API服务,无论是把API给到浏览器应用,还是给到手机APP使用,还是给到小程序应用,给到它们的时候是以API实现的。要实现这个要有WEB服务器接收连接,对接后端的业务代码,如果你要再进行文件存储,后端的结构化存储,或者有一些缓存需要读写,你的应用服务器后面可能还要对接相应的文件存储,结构化数据库,后续如果想使用缓存,再对接到相应的服务器或相应产品。如果把现有的API服务向Serverless架构演进,那么它将怎么样呈现呢?
在不改变 API 的情况下,它的前端浏览器应用、APP、小程序,都可以无缝对接上来。而使用API网关来承接 API 请求,当这个请求来到API网关,由它转发给云函数,触发云函数执行。云函数执行时运行业务逻辑。实际上云函数运行时要求无状态,因此这样的状态存储也需要用到后面的一些存储,无论做缓存也好还是数据库也好都要用上。因此,云上提供的产品一样可以进行对接。像文件存储的话可以用对象存储来进行。数据库的话一样的有相应的数据库产品,结构化还是非结构化数据库都有相应的产品可以使用。同样的,缓存也有相应的产品做对接。云函数通过代码编写,直接进行数据库的读写,或者缓存的读写都是可以的。
从整个服务架构来看的话,我们使用最前面的API网关,提供是API能力,甚至进一步能够直提供有SDK服务,更加的方便开发。SDK提供了各种开发语言来直接进行API调用。云函数在中间起到的是业务逻辑处理的作用,而状态数据或者其他业务数据的存储是依赖于后面的文件存储或者数据库进行的。API服务也是Serverless最常用的一种落地形式。
这里介绍的场景,都是我们客户在实际使用的场景。在 serverless落地场景中,对对象文件的处理也很常见。对象文件处理指的是对对象文件进行操作后的回调处理。回调通常是在对象文件创建或删除操作后产生的事件。云函数可以在获取到这个事件后进行后续的处理。这里常见的处理逻辑是下面几种,比如说图片处理,针对图片去生成各种尺寸的缩略图或者进行裁剪,然后再次存储到对象存储数据中,之后可以根据不同客户端的请求展示不同大小的图片到前端。
文件批量打包,用户需要进行文件筛选和打包的时候可以通过使用云函数来处理。在上传文件后,如果需要选择哪些文件来打包,把文件生成压缩包以供下载,这都可以由事件处理来进行。
日志归档分析,以及业务系统回调,也是云函数所承载的业务逻辑。比如说日志归档分析这种用法,用户会把每天的前端应用服务器的日志上传到对象存储中归档,归档后会触发云函数执行,云函数会拉下这些日志文件进行实时分析,它会抽取这些日志中的错误数,或者是其他业务相关或者用户关注的内容,然后再把它抽取到的信息或者统计到的信息写回数据库,供用户后续进行排查、使用。用户自身API调用也是,例如用户生成的一些视频文件上传到对象存储,会触发云函数,将上传文件的信息通知到用户的转码系统,通过视频转码转成不同分辨率然后再进行存储。当然转码是用户自身实现的业务系统,这块通过回调通知,通知它自身的业务系统。这些就是云函数在Serverless架构和对象存储连用的落地场景。
再就是CKafka消息处理。CKafka目前比较多的应用场景是做日志存储和日志搜集,例如有多台应用服务器在不断产生日志的情况下,可以把日志写到CKafka,然后CKafka再进行归档和后续分析。而 CKafka和云函数对接是由CKafka收到的信息来进行触发的。日志搜集后,要归档的日志,一般存储到对象存储当中。这种情况CKafka消息,会被推送给云函数,云函数再再把这些消息写到对象存储中去。有些用户不是写对象存储,而是写数据库,以数据库形式归档,其实也是一样的。有的使用场景,需要进行消息分析,会实时拿到消息后立刻分析里面的关键字,如果捕捉到了关键字,会立刻把这些消息推送到ckafka 的另一个topic 中,去及时的发出告警给到业务和运维人员。这也是 serverless 的一种用法,就是对消息的分析和转发。
消息队列和CKafka类似,但是消息队列一般不是进行日志的搜集,而是进行业务解耦。消息队列 CMQ 是腾讯云提供的一个高可靠金融级消息队列,通常进行一些业务级消息转发和处理。使用这个产品,实际上做的是业务解耦。云函数在这里承载着消息的逻辑处理过程,它能够在接收到消息后对消息立刻进行业务处理。这个业务处理就是实际的业务逻辑,比如我要根据里面某个消息进行判断,判断它是否合适,要不要进行后续的转发,或者转发到另外的业务系统中去?这就是业务之间执行的逻辑。
同时,我们也可以使用云函数,再次进行消息的分派,做状态转移。这个状态转移和后面消息转发都是一样的,它会识别消息里的内容,根据消息里的内容进行转发。这种情况下类似于我们使用云函数进行逻辑处理,把它转移到合适的消息队列,然后再进行处理。这也是我们所见过直接用云函数进行消息派发的使用方式。
最后一种形式现在也不少,就是利用定时器触发。原本大家更多是在运维场景下使用定时任务,在原有使用 crontab 脚本的情况下,大家通常还要关心脚本运行是否成功,这台虚拟机是否还在工作。云函数抛弃了大家使用传统的虚拟机或者物理机来去写crontab脚本还要确保可靠性的问题。而在实际使用定时器触发的场景下,这里也有几种用法:一种是业务拨测。这个是周期性的去拨测业务是否还在工作,如果出现异常的情况下能够及时的发出告警,发出邮件或者短信告诉到运维或开发人员。
另一个是定时备份,这个是在所需要的周期内,比如每天,或者每两天对数据库进行备份,针对数据库需要做数据导出,导出后再将导出内容以文件的形式存储到合适的地方,例如对象存储中,做好定时备份。
还有一个是定时数据计算。因为有些计算是根据一段时间内的统计之后,进行计算并展示。在实际场景中,我们腾讯云内部有业务就是在进行定时数据计算,每两小时做一次统计,然后再把统计数据写到数据库做后续业务的展示以及业务分析。
总结:Serverless架构本身给用户带来什么?它实际上就是允许我们更关注业务代码,因此可以更快速的构建业务然后上线。现在互联网开发速度越来越快,因此大家期望的是进一步加快开发和业务真正上线的速度,提高迭代的能力。因此,使用Serverless的话可以更快速让业务上线,让我们更快实现我们的想法。而按需使用是我们这个业务在上线之后,在真正产生请求后,业务才会被调动触发,才会有计算。而如果你的业务产生了爆发式增长,其实也不需要担心平台承载能力或者业务扩展是否跟得上,因为平台提供自动扩展能力,降低了大家对运维的诉求,大家不用关心很底层的东西,而运维人员也可以更偏重流程化和业务相关的运维。这就是Serverless架构给大家带来的一些好处。而作为Serverless里的核心,函数即服务这种产品,是Serverless中所呈现出来的计算型的组件,大家也可以看到它和触发源和后端的各种产品或服务有紧密关联,它可以更多的被看做是云时代的脚本,类似于黏合剂,把前面的触发源和后端的各种存储,数据,服务进行了黏合,真正实现架构落地,才是真正实现业务逻辑落地的能力。
Q&A
Q:云函数有无限扩展能力,但是整个系统也有可能是有限制的,比如它的背后的数据库和存储,我能不能设这样的一个扩展上限?
A:这可以设置上限的。目前可以通过提交工单的方式来设置期望的合适上限。扩展可以在后台设置一个合适值,并发实例扩展到这个就不会再扩展了,避免大量实例连接造成后端的数据库或存储超过连接数限制。
Q:实现 API 服务有哪些开发方式?
A:云函数实现 API 服务的开发方式有好几种,一个是全部在一个函数里完成,路径和方法解析都在函数里进行。这也是偏传统的开发方式。另外一种是进行拆解,每个函数处理一个 API 路径和方法的请求,这种是微服务的开发方式。
而函数和函数之间调用也是可以实现。一种是直接使用云API,一种是使用 API网关包装后的 API。云函数被触发调用的话,除了介绍的很多触发器,在不使用这些触发器的情况下,通过代码或者脚本也可以通过腾讯云的云API调用。
Q:在事件触发的时候,就是CMQ事件触发的时候,是否可以保证函数被执行呢?因为不像API网关,调用一下函数可以启动,前端可以感知到。但是CMQ,就是扔到消息队列能否保证这个函数被执行呢?
A:因为不像API网关是同步调用。同步调用就是这个调用在运行过程中如果出了问题,无论是平台,比如说资源不足,并发不够,或者比如使用的超时了,这个时候可以立刻感知到。 而像CMQ或者CKafka都是异步的,这样意味着调用后你感知不到,这个消息什么时候执行,执行结果都没法感知。这种解决方法有两种:一种是函数运行后的结果输出,把消息处理后的输出结果再放到另一个消息队列中去,让你外部的业务系统能够感知到。当然这种对外通知也是异步通知。同步通知是另外一种,就是函数里可以对自身业务进行回调API,可以通过代码知道现在的数据处理是什么样的结果,处理完后可以立刻回调到API让业务系统接收到处理结果。
Q:像COS触发,拿视频转码来说,这个有可能在300秒内处理不完。现在函数设置时间只能最高300秒,这个有什么解决方案吗?
A:为什么各家的云平台,都把这个时间大致定在这个范围内,就是不希望在云函数中进行太重的计算。视频转码就属于太重的计算,而云函数提供的包括CPU能力,内存大小都有限,实际上都不太适合在云函数内进行转码。实际上可以用一些视频服务来实现转码,使用云函数来做这两者之间的桥梁,例如对象存储的事件触发后,云函数拿到这个事件通过调用视频转码服务来转码,而不是在云函数转码。目前腾讯云有这个服务,你可以试试看。
获取更多详细资料,请戳以下链接:
Serverless 架构.pdf
问答
serverless:如何删除一个函数?
相关阅读
让业务感知不到服务器的存在——基于弹性计算的无服务器化实践
使用 SCF 无服务器云函数定时备份数据库
云学院 · 课程推荐 | 腾讯专项技术测试组长,结合8年经验为你细说冷热分离法则
**此文已由作者授权腾讯云+社区发布,原文请点击 **
搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!
海量技术实践经验,尽在云加社区!
上一篇: 腾讯无服务器小时在线分享会 1
推荐阅读
-
SSM三大框架基础面试题-一、Spring篇 什么是Spring框架? Spring是一种轻量级框架,提高开发人员的开发效率以及系统的可维护性。 我们一般说的Spring框架就是Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。这些模块是核心容器、数据访问/集成、Web、AOP(面向切面编程)、工具、消息和测试模块。比如Core Container中的Core组件是Spring所有组件的核心,Beans组件和Context组件是实现IOC和DI的基础,AOP组件用来实现面向切面编程。 Spring的6个特征: 核心技术:依赖注入(DI),AOP,事件(Events),资源,i18n,验证,数据绑定,类型转换,SpEL。 测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。 数据访问:事务,DAO支持,JDBC,ORM,编组XML。 Web支持:Spring MVC和Spring WebFlux Web框架。 集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。 语言:Kotlin,Groovy,动态语言。 列举一些重要的Spring模块? Spring Core:核心,可以说Spring其他所有的功能都依赖于该类库。主要提供IOC和DI功能。 Spring Aspects:该模块为与AspectJ的集成提供支持。 Spring AOP:提供面向切面的编程实现。 Spring JDBC:Java数据库连接。 Spring JMS:Java消息服务。 Spring ORM:用于支持Hibernate等ORM工具。 Spring Web:为创建Web应用程序提供支持。 Spring Test:提供了对JUnit和TestNG测试的支持。 谈谈自己对于Spring IOC和AOP的理解 IOC(Inversion Of Controll,控制反转)是一种设计思想: 在程序中手动创建对象的控制权,交由给Spring框架来管理。IOC在其他语言中也有应用,并非Spring特有。IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象。 将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。IOC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。在实际项目中一个Service类可能由几百甚至上千个类作为它的底层,假如我们需要实例化这个Service,可能要每次都搞清楚这个Service所有底层类的构造函数,这可能会把人逼疯。如果利用IOC的话,你只需要配置好,然后在需要的地方引用就行了,大大增加了项目的可维护性且降低了开发难度。 Spring中的bean的作用域有哪些? 1.singleton:该bean实例为单例 2.prototype:每次请求都会创建一个新的bean实例(多例)。 3.request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。 4.session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。 5.global-session:全局session作用域,仅仅在基于Portlet的Web应用中才有意义,Spring5中已经没有了。Portlet是能够生成语义代码(例如HTML)片段的小型Java Web插件。它们基于Portlet容器,可以像Servlet一样处理HTTP请求。但是与Servlet不同,每个Portlet都有不同的会话。 Spring中的单例bean的线程安全问题了解吗? 概念用于理解:大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例bean存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。 有两种常见的解决方案(用于回答的点): 1.在bean对象中尽量避免定义可变的成员变量(不太现实)。 2.在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal(线程本地化对象)中(推荐的一种方式)。 ThreadLocal解决多线程变量共享问题(参考博客):https://segmentfault.com/a/1190000009236777 Spring中Bean的生命周期: 1.Bean容器找到配置文件中Spring Bean的定义。 2.Bean容器利用Java Reflection API创建一个Bean的实例。 3.如果涉及到一些属性值,利用set方法设置一些属性值。 4.如果Bean实现了BeanNameAware接口,调用setBeanName方法,传入Bean的名字。 5.如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader方法,传入ClassLoader对象的实例。 6.如果Bean实现了BeanFactoryAware接口,调用setBeanClassFacotory方法,传入ClassLoader对象的实例。 7.与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。 8.如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执postProcessBeforeInitialization方法。 9.如果Bean实现了InitializingBean接口,执行afeterPropertiesSet方法。 10.如果Bean在配置文件中的定义包含init-method属性,执行指定的方法。 11.如果有和加载这个Bean的Spring容器相关的BeanPostProcess对象,执行postProcessAfterInitialization方法。 12.当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy方法。 13.当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法。 Spring框架中用到了哪些设计模式? 1.工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。 2.代理设计模式:Spring AOP功能的实现。 3.单例设计模式:Spring中的bean默认都是单例的。 4.模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到了模板模式。 5.包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。 6.观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。 7.适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。 还有很多。。。。。。。 @Component和@Bean的区别是什么 1.作用对象不同。@Component注解作用于类,而@Bean注解作用于方法。 2.@Component注解通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用@ComponentScan注解定义要扫描的路径)。@Bean注解通常是在标有该注解的方法中定义产生这个bean,告诉Spring这是某个类的实例,当我需要用它的时候还给我。 3.@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean注解来注册bean。比如当引用第三方库的类需要装配到Spring容器的时候,就只能通过@Bean注解来实现。 @Configuration public class AppConfig { @Bean public TransferService transferService { return new TransferServiceImpl; } } <beans> <bean id="transferService" class="com.kk.TransferServiceImpl"/> </beans> @Bean public OneService getService(status) { case (status) { when 1: return new serviceImpl1; when 2: return new serviceImpl2; when 3: return new serviceImpl3; } } 将一个类声明为Spring的bean的注解有哪些? 声明bean的注解: @Component 组件,没有明确的角色 @Service 在业务逻辑层使用(service层) @Repository 在数据访问层使用(dao层) @Controller 在展现层使用,控制器的声明 注入bean的注解: @Autowired:由Spring提供 @Inject:由JSR-330提供 @Resource:由JSR-250提供 *扩:JSR 是 java 规范标准 Spring事务管理的方式有几种? 1.编程式事务:在代码中硬编码(不推荐使用)。 2.声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务。 Spring事务中的隔离级别有哪几种? 在TransactionDefinition接口中定义了五个表示隔离级别的常量:ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。 Spring事务中有哪几种事务传播行为? 在TransactionDefinition接口中定义了八个表示事务传播行为的常量。 支持当前事务的情况:PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。 不支持当前事务的情况:PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。 其他情况:PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED。 二、SpringMVC篇 什么是Spring MVC ?简单介绍下你对springMVC的理解? Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。 Spring MVC的工作原理了解嘛? image.png Springmvc的优点: (1)可以支持各种视图技术,而不仅仅局限于JSP; (2)与Spring框架集成(如IoC容器、AOP等); (3)清晰的角色分配:前端控制器(dispatcherServlet) , 请求到处理器映射(handlerMapping), 处理器适配器(HandlerAdapter), 视图解析器(ViewResolver)。 (4) 支持各种请求资源的映射策略。 Spring MVC的主要组件? (1)前端控制器 DispatcherServlet(不需要程序员开发) 作用:接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。 (2)处理器映射器HandlerMapping(不需要程序员开发) 作用:根据请求的URL来查找Handler (3)处理器适配器HandlerAdapter 注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。 (4)处理器Handler(需要程序员开发) (5)视图解析器 ViewResolver(不需要程序员开发) 作用:进行视图的解析,根据视图逻辑名解析成真正的视图(view) (6)视图View(需要程序员开发jsp) View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等) springMVC和struts2的区别有哪些? (1)springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilter)。 (2)springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。 (3)Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。 SpringMVC怎么样设定重定向和转发的? (1)转发:在返回值前面加"forward:",譬如"forward:user.do?name=method4" (2)重定向:在返回值前面加"redirect:",譬如"redirect:http://www.baidu.com" SpringMvc怎么和AJAX相互调用的? 通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 : (1)加入Jackson.jar (2)在配置文件中配置json的映射 (3)在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。 如何解决POST请求中文乱码问题,GET的又如何处理呢? (1)解决post请求乱码问题: 在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8; <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> (2)get请求中文参数出现乱码解决方法有两个: ①修改tomcat配置文件添加编码与工程编码一致,如下: <ConnectorURIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/> ②另外一种方法对参数进行重新编码: String userName = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8") ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码。 Spring MVC的异常处理 ? 统一异常处理: Spring MVC处理异常有3种方式: (1)使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver; (2)实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器; (3)使用@ExceptionHandler注解实现异常处理; 统一异常处理的博客:https://blog.csdn.net/ctwy291314/article/details/81983103 SpringMVC的控制器是不是单例模式,如果是,有什么问题,怎么解决? 是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决方案是在控制器里面不能写成员变量。(此题目类似于上面Spring 中 第5题 有两种解决方案) SpringMVC常用的注解有哪些? @RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。 @RequestBody:注解实现接收http请求的json数据,将json转换为java对象。 @ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。 SpingMvc中的控制器的注解一般用那个,有没有别的注解可以替代? 一般用@Controller注解,也可以使用@RestController,@RestController注解相当于@ResponseBody + @Controller,表示是表现层,除此之外,一般不用别的注解代替。 如果在拦截请求中,我想拦截get方式提交的方法,怎么配置? 可以在@RequestMapping注解里面加上method=RequestMethod.GET。 怎样在方法里面得到Request,或者Session? 直接在方法的形参中声明request,SpringMVC就自动把request对象传入。 如果想在拦截的方法里面得到从前台传入的参数,怎么得到? 直接在形参里面声明这个参数就可以,但必须名字和传过来的参数一样。 如果前台有很多个参数传入,并且这些参数都是一个对象的,那么怎么样快速得到这个对象? 直接在方法中声明这个对象,SpringMVC就自动会把属性赋值到这个对象里面。 SpringMVC中函数的返回值是什么? 返回值可以有很多类型,有String, ModelAndView。ModelAndView类把视图和数据都合并的一起的。 SpringMVC用什么对象从后台向前台传递数据的? 通过ModelMap对象,可以在这个对象里面调用put方法,把对象加到里面,前台就可以拿到数据。 怎么样把ModelMap里面的数据放入Session里面? 可以在类上面加上@SessionAttributes注解,里面包含的字符串就是要放入session里面的key。 SpringMvc里面拦截器是怎么写的: 有两种写法,一种是实现HandlerInterceptor接口,另外一种是继承适配器类,接着在接口方法当中,实现处理逻辑;然后在SpringMvc的配置文件中配置拦截器即可: <!-- 配置SpringMvc的拦截器 --> <mvc:interceptors> <!-- 配置一个拦截器的Bean就可以了 默认是对所有请求都拦截 --> <bean id="myInterceptor" class="com.zwp.action.MyHandlerInterceptor"></bean> <!-- 只针对部分请求拦截 --> <mvc:interceptor> <mvc:mapping path="/modelMap.do" /> <bean class="com.zwp.action.MyHandlerInterceptorAdapter" /> </mvc:interceptor> </mvc:interceptors> 注解原理: 注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池 三、Mybatis篇 什么是MyBatis? MyBatis是一个可以自定义SQL、存储过程和高级映射的持久层框架。 讲下MyBatis的缓存 MyBatis的缓存分为一级缓存和二级缓存,一级缓存放在session里面,默认就有, 二级缓存放在它的命名空间里,默认是不打开的,使用二级缓存属性类需要实现Serializable序列化接口, 可在它的映射文件中配置<cache/> Mybatis是如何进行分页的?分页插件的原理是什么? 1)Mybatis使用RowBounds对象进行分页,也可以直接编写sql实现分页,也可以使用Mybatis的分页插件。 2)分页插件的原理:实现Mybatis提供的接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql。 举例:select * from student,拦截sql后重写为:select t.* from (select * from student)t limit 0,10 简述Mybatis的插件运行原理,以及如何编写一个插件? 1)Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、 Executor这4种接口的插件,Mybatis通过动态代理, 为需要拦截的接口生成代理对象以实现接口方法拦截功能, 每当执行这4种接口对象的方法时,就会进入拦截方法, 具体就是InvocationHandler的invoke方法,当然, 只会拦截那些你指定需要拦截的方法。 2)实现Mybatis的Interceptor接口并复写intercept方法, 然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可, 记住,别忘了在配置文件中配置你编写的插件。 Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不? 1)Mybatis动态sql可以让我们在Xml映射文件内, 以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能。 2)Mybatis提供了9种动态sql标签:trim|where|set|foreach|if|choose|when|otherwise|bind。 3)其执行原理为,使用OGNL从sql参数对象中计算表达式的值, 根据表达式的值动态拼接sql,以此来完成动态sql的功能。 #{}和${}的区别是什么? 1)#{}是预编译处理,${}是字符串替换。 2)Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值(有效的防止SQL注入); 3)Mybatis在处理${}时,就是把${}替换成变量的值。 为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里? Hibernate属于全自动ORM映射工具, 使用Hibernate查询关联对象或者关联集合对象时, 可以根据对象关系模型直接获取,所以它是全自动的。 而Mybatis在查询关联对象或关联集合对象时, 需要手动编写sql来完成,所以,称之为半自动ORM映射工具。 Mybatis是否支持延迟加载?如果支持,它的实现原理是什么? 1)Mybatis仅支持association关联对象和collection关联集合对象的延迟加载, association指的就是一对一,collection指的就是一对多查询。 在Mybatis配置文件中, 可以配置是否启用延迟加载lazyLoadingEnabled=true|false。 2)它的原理是,使用CGLIB创建目标对象的代理对象, 当调用目标方法时,进入拦截器方法, 比如调用a.getB.getName, 拦截器invoke方法发现a.getB是null值, 那么就会单独发送事先保存好的查询关联B对象的sql, 把B查询上来,然后调用a.setB(b), 于是a的对象b属性就有值了, 接着完成a.getB.getName方法的调用。 这就是延迟加载的基本原理。 MyBatis与Hibernate有哪些不同? 1)Mybatis和hibernate不同,它不完全是一个ORM框架, 因为MyBatis需要程序员自己编写Sql语句, 不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句, 并将java对象和sql语句映射生成最终执行的sql, 最后将sql执行的结果再映射生成java对象。 2)Mybatis学习门槛低,简单易学,程序员直接编写原生态sql, 可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发, 例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁, 一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性, 如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。 3)Hibernate对象/关系映射能力强,数据库无关性好, 对于关系模型要求高的软件(例如需求固定的定制化软件) 如果用hibernate开发可以节省很多代码,提高效率。 但是Hibernate的缺点是学习门槛高,要精通门槛更高, 而且怎么设计O/R映射,在性能和对象模型之间如何权衡, 以及怎样用好Hibernate需要具有很强的经验和能力才行。 总之,按照用户的需求在有限的资源环境下只要能做出维护性、 扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。 MyBatis的好处是什么? 1)MyBatis把sql语句从Java源程序中独立出来,放在单独的XML文件中编写, 给程序的维护带来了很大便利。 2)MyBatis封装了底层JDBC API的调用细节,并能自动将结果集转换成Java Bean对象, 大大简化了Java数据库编程的重复工作。 3)因为MyBatis需要程序员自己去编写sql语句, 程序员可以结合数据库自身的特点灵活控制sql语句, 因此能够实现比Hibernate等全自动orm框架更高的查询效率,能够完成复杂查询。 简述Mybatis的Xml映射文件和Mybatis内部数据结构之间的映射关系? Mybatis将所有Xml配置信息都封装到All-In-One重量级对象Configuration内部。 在Xml映射文件中,<parameterMap>标签会被解析为ParameterMap对象, 其每个子元素会被解析为ParameterMapping对象。 <resultMap>标签会被解析为ResultMap对象, 其每个子元素会被解析为ResultMapping对象。 每一个<select>、<insert>、<update>、<delete> 标签均会被解析为MappedStatement对象, 标签内的sql会被解析为BoundSql对象。 什么是MyBatis的接口绑定,有什么好处? 接口映射就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定, 我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置. 接口绑定有几种实现方式,分别是怎么实现的? 接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加 上@Select@Update等注解里面包含Sql语句来绑定, 另外一种就是通过xml里面写SQL来绑定,在这种情况下, 要指定xml映射文件里面的namespace必须为接口的全路径名. 什么情况下用注解绑定,什么情况下用xml绑定? 当Sql语句比较简单时候,用注解绑定;当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多 MyBatis实现一对一有几种方式?具体怎么操作的? 有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配置association节点配置一对一的类就可以完成; 嵌套查询是先查一个表,根据这个表里面的结果的外键id, 去再另外一个表里面查询数据,也是通过association配置, 但另外一个表的查询通过select属性配置。 Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别? 能,Mybatis不仅可以执行一对一、一对多的关联查询, 还可以执行多对一,多对多的关联查询,多对一查询, 其实就是一对一查询,只需要把selectOne修改为selectList即可; 多对多查询,其实就是一对多查询,只需要把selectOne修改为selectList即可。 关联对象查询,有两种实现方式,一种是单独发送一个sql去查询关联对象, 赋给主对象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用join查询, 一部分列是A对象的属性值,另外一部分列是关联对象B的属性值, 好处是只发一个sql查询,就可以把主对象和其关联对象查出来。 MyBatis里面的动态Sql是怎么设定的?用什么语法? MyBatis里面的动态Sql一般是通过if节点来实现,通过OGNL语法来实现, 但是如果要写的完整,必须配合where,trim节点,where节点是判断包含节点有 内容就插入where,否则不插入,trim节点是用来判断如果动态语句是以and 或or 开始,那么会自动把这个and或者or取掉。 Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式? 第一种是使用<resultMap>标签,逐一定义列名和对象属性名之间的映射关系。 第二种是使用sql列的别名功能,将列别名书写为对象属性名, 比如T_NAME AS NAME,对象属性名一般是name,小写, 但是列名不区分大小写,Mybatis会忽略列名大小写,
-
无服务器架构解析:开发人员如何专注于业务代码本身?
-
对话NGC蔡岩:从机制创新到价值沉淀,解析DeFi产品开发逻辑 |链捕手 - 真正的DeFi产品首先要有足够的安全性和稳定性,如果能在此基础上有一些功能创新,那就非常好了。像 Uniswap 这样逐渐成为 DeFi 基础架构的产品,可遇而不可求。 链式捕手:固定利率协议之前关注度比较高,但观察下来发现,大部分协议还是类似于传统金融CDO(抵押债务凭证)的玩法,风险系数很高,您如何理解这块业务的价值和风险? 蔡岩:确实有些定息协议类似CDO玩法,背后绑定一个债券,但并不是所有的定息协议都是这样的玩法,像这种CDO玩法的主要代表项目是88mph,背后绑定的是Aave、Compoud这样的借贷协议,在此基础上做定息和浮息债券;像APWine,背后同样是Aave,它会发行期货收益代币来锁定你的收益;Notional本身是做借贷市场的,在此基础上做定息协议。 非 CDO 的玩法,比如 Horizon,更像是一个利率撮合器,背后需要用户通过拍卖产生更合适的目标收益率;像 Saffron、BarnBridge 等是通过风险分级来定义不同的收益率。总的来说,创新还是挺多的。 价值层面是创新和想象力,因为在传统金融领域,比如银行做固定收益证券,或者评级机构给风险分级,这些业务都非常大,利润也很丰厚。而 DeFi 的对口业务给了类似业务很大的想象空间。尤其是固定利率协议的成熟产品不多,尝试各种微创新是很有意义的。 风险程度还是要具体到不同的玩法,比如,在 Aave、Compoud 等借贷协议的固定利率协议背后,如果这些借贷协议受到攻击,与之绑定的固定利率协议也会受损。 同样,如果自己做借贷市场,可能更需要更强的开发能力。再有,如果该程序的机制或参数设计不当,同样会导致协议运行不稳定,并可能造成大量用户清盘。 总的来说,风险在于固定利率协议的设计,这是一个非常复杂的过程,需要不断地尝试和出错。 链式捕捉器:刚刚提到背后是Aave/Compound的固定费率协议风险较大,您认为Aave最大的不确定性和创新点分在哪里? 蔡岩:其实爱钱进一直被认为是走在行业前列的项目,他们的迭代速度非常快,比如率先尝试闪贷、推出新的经济激励模式、推出目前业内首个安全模块、尝试L2解决方案等等。 而在主要的借贷业务上,他们又十分谨慎,比如在抵押率、清算系数等风险参数的设计上相对于其他借贷协议较为保守,并不会存在为了吸引更多借贷资金而降低风险的要求。 与许多 DeFi 项目一样,即使 Aave 进行了多次审计,也无法保证不存在漏洞。前段时间,Aave 刚进入 V2 阶段时,白帽黑客就指出了某个漏洞。 之前的创新点可能是闪电借贷,这是当时业内独一无二的新产品功能,也为 Aave 带来了不少收益。当然,也有人批评闪电贷只能方便黑客实现资金效益的最大化,但工具本身并没有错,未来闪电贷肯定会有更多的应用场景。 其次是安全模块的设计,这有点像项目本身的储备金库,保障项目的安全性,这也是爱维开创的先河。说实话,目前大多数项目都没有做到代币模式的良性或正向运营,也做不到像Aave一样的安全模块,这是一个不小的门槛。 Chaincatcher从某种程度上来说,挖矿模式是DeFi财富效应的根本支撑,但Aave的CEO却说挖矿机制带来的动力是不可持续的,您怎么看这个观点? 蔡岩:"挖矿机制 "不可能失效,因为它是一种激励机制,或者说是项目冷启动的一种方式。但流动性开采亚博体育手机客户端不会一直高涨。比如去年11月的流行性挖矿高APY持续了一两个月就崩盘了,导致DeFi市场大幅回调。 Aave、Uniswap、Synthetix等项目真正爆发进入市值前15名也是在今年2月,我更倾向于这是头部DeFi长期价值的体现。虽然大家都喜欢抢高APY的矿机,但我个人很少参与挖矿,所以我并不觉得流动性挖矿是DeFi的基本面支撑。