项目开发模式
項目開發模式
在預測未來之前,首先分析一下程序員的日常工作中遇到哪些困難與挑戰。
需求和設計
在需求溝通階段,程序員需要準確的獲取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(壓縮鏈表)
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);@Autowiredprivate 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
https://zhuanlan.zhihu.com/p/159573084
前后端合作開發:
前端要求后端傳來的數據格式能夠直接用于渲染,是什么一個情況呢?舉個例子,后端提供一個接口獲取某單位所有人員的接口。前端則需要根據用戶的職級分類顯示,那么前端的數據顯然是要根據不同職級進行合并,這合并的數據操作能放到后端的接口中么,顯然是不行的。假如需求改了,或者其他入口需要根據年齡段分類顯示所有人員,本來這只是跟前端顯示有關系的需求變更反而還需要改后臺代碼。
后端直接從數據庫里扒下來的數據格式就丟給前端,又是個什么情況?一個更為常見的例子就是樹格式。數據庫的格式是基于行和表的,并不存在嵌套,是不可能的存在一個children 屬性里面包含子節點的格式。所以一般數據庫存一個樹格式的數據是通過 parent 標識當前節點的父結點的方式。然而前端這邊是不可能需要這種格式渲染一個樹的,于是每一個調用接口的地方都會寫這么一套轉換邏輯。當然前端可以抽離一個通用的函數進行統一轉換,但是不同的平臺仍要都要寫一套轉換邏輯。僅僅是多些幾次也就罷了,但是通過 _parent 這種方式描述一個樹是不準確的,萬一某個節點的父結點不存集合中是應該直接舍棄還是當作根節點?parent 存在循環引用怎么辦?這種業務邏輯和錯誤本應該在后端做處理,然而現在則上浮到前端才被發現。
前后端分離的場景下,大家都知道要做到前后端職責的分離。前端的職責是通過調用后臺提供的接口,構造用戶界面。前端需要感知到數據庫結構嗎?并不需要,數據庫是后端用于數據持久化的工具罷了,后端用什么方式做數據持久化跟我如何構造用戶界面并無關系。反之,后臺需要感知到用戶界面是什么樣子嗎?同樣不需要,后端只需要提供符合業務模型的接口罷了,如何構造用戶界面是前端的事。
為啥又說知道這個沒卵用,該撕逼還是撕逼呢?以上所說的都是理想情況,然而實際開發往往并非能夠很好落實。我知道什么是"正確"的做法,如何去抽象,我可以用這一套約束我的代碼,但是當我開始約束別人的時候往往會遇到問題。抽象和規范是有成本的,我來付這成本當然沒人有意見,當要求別人付出別人難免會有意見,特別是項目工期緊張/跨工種/架構不合理/人員素質良莠不齊的情況下。很多答者所認為“前端能處理就放到前端處理”“前端做比較方便”,其實都是這種思路。而很多人提到的性能問題,做一些數據格式轉換和數據校驗說實話我并不覺得會對服務器產生多大的性能瓶頸,相比服務端渲染,這點性能消耗只能算小巫見大巫。當然這也不是針對后端同學,像很多答者沒提到的問題比如瀏覽器對并行的請求數量有限制要求后端對提供的接口進行合并,其實也就是前端不愿付出 BFF 層的成本罷了。
總結
- 上一篇: html静态网页制作天天生鲜,天天生鲜
- 下一篇: java == 如何避免空指针异常