微服務的編程測評系統11-jmeter-redis-競賽列表

提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔

文章目錄

  • 前言
  • 1. 退出登錄
    • 1.1 后端
    • 1.2 前端
  • 2. 獲取當前用戶信息
  • 3. C端用戶競賽列表功能
    • 3.1 后端
    • 3.2 Jmeter-基本操作
    • 3.3 數據版本性能測試-壓力測試
    • 3.4 redis版本-緩存結構設計
    • 3.5 redis版本代碼開發
    • 3.6 redis版本性能測試
  • 總結


前言

1. 退出登錄

1.1 后端

后端直接拷貝代碼就可以了

但是我們點擊了退出登錄,用戶還是可以查看競賽和題目列表的,但是不能答題,怎么實現這種可以一些操作的功能呢–》網關—》配置白名單之類的就可以了

1.2 前端

先是home.vue

<template><div class="oj-main-layout"><div class="oj-main-layout-header"><div class="oj-main-layout-nav"><Navbar></Navbar></div></div><div ><img src="@/assets/images/log-banner.png" class="banner-img"></div></div><RouterView />
</template><script setup>
import Navbar from '@/components/Navbar.vue'
</script><style lang="scss" scoped>
.el-main {padding: 0;
}.oj-main-layout {// background-color: #f7f7f7;padding-top: 20px;.banner-img {max-width: 1520px;margin: 0 auto;border-radius: 16px;width: "100%"}.oj-main-layout-header {height: 60px;position: absolute;width: 100%;background: #fff;left: 0;top: 0;z-index: 3;overflow: hidden;}.oj-main-layout-nav {max-width: 1520px;min-width: 100%;margin: 0 auto;height: 60px;background: #fff;}// banner 圖.oj-ship-banner {max-width: 1520px;min-width: 1520;margin: 0 auto;width: 100%;height: 100%;height: 350px;// width: 1677px;color: #ffffff;background: url("@/assets/index_bg.png") left top no-repeat;background-size: cover;overflow: hidden;}
}
</style>

然后是我們自定義的組件Navbar
放在components

<template><div class="oj-navbar"><div class="oj-navbar-menus"><img class="oj-navbar-logo" src="@/assets/logo.png" /><el-menu router class="oj-navbar-menu" mode="horizontal"><el-menu-item index="/c-oj/home/question">題庫</el-menu-item><el-menu-item index="/c-oj/home/exam">競賽</el-menu-item></el-menu></div><div class="oj-navbar-users"><img v-if="isLogin" class="oj-message" @click="goMessage" src="@/assets/message/message.png" /><el-dropdown v-if="isLogin"><div class="oj-navbar-name"><img class="oj-head-image" v-if="isLogin" :src="userInfo.headImage" /><span>{{ userInfo.nickName }}</span></div><template #dropdown><el-dropdown-menu><el-dropdown-item @click="goUserDetail"><div class="oj-navabar-item"><span>個人中心</span></div></el-dropdown-item><el-dropdown-item @click="goMyExam"><div class="oj-navabar-item"><span>我的競賽</span></div></el-dropdown-item><el-dropdown-item><div class="oj-navabar-item"><span @click="handleLogout">退出登錄</span></div></el-dropdown-item></el-dropdown-menu></template></el-dropdown><span class="oj-navbar-login-btn" v-if="!isLogin" @click="goLogin">登錄</span></div></div>
</template><script setup>
import { reactive, ref } from 'vue';
import router from '@/router';
import { getToken, removeToken } from '@/utils/cookie';const isLogin = ref(false)
const userInfo = reactive({nickName: '',headImage: ''
})</script><style lang="scss" scoped>
.oj-navbar {display: flex;justify-content: space-between;align-items: center;padding: 0 20px;box-sizing: border-box;max-width: 1520px;margin: 0 auto;.oj-navbar-menus {display: flex;align-items: center;height: 50px;.el-menu-item {font-family: PingFangSC, PingFang SC;font-weight: 400;font-size: 20px;color: #222222;line-height: 28px;text-align: center;width: 42px;text-align: left;margin-right: 25px;}}.oj-navbar-logo {width: 38px;height: 38px;background: #32C5FF;border-radius: 8px;cursor: pointer;object-fit: contain;margin-right: 59px;}.oj-navbar-menu {// margin-left: 18px;width: 600px;border: none;.el-menu-item {font-size: 16px;font-weight: 500;background-color: transparent !important;transition: none;border: none;line-height: 60px;}}.oj-navbar-users {display: flex;align-items: center;}.oj-navbar-login-btn {line-height: 60px;display: inline-block;font-family: PingFangSC, PingFang SC;font-weight: 400;font-size: 18px;color: #222222;text-align: center;cursor: pointer;.line {display: inline-block;width: 25px;}}.oj-message {cursor: pointer;margin-top: 15px;}.oj-head-image {width: 30px;height: 30px;border-radius: 30px;margin-right: 10px;}.oj-navbar-name {cursor: pointer;margin-top: 15px;font-weight: 400;color: #000;margin-left: 15px;font-size: 20px;width: 100px;display: flex;align-items: center;}.oj-navabar-item {display: flex;align-items: center;justify-content: center;padding: 0 32px;}
}
</style>

在這里插入圖片描述
然后是配置登錄按鈕

function goLogin(){router.push("/c-oj/login")
}

登錄成功以后還要修改登錄狀態了isLogin
我們先要判斷用戶狀態,第一token存不存在,存在了也不能說明登錄了,因為可能過期了,后端過期了,不會刪除token,所以還要請求后端,判斷token是否過期

        <div class="oj-navbar-name"><img class="oj-head-image" v-if="isLogin" src="@/assets/images/headImage.jpg" /><span>CK</span></div>

我們先把這個寫死
這個也要請求后端,順便就可以判斷是否過期了
所以這個請求用戶頭像和昵稱的接口就可以判斷是否過期了

function checkToken(){if(getToken()){//判斷是否過期,獲取用戶信息isLogin.value = true;}
}checkToken()

在這里插入圖片描述
這樣就成功了,獲取頭像和昵稱的接口還沒實現

最后是退出登錄的邏輯實現了

export function logoutService() {return service({url: "/user/logout",method: "delete",});
}

header已經在請求攔截器中添加了

async function handleLogout(){await ElMessageBox.confirm('退出登錄','溫馨提示',{confirmButtonText: '確認',cancelButtonText: '取消',type: 'warning',})await logoutService();removeToken();isLogin.value=false;
}

2. 獲取當前用戶信息

這個還可以判斷是否過期
在這里插入圖片描述

在request.js里面,這個方法中的router.push(‘/c-oj/login’)不要了,沒有登錄就不要跳轉到登錄頁面了

如果已經過期了–》網關就過不去

@Data
public class LoginUserVO {private String nickName;private String headImage;
}

這個是返回的用戶數據,core中,那么這樣的話,就要在登錄的時候就把用戶昵稱存入redis

@Data
public class LoginUser {//存儲在redis中的用戶信息private Integer identity;private String nickName;private String headImage;
}
    public String createToken(Long userId, String secret,Integer identity,String nickName,String headImage){Map<String, Object> claims = new HashMap<>();String userKey = UUID.fastUUID().toString();claims.put(JwtConstants.LOGIN_USER_ID, userId);claims.put(JwtConstants.LOGIN_USER_KEY, userKey);String token = JwtUtils.createToken(claims, secret);LoginUser loginUser = new LoginUser();loginUser.setIdentity(identity);//2表示管理員,1表示普通用戶loginUser.setNickName(nickName);loginUser.setHeadImage(headImage);redisService.setCacheObject(getTokenKey(userKey), loginUser, CacheConstants.EXPIRED, TimeUnit.MINUTES);return token;}

在這里插入圖片描述

然后就是在登錄的時候存入圖片
在這里插入圖片描述
這樣就成功了

管理員那里的createToken傳入null就可以了

然后就是前端了

export function infoService() {return service({url: "/user/info",method: "get",});
}
async function checkToken(){if(getToken()){//判斷是否過期,獲取用戶信息const ret = await infoService()Object.assign(userInfo,ret.data)isLogin.value = true;}
}
        <div class="oj-navbar-name"><img class="oj-head-image" v-if="isLogin" :src="userInfo.headImage" /><span>{{ userInfo.nickName }}</span></div>

注意加了冒號的src是針對響應式數據的,沒有加冒號的就是真對字符串進行處理了

這樣以后就可以了

3. C端用戶競賽列表功能

3.1 后端

C端列表只展示已經發布的競賽
C端列表功能可以不登錄就使用了—》網關配置,m還有題目列表也不需要登錄,配一個統一前綴表示不用登錄就可以使用,比如semiLogin

在這里插入圖片描述
在這里插入圖片描述
未完賽指的是比賽還沒有開始和已經開始的,歷史競賽指的是比賽已結束了
用一個type字段來區分,這也是一個過濾條件,type為0表示沒有結束的競賽,type為1表示已經結束的競賽

開始寫代碼
先直接復制管理員的列表接口,然后在做一些修改

@Data
public class ExamQueryDTO extends PageQueryDTO {private String title;private String startTime;private String endTime;private Integer type;//0表示未結束的競賽,1表示已經結束的競賽
}
@Data
public class ExamVO {@JsonSerialize(using = ToStringSerializer.class)private Long examId;private String title;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime startTime;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime endTime;}
<select id="selectExamList" resultType="com.ck.friend.domain.exam.vo.ExamVO">SELECTte.exam_id,te.title,te.start_time,te.end_timeFROMtb_exam te<where>status = 1<if test="title !=null and title !='' ">AND te.title LIKE CONCAT('%',#{title},'%')</if><if test="startTime != null and startTime != '' ">AND te.start_time >= #{startTime}</if><if test="endTime != null and endTime != ''">AND te.end_time &lt;= #{endTime}</if><if test="type  ==  0">AND te.end_time > Now()</if><if test="type  ==  1">AND te.end_time &lt;= Now()</if></where>ORDER BYte.create_time DESC</select>
    @GetMapping("/semiLogin/list")public TableDataInfo list(ExamQueryDTO examQueryDTO){log.info("獲取競賽列表信息,examQueryDTO:{}", examQueryDTO);return getTableDataInfo(examService.list(examQueryDTO));}
security:ignore:whites: - /**/login- /friend/user/sendCode- /friend/user/loginOrRegister- /**/semiLogin/**

在這里插入圖片描述
這樣就成功了

在這里插入圖片描述
記得還有攔截器也要配置一下

3.2 Jmeter-基本操作

Apache JMeter是Apache組織開發的基于Java的壓?測試?具。?于對軟件做壓?測試,它最初被設計?于Web應?測試,但后來擴展到其他測試領域。
下載官網

一個linux來服務部署
一個linux服務器來組件部署,比如redis,mysql,nacos
一個linux服務器來部署jemeter

壓縮之后,在bin目錄下找到jmeter.bat文件,雙擊就可以運行了。就可以啟動了
還有一個方法是打開cmd,輸入jmeter就可以打開的
----》注意這樣的話,就要添加環境變量的,就是添加bin目錄就可以了
但是我們的jmeter是英文的,可以變為中文的
在bin目錄下找到jmeter.properties
找到language配置,默認是en
改為language=zh_CN就可以了
在這里插入圖片描述
在這里插入圖片描述

點擊f12,中的network中的XHR可以找到發送的后端請求
先添加一個線程組
在這里插入圖片描述
然后在添加http請求
在這里插入圖片描述
點擊左上角的運行之后要保存才可以
但是我們直接運行看不到響應的數據,所以還要添加一個監聽器
在這里插入圖片描述
在這里插入圖片描述
這樣運行以后就可以看到結果那些了
在這里插入圖片描述
這個就是10個請求,相當于10個用戶同時發送
在這里插入圖片描述
點擊這個可以清楚結果樹
在這里插入圖片描述
這樣就有10個了

3.3 數據版本性能測試-壓力測試

在這里插入圖片描述
這樣寫的意思就是有1000個用戶在120s內不斷地發送請求

在linux執行jmeter
在這里插入圖片描述
4.7/s
表示每s處理4.7個請求JPS

第一行表示42s內處理了201個請求,然后每s4.7個
=就是上面累加的和,是請求數和時間的累加
+表示是不同的數據
最終表示每s只能處理5.9個請求
所以性能不好
Err:表示錯誤數,和錯誤數占總數的比例
怎么提高性能呢----》redis
redis可以提高很多的性能
這樣也說明了mysql性能太低了
而C端肯定人很多的,所以我們一定要用redis提高速度
因為B端管理員太少了,所以不用管是否用redis提高速度
MySQL可以保證數據的準確性,redis保證速度
所以我們先從redis中查詢,如果沒有再去數據庫,在同步到redis

3.4 redis版本-緩存結構設計

發布競賽的時候可以存入redis,因為只有發布的競賽才會展示給用戶
取消發布的時候就從redis中移除

然后就是已經發布的競賽不允許修改(在前端的按鈕實現了),所以不用擔心redis數據不準確

然后就是選擇什么數據結構存儲到redis中
—》結構是有序的,可以按照開賽時間來排序,而且支持分頁

redis中有list這個數據結構–》支持分頁查詢

然后就未完賽和歷史競賽可以分開存儲到不同redis中,要用兩個list
key是什么—》exam:history:list和exam:time:list(未完賽)
然后就是還有一個list就是我報名的競賽列表,然后就是我報名的競賽列表一定是會和未完賽和歷史競賽重復一些的–》不好,重復次數太多了就不好了,所以就不要用list來存基本信息了,會重復的
—》一個競賽基本信息存一份,這樣就不會浪費了
value為String,json格式,key為exam:detail:examId

所以exam:history:list和exam:time:list(未完賽)存儲examId
exam:detail:examId存儲詳細信息,這樣就不會很浪費了

list存儲examId的,一份基本信息存一份String

3.5 redis版本代碼開發

注意發布的競賽肯定是一個還沒有開始,還沒結束的競賽

—》e:t:l,和e:d:exmaId

取消發布的競賽也是一個還沒有開始,還沒結束的競賽

查詢redis數據的時候,第一次(發布上線的時候自己調用)從數據庫中查詢,然后存入redis,后面才是從redis中查詢

現在system下面寫代碼
就是發布的時候加入redis,取消發布的時候從redis中刪除
創建一個manager的包

@Component
public class ExamCacheManager {@Autowiredprivate RedisService redisService;public void addCache(Exam exam) {redisService.leftPushForList(getExamListKey(), exam.getExamId());redisService.setCacheObject(getDetailKey(exam.getExamId()), exam);}public void deleteCache(Long examId) {redisService.removeForList(getExamListKey(), examId);redisService.deleteObject(getDetailKey(examId));}private String getExamListKey() {return CacheConstants.EXAM_UNFINISHED_LIST;}private String getDetailKey(Long examId) {return CacheConstants.EXAM_DETAIL + examId;}}

leftPushForList往左邊插入,這個就相當于是把早創建的放到前面了

    public final static String EXAM_UNFINISHED_LIST = "exam:time:list"; // 未完賽競賽列表public final static String EXAM_HISTORY_LIST = "exam:history:list";  // 歷史競賽列表public final static String EXAM_DETAIL = "exam:detail:";    //競賽詳情信息

然后就是修改cancelPublish和publish的代碼
把這兩個方法添加進去就可以了
這兩個就是對redis的增加和刪除了
然后就是對redis的查詢了

在這里插入圖片描述
至于對redis的修改呢
我們規定
已經發布的,或者已經開賽的,已經存入redis的,不能修改exam相關數據
撤銷發布的,沒有存入redis的才可以修改exam,這樣就沒有問題了

在這里插入圖片描述
然后就是redis的查詢了

在friend中

@AllArgsConstructor
@Getter
public enum ExamListType {EXAM_UN_FINISH_LIST(0),EXAM_HISTORY_LIST(1);private final Integer value;}
    public <T> List<T> multiGet(final List<String> keyList, Class<T> clazz) {List list = redisTemplate.opsForValue().multiGet(keyList);if (list == null || list.size() <= 0) {return null;}List<T> result = new ArrayList<>();for (Object o : list) {result.add(JSON.parseObject(String.valueOf(o), clazz));}return result;}public <K, V> void multiSet(Map<? extends K, ? extends V> map) {redisTemplate.opsForValue().multiSet(map);}

在RedisService中插入這兩個方法。這兩個方法是批量處理的方法,就是對examId的list批量從redis中獲取詳細數據,或者批量設置數據,不然一個一個設置,效率還是太低了

還是創建一個manager的包

@Component
public class ExamCacheManager {@Autowiredprivate ExamMapper examMapper;@Autowiredprivate RedisService redisService;public Long getListSize(Integer examListType) {String examListKey = getExamListKey(examListType);return redisService.getListSize(examListKey);}public List<ExamVO> getExamVOList(ExamQueryDTO examQueryDTO) {int start = (examQueryDTO.getPageNum() - 1) * examQueryDTO.getPageSize();int end = start + examQueryDTO.getPageSize() - 1; //下標需要 -1String examListKey = getExamListKey(examQueryDTO.getType());List<Long> examIdList = redisService.getCacheListByRange(examListKey, start, end, Long.class);List<ExamVO> examVOList = assembleExamVOList(examIdList);if (CollectionUtil.isEmpty(examVOList)) {//說明redis中數據可能有問題 從數據庫中查數據并且重新刷新緩存examVOList = getExamListByDB(examQueryDTO); //從數據庫中獲取數據refreshCache(examQueryDTO.getType());}return examVOList;}//刷新緩存邏輯public void refreshCache(Integer examListType) {List<Exam> examList = new ArrayList<>();if (ExamListType.EXAM_UN_FINISH_LIST.getValue().equals(examListType)) {//查詢未完賽的競賽列表examList = examMapper.selectList(new LambdaQueryWrapper<Exam>().select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime).gt(Exam::getEndTime, LocalDateTime.now()).eq(Exam::getStatus, Constants.TRUE).orderByDesc(Exam::getCreateTime));} else if (ExamListType.EXAM_HISTORY_LIST.getValue().equals(examListType)) {//查詢歷史競賽examList = examMapper.selectList(new LambdaQueryWrapper<Exam>().select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime).le(Exam::getEndTime, LocalDateTime.now()).eq(Exam::getStatus, Constants.TRUE).orderByDesc(Exam::getCreateTime));}if (CollectionUtil.isEmpty(examList)) {return;}Map<String, Exam> examMap = new HashMap<>();List<Long> examIdList = new ArrayList<>();for (Exam exam : examList) {examMap.put(getDetailKey(exam.getExamId()), exam);examIdList.add(exam.getExamId());}redisService.multiSet(examMap);  //刷新詳情緩存redisService.deleteObject(getExamListKey(examListType));redisService.rightPushAll(getExamListKey(examListType), examIdList);      //刷新列表緩存}private List<ExamVO> getExamListByDB(ExamQueryDTO examQueryDTO) {PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());return examMapper.selectExamList(examQueryDTO);}private List<ExamVO> assembleExamVOList(List<Long> examIdList) {if (CollectionUtil.isEmpty(examIdList)) {//說明redis當中沒數據 從數據庫中查數據并且重新刷新緩存return null;}//拼接redis當中key的方法 并且將拼接好的key存儲到一個list中List<String> detailKeyList = new ArrayList<>();for (Long examId : examIdList) {detailKeyList.add(getDetailKey(examId));}List<ExamVO> examVOList = redisService.multiGet(detailKeyList, ExamVO.class);CollUtil.removeNull(examVOList);if (CollectionUtil.isEmpty(examVOList) || examVOList.size() != examIdList.size()) {//說明redis中數據有問題 從數據庫中查數據并且重新刷新緩存return null;}return examVOList;}private String getExamListKey(Integer examListType) {if (ExamListType.EXAM_UN_FINISH_LIST.getValue().equals(examListType)) {return CacheConstants.EXAM_UNFINISHED_LIST;} else if (ExamListType.EXAM_HISTORY_LIST.getValue().equals(examListType)) {return CacheConstants.EXAM_HISTORY_LIST;}return null;}private String getDetailKey(Long examId) {return CacheConstants.EXAM_DETAIL + examId;}}

現在開始挨個講一下這些方法的使用
getListSize是根據key查出對應的list的元素數量

getExamVOList是根據service傳入的參數進行分頁查詢
其實就是根據頁數和每頁數量進行下標的查詢罷了
中的assembleExamVOList是根據從redis查出的examId再從redis查出對應的exam詳細數據
getExamListByDB中的CollUtil.removeNull(examVOList);就是移除數組中null的元素

refreshCache是刷新緩存,從數據庫中查詢exam列表數據,和詳細數據
將詳細數據直接multiSet,將列表數據,先deleteObject,在rightPushAll
是尾插
然后就可以寫正式的代碼了

    @GetMapping("/semiLogin/redis/list")public TableDataInfo redisList(ExamQueryDTO examQueryDTO){log.info("獲取競賽列表信息,examQueryDTO:{}", examQueryDTO);return examService.redisList(examQueryDTO);}
    @Overridepublic TableDataInfo redisList(ExamQueryDTO examQueryDTO) {Long listSize = examCacheManager.getListSize(examQueryDTO.getType());List<ExamVO> list;TableDataInfo tableDataInfo =new TableDataInfo();if(listSize==null||listSize==0){//說明緩存中沒有數據,所以要先從數據庫中獲取數據,然后存入redislist = list(examQueryDTO);examCacheManager.refreshCache(examQueryDTO.getType());long total = new PageInfo<>(list).getTotal();return TableDataInfo.success(list, total);}else{//直接從redis中獲取數據list = examCacheManager.getExamVOList(examQueryDTO);listSize = examCacheManager.getListSize(examQueryDTO.getType());return TableDataInfo.success(list, listSize);}}

這樣就可以了
在這里插入圖片描述
這樣就可以了
然后就是競賽慢慢就變為結束了的,怎么轉移redis數據呢–》后面再說

3.6 redis版本性能測試

再次進行壓力測試
我們在Windows下用jmeter進行測試,發現快得多有了redis之后,相比以前
然后在linux下的jmeter進行測試
在這里插入圖片描述
發現速度直接提升了幾十倍

總結

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93865.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93865.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93865.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

海濱浴場應急廣播:守護碧海藍天的安全防線

海濱浴場應急廣播&#xff1a;守護碧海藍天的安全防線&#xff01;海濱浴場&#xff0c;是人們休閑娛樂、親近自然的理想場所。然而&#xff0c;變幻莫測的海洋環境也潛藏著諸多安全隱患&#xff0c;如溺水、離岸流、海蜇蜇傷、極端天氣等。為了有效應對突發事件&#xff0c;保…

華曦達港股IPO觀察丨以創新研發為筆,構建AI Home智慧生活新藍圖

深圳市華曦達科技股份有限公司自創立伊始&#xff0c;便將敏銳的市場洞察與前沿技術追蹤視為生命線。通過構建一支卓越的研發團隊&#xff0c;公司專注于自主核心技術的深耕與積累&#xff0c;以精密的硬件與創新的軟件筑起堅實的技術壁壘。其精心打造的“技術創新&#xff0d;…

構建現代化的Web UI自動化測試框架:從圖片上傳測試實踐說起

構建現代化的Web UI自動化測試框架&#xff1a;從圖片上傳測試實踐說起如何設計一個可維護、可擴展的Web UI自動化測試框架&#xff1f;本文通過一個圖片上傳測試實例&#xff0c;詳細介紹專業測試框架的搭建與實踐。當前測試框架結構 首先&#xff0c;讓我們了解一下當前的測試…

Apache IoTDB:大數據時代時序數據庫選型的技術突圍與實踐指南

摘要&#xff1a;時序數據庫在大數據時代迎來爆發式增長&#xff0c;IoTDB作為Apache頂級開源項目展現出顯著優勢&#xff1a;1. 性能卓越&#xff1a;支持千萬級數據點/秒寫入&#xff0c;18:1高壓縮比&#xff0c;查詢延遲低至500ms&#xff1b;2. 創新架構&#xff1a;采用樹…

2025年8月16日(星期六):雨騎古蓮村游記

清晨&#xff0c;當第一縷微光還未完全驅散夜幕的靜謐&#xff0c;我們這群由校長領銜的騎行愛好者已整裝待發。咖啡節早市尚未開攤&#xff0c;空氣中彌漫著一種期待與寧靜交織的氛圍&#xff0c;仿佛連時間都在為我們即將開啟的旅程而放慢腳步。今天的目標是古蓮村&#xff0…

Pandas數據預處理中缺失值處理

一、缺失值的概念表現形式1.數據庫中常用null表示2.部分編程語言中用NA表示3.可能表現為空字符串&#xff08;‘’&#xff09;或特定數值4.在Pandas中統一用NaN表示&#xff08;來自NumPy庫&#xff0c;NaN、NAN、nan本質一致&#xff09;NaN的特性1.與任何值都不相等&#xf…

計算機網絡:(十五)TCP擁塞控制與擁塞控制算法深度剖析

> 當網絡變成"堵城",TCP如何化身智能交通指揮家?揭秘百萬級并發背后的流量控制藝術! ### 一、生死攸關:為什么需要擁塞控制? **真實災難案例**:1986年勞倫斯伯克利實驗室網絡大崩潰,因缺乏擁塞控制導致全網癱瘓36小時。TCP擁塞控制由此誕生,核心解決**資…

python中的單下劃線“_”與雙下劃線“__”的使用場景及“左右雙下劃線”(魔術方法:`__xxx__`)

在Python中&#xff0c;單下劃線“_”和雙下劃線“__”的使用場景和含義有顯著區別&#xff0c;主要體現在命名約定和語法 一、單下劃線“_”的使用場景 單下劃線更多是編程約定&#xff08;而非強制語法&#xff09;&#xff0c;用于傳遞特定的“暗示”&#xff0c;不影響代碼…

我們為什么需要時序數據庫?

引言在當今數據驅動的世界中&#xff0c;時間序列數據正以前所未有的速度增長。從物聯網設備傳感器、金融交易記錄到應用程序性能監控&#xff0c;時間序列數據無處不在。傳統的關系型數據庫在處理這類數據時往往力不從心&#xff0c;這時時序數據庫(Time Series Database, TSD…

python-林粒粒的視頻筆記1

python的方法和函數指什么 可變類型和不可變類型 不可變類型&#xff0c;比如字符串通過方法調用后&#xff0c;字符串本身的值不改變 要改變需要重新賦值才能進行改變 比如可變數據類型類型&#xff0c;調用方法后可以直接改變原列表 因此&#xff0c;可變數據類型需要再重新賦…

CentOS 7的下載與安裝

一 、CentOS 7的下載與安裝 注意&#xff1a; CentOS 7 已于2024年6月30日停止維護&#xff01; 1、下載 由于 centos 7 已經停止維護&#xff0c;部分鏡像網站移除了對centos 7的支持&#xff0c;這里找到了部分現在還可以使用的鏡像網站 阿里云開源鏡像站&#xff1a;http…

礦物分類系統開發筆記(二):模型訓練[刪除空缺行]

目錄 一、階段銜接與開發目標 二、數據準備 三、模型選擇與訓練 1. 邏輯回歸&#xff08;LR&#xff09; 2. 隨機森林&#xff08;RF&#xff09; 3. 高斯樸素貝葉斯&#xff08;GNB&#xff09; 4. 支持向量機&#xff08;SVM&#xff09; 5. AdaBoost 6. XGBoost 四…

通信方式:命名管道

一、命名管道 1. 命名管道的原理 有了匿名管道&#xff0c;理解命名管道就非常簡單了。 對于普通文件而言&#xff0c;兩個進程打開同一個文件&#xff0c;OS是不會將文件加載兩次的&#xff0c;這兩個進程都會指向同一個文件&#xff0c;那么&#xff0c;也就享有同一份 in…

如何將數據庫快速接入大模型實現智能問數,實現chatbi、dataagent,只需短短幾步,不需要配置工作流!

智能問數系統初始化操作流程 一、系統初始化與管理員賬號創建登錄與初始化提示&#xff1a;首次訪問系統登錄頁&#xff0c;若系統未初始化&#xff0c;會彈出 “系統未完成初始化&#xff0c;請初始化管理員賬號” 提示&#xff0c;點擊【去創建】。填寫管理員信息&#xff1a…

告別手寫文檔!Spring Boot API 文檔終極解決方案:SpringDoc OpenAPI

在前后端分離和微服務盛行的今天&#xff0c;API 文檔是團隊協作的“通用語言”。一份清晰、準確、實時同步的文檔&#xff0c;能極大提升開發和聯調效率。然而&#xff0c;手動編寫和維護 API 文檔&#xff08;如 Word、Markdown 或 Postman&#xff09;是一場永無止境的噩夢—…

N4200EX是一款全智能超聲波檢測儀產品簡析

N4200EX是一款全智能超聲波檢測儀&#xff0c;適用于石油、石化、天然氣、氣體生產等行業的壓力管路、閥門、設備的各種防爆場合氣體泄漏、真空泄漏、閥門內漏檢測。●本安防爆設計&#xff0c;防爆、防塵、防水、抗摔。●適應惡劣環境&#xff0c;可在-25℃超低溫環境檢測&…

NestJS @Inject 裝飾器入門教程

一、核心概念解析 1.1 依賴注入&#xff08;DI&#xff09;的本質 依賴注入是一種設計模式&#xff0c;通過 IoC&#xff08;控制反轉&#xff09;容器管理對象生命周期。在 NestJS 中&#xff0c;Injectable() 標記的類會被容器管理&#xff0c;而 Inject() 用于顯式指定依賴項…

網絡地址詳解

子網劃分詳解&#xff1a;從 IP 地址結構到實際應用 在計算機網絡中&#xff0c;子網劃分是一項關鍵的技術&#xff0c;它能幫助我們更高效地管理 IP 地址資源&#xff0c;優化網絡性能。要深入理解子網劃分&#xff0c;首先需要從 IP 地址的基本結構說起。 一、IPv4 地址的基…

吾日三省吾身 | 周反思 8.19

上周一覽總體來說&#xff0c;上個周是一個被項目驅使而險些喪失自主思考能力的危險階段。相比任何有機械化工作經驗的讀者都有類似的體驗&#xff0c;在手上打螺絲的無盡循環中&#xff0c;自己的腦子就會逐漸喪失對自身的感知以及自主思考的能力。而這個負循環一旦開始&#…

08.19總結

連通性 在無向圖中&#xff0c;若任意兩點間均存在路徑相連&#xff0c;則該圖稱為連通圖。 若刪除圖中任意一個頂點后&#xff0c;剩余圖仍保持連通性&#xff0c;則該圖為點雙連通圖。 若刪除圖中任意一條邊后&#xff0c;圖仍保持連通性&#xff0c;則該圖為邊雙連通圖。 在…