Category: 后端开发

如何使用MySQL保存一个图片并且用PHP得到它 0

如何使用MySQL保存一个图片并且用PHP得到它

首先,我要说的是把一个图片保存在MySQL中,再读出来是一个很不推荐的做法。我们只是看看假如真的要这样做,该如何做,最后我会和大家聊聊一般我们如何处理这种使用情况,以及不推荐这样做的原因。 首先图片是一个BLOB(Binary Large Object)是可以用来存储二进制的数据。这就是用来保存图片,文件等等的数据。因为这种object通常都很大,所以我们需要定义一个很大的域来保存信息。如何通过PHP来插入呢,其实很简单: 1)读取图片成二进制 2)准备插入到数据库 3)使用SQL语句插入相关数据到数据库 下面我们来看看简单的PHP代码: 这里我们使用了fread()来把图片读取成比特,有了相关比特数据之后,就是简单地把它插入到BLOB的列就可以了。我们例子中image列是一个BLOB的数据类型。需要注意的是你选择的BLOB的列大小需要能够保存相应图片的大小,因为MySQL还是有不同大小的BLOB数据类型的。 下面我们来看一下如何获取图片数据,同样需要三个步骤: 1)设置content的type到图片的类型 2)找到图片,并且把它从数据库中拿出来 3)显示图片 相关的代码如下: 我们把数据从image列中拿出来,并且保存在一个变量中,然后我们设置相关content-type为image/jpeg就可以了,然后就可以显示相关的内容了。 需要注意的是不要再header()调用之前打印任何部分,否则在调用header的时候会报错”Headers already sent”。 其实说白了,这个和别的BLOB得数据的插入读取是一样的,并没有任何差别。 下面我们来谈谈为什么不要把图片保存到数据库中呢?因为数据库一般为了查询的方便会进行分页,而一些大的数据则会导致查询变得很慢,所以我们还是不要这样做为好。 那么推荐的做法是什么呢?我们一般会存储相关文件的路径就可以了。当想要找相关文件的时候,只要找到相关的路径就可以了,然后再根据相关的路径去磁盘找到需要显示的文件就可以了。这样做有两个好处:1)这样数据库的数据就很小了。 2)有了文件的名字,我们就可以用文件名来查找相关的文件了。 好了,这就是本文要讲的内容了。 参考文章:https://www.dreamincode.net/forums/blog/114/entry-3598-inserting-images-into-mysql-and-retrieving-them-using-php/

0

Database数据一致性介绍

数据一致性的场景很简单,假如我们把一个值赋给一个变量,那么我们之后立即再去读这个变量,突然发现它的值和我们想象的不一致,是不是很沮丧,对的,这就是数据的一致性。 而在使用分布式的数据库的时候,尤其是那些对数据一致性不是很严格保证的情况下,这种情况经常会发生。也许你会问,什么?难道他们不应该给我们保证数据是一致的吗?我的回答呢,总得来说最后他都会能够保证的,但是这个中间的间隔时间则是取决于不同数据库的实现。 它们这样做是因为有一个trade off在里面,有些数据库为了提供可靠性和性能,则会牺牲掉一致性的保证。而有些数据库则允许你进行选择,你是要强一致性还是要高性能,比如Azure的cosmos DB和Cassandra。 数据库请求分析 我们来分析一下数据请求的时间线是什么样的,理想状况下,你的请求会被立即执行。 然而事实上肯定不是这样,我们需要时间连接存储数据的地方,然后还需要时间返回response给你。如下图所示: 这里最好的保证就是请求是在你调用和完成中间的某个时间点完成的。你可能会想,这个和你给变量赋值是类似,比如x=1,然后再读,你会发现它是1(前提就是这中间没有别的thread再写这个x)。但是假如是一个分布式系统,就不是这么简单了,你需要考虑别的server是如何更新到这个新的值的,这里就有一个设计的trade off,尤其是可靠性,性能和一致性之间的考虑。 假如我们有一个简单的分布式的键值存储,他由一系列的副本组成。这些副本在他们中间选择了一个leader,然后只要这个leader才能做写的操作。当这个leader接收到了一个写的请求,它会广播这个请求到所有的副本。即使这些所有的副本受到的请求是一样的顺序,他们更新的时间都是不同的。 所以,现在假如你来考虑读的情况,你该怎么处理?因为读是可以发生在leader上,也可能发生在任何一个副本上面,但假如所有的读都要到leader这边,那么显然性能就不会好。从另一个方面来说,假如我们让读发生在副本上,这样一来性能就会好很多,不管我们系统扩大到什么程度,性能显然都不会有大的问题。但是这里会有一个额外的问题,那就是因为每一个副本的更新可能都会不及时,所以他们的值就会有差别,这就会导致我们在不同副本读出来的值会有差异。 所以,最终这里有一个trade off,那就是系统的性能,可靠性和一致性。为了更好地理解这个关系,我们来先看看我们是怎么定义一致性的。 强一致性 假如所有的读写请求都是通过leader进行的,那么不管有多少的副本,对客户端来说,他们的操作都是原子的,他们总能够得到他们想要的数据。 当然因为一个请求并不是理解执行的,总归需要一个时间,数据的更新是发生在执行和完成之间,假如我们做这个保证,那这就是强一致性。 但是总是通过leader来执行也是有问题的,比如说假如客户端发送了一个请求给leader,在客户发送请求到leader之后,leader还是认为它自己是leader,但是事实上它已经不是了。这个时候,leader还来处理这个写的请求,那么系统其实就已经不是强一致性了。有处理这种情况,那么leader就需要问一下大多数副本,是不是还认为它是leader,只有这种情况下才能够执行写的请求,并且把response返回给客户端,这样的操作,显然增加了读的时间,性能必然会下降。 顺序一致性 前面我们讨论了强一致性,所有的读都会去leader那边,而事实上,这样做就会导致读的速度很慢,因为leader还需要和大多数副本进行商议。这种情况下为了提高读的性能,我们需要允许副本支持读。 尽管副本的读是在leader之后的,但是它的更新顺序始终是和leader一致的。假如客户端A总是读副本1,而客户端B总是读副本2,那么这两个客户端看到的数据就是不相同的,因为他们的副本并不是总是一致的。如下所示: 这里两个副本虽然更新的时间不同,但是他们的更新顺序则是相同的,这就是所谓的顺序一致性。 最终一致性 尽管我们希望增加读的速率,但是这里还有一个问题,副本假如出问题了怎么办?我们需要允许客户端查询所有的副本,从而来增加一致性。但是这里的对一致性来说就有了新的挑战,比如说假如客户端1开始是在副本1上查询,而副本2是落后副本1的,在某一个时间点,假如客户端切换到了副本2,会发现读出来的值竟然是以前的值,这个显然就是有问题的。这里唯一保证的就是所有的副本最终读出来的值都是一致的,这就是所谓的最终一致性。 使用最终一致性的系统是非常复杂的,因为这个和你平时的读写有很大的差别。可能会遇到很多很难发现的问题。所以这里就需要根据你的应用来看,它最终需要的是什么。比如说,这个最终一致性的系统对你想看看有多少人访问你的系统这样的应用是足够的,但对那些付费系统则显示强一致性是更好的选择。 总结 有很多文章会介绍各种一致性的模型,但是背后的理论其实是一致的:越严格的一致性,则需要牺牲越多的性能,以及发生问题时,可靠性越低。这就是所谓的PACELC理论。这个理论认为,分布式系统中有网络分区P,那么我们就需要在可靠性(A)以及一致性(C)之间进行选择。即使在没有分区的情况下正常运行,我们也必须在延迟(L)和一致性(C)之间进行选择。 参考文章:https://robertovitillo.com/what-every-developer-should-know-about-database-consistency/

0

Protobuf和gRPC基础介绍

本文将会讨论一下Protobuf,以及最简单和便捷实现protobuf的gRPC。 什么是protobuf 在官网介绍中,Protocol Buffers是Google用来进行序列化数据的一个机制,他和语言无关,也独立于平台。就和JSON类似,但是它比JSON要更小,更快更加简单。你可以自己定义如何组织你的数据,然后你就可以使用相关的代码来很方便地进行数据的读写。 目前有两个版本的proto,分别是proto2和proto3. 本文使用的是proto3,从语法角度来看,proto2和proto3是有很区别的,不过具体的rpc这一块,两者其实是相似的。 什么是gRPC 所谓gPRC就是一个RPC(Remote Procedure Call)的框架,它可以在任何环境下运行,并且支持多种编程语言。它使用protobuf作为其接口定义语言以及基础信息交换的格式。gRPC的很多很赞的功能都是通过双向流以及多路复用的HTTP/2来实现的。更多的gRPC的介绍可以参见这里。 gRPC还是REST? 其实有很多理由让我们应该选择gRPC而不是Rest: 1)gRPC通过payload来传输二进制数据而不是文本,这样就使得相互的交互更加迅捷和紧凑。 2)gRPC是通过HTTP/2的长连接来实现的,所以不需要每一个请求都建立一个连接。 3)gRPC是类型安全的,所以假如你期待的是整形,那么就没有人能够发送字符串。 4)gRPC比REST要稍微快一点,虽然只是毫秒级的。假如你对毫秒级的速度比较敏感,那么gRPC是一个更好的选择。 5)gRPC支持流,所以假如你正在寻找分布式流支持,那么gRPC可以很好的完成这项任务。 不同的gRPC交互机制 1)一元化 2)客户端流 3)服务器端流 4)双向流 本文主要介绍第一种,一元化的情况。我们的使用场景很简单,就是一个订购服务会调用订购确认服务来确认订购已经被加入到了购物车。首先是要创建一个client和server端的合同: Protobuf contract: 上面代码中各种数据类型可以参见这里。server端需要实现一个OrderConfrimationService::confirm,它把Order作为输入,然后返回OrderConfirmation。在上面contract定义之后,我们可以使用下面的maven插件来产生代码: 执行mvn clean install命令来产生代码。 创建server gRPC需要它自己的server,当然创建server是非常简单的。首先我们来实现gRPC的服务: 这里我们扩展了基本的OrderConfrimationServiceImplBase,主要是override了confirm的方法。这里你可以看到confirm其实返回的是void,那么是如何把response传递给clinet的呢,其实他是通过StreamObserver的onNext()方法实现的,当然最终还要调用一下onCompleted方法。 创建client Client端的代码非常直接:...

2020年web开发(前后端)路线图 0

2020年web开发(前后端)路线图

2020年最新的Web开发路线图已经出炉了,这个路线图主要分成四个部分:通用的技能,前端技能,后端技能以及Devops。通用技能就是所有人都需要知道的,而前后端技能则是根据你的选择来学习,当然假如你的目标是全栈工程师的话,那前后端技能都是需要学习的。 本文的路线图其实是由Kamranahmedse制作的,他发布在他的github上。 通用技能 不管你是想做前端还是后端,有一些通用技能是都需要学习的,如下图所示: Git 我想这个大家都很熟悉,git是一个很好的版本控制软件,也是所有程序员应当掌握的一个工具。 SSH 他主要用来进行远程登录。 HTTP/HTTPS HTTP/HTTPS是web开发的骨干部分,对所有web开发者来说,理解这一部分是必不可少的。 基本的终端使用 如何使用各种终端的命令,尤其是Linux系统中各种命令的使用,是所有程序员的基础。 数据结构和算法 这个没啥好说的,为了面试也得好好学学,哈哈 字符编码 对网页开发来说,尤其是一些全球性的网站,如何显示不同语言蛮重要的,了解一下这方面的知识对一个web开发者来说绝对不会浪费时间。 GitHub 最大的程序员交友网站,应该不需要多说什么。 前端路线图 下面是前端开发者路线图,主要关注于黄色高亮图。 Web开发基础 很明显要想成为一个web开发者,有很多基础的知识你需要了解,比如网络的基础,web的应用,HTTP等protocol。 HTML和CSS HTML和CSS是每一个前端开发者最先应该了解的部分。HTML主要是网页的结构和CSS主要是显示的格式。 JavaScript 上面的HTML和CSS提供了结构和风格,而JavaScript才是真正让他们可以进行交互的核心。 TypeScript TypeScript和JavaScript最大的差别可能就在于类型的安全,以及可以更简单地提供面向对象的思想。 Angular 众所周知,简单的HTML,CSS和JavaScript开发网页的时代已经过去了,现在更多的是基于一个框架来进行开发网页,而Angular就是其中最流行的框架之一。Angular是由Google支持(收购)开发的。 ReactJS 和Angular类似的另外一个很流行的web框架。它最大的特点是可以开发可重复利用的模块。同样和Angular类似,ReactJS背后的大佬是Facebook Vue.js Vue.js这两年的发展趋势很强劲,大有超过Angular和ReactJS的势头,学习它应该是不会错的。...

0

如何通过重试来改进你的后端API call

无论你是从Node.js还是浏览器调用一个API Call,连接失败总是会发生。有些request的失败是有效的,也许endpoint有问题,或者客户端发送了一个错误的数据。另外一些则是连接的问题,比如连接到服务器的问题,或者是这之间的某一个节点出现了问题。虽然API和web服务检测可以看到这些问题,但是一个更好的方案也许可以处理这个问题。 解决这个问题,你可以在你的HTPP调用中假如一个重试的机制。这可以让你的API调用成功。有些库,比如got,就支持失败的重试,而另外一些库,比如axios,则需要一个独立的插件。但是假如你的库不支持这个,那么可以参考这篇文章。我们将基于返回的status来决定如何重试一个请求。 重试的基础 决定何时需要重试一个request,我们需要知道正在找寻什么。有很多HTTP status code可以用来检查。这样你的重试机制就根据不同的错误来进行,比如一个网络错误就是很好的重试情况,而别的,比如404错误,则不是一个很好的重试情况。我们的例子中,将会使用408,500,502,503,504,522和524.你也可以检查429,这个错误表示你可能调用了太多request。 下一个问题就是重试的频率。我们从一个小的延迟开始,然后每次都加上额外的延时。我们称之为“退避”。每次重试之间的时间都会不断增加。最终,我们还需要确定在多少次重试之后放弃。  下面是有一个伪码的逻辑 If total attempts > attempts, continue if status code type matches, continue if (now – delay) > last attempt, try request else, return to the...

0

怎样把一个付费的ETA服务变成三个免费的服务

这篇文章是关于我如何不花一分钱用三个免费ETA(预测到达时间estimated time of arrival)服务取代一个的。所有的一切都是基于我在GoDee这个项目中后端开发的经验。GoDee是一个创业项目,他可以提供在线的巴士位置预订服务。关于这个项目你可以在这里查阅更多的信息。 历史 GoDee是一个公共交通服务。在东南亚,GoDee的公共交通相比于摩托车来说更加舒适,比Taxi更加便宜。这个app的系统,可以让用户找到最适合的路线,选择时间,预订座位然后在线付费。GoDee的一个问题就是交通状况很容易影响到用户的体验。用户不想在那边等待并猜测这个巴士什么时候可以到达。所以,为了提供用户体验,我们需要一个服务能够计算巴士的预计到达时间,简称ETA。 从头开发ETA预计大概要一年的时间。所以,为了加速开发的时间,GoDee决定使用Google的Distance Matrix API工具来实现。后来,他们开发了他们自己的Pifia微服务。 问题 随着时间的推移,整个商业都在增长,用户的基数也在增加。我们面临的问题就是不断增长的Google Distance Matrix API的请求数量。 这个为什么会成为一个问题? 因为这个请求需要耗费金钱,Google API每个月提供了10000个免费的请求,超过之后,每1000个请求就需要付费20刀。而那时候,我们每个月大概有150,000个请求。 我的导师对这件事情非常不满意。他说,我们需要把ETA做个cache,比如30分钟超时的cache。那时候,我们的系统每3秒给Google API发送一个次请求来更新数据。然而,这种基于cache的算法并没有什么效率,因为巴士很容易堵在路上。因此,这个距离可能每10分钟才改变一次。不过,它也能解决一些问题,比如同时有五个用户在查询同样的巴士,那么他们就是同样的请求,cache可以解决这样的问题。 替代服务 cache可以解决一部分问题,但是随着GoDee的用户增长,我们还是会遇到同样的问题 — 查询的请求数目增加了。 我们决定要OSRM来取代Google的API,简单来说,OSRM是一个基于ETA来创建路径的服务(你如果想了解具体的内容,可以参见这里) OSRM有一个问题:就是他创建路径和计算ETA的时候,没有把交通状况考虑进去。为了解决这个问题,我们开始研究有哪些服务可以提供城市特定区域的交通状况。HERE Traffic提供了我想要的数据。在一些简单调查之后,我写了一个小的代码来每30分钟获取一下交通的信息。然后把交通的信息上传到OSRM,我写了一个小的命令行脚本。  ./osrm-contract data.osrm –segment-speed-file updates.csv 具体的信息可以参见这里 数学计算:每个小时,会发两个request到HERE去得到交通的信息,也就是说一天48个request,然后一个月有大概1488(48*31=1488),一年大概就是17520。所以我们可以大概免费使用HERE的请求15年,这足够了。 初步测试显示这个服务工作起来还是不错的,但是这里有一个问题,就是HERE所提供的数据格式有点乱,它不符合OSRM的格式要求。为了让这个信息能够使用,你需要使用另外一个服务,HERE for...

REST API设计的五个小建议 0

REST API设计的五个小建议

一个很好的API设计是非常难的,他需要维护向后的兼容,有效的测试,处理API的升级等等。希望这篇文章能给你以帮助。 毫无疑问,API已经成为系统信息交互的一个重要渠道,同时也是系统内部各个模块有机组成的重要部分。 这篇文章,就和大家分享一下我在REST API设计和实现中所遵循的一些建议: 能使用客户端的SDK就不要自己重写代码 假如服务供应商有自己的SDK,那么我们就尽量去使用他提供的SDK,而不要在本地REST调用上面写自己的库。一个很好的例子就是,当你使用亚马逊的网络服务时可以选择的AWS SDK。直接选择使用AWS SDK使得我的团队节约了很多时间去写我自己的逻辑处理安全,网络超时,重试,回调等等问题。 因为这些SDK都是由服务商进行维护的,开发者不需要处理测试,集成,修改来支持新的API端口。很多SDK都是开源的,并且基于标准的接口(REST, WebSocket,gRPC等),这些都能够很快地集成。 API SDK最大的问题就是可靠性和你选择的编程语言的支持。这种情况下,开发者就需要开发自定义的REST的客户端。我总是推荐开发者使用一个独立的Maven项目来设计和实现它。并且把它发送商业的git repo中,写好文档,在你组织内部可以共享。 使用Swagger来做文档 Swagger对API开发者来说实在是太重要了,尤其是对那些具有不同背景的开发者来说。它也是一个最优秀的开发框架而且对开发者来说很简单。作为开发者来说,我可以很方便地执行API并且做相关的测试,而不需要看很多文档,读很多文字。作为API的使用者,你可以搞懂API,包括他的参数啊,所希望的payload的模式啊之类。 我并不是说一个有更具体信息的的文档是没有价值,只是我发现这些文档总是不能及时更新。所以,在我看来,使用Swagger来完成文档,并且帮助别的开发者更开的开始集成是一个很好的选择。 对端口的设计遵循一个标准的方法 当设计一个API的时候,很多开发者都不遵循端口设计的标准,或者不根据HTTP的操作来定义接口。网上其实有很多资料讲这个问题,大家可以参考最后参考链接。 下面是一些我严格遵守并要求我的开发者也遵守的规则: 不要在API的定义中混入大小写。比如不要使用/users/userId,而使用/users/{id}。不要使用POST “/deleteUser/userId”,而使用DELETE “users/{id}”。 在URL中总是使用版本的控制,比如把”api/v1/”作为一个必须的部分。 把https作为一个客户端默认连接的选项。 对payload做一个校验,并且在代码的第一步中执行这个。不要把它放到后面,然后要catch exception的方式来处理。 处理升级 想象一下,你的服务使用API来传递一些数据,然后他突然不工作了。通过很多email的讨论,message的传递,你终于搞清楚了,oh,原来在payload上有一个改动,需要一个额外的强制部分。这个问题就是API升级了,但是并不向前兼容。 这类的问题其实是可以在早起使用任何CI的流程来发现的,但是作为一个API的开发者,在你进行修改之前,你需要好好想想怎样处理已经存在的客户端。比如,你加入了一个新的必须的部分,那么要不使用一个默认的值,要不定义一个新的版本,比如/api/v2等等。 测试 不要只做功能性测试,你还需要考虑压力测试。你需要知道服务器每分钟能处理多少个API的调用。当网络latency增加时,响应的时间是多少。许多商业化的客户,他们的数据中心都是全球部署的,或者使用的是多region的云环境。假如你可以向客户提供一个基准数据,比如你把API的服务部署在美西,然后在美东,印度,澳大利亚客户端调用的时间。 我比较倾向于使用一个API的测试工具,比如Postman,Apach JMeter而不是自己写一个新的。他们不仅节约时间,而且也允许把check in到git中的模板导出。...