
我们一个请求--->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降级, 如果服务挂了或者说超时了

按照我们的请求 是查询到了 基于场次找到对应的秒杀商品信息



















