项目开发模式

项目开发模式

在预测未来之前,首先分析一下程序员的日常工作中遇到哪些困难与挑战。

需求和设计

在需求沟通阶段,程序员需要准确的获取PRD上的需求信息,并对业务的价值和发展有一定的预判。根据以上的信息进行技术选型,数据建模,设计抽象。在这个过程中主要的挑战有:

  • 高效的需求沟通

  • 合理的技术方案选择

  • 对现实世界的逻辑抽象

开发和测试

在编码和测试阶段,程序员需要选择适合的数据结构并编写算法,合理的运用设计模式高效的实现功能,并且保证尽可能少的存在bug。在这个过程中主要的挑战有:

  • 高效的编码

  • 避免语言的陷阱

  • 尽量少的bug

  • 代码有可读性和机器执行效率

部署和运维

在部署和运维阶段涉及到测试环境和生产环境2个方面,程序员分别需要在测试环境和生产环境部署代码和定位问题。在这个过程中主要的挑战有:

  • 方便的提供多套隔离的测试环境

  • 方便的在多集群多机器的生产环境部署和回滚代码

  • 方便的部署和升级基础设施(例如mysql)

  • 生产环境和测试环境的一致

  • 生产环境可以弹性伸缩

  • 有工具能辅助发现定位问题

系统内部

系统内部的主要挑战来自代码腐烂和功能复用2个方面。

  • 代码腐烂:如何在系统复杂度不断增加,几千几万次迭代之后依然可以高效的进行功能开发,而不是系统变成谁都不敢动的层层叠,开发效率越来越慢。

  • 功能复用:代码和系统的复用往往意味着更高的开发效率,更少的系统风险,一次修改就可以解决问题,而不是满世界的查找相同功能的代码进行修改。

系统外部

系统外部的主要挑战来自高并发,高并发带来3个问题,抗压,竞态条件,热点数据

  • 抗压 高并发意味着系统需要支撑更高的吞吐量,如果存在比较严重的性能瓶颈往往导致服务的不可用,无状态的节点可以比较容易的水平扩展,有状态的节点通常会成为系统的瓶颈。比较常见的解决方案是分库分表+缓存,但是同样带来开发难度提升,数据不一致,数据迁移等问题。

  • 竞态条件 高并发也会导致小概率的竞态条件不断出现,导致不能得到正确的结果。例如cache aside模式中读缓存为空并且DB读耗时>DB写耗时会导致缓存脏数据问题。

  • 热点数据 高并发在秒杀等场景中也会导致热点数据问题,在DB中会导致严重的锁冲突性能急剧下降,在cache中会导致缓存穿透,击穿,雪崩等问题。

业务约束

不同业务的特点也会带来不一样的挑战,例如很多金融业务会要求强一致而非最终一致,计费等业务的错误容忍度也远低于社区,微博突发热点话题的业务特点也会带来更大的技术挑战。

微服务相关

微服务:https://www.jianshu.com/p/7293b148028f es与mysql同步:https://blog.csdn.net/RuiKe1400360107/article/details/104270421

Java interview: https://blog.csdn.net/X5fnncxzq4/article/details/81611991

springcloud项目:https://blog.csdn.net/wzy18210825916/article/details/103444346

https://github.com/QiuFengYanMeng/cloud-demo

https://www.cnblogs.com/springboot/p/8445780.html

https://blog.csdn.net/u010938610/article/details/79282624

https://blog.csdn.net/sunroyi666/article/details/95189938

synchronize锁优化:https://blog.csdn.net/qq_25843323/article/details/104565777

缓存更新套路:https://coolshell.cn/articles/17416.html

分布式事务处理:https://coolshell.cn/articles/10910.html

https://blog.csdn.net/alex_xfboy/article/details/77572970

msyql与es数据同步问题:https://blog.csdn.net/RuiKe1400360107/article/details/104270421

分布式学习最佳实践:https://www.cnblogs.com/xybaby/p/8544715.html

动态规划算法:https://blog.csdn.net/baidu_28312631/article/details/47426445

zookeeper的zab协议:https://blog.csdn.net/u012151684/article/details/108979828

java并发之AQS:https://www.cnblogs.com/caison/p/11641159.html

Redis为什么快?

1、Redis是基于内存的,我们大家都知道内存的寻址和带宽都是远远快于磁盘的;

3、合理线程模型:Redis网络模型是采用I/O多路复用器,对key的处理是单线程 避免了多线程之间的竞争,省去了线程切换带来的时间和空间上的性能开销,而且也不会导致死锁的问题。

4、合理的数据编码

2、高效的数据结构:Redis底层数据结构有简单动态字符串,跳跃表,压缩列表,字典,整数集合;针对,简单动态字符串,压缩列表,主要是考虑到节约内存;像跳跃表,字典,主要是考虑到查询速度,整数集合即考虑到了空间又考虑到了时间;其实像字典中的渐进式rehash,以及间断key查找,都是考虑到了节约时间;在Redis中,不管是底层数据结构的设计,以及事务的设计和key过期策略,乃至key的查找,还有持久化,都为了快速这个特性而有了自己的特殊实现;String简单动态字符串、hash、set\zset(跳表)、list(压缩链表)

img

Question:

hashtable和hashmap的区别?默认容量多少,什么时候扩容,扩容为多少?

线程池的创建方式有几种?

线程池的队列有哪几种?

线程池的拒绝策略有几种?

线程池的参数keepalivetime在什么时候会生效?

Redis缓存的更新策略?如果想要强一致性可以怎么做?

Redis的分布式锁实现思路?如何优化?Redis的分布式锁有什么问题?

缓存击穿,缓存穿透,缓存雪崩是什么?如何处理?

elasticsearch的原理?

解决过什么难点问题?如何解决

线程池的工作原理:

Redis设计一个限制登录次数,超过次数锁定对应的账号一定时间

package com.java_lettuce.ServiceImpl;
​
​
import com.java_lettuce.Util.SendEmailUtil;
import com.java_lettuce.config.redisConfig;
import com.java_lettuce.entity.User;
import jdk.nashorn.internal.runtime.logging.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
​
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
​
@Logger
@Service
public class LettceServiceImp {
     private org.slf4j.Logger logger = LoggerFactory.getLogger(redisConfig.class);
 @Autowired
 private RedisTemplate<String,String> redisTemplate;
​
​
 public String judge(String u){
     String keyLoginFail="user:"+u+": login:fail: count";//用户登录失败次数
     String keyLoginStop="user:"+u+": login:stop:time";//用户禁止登录时间
​
     int num=5;//总登录次数
​
     //判断用户是否处于限制时间内
     if (redisTemplate.hasKey(keyLoginStop)){
​
         long stopTime=redisTemplate.getExpire(keyLoginStop,TimeUnit.MINUTES);//剩余 禁止登陆时间
         return  "处于限制登陆状态,请在"+stopTime+"分钟后重新登陆";
     }else {
         if (!redisTemplate.hasKey(keyLoginFail)){//是首次失败
             //设置过期时间要和存值分开,不然会失效。
             redisTemplate.opsForValue().set(keyLoginFail,"1");
             redisTemplate.expire(keyLoginFail,2,TimeUnit.MINUTES);
             return "密码输入错误,2分钟内还剩"+(num-1)+"次机会登陆";
​
         }else {
             //2分钟内非首次登陆失败
             int failCount=Integer.parseInt(redisTemplate.opsForValue().get(keyLoginFail));
             if (failCount>=num-1){
                 //超过限制次数,冻结帐号
                 redisTemplate.opsForValue().set(keyLoginStop,"1");
                 redisTemplate.expire(keyLoginStop,1,TimeUnit.HOURS);
                 return  "密码输入错误超过五次,冻结帐号一小时";
             }else {
                 redisTemplate.opsForValue().increment(keyLoginFail,1);
                 Long waitTime = redisTemplate.getExpire(keyLoginFail, TimeUnit.SECONDS);
​
                 return "密码输入错误"+(failCount+1)+"次,在2分钟内还可输入"+(num-(failCount+1))+"次"
                         +"次数将于"+waitTime+"秒后重置";
​
             }
​
​
         }
​
​
     }
​
 }
​
​
}

1、判断当前登录的用户是否被限制登录 1.1如果没有被限制(执行登录功能)

2、 判断是否登录成功 2.1登录成功–>(清除输入密码错误次数信息) 2.2登录不成功

2.2.1记录登录错误次数(判断 Redis中的登录次数KEY是否存在)

user: loginCount:fail:用户名 2.2.1.1如果不存在 是第一次登录失败次数为1user; loginCount:fai1:用户名进行赋值,同时设置失效期

2.2.1.2 如果存在

查询登录失败次数的key结果 if(结果<4) user: loginCount: fail: +1 else{//4 限制登录KEY存在,同时设置限制登录时间锁定1小时。}

3、 如果被锁定 做出相应的提示

RocketMQ

img

https://zhuanlan.zhihu.com/p/159573084

前后端合作开发:

前端要求后端传来的数据格式能够直接用于渲染,是什么一个情况呢?举个例子,后端提供一个接口获取某单位所有人员的接口。前端则需要根据用户的职级分类显示,那么前端的数据显然是要根据不同职级进行合并,这合并的数据操作能放到后端的接口中么,显然是不行的。假如需求改了,或者其他入口需要根据年龄段分类显示所有人员,本来这只是跟前端显示有关系的需求变更反而还需要改后台代码。

后端直接从数据库里扒下来的数据格式就丢给前端,又是个什么情况?一个更为常见的例子就是树格式。数据库的格式是基于行和表的,并不存在嵌套,是不可能的存在一个children 属性里面包含子节点的格式。所以一般数据库存一个树格式的数据是通过 parent 标识当前节点的父结点的方式。然而前端这边是不可能需要这种格式渲染一个树的,于是每一个调用接口的地方都会写这么一套转换逻辑。当然前端可以抽离一个通用的函数进行统一转换,但是不同的平台仍要都要写一套转换逻辑。仅仅是多些几次也就罢了,但是通过 _parent 这种方式描述一个树是不准确的,万一某个节点的父结点不存集合中是应该直接舍弃还是当作根节点?parent 存在循环引用怎么办?这种业务逻辑和错误本应该在后端做处理,然而现在则上浮到前端才被发现。

前后端分离的场景下,大家都知道要做到前后端职责的分离。前端的职责是通过调用后台提供的接口,构造用户界面。前端需要感知到数据库结构吗?并不需要,数据库是后端用于数据持久化的工具罢了,后端用什么方式做数据持久化跟我如何构造用户界面并无关系。反之,后台需要感知到用户界面是什么样子吗?同样不需要,后端只需要提供符合业务模型的接口罢了,如何构造用户界面是前端的事。

为啥又说知道这个没卵用,该撕逼还是撕逼呢?以上所说的都是理想情况,然而实际开发往往并非能够很好落实。我知道什么是"正确"的做法,如何去抽象,我可以用这一套约束我的代码,但是当我开始约束别人的时候往往会遇到问题。抽象和规范是有成本的,我来付这成本当然没人有意见,当要求别人付出别人难免会有意见,特别是项目工期紧张/跨工种/架构不合理/人员素质良莠不齐的情况下。很多答者所认为“前端能处理就放到前端处理”“前端做比较方便”,其实都是这种思路。而很多人提到的性能问题,做一些数据格式转换和数据校验说实话我并不觉得会对服务器产生多大的性能瓶颈,相比服务端渲染,这点性能消耗只能算小巫见大巫。当然这也不是针对后端同学,像很多答者没提到的问题比如浏览器对并行的请求数量有限制要求后端对提供的接口进行合并,其实也就是前端不愿付出 BFF 层的成本罢了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值