前言
大家好!我是sum墨,一個一線的底層碼農,平時喜歡研究和思考一些技術相關的問題並整理成文,限於本人水平,如果文章和程式碼有表述不當之處,還請不吝賜教。
作為一名從業已達六年的老碼農,我的工作主要是開發後端Java業務系統,包括各種管理後臺和小程式等。在這些專案中,我設計過單/多租戶體系系統,對接過許多開放平臺,也搞過訊息中心這類較為複雜的應用,但幸運的是,我至今還沒有遇到過線上系統由於程式碼崩潰導致資損的情況。這其中的原因有三點:一是業務系統本身並不複雜;二是我一直遵循某大廠程式碼規約,在開發過程中儘可能按規約編寫程式碼;三是經過多年的開發經驗積累,我成為了一名熟練工,掌握了一些實用的技巧。
我們在做系統的時候,只要這個系統裡面存在角色和許可權相關的業務需求,那麼介面的許可權控制肯定必不可少。但是大家一搜介面許可權相關的資料,出來的就是整合Shrio、Spring Security等各種框架,然後下面一頓貼配置和程式碼,看得人云裡霧裡。實際上介面的許可權控制是整個系統許可權控制裡面很小的一環,沒有設計好底層資料結構,是無法做好介面的許可權控制的。那麼怎麼做一個系統的許可權控制呢?我認為有以下幾步:
那麼接下來我就按這個流程一一給大家說明許可權是怎麼做出來的。(注:只需要SpringBoot和Redis,不需要額外許可權框架。)
本文參考專案原始碼地址:summo-springboot-interface-demo
一、許可權底層表結構設計
第一,只要一個系統是給人用的,那麼這個系統就一定會有一張使用者表;第二,只要有人的地方,就一定會有角色許可權的劃分,最簡單的就是超級管理員、普通使用者;第三,如此常見的設計,會有一套相對規範的設計標準。
而許可權底層表結構設計
的標準就是:RBAC模型
1. RBAC模型簡介
RBAC(Role-Based Access Control)許可權模型的概念,即:基於角色的許可權控制。透過角色關聯使用者,角色關聯許可權的方式間接賦予使用者許可權。
回到業務需求上來,應該是下面這樣的要求:
上圖可以看出,使用者
多對多
角色多對多
許可權
用表結構展示的話就是這樣,一共5張表,3張實體表,2張關聯表
2. 建表語句
(1) t_user
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`user_id` bigint(20) unsigned zerofill NOT NULL AUTO_INCREMENT COMMENT '使用者ID',
`user_name` varchar(32) DEFAULT NULL COMMENT '使用者名稱稱',
`gmt_create` datetime DEFAULT NULL COMMENT '建立時間',
`gmt_modified` datetime DEFAULT NULL COMMENT '更新時間',
`creator_id` bigint DEFAULT NULL COMMENT '建立人ID',
`modifier_id` bigint DEFAULT NULL COMMENT '更新人ID',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ;
(2) t_role
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
`role_id` bigint(20) unsigned zerofill NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`role_name` varchar(32) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '角色名稱',
`role_code` varchar(32) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '角色code',
`gmt_create` datetime DEFAULT NULL COMMENT '建立時間',
`gmt_modified` datetime DEFAULT NULL COMMENT '更新時間',
`creator_id` bigint DEFAULT NULL COMMENT '建立人ID',
`modifier_id` bigint DEFAULT NULL COMMENT '更新人ID',
PRIMARY KEY (`role_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
(3) t_auth
DROP TABLE IF EXISTS `t_auth`;
CREATE TABLE `t_auth` (
`auth_id` bigint(20) unsigned zerofill NOT NULL AUTO_INCREMENT COMMENT '許可權ID',
`auth_code` varchar(32) DEFAULT NULL COMMENT '許可權code',
`auth_name` varchar(32) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '許可權名稱',
`gmt_create` datetime DEFAULT NULL COMMENT '建立時間',
`gmt_modified` datetime DEFAULT NULL COMMENT '更新時間',
`creator_id` bigint DEFAULT NULL COMMENT '建立人ID',
`modifier_id` bigint DEFAULT NULL COMMENT '更新人ID',
PRIMARY KEY (`auth_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
(4) t_user_role
DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (
`id` bigint(20) unsigned zerofill NOT NULL AUTO_INCREMENT COMMENT '物理ID',
`user_id` bigint NOT NULL COMMENT '使用者ID',
`role_id` bigint NOT NULL COMMENT '角色ID',
`gmt_create` datetime DEFAULT NULL COMMENT '建立時間',
`gmt_modified` datetime DEFAULT NULL COMMENT '更新時間',
`creator_id` bigint DEFAULT NULL COMMENT '建立人ID',
`modifier_id` bigint DEFAULT NULL COMMENT '更新人ID',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
(5) t_role_auth
DROP TABLE IF EXISTS `t_role_auth`;
CREATE TABLE `t_role_auth` (
`id` bigint(20) unsigned zerofill NOT NULL AUTO_INCREMENT COMMENT '物理ID',
`role_id` bigint DEFAULT NULL COMMENT '角色ID',
`auth_id` bigint DEFAULT NULL COMMENT '許可權ID',
`gmt_create` datetime DEFAULT NULL COMMENT '建立時間',
`gmt_modified` datetime DEFAULT NULL COMMENT '更新時間',
`creator_id` bigint DEFAULT NULL COMMENT '建立人ID',
`modifier_id` bigint DEFAULT NULL COMMENT '更新人ID',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
二、使用者身份認證和授權
上面已經把表設計好了,接下來就是程式碼開發了。不過,在開發之前我們要搞清楚認證
和授權
這兩個詞是啥意思。
- 什麼是認證?
認證是確認一個使用者的身份,確保使用者是其所聲稱的人。它透過驗證使用者的身份資訊,例如使用者名稱和密碼,來確認使用者的身份。 - 什麼是授權?
授權是根據使用者的身份和許可權,給予使用者特定的訪問許可權或使用某些資源的權力。它確定使用者可以執行的操作,並限制他們不能執行的操作。授權確保使用者只能訪問他們被允許的內容和功能。
光看定義也很難懂,這裡我舉個例子配合說明。
現有兩個使用者:小A和小B;兩個角色:管理員和普通使用者;4個操作:
新增
/刪除
/修改
/查詢
。圖例如下:
那麼,對於小A來說,認證
就是小A登入系統後,會授予管理員的角色,授權
就是授予小A新增/刪除/修改/查詢的許可權;
同理,對於小B來說,認證
就是小B登入系統後,會授予普通使用者的角色,授權
就是授予小B查詢的許可權。
接下來且看如何實現
1. 初始化資料
t_user表資料
INSERT INTO `t_user` (`user_id`, `user_name`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (1, '小A', '2023-09-21 09:48:14', '2023-09-21 09:48:19', -1, -1);
INSERT INTO `t_user` (`user_id`, `user_name`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (2, '小B', '2023-09-21 09:48:14', '2023-09-21 09:48:19', -1, -1);
t_role表資料
INSERT INTO `t_role` (`role_id`, `role_name`, `role_code`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (1, '管理員', 'admin', '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
INSERT INTO `t_role` (`role_id`, `role_name`, `role_code`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (2, '普通使用者', 'normal', '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
t_auth表資料
INSERT INTO `t_auth` (`auth_id`, `auth_code`, `auth_name`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (1, 'add', '新增', '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
INSERT INTO `t_auth` (`auth_id`, `auth_code`, `auth_name`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (2, 'delete', '刪除', '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
INSERT INTO `t_auth` (`auth_id`, `auth_code`, `auth_name`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (3, 'query', '查詢', '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
INSERT INTO `t_auth` (`auth_id`, `auth_code`, `auth_name`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (4, 'update', '更新', '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
t_user_role表資料
INSERT INTO `t_user_role` (`user_id`, `role_id`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (1, 1, '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
INSERT INTO `t_user_role` (`user_id`, `role_id`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (2, 2, '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
t_role_auth表資料
INSERT INTO `t_role_auth` (`role_id`, `auth_id`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (1, 2, '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
INSERT INTO `t_role_auth` (`role_id`, `auth_id`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (1, 1, '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
INSERT INTO `t_role_auth` (`role_id`, `auth_id`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (1, 3, '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
INSERT INTO `t_role_auth` (`role_id`, `auth_id`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (1, 4, '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
INSERT INTO `t_role_auth` (`role_id`, `auth_id`, `gmt_create`, `gmt_modified`, `creator_id`, `modifier_id`) VALUES (2, 3, '2023-09-21 09:52:45', '2023-09-21 09:52:47', -1, -1);
2、新增/user/login介面模擬登入
介面程式碼如下
@GetMapping("/login")
public ResponseEntity<String> userLogin(@RequestParam(required = true) String userName,
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {
return userService.login(userName, httpServletRequest, httpServletResponse);
}
業務程式碼如下
@Override
public ResponseEntity<String> login(String userName, HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {
//根據名稱查詢使用者資訊
UserDO userDO = userMapper.selectOne(new QueryWrapper<UserDO>().lambda().eq(UserDO::getUserName, userName));
if (Objects.isNull(userDO)) {
return ResponseEntity.ok("未查詢到使用者");
}
//查詢當前使用者的角色資訊
List<UserRoleDO> userRoleDOList = userRoleMapper.selectList(
new QueryWrapper<UserRoleDO>().lambda().eq(UserRoleDO::getUserId, userDO.getUserId()));
if (CollectionUtils.isEmpty(userRoleDOList)) {
return ResponseEntity.ok("當前使用者沒有角色");
}
//查詢當前使用者的許可權
List<RoleAuthDO> roleAuthDOS = roleAuthMapper.selectList(new QueryWrapper<RoleAuthDO>().lambda()
.in(RoleAuthDO::getRoleId, userRoleDOList.stream().map(UserRoleDO::getRoleId).collect(
Collectors.toList())));
if (CollectionUtils.isEmpty(roleAuthDOS)) {
return ResponseEntity.ok("當前角色沒有對應許可權");
}
//查詢許可權code
List<AuthDO> authDOS = authMapper.selectList(new QueryWrapper<AuthDO>().lambda()
.in(AuthDO::getAuthId, roleAuthDOS.stream().map(RoleAuthDO::getAuthId).collect(
Collectors.toList())));
//生成唯一token
String token = UUID.randomUUID().toString();
//快取使用者資訊
redisUtil.set(token, JSONObject.toJSONString(userDO), tokenTimeout);
//快取使用者許可權資訊
redisUtil.set("auth_" + userDO.getUserId(),
JSONObject.toJSONString(authDOS.stream().map(AuthDO::getAuthCode).collect(Collectors.toList())),
tokenTimeout);
//向localhost中新增Cookie
Cookie cookie = new Cookie("token", token);
cookie.setDomain("localhost");
cookie.setPath("/");
cookie.setMaxAge(tokenTimeout.intValue());
httpServletResponse.addCookie(cookie);
//返回登入成功
return ResponseEntity.ok(JSONObject.toJSONString(userDO));
}
上面程式碼用流程圖表示如下
3. 呼叫登入介面
小A登入:http://localhost:8080/user/login?userName=小A
小B登入:http://localhost:8080/user/login?userName=小B
(沒畫前端介面,大家將就看下哈)
小A登入呼叫返回如下
小B登入呼叫返回如下
三、使用者許可權驗證邏輯
透過第二步,使用者已經進行了認證、授權的操作,那麼接下來就是使用者驗權:即驗證使用者是否有呼叫介面的許可權。
1. 定義介面許可權註解
前面定義了4個許可權:新增
/刪除
/修改
/查詢
,分別對應著4個介面。這裡我們使用註解進行一一對應。
註解定義如下:
RequiresPermissions.java
package com.summo.demo.config.permissions;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermissions {
/**
* 許可權列表
* @return
*/
String[] value();
/**
* 許可權控制方式,且或者和
* @return
*/
Logical logical() default Logical.AND;
}
該註解有兩個屬性,value和logical。value是一個陣列,代表當前介面擁有哪些許可權;logical有兩個值AND和OR,AND的意思是當前使用者必須要有value中所有的許可權才可以呼叫該介面,OR的意思是當前使用者只需要有value中任意一個許可權就可以呼叫該介面。
註解處理程式碼邏輯如下:
RequiresPermissionsHandler.java
package com.summo.demo.config.permissions;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import com.alibaba.fastjson.JSONObject;
import com.summo.demo.config.context.GlobalUserContext;
import com.summo.demo.config.context.UserContext;
import com.summo.demo.config.manager.UserManager;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class RequiresPermissionsHandler {
@Autowired
private UserManager userManager;
@Pointcut("@annotation(com.summo.demo.config.permissions.RequiresPermissions)")
public void pointcut() {
// do nothing
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//獲取使用者上下文
UserContext userContext = GlobalUserContext.getUserContext();
if (Objects.isNull(userContext)) {
throw new RuntimeException("使用者認證失敗,請檢查是否登入");
}
//獲取註解
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
//獲取當前介面上資料許可權
String[] permissions = requiresPermissions.value();
if (Objects.isNull(permissions) && permissions.length == 0) {
throw new RuntimeException("使用者認證失敗,請檢查該介面是否新增了資料許可權");
}
//判斷當前是and還是or
String[] notHasPermissions;
switch (requiresPermissions.logical()) {
case AND:
//當邏輯為and時,所有的資料許可權必須存在
notHasPermissions = checkPermissionsByAnd(userContext.getUserId(), permissions);
if (Objects.nonNull(notHasPermissions) && notHasPermissions.length > 0) {
throw new RuntimeException(
MessageFormat.format("使用者許可權不足,缺失以下許可權:[{0}]", JSONObject.toJSONString(notHasPermissions)));
}
break;
case OR:
//當邏輯為and時,所有的資料許可權必須存在
notHasPermissions = checkPermissionsByOr(userContext.getUserId(), permissions);
if (Objects.nonNull(notHasPermissions) && notHasPermissions.length > 0) {
throw new RuntimeException(
MessageFormat.format("使用者許可權不足,缺失以下許可權:[{0}]", JSONObject.toJSONString(notHasPermissions)));
}
break;
default:
//預設為and
}
return joinPoint.proceed();
}
/**
* 當資料許可權為or時,進行判斷
*
* @param userId 使用者ID
* @param permissions 許可權組
* @return 沒有授予的許可權
*/
private String[] checkPermissionsByOr(Long userId, String[] permissions) {
// 獲取使用者許可權集
Set<String> permissionSet = userManager.queryAuthByUserId(userId);
if (permissionSet.isEmpty()) {
return permissions;
}
//一一比對
List<String> tempPermissions = new ArrayList<>();
for (String permission1 : permissions) {
permissionSet.forEach(permission -> {
if (permission1.equals(permission)) {
tempPermissions.add(permission);
}
});
}
if (Objects.nonNull(tempPermissions) && tempPermissions.size() > 0) {
return null;
}
return permissions;
}
/**
* 當資料許可權為and時,進行判斷
*
* @param userId 使用者ID
* @param permissions 許可權組
* @return 沒有授予的許可權
*/
private String[] checkPermissionsByAnd(Long userId, String[] permissions) {
// 獲取使用者許可權集
Set<String> permissionSet = userManager.queryAuthByUserId(userId);
if (permissionSet.isEmpty()) {
return permissions;
}
//如果permissions大小為1,可以單獨處理一下
if (permissionSet.size() == 1 && permissionSet.contains(permissions[0])) {
return null;
}
if (permissionSet.size() == 1 && !permissionSet.contains(permissions[0])) {
return permissions;
}
//一一比對
List<String> tempPermissions = new ArrayList<>();
for (String permission1 : permissions) {
permissionSet.forEach(permission -> {
if (permission1.equals(permission)) {
tempPermissions.add(permission);
}
});
}
//如果tempPermissions的長度與permissions相同,那麼說明許可權吻合
if (permissions.length == tempPermissions.size()) {
return null;
}
//否則取出當前使用者沒有的許可權,並返回用作提示
List<String> notHasPermissions = Arrays.stream(permissions).filter(
permission -> !tempPermissions.contains(permission)).collect(Collectors.toList());
return notHasPermissions.toArray(new String[notHasPermissions.size()]);
}
}
2. 註解使用方式
使用比較簡單,直接放到介面的方法上
@GetMapping("/add")
@RequiresPermissions(value = "add", logical = Logical.OR)
public ResponseEntity<String> add(@RequestBody AddReq addReq) {
return userService.add(addReq);
}
@GetMapping("/delete")
@RequiresPermissions(value = "delete", logical = Logical.OR)
public ResponseEntity<String> delete(@RequestParam Long userId) {
return userService.delete(userId);
}
@GetMapping("/query")
@RequiresPermissions(value = "query", logical = Logical.OR)
public ResponseEntity<String> query(@RequestParam String userName) {
return userService.query(userName);
}
@GetMapping("/update")
@RequiresPermissions(value = "update", logical = Logical.OR)
public ResponseEntity<String> update(@RequestBody UpdateReq updateReq) {
return userService.update(updateReq);
}
3. 介面驗權的流程
四、使用者許可權變動後的狀態重新整理
其實前面三步完成後,正向流已經完成了,但使用者的許可權是變化的,比如:
小B的許可權從
查詢
變為了查詢
加更新
但小B的token還未過期,這時應該怎麼辦呢?
還記得登入的時候,我有快取兩個資訊嗎
對應程式碼中的
//快取使用者資訊
redisUtil.set(token, JSONObject.toJSONString(userDO), tokenTimeout);
//快取使用者許可權資訊
redisUtil.set("auth_" + userDO.getUserId(),JSONObject.toJSONString(authDOS.stream().map(AuthDO::getAuthCode).collect(Collectors.toList())),tokenTimeout);
在這裡我其實將token和許可權是分開儲存的,token只存使用者資訊,而許可權資訊用
auth_userId
為key進行儲存的,這樣就可以做到即使token還在,我也能動態修改當前使用者的許可權資訊了,且許可權實時變更不會影響使用者體驗。
不過,這個地方有一個爭議的點
使用者許可權發生變更的時候,是更新許可權快取呢?還是直接刪除使用者的許可權快取呢?
我的建議是:刪除許可權快取。原因有三
- 使用者許可權快取並不是一直存在,存在連快取都沒有的情況。
- 快取更新只適用於單個使用者許可權的更新,但是我要把角色和許可權的關聯變動了呢?
- 直接把許可權快取刪除,使用者會不會報錯?我查詢許可權快取的方式是:
先查詢快取,快取沒有在查詢資料庫
,所以並不會出現快取被刪除就報錯的情況。
tips:如何優雅的實現“先查詢快取再查詢資料庫?”請看我這篇文章:https://juejin.cn/post/7124885941117779998
五、認證失敗或無許可權等異常情況處理
出現由於許可權不足或認證失敗的問題,常見的做法有重定向到登入頁、通知使用者重新整理介面等,具體怎麼處理還要看產品是怎麼要求的。
關於網站的異常有很多,許可權相關的狀態碼是401、伺服器錯誤的狀態碼是500,除此之外還會有自定義的錯誤碼,我打算放在介面最佳化系列的後面用專篇說明,敬請期待哦~
寫在最後
《最佳化介面設計的思路》系列已結寫到第四篇了,前面幾篇都沒有總結,在這篇總結一下吧。
從我開始寫部落格到現在已經6年了,差不多也寫了將近60篇左右的文章。剛開始的時候就是寫SpringBoot,寫SpringBoot如何整合Vue,那是2017年。
得益於老大的要求(或者是公司想省錢),剛工作的時候就是前後端程式碼都寫,但是寫的一塌糊塗,甚至連最基礎的專案環境都搭不好。那時候在網上找個pom.xml配置,依賴死活下載不下來,後來才知道maven倉庫預設國外的源,要把它換成國內的才能提高下載速度。那時候上班就是下午把專案跑起來了,第二天上午專案又啟動不了了,如此迴圈往復,我的筆記裡面存了非常多的配置檔案。再後來技術水平提高了點,單專案終於會玩了,微服務又火起來了,瞭解過SpringCloud的小夥伴應該知道SpringCloud的版本更復雜,搭建環境更難。在這可能有人會疑惑,你不會不能去問人嗎?我也很無奈,一則是社恐不敢問,二則是我們部門全是菜鳥,都等著我學會教他們呢...
後來我老大說,既然用不來人家的,那就自己寫一套,想起來那時真單純,我就真的自己開始寫微服務架構。最開始我對微服務的唯一印象就是一個服務提供者、一個服務消費者,肯定是兩個應用,至於為啥是這樣,查的百度都是這樣寫的。然後我就建了兩個應用,一個閘道器應用、一個業務應用,自己寫HttpUtil進行服務間呼叫,也不知道啥是註冊中心,我只知道閘道器應用那裡要有業務應用的IP地址,否則閘道器調不了業務程式碼。當時的呼叫程式碼我已經找不了,只記得當時程式碼的形狀很像一個“>”,用了太多的if...else...了!!!
那時候雖然程式碼寫的很爛、bug一堆,但我們老大也沒罵我們,每週四還會給我們上夜校,跟我們講一些大廠的框架和技術棧。他跟我們說,現在多用用人家的技術,到時候出去面試大廠也容易一些。寫博文也是老大讓我們做的,他說現在一點點的積累,等到過幾年就會變成文庫了。現在想來,真是一個不錯的老大!
現在2023年了,我還在寫程式碼,但也不僅僅只是寫程式碼,還帶一些專案,獨立負責的也有。要說我現在的程式碼水平嘛,屬於那種工廠熟練工水平,八股裡面的什麼JVM調優啊、高併發系統架構設計啊我一次都沒有接觸到過,遠遠稱不上大神。不過我還是想寫一些文章,不是為了炫技,只是想把我工作中遇到的問題變成後續解決問題的經驗,說真的這些文章已經開始幫到我了,如果它們也能幫助到你,榮幸之至!