我们一个请求--->tomcat--->db 我们只需要把我们的应用部署在tomcat中,
就可以了
这就是你单体的感念,单机结构你只用一个服务器就完成了你项目的部署
单点问题一旦这台机器挂了,用户就没有办法用你这个服务,单机能力有限 随着你用户量增长的过程中 用户越来越多,呢你此时的应用你的db就不能承受这么多用户了,所以这个时候要针对于
我们可以做应用和数据进行分离
这个就是分布式系统,应用和数据分离,用户发起一个请求 tomcat和mysql共同完成我们的响应
整体分布在不同的服务器上来完成我们的用户请求响应的 我们就叫做分布式系统
分布式系统讲的是更多从部署层面,只要你部署超过一台机器 我们就叫做分布式系统
上述系统中还是有些问题的,比如Tomcat存在单点故障问题,一旦Tomcat所在的服务器宕机不可用了,我们就无法提供服务了,所以针对单点故障问题,我们会使用集群来解决,单机处理到达瓶颈的时候,集群中每台服务器就叫做这个集群的一个节点”,所有节点构成了一个集群。每个节点都提供相同的服务,那么这样系统的处理能力就相当于提升了好几倍
当前所有节点的负载情况,决定将这个请求交给哪个节点处理,所以就有了ngixn 做负载
nginx 做负载 有负载均衡算法
我们一般访问的会是服务的域名的,比如说我们访问京东
我们此时是根据域名进行轮询调用后端的服务 比如说tomcat1 或者tomcat2
我们做集群后 通常要做负载均衡
将请求交给ngixn,再经过ngixn分发给对应的服务器,我nginx仅仅做到的将请求进行转发
应用+组件 是我们的系统,
##比如说我们的旅游项目 叫做应用
##它附属的组件 叫做组件
单体应用架构 从部署角度来看
最终打包的过程中 你一个项目全部东西打到一个jar包中
这就是我们的单体应用
随着我们的项目越来越大 参与的人数越来越多 模块越来越多 沟通能力越来越多
甚至项目部署 我可能会影响到你 的内容 对部署来说也比较困难\
###> 然后我们会将我们的应用进行拆分 拆分为垂直应用架构
我将我的项目进行拆分,我就会有3个团队来负责开发 , 这样的拆分相互部署不会有影响
比如说A服务挂了 不会影响B服务
##缺点就是说 会有相同的代码重复 比如说
前端系统登录 你要写一个登录 后台系统需要登录 你也要写一个登录
所以 我们后续会有分布式架构,我们把重复的工作进行抽取
springcloud
>我们的整体项目部署
然后我们的秒杀功能
nginx 进行请求的分发,我们可以实现我们的nginx 高可用
gateway 集群, canal 做数据同步
mysql优化 可以实现读写分离 分库分表
redis+token 的方式 实现分布式session
>>>>>>>我们把我们的配置统一放在nacos上
我们统一的入口是我们的网关 我先把我们的网关跑起来
首先我们要解决的是我们跨域问题
我们
前端--------->gateway--------->uaa
前端发送请求到后端只要ip或者端口不一致就会出现跨域问题,我们需要在网关中实现跨域
我们需要在网关中实现跨域#
第二就是真实ip 的问题
服务部署登录信息,当我们一个请求
用户--->nginx--->gateway--->uaa 的时候 我们在uaa中做登录的时候 此时uaa服务
当我们在request.getRemoteIp 中是获取的网关的ip 我们怎么拿到用户的真实ip
package cn.wolfcode.filters;
import cn.wolfcode.common.constants.CommonConstants;
import cn.wolfcode.redis.CommonRedisKey;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 定义全局过滤器,功能如下:
* 1.把客户端真实IP通过请求同的方式传递给微服务
* 2.在请求头中添加FEIGN_REQUEST的请求头,值为0,标记请求不是Feign调用,而是客户端调用
* 3.刷新Token的有效时间
*/
@Component
public class CommonFilter implements GlobalFilter {
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
/**
* pre拦截逻辑
* 在请求去到微服务之前,做了两个处理
* 1.把客户端真实IP通过请求同的方式传递给微服务
* 2.在请求头中添加FEIGN_REQUEST的请求头,值为0,标记请求不是Feign调用,而是客户端调用
*/
ServerHttpRequest request = exchange.getRequest().mutate().
/*在过滤器中拿到ip*/
header(CommonConstants.REAL_IP,exchange.getRequest().getRemoteAddress().getHostString()).
header(CommonConstants.FEIGN_REQUEST_KEY,CommonConstants.FEIGN_REQUEST_FALSE).
build();
return chain.filter(exchange.mutate().request(request).build()).then(Mono.fromRunnable(()->{
/**
* post拦截逻辑
* 在请求执行完微服务之后,需要刷新token在redis的时间
* 判断token不为空 && Redis还存在这个token对于的key,这时候需要延长Redis中对应key的有效时间.
* 没有带token 不会走这里
*/
String token,redisKey;
if(!StringUtils.isEmpty(token =exchange.getRequest().getHeaders().getFirst(CommonConstants.TOKEN_NAME))
&& redisTemplate.hasKey(CommonRedisKey.USER_TOKEN.getRealKey(token))){
// 我给key 许时间 每次续30分钟
String s = CommonRedisKey.USER_TOKEN.getRealKey(token);
redisTemplate.expire(s , CommonRedisKey.USER_TOKEN.getExpireTime(), CommonRedisKey.USER_TOKEN.getUnit());
}
}));
}
}
我们需要在gateway中拿到请求ip放入header中给传递到后续的微服务中
header(CommonConstants.REAL_IP,exchange.getRequest().getRemoteAddress().getHostString())
##>>>
我们这样就会在uaa中拿到用户的真实ip,
在网关中加个拦截器 fiter 获取到真实的ip地址,转发请求的时候把你的真实ip地址放到请求头中,
在网关中获取用户的真实ip 放在header中传递给下来的微服务中
LoginLog loginLog = new LoginLog(phone, ip, new Date());
我们要把用户的登录信息记录下来,发送mq进行记录 进行写日志 高频日志记录
我们会专门有一个服务来写日志的,
如果我们的业务数据库除了写业务之后还得插入日志.
业务日志插入比较频繁 势必会影响db的写的性能
所以我们会把数据库进行拆分, 业务和日志db 来分开 mq慢慢写
## 我们此时还得将用户的账号密码信息和用户的基本信息分开
t_user_login 用户账号密码
t_user_base_info 用户基本信息
前端会把token进行缓存 我们用LocalStorage()进行存储token
UserLogin userLogin = this.getUser(phone);
###>>>>>>.
userhash 18080018188 userInfo
17070017177 userInfo
userZset 17070017177 记录当前key 的时间
我会启动一个定时任务来进行批次得 就是说我要删除7天以外得用户登录,所以用户每次登录进来 我都要刷新时间
private UserLogin getUser(Long phone) {
UserLogin userLogin;
String hashKey = "userHash";
String zSetKey ="userZset";
String userKey = String.valueOf(phone);
String objStr = (String) redisTemplate.opsForHash().get(hashKey, String.valueOf(phone));
if ("null".equals(objStr) || StringUtils.isEmpty(objStr)) {
//缓存中并没有,从数据库中查询
userLogin = userMapper.selectUserLoginByPhone(phone);
//把用户的登录信息存储到Hash结构中.
redisTemplate.opsForHash().put(hashKey, userKey, JSON.toJSONString(userLogin));
//使用zSet结构,value存用户手机号码,分数为登录时间,在定时器中找出7天前登录的用户,然后再缓存中删除.
//我们缓存中的只存储7天的用户登录信息(热点用户)
} else {
//缓存中有这个key
userLogin = JSON.parseObject(objStr, UserLogin.class);
}
//
redisTemplate.opsForZSet().add(zSetKey, userKey, new Date().getTime());
return userLogin;
}
token 用户登录成功我会生成一个token 默认为30分钟,
private String createToken(UserInfo userInfo) {
//token创建
String token = UUID.randomUUID().toString().replace("-", "");
//把user对象存储到redis中
CommonRedisKey redisKey = CommonRedisKey.USER_TOKEN;
redisTemplate.opsForValue().set(redisKey.getRealKey(token), JSON.toJSONString(userInfo), redisKey.getExpireTime(), redisKey.getUnit());
return token;
}
##############>>>>>>>> 然后在用户每次请求后 我都会给这个用户来续时间
你每次访问的时候我们都要延迟一下有效时间 >>>>>>>..
每次访问的时候 我要对token进行刷新
后置拦截 意思是我要让我每个接口都给toknen 续命 一定是我不用这个软件的后续30分钟
保证我们的token 始终是有效的 当你没有活跃后的30分钟后失效de
##>>>>登录得缓存设计
限时抢购场景
首页秒杀列表功能 场次的秒杀 场次----->秒杀场次对应的商品
首先第一步 商家要在后台上架秒杀商品,
##>我此时我要查询秒杀商品,我就得做rpc发起调用
商品服务 t_ptoduct
秒杀服务 t_seckil_product
做rpc 我们进行微服务拆分后很多都是单表查询
rpc远程调用我们用到了feign
##>秒杀服务--------->商品服务 我们此时会用到feign降级, 如果服务挂了或者说超时了
按照我们的请求 是查询到了 基于场次找到对应的秒杀商品信息