0
点赞
收藏
分享

微信扫一扫

开发中的工作随笔 -高质量

nginx

为什么用它,它有什么好处

1.做文件服务器(静态资源访问)

2.限流

3.负载均衡(随机,轮询,权重,IPHash)

4.反向代理,统一路由

nginx(geteway网关代替它了)

为什么用nginx?

1.限流(IP,用户)

2.负载均衡(随机,轮询,权重,一致性hash,最少使用)

3.统一路由(文件服务器)

网关(网络的关口)

1.安全

2.统一路由

3.鉴权

4.安全认证,解密,签名(sign)

为什么要有文件服务器?

nginx搭建文件服务器

docker pull nginx:latest

-d 在后台运行 -p 将容器的 80 端口映射到本机的 80 端口 -v 挂载目录;必须使用绝对路径(/开头的地址) docker run -d --name nginx -p 80:80 -v /mydata/nginx/conf/conf.d:/etc/nginx/conf.d -v /mydata/nginx/html:/usr/share/nginx/html nginx

把 nginx.conf放到挂载目录下

/mydata/nginx/conf

nginx.conf

关注配置:include /etc/nginx/conf.d/*.conf;

在conf.d目录新增:static-gmall.conf文件

server { listen 80; server_name static.gmall.com; location /static/ { root /usr/share/nginx/html; } } 修改本地hosts文件

C:\Windows\System32\drivers\etc

锁?

分布式锁

为什么用分布式锁?

分布式得情况下,保证线程安全,分布式锁

分布式锁得实现

1.数据库

2.redis

3.zookeeper

参数(商品id,数量)

加锁synchronized lock

{

根据id查询商品信息(库存10)

判断限购数量

(判断当前库存-数量)>=0才能购买

减库存设置到数据库

创建订单

}

分布式锁

当前页pageNum=1

总记录数 total

总页数totalPage

每页显示多少条,pageSize

totalPage = total%pageSize==0?total/pageSize:(total/pageSize)+1

org.redisson redisson 3.12.0

数据库查询数据量大了后,模糊查询不走索引,效率低

速度快,倒排索引

ES

match模糊查询 like

term等值查询 =

terms多个值的查询 in

range区间查询

sort排序

highlight高亮(华为)

分页

pageSize 每页显示多少条 20 100

pageNum 当前页 1

total 总记录数

totalPage 总页数

int totalPage = total %pageSize ==0?total /pageSize :(total /pageSize)+1

幂等:无论执行多少次都对我自身业务没有任何影响

//1:电信2G_1:移动3G_1:联通4G,2:4.0-4.9英寸_2:4.0-4.9英寸 线程和进程区别?

线程是任务执行的最小单元,一个进程里面包含多个线程

如何实现一个线程?

继承Thread类,实现Runable接口

现在Callable接口,可以返回线程执行结果

线程安全问题?

有没有共享统一资源操作

解决线程安全的方式?

synchronized Lock AtomicInteger 线程安全的工具类ConcurrentHashMap ThreadLocal 为什么使用线程池?

线程的状态:就绪,运行,休眠,等待,唤醒,销毁

线程池的好处:提高线程的重复利用率

进程和线程有什么区别?

线程是执行的最小单位,一个进程里面会有多个线程

进程类似QQ

Java中实现一个线程有什么方式? 继承Thread类,重写run方法

实现Runable接口,重写run方法

开启一个线程去执行,没有返回结果 CompletableFuture.runAsync(()->{
System.out.println("开启了一个线程执行2"); }); //开启线下得到执行返回结果 CompletableFuture infoFuture = CompletableFuture.supplyAsync(() -> { System.out.println("查询商品信息");
try {
Thread.sleep(5000);
}catch (Exception ex){
ex.printStackTrace();
}
return "商品信息"; }); System.out.println(infoFuture.get()); //等待所有线程执行完后,代码才继续执行 CompletableFuture.allOf(f1,f2,f3,f4,f5).join(); //f1执行完后,依赖f1执行结果继续执行 CompletableFuture f6 = f1.thenApplyAsync((f)->{
try {
Thread.sleep(1000);
} catch (Exception ex) {
ex.printStackTrace();
}
return "查询图片信息"; }); 为什么要用线程池?

线程数一旦多了后,cpu飙升

线程生命周期:创建,待续,执行,销毁,中断,唤醒

避免频繁的创建线程,提高线程重复利用率

ThreadPoolExecutor(int corePoolSize 核心线程数,
int maximumPoolSize 最大线程数,
long keepAliveTime 线程存活时间,
TimeUnit unit ,
BlockingQueue workQueue) 阻塞队列
RejectedExecutionHandler handler 拒绝策略*/ //当我们创建一个线程池的时候,默认情况下是一个线程都没有的,随着任务的提交,它会拿到当前线程数和 //核心线程数比较,如果当前线程数小于核心线程数,那么创建线程,如果大于核心线程数,会把当前任务放到阻塞队列 //如果队列没有满,继续放,如果满了,再拿到当前线程数和最大线程数做比较,如果小于最大线程数,继续创建线程 //如果超过了最大线程数,触发拒绝策略
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,3,2,TimeUnit.SECONDS, new LinkedBlockingQueue<>(2)); //当前线程数0,核心线程数为2 threadPoolExecutor.submit(()->{ System.out.println(Thread.currentThread().getName()+" 第一个线程");
try {
Thread.sleep(1000);
}catch (Exception ex){
ex.printStackTrace();
}
});
//当前线程数1,核心线程数为2
threadPoolExecutor.submit(()->{ System.out.println(Thread.currentThread().getName()+" 第二个线程");
printStackTrace();
} });
//当前线程数2,核心线程数为2,阻塞队列放入1个
threadPoolExecutor.submit(()->{ System.out.println(Thread.currentThread().getName()+" 第三个线程"); });
//当前线程数2,核心线程数为2,阻塞队列放入2个
threadPoolExecutor.submit(()->{ System.out.println(Thread.currentThread().getName()+" 第四个线程"); });
//当前线程数2,核心线程数为2,最大线程数为3 threadPoolExecutor.submit(()->{ System.out.println(Thread.currentThread().getName()+" 第五个线程"); }); //当前线程数3,核心线程数为2,最大线程数为3
threadPoolExecutor.submit(()->{ System.out.println(Thread.currentThread().getName()+" 第六个线程");
});
Thread.sleep(3000);xi 核心线程数和最大线程数设置?

CPU密集度(任务执行比较快,没有IO操作),核心线程数差不多设置为1或者2*cpu核数,最大线程数设置为

核心线程数基础上加30左右

IO密集度(文件读写IO,网络传输IO,操作数据库IO)核心线程数差不多设置为3或者4*cpu核数,最大线程数设置为

核心线程数基础上加30左右

想让子线程执行完后主线程继续执行,怎么办?或者让某些线程有序的执行

join,CountDownLatch

线程安全问题?

多线程同时访问同一资源,数据不一致情况的问题

购物车

List

key: "cartItemList_"+userId value: JSON.tonstring(list);

key: "cartItemList_"+userId HashMap<Long,String> (put(skuId, JSON.tonstring(cartItem)))

为什么使用MQ?

异步(发送短信)

削峰(12306)

解耦(调用es接口,换成MQ解耦)

MQ弊端?

为什么使用MQ?

异步(发送短信,不需要等待用户操作完)

削峰(12306排队抢票)

解耦(不用依赖外部接口)

用MQ有什么弊端?

使系统变得更加复杂,整个服务链路变长了(中间需要经过MQ)

MQ出现单点故障问题(做集群,持久化)

业务MQ消息丢失,重复消费

RabbitMQ

direct点对点模式,一个生产者对应着一个消费者(发送短信)

fanout广播模式,一个生产者对应着多个消费者(注册的时候,送优惠券,短信提示,消息提示)

topic广播模式,支持表达式匹配(交换机下面的队列,只要满足routekey表达式匹配就可以路由到队列)

org.springframework.boot
spring-boot-starter-amqp

消息队列丢失的原因?

MQ(持久化做集群,几乎不会丢)

消息发到MQ,消费者监听的时候丢了(原因是:默认情况下rabbitMQ是自动签收,也就是消息进去消费者监听里面消息就立马删除了,消费者还没消费完,消息就删除了,万一消息消费失败,也就消息丢失了)

解决方案:采用ack确认机制,也就是消息手动签收

如果生产者发到MQ的过程消息丢失了,怎么办?

消息落地(消息保存到数据库【id,msgId,type,content,retry_num,create_time】),消费者消费完成后根据msgId删除,那么在消息记录表里面都是没有消费成功的,通过定时任务查询继续往队列里面发(定时任务做补偿)

因为重回队列,可能产生重复消费?

我们给每个消息分配一个msgId,消息消费完之后存入redis一段时间6小时,每次消费之前都会校验一下redis是否存在,如果存在说明之前已经消费过,直接结束

延迟队列?

订单15分钟后失效?拼团48小时失效?

多长时间后做什么事情,可以用延迟队列?

如果没有延迟队列,这些需求能不能做?(定时任务)

延迟队列和定时任务对比?

延迟队列实时性比较好

定时任务弊端(1.当数据量大时,每次定时查询对数据库性能有影响,2.实时性不好)

死信队列?

1.rabbitMQ消息设置时间后,消息过期了,会进去死信队列

2.rabbitMQ在消费消息达到一定的重试次数后,会进去死信队列

rabbitMQ延迟队列实现原理

有两个队列,一个队列设置了它的ttl消息过期时间,同时设置了消息过期后的路由交换机和路由的routeKey,

另外一个队列是普通队列,监听的时候只用去监听普通队列,达到延迟队列的效果

@Configuration public class RabbitMqOrderConfig {
@Bean
public Queue createOrderDealQueue(){
Map<String,Object> arguments = new HashMap<>();
arguments.put("x-message-ttl",60000);//消息过期时间
arguments.put("x-dead-letter-exchange","order-event-exchange");//消息过期时候的交换机 arguments.put("x-dead-letter-routing-key","gmall.order.release.queue");//消息过期的routing-key
Queue queue = new Queue("gmall.order.deal.queue",true,false, false,arguments);
return queue;
}
@Bean
public Queue createOrderReleaseQuque(){
Queue queue = new Queue("gmall.order.release.queue");
return queue; }
@Bean
public Exchange createOrderExchange(){
Exchange exchange = new DirectExchange("order-event-exchange");
return exchange; }
@Bean
public Binding createBindingDealQueue(){
//String destination, Binding.DestinationType destinationType, String exchange,
// String routingKey, Map<String, Object> arguments
Binding binding = new Binding("gmall.order.deal.queue", Binding.DestinationType.QUEUE, "order-event-exchange","gmall.order.deal.queue",null);
return binding;
}
@Bean public Binding createBindingReleaseQueue(){
//String destination, Binding.DestinationType destinationType, String exchange, // String routingKey, Map<String, Object> arguments
Binding binding = new Binding("gmall.order.release.queue", Binding.DestinationType.QUEUE, "order-event-exchange","gmall.order.release.queue",null);
return binding; } } 幂等(无论执行多少次都对自身业务没有任何影响)

select 不需要考虑

insert 需要考虑

update a set c=2 where id=1 不需要考虑

update a set c=c+2 where id=1 需要考虑

delete from a where id=1 不需要考虑

解决幂等,有哪些方式,订单重复提交?

1.token机制(进入页面的时候产生一个token放入redis里面10分钟,提交订单的时候把token传过去,如果token存在直接删除,并且执行任务,如果token不存在,提示用户请刷新页面提交)

2.加分布式锁

3.前台通过按钮置灰处理,请求开始按钮变不可用,请求结束再操作

订单业务处理

OrderSubmitVo(用户名,购物车商品idsList,token,支付金额,收货地址id)

//解决幂等问题,校验token是否有效

//根据地址id查询地址具体信息

//根据商品idsList查询购物车信息

//后台计算价格和前台做对比

//检验所有商品库存是否足够(调用wms接口)

//锁定库存(调用wms接口)

//保存订单表和订单明细表

//发送信息到延迟队列15分钟后失效

延迟队列监听

//如果订单状态不为待支付,直接结束(避免出现已经支付的订单取消)

//根据订单号修改订单状态为已取消

//释放库存(调用wms接口)

分布式事务

跨数据库,跨服务的,是没有办法用到@Transaction的,需要用到分布式事务

XA协议

TCC做补偿(T代表try,C代表confirm确认,C代表取消cancle)

最终一致性(做补偿)

satea(阿里框架)

CAP理论(C一致性,A可用性,P分区容错性)

zk保证的是CP

eureka保证AP

支付宝对接

com.alipay.sdk alipay-sdk-java
4.9.28.ALL

支付宝下单发起支付逻辑

//根据订单号查询订单信息是否存在

//防止重复支付

//防止出现重复支付问题(幂等问题)

//保存一笔交易流水

//调用支付宝下单接口

支付宝回调逻辑

//验签(防止数据被篡改)

//根据交易流水号查询流水状态,如果不等于处理中,直接拒绝(解决重复回调幂等问题)

//修改交易流水状态

//修改订单状态

//减库存(调用wms接口)

支付宝回调失败补偿

//通过定时任务查询所有交易状态为处理中的,根据商户交易流水号去查询支付宝的交易状态,

//修改交易流水状态

//修改订单状态

//减库存(调用wms接口)

为什么分库分表

1.数据库的连接数都是有限的

分库分表

读写分离

1.单台数据库存在性能瓶颈,它的连接数是有限的(搞多台数据库,读写分离)

2.一主多从(读写分离)

可能存在的问题(1.资源浪费,数据做了冗余,2.从库需要同步主库的数据(binlog日志同步),网络不好的情况出现数据延迟)

3.单表数据量超过4000w,数据还在增长需要考虑分表

为什么分库分表

1.单表数据量超过4000w,数据还在增长,哪怕建了索引效率也会低的

2.单台数据库存在性能瓶颈,它的连接数是有限的,搞多台数据库可以增加并发量

分库分表弊端

1.表之间关联比较复杂,没有办法inner join

2.分布式事务问题

分库分表框架

1.TDDL(阿里的,收费的)

2.mycat(是一个第三方的数据库中间件,学习成本比较高,一般需要单独招一个人去维护)

3.shareding-jdbc(java语音开发,比较轻量级)

1.sql层面

2.数据库做主从(读写分离,mysql主从同步原理。binlog)

Java代码: springboot aop读写分离,多数据源切换 ,地址: https://www.cnblogs.com/yeya/p/11936239.html ,shareding-jdbc

3.分库分表

垂直划分(各个库表结构不一样)

水平划分(各个库表结构一样数据不一样)

ds_0

user

address

ds_1

user

address

ds_2

user

address

分库分表的规则

1.通过id做hash取模的方式划分 order_0,order_1,order_2(扩展性比较差)

2.根据时间区间分表 order_202301, order_202302, order_202303(扩展性比较好)

QPS(每秒查询访问量)

TPS(每秒处理量)

xxl-job

为什么用xxl-job?

1.解决分布式系统并发执行问题

2.统一得任务管理,配置,调度,监控

3.有负载均衡算法,还有分片广播(提高任务执行效率)

秒杀 (10000)

秒杀下单逻辑(商品Id)

lock

try{

//是否在活动时间段

//限购商品

//根据id查询商品信息 100

//判断库存是否足够

//预减库存

//保持订单和订单明细

//发送延迟队列5分钟失效

}catch(){

}finally{

unlock

}

高并发,流量大(线程安全问题,超卖,分布式锁)

熔断限流(Sentine)

接口安全问题(加密签名)

拼团

展示拼团列表

复制生成得页面跳转地址(http://127.0.0.1:80/pingtuan?id=jkfsdkljfksjlfkljkjlfds) des,aes

立刻拼团

lock(active_pingtuan_+id)

//根据id查询拼团商品信息()拼团人数10

//判断本人是否拼团过

//判断拼团人数

//预减拼团人数

//保存拼团记录

//延迟对了15分钟后失效

unlock

砍价

展示砍价列表

复制生成得页面跳转地址(http://127.0.0.1:80/pingtuan?id=jkfsdkljfksjlfkljkjlfds) des,aes

砍价

//根据id查询砍价商品信息()

//判断本人是否砍价过

//砍价记录表查询(根据id查询记录,如果一条记录都没有,97%,发延迟队列)

//保存用户砍价记录

身份认证

//调用ocr接口获取身份证上得信息

认证(身份证正面,反面,活体视频,身份证号,姓名)

//校验参数

//姓名身份证号是否被认证

//判断用户是否认证

//上传oss

//调用身份认证接口

//调用活体认证接口

//判断三个任务是否成功,如果成功修改数据库

积分流水表(签到)

select * from qiandao where between '2023-03-25 17:17:34 ' and '2023-04-03 17:17:34 '

分库分表

为什么分库分表

1.一个数据库得连接数是有限得,如果表全部放一个数据库,性能下降(分库)

2.数据量太大,建了索引也会比较慢,如果数据量超过4000w,数据还在猛烈增长靠谱分表(分表)

分库分表弊端

1.需要解决分布式事务问题

2.表之间join关联问题

垂直划分和水平划分

垂直分库:按业务划分,每个库里面的表结构不一样

水平分库:库里面表结构一样,只是数据不一样

垂直分表:按业务把字段多的大表拆分多个小表,表结构字段不一样

水平分表:表结构字段都是一样,只是数据不一样

分库分表框架

阿里TDDL(收费)

Mycat(需要mycat中间件,学习中间件,学习成本很高,并且耦合度很大)

shareding-jdbc(Java代码开发,导入依赖,配置写策略,代码耦合度小,学习成本低,通过改写sql路由)

分表算法

通过hash去模算法(tb_order,tb_order_0,tb_order_1,tb_order_2,tb_order_3,tb_order_4,tb_order_5)

不利于扩展(一旦选择了,就没有办法扩展)

通过时间分表(tb_order,tb_order_2023_02,tb_order_202303,tb_order_202304)

举报

相关推荐

0 条评论