分布式和集群的概念经常被搞混,现在一句话让你明白两者的区别。
分布式:一个业务拆分成多个子业务,部署在不同的服务器上
集群:同一个业务,部署在多个服务器上
例如:电商系统可以拆分成商品,订单,用户等子系统。这就是分布式,而为了应对并发,同时部署好几个用户系统,这就是集群
每个系统的处理能力是有限的,为了提高并发访问量,需要对应用服务器做集群
这时会涉及到两个问题:
负载均衡就是将请求均衡地分配到多个系统上,常见的技术有如下几种
DNS
DNS是最简单也是最常见的负载均衡方式,一般用来实现地理级别的均衡。例如,北方的用于访问北京的机房,南方的用户访问广州的机房。一般不会使用DNS来做机器级别的负载均衡,因为太耗费IP资源了。例如,百度搜索可能要10000台以上的机器,不可能将这么多机器全部配置公网IP,然后用DNS来做负载均衡。
Nginx&LVS&F5 下图形象的展示了一个实际请求过程中,地理级别的负载均衡和机器级别的负载均衡是如何分工和结合的,其中粗线是地理几倍的负载均衡,细线是机器级别的负载均衡,实线代表最终的路由路径 session共享就是用户在A服务器登录,结果查看购物车时,请求发送到了B服务器,因此用户的session存在A服务器上,所以当请求发送到B服务器上时,会认为用户没有登录 简单说一下session和cookie的交互过程,有想深入了解的看推荐阅读,假设用户在A服务器登录,tomcat会在HttpServletReponse中添加一个cookie,key为JSESSIONID,当用户第二次访问的时候会带上这个cookie,而tomcat会根据这个JSESSIONID找到HttpSession,怎么找到呢?因为tomcat中有这样一个ConcurrentHashMap,key为JSESSIONID value为 Session对象,以后会详细讲这块,要想真正理解清楚,内容也挺多的。 所以当应用服务器只有一个时,登录一般都是写成如下: 查看购物车时,写成如下 目前解决session跨域共享问题有如下几种方式 数据库读写分离的基本实现如下: 实现方式 读写分离,包括后面要提到的分库分表的实现方式有两种: 程序代码封装指在代码中抽象一个数据访问层来实现读写分离,分库分表 中间件封装指的是独立一套系统出来,实现读写分离和分库分表操作,如我们熟悉的MySQL Router和Mycat等 传统的关系型数据库通过索引来达到快速查询的目的,但是在全文搜索的业务场景下,索引也无能为力,主要体现在如下几点: 常见的方式有如下几种: Redis与Memcached 以我们常见的Mybatis为例,很容易和Redis与Memcached整合起来,缓存已经查询过的SQL,因为Mybatis知道自己不擅长缓存,所以提供了接口让这些缓存工具进行整合 CDN 读写分离分散了数据库读写操作的压力,但没有分散存储压力,当数据量达到千万甚至上亿条的时候,单台服务器的存储能力会成为系统的瓶颈。常见的分散存储的方法有分库和分表两大类 业务分库 业务分库指的是按照业务模块将数据分散到不同的数据库服务器。例如,一个简单的电商网站,包括商品,订单,用户三个业务模块,我们可以将商品数据,订单数据,用户数据,分开放到3台不同的数据库服务器上,而不是将所有数据都放在一台数据库服务器上 当然业务分库也会带来新的问题: 分表 垂直分表:垂直分表适合将表中某些不常用且占了大量空间的列拆分出去。如上图的nickname和description字段不常用,就可以将这个字段独立到另外一张表中,这样在查询name时,就能带来一定的性能提升 水平分表:水平分表适合表行数特别大的表,如果单表行数超过5000万就必须进行分表,这个数字可以作为参考,但并不是绝对标准,关键还是要看表的访问性能 水平分表后,某条数据具体属于哪个切分后的子表,需要增加路由算法进行计算,常见的路由算法有 范围路由:选取有序的数据列(例如,整型,时间戳等)作为路由条件,不同分段分散到不同的数据库表中。以最常见的用户ID为例,路由算法可以按照1000000的范围大小进行分段,1-999999放到数据库1的表中,1000000-1999999放到数据库2的表中,以此类推 Hash路由:选取某个列(或者某几个列组合也可以)的值进行Hash运算,然后根据Hash结果分散到不同的数据库表中。同样以用户Id为例,假如我们一开始就规划了10个数据库表,路由算法可以简单地用user_id%10的值来表示数据所属的数据库表编号,ID为985的用户放到编号为5的子表中,ID为10086的用户放到编号为6的子表中。 配置路由:配置路由就是路由表,用一张独立的表来记录路由信息,同样以用户ID为例,我们新增一张user_router表,这个表包含user_id和table_id两列,根据user_id就可以查询对应的table_id 随着业务的发展,业务越来越多,应用的压力越来越大。工程规模也越来越庞大。这个时候就可以考虑将应用拆分,按照领域模型将我们的商品,订单,用户分拆成子系统。 这样拆分以后,可能会有一些相同的代码,比如订单模块有对用户数据的查询,用户模块中肯定也有对用户数据的查询。这些相同的代码和模块一定要抽象出来。这样有利于维护和管理。这时可以将模块变为一个个服务,模块之间互相调用来获取数据,系统就变成一个微服务了。 服务拆分以后,服务之间的通信可以通过RPC技术,比较典型的有:Webservice、Hessian、HTTP、RMI等。如当前的Dubbo和Spring Cloud都是目前比较流行的微服务框架,2者之间还是有很多区别的,以后分享。 [1]https://www.cnblogs.com/dump/p/8125539.html
DNS是用于实现地理级别的负载均衡,而Nginx&LVS&F5用于同一地点内机器级别的负载均衡。其中Nginx是软件的7层负载均衡,LVS是内核的4层负载均衡,F5是硬件做4层负载均衡,性能从低到高位Nginx@RequestMapping(value = "login", method = RequestMethod.POST)
// ServerResponse是小编封装的一个返回对象
public ServerResponse<User> login(String username, String passwrod, HttpSession session) {
ServerResponse<User> response = userService.login(username, passwrod);
// 登录成功,将用户信息放到session中
if (response.isSuccess()) {
session.setAttribute(Const.CURRENT_USER, response.getData());
}
return response;
}
@RequestMapping(value = "list", method = RequestMethod.GET)
public ServerResponse<CartVo> list(HttpSession session) {
User user = (User)session.getAttribute(Const.CURRENT_USER);
if (user == null) {
// session中没有有用户信息,需要登录
return ServerResponse.errorCodeMsg(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
}
return cartService.list(user.getId());
}
将请求都落到同一个服务器上,如Nginx的url hash
session复制,每台服务器都保存一份相同的session
存储在db、 存储在缓存服务器 (redis)
将信息存在加密后的cookie中4 数据库高性能操作
读写分离需要将读/写操作区分开来,然后访问不同的数据库服务器;分库分表需要根据不同的数据访问不同的数据库服务器,两者本质上都是一种分配机制,即将不同的SQL语句发送到不同的数据库服务器。
5 引入搜索引擎来查询
6 增加缓存
CDN是为了解决用户网络访问时的“最后一公里”效应,本质上是一种“以空间换空间”的加速策略,即建内容缓存在离用户最近的地方,用户访问的是缓存的内容,而不是站点实时的内容。7 分库分表
8 应用拆分/微服务
参考博客
[2]https://blog.csdn.net/zty1317313805/article/details/80433643
[3]https://www.zhihu.com/question/20004877
[4]https://www.cnblogs.com/chenpi/p/5434537.html