京淘项目架构图:
当用户展现商品列表信息时,里边的商品类目应该展现的是具体商品分类的名称,而不是ID.所以需要再次发起ajax请 求根据id获取商品分类的名称
现在需要显示商品信息的情况下,商品信息是一张表,商品类目又是一张表,如果此时
Ajax:
如果使用嵌套Ajax并且是异步操作的情况下会造成压面中的数据显示不正常;
过滤器和拦截器的区别,
过滤器的范围会更大,请求在到达前端控制器之前就会进行拦截(例如修改字符集编码), 而拦截器是在到达controller之前进行的拦截,
Nginx高性能的HTTP服务器,可以将服务器上的静态文件(如HTML、图片)通过HTTP协议展现给客户端。
同时也是一个代理服务器用来实现负载均衡:
Nginx代理呢是分为正向代理和反向代理,
正向代理最大的特点是,客户端非常明确要访问的服务器;服务器只清楚请求是来自哪一个代理服务器,而不清楚来自哪个具体的客户端;
反向代理呢,说一个业务场景,比如一个网站在日益增加的访问量的情况下,单台服务器已经无法满足这个需求了,这时就可能会选用一个分布式的部署,也就是通过多台服务器来解决访问人数限制的问题,多个客服端向服务器发送请求,Nginx接收到之后就会按照规则分发给后端服务器进行处理,此时请求来源,也就是客户端是明确的,但是具体分发给哪一个服务器是不知道的,这个就是反向代理,
通过反向代理也可以实现一个负载均衡的作用,,分别有权重,轮询,IPhash这些。
Nginx的高级属性策略:
down属性:当出现故障机的时候,Nginx是不知道的,这个时候他还是会频繁的访问这台故障机,此时可以使用down属性进行一个标记,这时他就不会再去访问故障机,
backup属性备用机策略:在正常情况下这台被标记了backup属性的服务是不会被访问的,只有其他的服务全部down掉之后才会去使用这台被backup标注的备用机
SpringBoot中无论jar包还是war包都是可以直接运行的;
数据库实现高可用:读写分离
- 当数据库主库Master 发生数据的修改时,则将修改的数据写入二进制日志文件中.(二进制日志文件需要手动开启);
- 从库会开启IO线程,去主库中读取更新的二进制日志文件,并且记录读写位置(POS).
- 为了实现数据库同步的(异步)功能,将IO线程读取信息通过中继日志进行保存.
- Sql线程会读取中继日志中的数据,之后将数据写入到数据库中.至此整个数据库热备份完成.
数据库高可用,主从搭建实现读写分离 (看到这儿是请回去在看一遍笔记)
Redis的使用:
由于redis中一般使用String数据类型保存业务数据.但是代码中java对象Redis没办法直接保存,所以需要中间的转化的过程.使用JSON方式进行数据中转.
List java对象 ————- JSON —————— Redis中 使用String数据类型保存. 这里可以使用ObjectMapper 这个API进行JSON和对象的格式转换
使用AOP缓存商品可以使用自定义注解的方式,可以再自定义注解中设定KEY ,也可以设置超时时间, 在Aop的定义中使用@Around环绕注解,并且参数是@Around(“@annotation(cacheFind)”) 其中cacheFind是自定义注解名称, 并且环绕通知的方法参数中要加入自定义注解参数,用来获取自定义注解中的KEY
这样就可以根据拿到的key到缓存中查询是否有该数据,如果有的话就直接走缓存,没有的话就走数据库,并且把查询出的数据放入到缓存中。
Redis的持久化策略,如果是允许数据少量丢失的情况下,可以选择使用RDB的方式进行持久化,因为他的速度是比较快的,
如果是数据不允许丢失的话那就选择AOF的方式进行持久化,他默认是不开启的所以需要手动进行开启。 在开启AOF的情况下,可以直接修改AOF的文件,删除flushAll之后重启即可
Redis的内存优化策略: 会用到几种算法,分别是LRU算法,LFU算法,还有一种RANDOM的随机算法
Redis运行的空间是内存.内存的资源比较紧缺.所以应该维护redis内存数据,将改让redis保留热点数据.
3.5.3 LRU算法
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
维度: 自上一次使用的时间T
最为理想的内存置换算法.
3.5.3 LFU算法
LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
least frequently used (LFU) page-replacement algorithm
即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
维度: 引用次数
3.5.4 RANDOM算法
3.5.3 内存策略优化
- volatile-lru 在设定了超时时间的数据, 采用lru算法进行删除.
2.allkeys-lru 所有数据采用lru算法
3.volatile-lfu 在设定了超时时间的数据, 采用LFU算法进行删除.
4.allkeys-lfu 所有数据采用LFU算法
5.volatile-random 设定超时时间数据采用随机算法
6.allkeys-random 所有数据采用随机算法
7.volatile-ttl 设定了超时时间的数据 根据ttl规则删除. 将剩余时间少的提前删除
8.noeviction 内存满了 不做任何操作.报错返回.
为什么使用分片机制:
redis可以通过修改内存的大小 实现数据的保存.但是内存的资源不易设置的过大,因为很多的时间都浪费在内存的寻址中.
需求: 如果有海量的数据,需要redis存储 问:应该如何处理?
解决方案: 可以采用Redis分片机制 实现内存数据的扩容.
知识点: 采用redis分片 主要的目的就是为了实现内存扩容.从而解决海量数据存储的问题
Redis高可用:
Redis分片机制存在的问题:
Redis分片机制可以实现内存数据的扩容.但是如果Redis服务器发生了宕机的现象,则会影响整个分片使用.
问题:Redis分片机制没有实现高可用. 当主机宕机之后.由从机自动的实现故障迁移.用户访问不受任何影响.
如果要实现redis高可用机制,则必须首先实现主从搭建.
1.首先启动Redis哨兵.由哨兵监控整个Redis主从状态. 主要监控M主机. 同时获取其从机的信息.
2.哨兵利用心跳检测机制(PING-PONG)的方式监控主机是否宕机. 如果连续3次主机没有响应.则哨兵判断主机宕机.
之后开始进行选举.
3.根据从主机中获取的从机信息.之后利用 选举机制算法.挑选新的主机.
4.之后将剩余的redis修改为当前主机的的从.并且修改配置文件.
Redis集群分区
搭建Redis集群分区底层是一个哈希槽的算法, 分片使用得是一个一致性hash算法;
到此出,请回顾 第四阶段的day15
分布式系统搭建流程:
首先是客户端发起请求通过Nginx进行一个反向代理,负载均衡的访问一个WEB的集群,我这个项目是采用的一个Dubbo的远程调用,从业务服务器中获取数据,然后在业务服务器中间也可以添加一个Redis服务器进行一个数据缓存,增加查询的效率,也可以添加数据库的一个集群进行一个数据库的高可用,减少数据库的压力。 一般这种是应对一个高并发的环境,
zk的选举机制:
假设这些服务器从id1-5,依序启动:
因为一共5台服务器,只有超过半数以上,即最少启动3台服务器,集群才能正常工作。
(1)服务器1启动,发起一次选举。
服务器1投自己一票。此时服务器1票数一票,不够半数以上(3票),选举无法完成;
服务器1状态保持为LOOKING;
(2)服务器2启动,再发起一次选举。
服务器1和2分别投自己一票,此时服务器1发现服务器2的id比自己大,更改选票投给服务器2;
此时服务器1票数0票,服务器2票数2票,不够半数以上(3票),选举无法完成;
服务器1,2状态保持LOOKING;
(3)服务器3启动,发起一次选举。
与上面过程一样,服务器1和2先投自己一票,然后因为服务器3id最大,两者更改选票投给为服务器3;
此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数(3票),服务器3当选Leader。
服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;
(4)服务器4启动,发起一次选举。
此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为1票。
此时服务器4服从多数,更改选票信息为服务器3;
服务器4并更改状态为FOLLOWING;
(5)服务器5启动,同4一样投票给3,此时服务器3一共5票,服务器5为0票;
服务器5并更改状态为FOLLOWING;
zk集群如果宕机的情况下暂时是不影响用户的访问的, 因为本地也保存着服务注册列表
最终Leader是服务器3,状态为LEADING;
其余服务器是Follower,状态为FOLLOWING。
————————————————————————————————————————————————————————————————————————————————-
Dubbo调用过程: 首先是定义一个第三方接口,服务的提供者和消费者都会在pom文件中依赖于这个第三方接口,服务的提供者实现这个第三方接口之后获取数据(此时的service需要使用dubbo中的service注解才可以生效),每一个服务都有自己的端口,在yml配置文件中配置指定使用的协议和(dubbo)端口,服务名称,注册中心的地址,还有dubbo的包扫描路径。 然后由服务的消费者直接通过Controller调用即可,注意此时不再使用@Autowired注解而是使用@Reference的一个注解 (利用的是一个代理机制为其创建代理对象,@Reference注解上可以设置loadbalance属性实现负载均衡) 也要在yml中配置注册中心地址,消费者名称,dubbo的包扫描路径。
3.2.2 SSO单点登录
单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是目前比较流行的 [1]
登陆操作:用户发起登录请求到WEB服务器,然后 WEB服务器通过rpc方式访问后台服务器验证数据是否有效,如果数据验证有效,之后将用户信息保存到redis中key 使用uuid, value使用userJSON. 其中用户的密码信息进行了脱密处理, 并且设定7天超时. 然后将uuid返回,在Controller层拿到这个uuid之后存到Cookie中进行一个保存,可以设置Cookie的存活时间,Cookie在哪些域名下实现数据的共享,此时如果想要在页面进行用户信息的回显的话,可以通过一个$.cookie的一个方法拿到指定的Cookie,然后发起一个跨域(jsonp)请求,将从cookie拿到的KEY( uuid ) 就可以从redis中拿到这个用户信息进行一个回显。
用户的退出操作: 直接重定向系统首页,然后删除redis和cookie,其中获取Cookie的操作可以使用HttpServletRequest(客户端请求对象)通过这个对象的一个getCookie()的方法可以拿到一个Cookie数组,通过遍历拿到对应的Cookie然后把有效期设置为0就ok了。
Dubbo框架实现商品详情展现:
首先是定义第三方接口,后端服务器实现这个接口,前端服务器Controller中调用这个接口拿到数据返回给页面进行展示,具体实现:前端客户发起请求并传回一个商品id, 在表设计中商品表和商品信息描述表是一个一对一的关系,商品表ID和商品描述信息表的ID是相同的,所以可以直接使用前端传过来的商品ID进行查询商品表和商品信息描述表,然后通过一个 model.addAttribute(“item”,item); 传回到页面当中, 并且这些商品信息可以通过Redis进行缓存提高查询效率。
购物车的实现:
当用户点击购物车按钮时,需要跳转到购物车展现页面cart.jsp中.并且在其中展现购物车列表数据;
首先是根据一个用户ID查询购物车记录显示到页面中,这里是先写死的版本稍后再更新
定义一个拦截器使用拦截器做一个权限验证,暂时只拦截购物车和订单模块。
当用户在不登录的条件下,不允许访问购物车/订单等受限的系统.并且重定向到用户的登录页面.
问题:
1.如何校验用户是否登录? Cookie /Redis
2.如何拦截用户的请求呢? 拦截器设定.
首先是定义一个@Configuration配置类,然后实现WebMvcConfigurer(相当于web项目中的web.xml配置文件)
添加一个拦截器配置实现一个方法是addInterceptors()
然后可以使用@Component去定义一个实现了 HandlerInterceptor拦截器的类并且重写了一个preHandle的一个方法然后在这个方法中呢我就可以做一个验证,校验用户是否登录:可以验证Cookie中和redis中是否有记录,如果是有记录就返回true,如果是没有记录的话就返回false 并重定向到登录页面 ,,然后把这个拦截器注入到配置类中就可以了,
如何动态的获取这个ID呢,我这边是直接在拦截器中拿到这个用户信息之后通过一个request.setAttribute(JTUSER, user); 转发到域中,如果需要使用的话,可以直接在需要的地方get就可以了 ,也可以通过线程的方式存入到线程中去
订单超时任务当规定的执行时间一到,触发器就会开启线程,执行指定的任务. 业务需求: 要求将超时订单关闭. 要求30分钟 status 由1改为6 * 如何定义超时:
