全栈工程师工作干货总结(一)
【代码】获取K8S中Token命令。
1. 获取K8S中Token命令
kubectl describe secret $(kubectl get secret -n kube-system |grep admin|awk '{print $1}') -n kube-system|grep '^token'|awk '{print $2}'
2. 一个项目一次push到多个远程仓库
git remote set-url --add origin git@gitee.com:SJshenjian/cloud.git
git remote -v
origin git@github.com:SJshenjian/cloud.git (fetch)
origin git@github.com:SJshenjian/cloud.git (push)
origin git@gitee.com:SJshenjian/cloud.git (push)
OK, 只需要push一次即可推送到github与码云
3. Git配置SSH-Key
全局参数信息配置
git config --global user.name '算法小生'
git config --global user.email 'sjshenjian@outlook.com'
初次使用 SSH 协议进行代码克隆、推送等操作时,需按下述提示完成 SSH 配置
3.1 生成 RSA 密钥
ssh-keygen -t rsa
3.2. 获取 RSA 公钥内容,并配置到 SSH公钥 中
cat ~/.ssh/id_rsa.pub
3.3. 公钥配置快捷入口
https://gitee.com/profile/sshkeys
https://github.com/SJshenjian/cloud/settings/keys
4. CentOS8切换中文输入法
1、命令行中输入:
yum install ibus ibus-libpinyin
2、在设置中Region & Language下添加–》汉语(智能拼音)若不存在, 则重启后添加
5. Uniapp使用AES128加解密16进制
在对接低功耗蓝牙时,我们需要对蓝牙传输数据进行加解密,由于我们对接的命令是16进制,如5500020101aa00,每个16进制表示特定的含义,所以直接对16进制加解密
import CryptoJS from 'crypto-js'
// AES128 加密函数
function aes128Encrypt(hexData, key, iv) {
// 将十六进制字符串转换为字节数组
var data = CryptoJS.enc.Hex.parse(hexData);
// 将密钥和 IV(初始向量)转换为字节数组
var keyBytes = CryptoJS.enc.Hex.parse(key);
var ivBytes = CryptoJS.enc.Hex.parse(iv);
// 执行 AES-128 加密,使用 CBC 模式和 PKCS7 填充
var encrypted = CryptoJS.AES.encrypt(data, keyBytes, {
iv: ivBytes,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 将加密后的字节数组转换为十六进制字符串
var encryptedHex = encrypted.ciphertext.toString();
return encryptedHex;
}
// AES128 解密函数
function aes128Decrypt(encryptedHex, key, iv) {
// 将密文的十六进制字符串转换为字节数组
var encryptedBytes = CryptoJS.enc.Hex.parse(encryptedHex);
// 将密钥和 IV(初始向量)转换为字节数组
var keyBytes = CryptoJS.enc.Hex.parse(key);
var ivBytes = CryptoJS.enc.Hex.parse(iv);
// 将字节数组转换为加密的参数对象
var encryptedData = CryptoJS.lib.CipherParams.create({
ciphertext: encryptedBytes
});
// 执行 AES-128 解密,使用 CBC 模式和 PKCS7 填充
var decrypted = CryptoJS.AES.decrypt(encryptedData, keyBytes, {
iv: ivBytes,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 将解密后的字节数组转换为十六进制字符串
var decryptedText = decrypted.toString(CryptoJS.enc.Hex);
return decryptedText;
}
export function aesDecrypt(encryptedHex) {
// 解密
//const key = 'suanfaxiaosheng@' // AES 密钥,必须是 16 字节(128 位)
const key = "7375616e66617869616f7368656e6740"
//const iv = 'suanfaxiaosheng@' // 初始化向量,必须是 16 字节(128 位)
const iv = "7375616e66617869616f7368656e6740"
const decryptedData = aes128Decrypt(encryptedHex, key, iv)
return decryptedData
}
export function aesEncrypt(hexData) {
// 加密
//const key = 'suanfaxiaosheng@' // AES 密钥,必须是 16 字节(128 位)
const key = "7375616e66617869616f7368656e6740"
//const iv = 'suanfaxiaosheng@' // 初始化向量,必须是 16 字节(128 位)
const iv = "7375616e66617869616f7368656e6740"
const encryptedData = aes128Encrypt(hexData, key, iv)
return encryptedData
}
OK,只要加解密出来的结果与嵌入式那边出来的结果一致,就可以愉快的对接了, 关注算法小生不迷路
6. MongoDB之服务启动
6.1. 编写docker-compose.yaml文件
version: "3"
services:
mongo:
image: mongo:4.2.6
ports:
- 27017:27017
volumes:
- ./data:/data/db:rw
- ./configdb:/data/configdb:rw
environment:
- TZ=Asia/Shanghai
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=admin
container_name: mongo
6.2. 服务启动
docker-compose up -d
7. ElasticSearch批量插入与更新
7.1 批量插入
import pandas as pd
from elasticsearch import helpers
actions = list()
count = 0
for index, item in merged_df.iterrows():
// 过滤nan值
filted_item = dict(filter(lambda x: pd.notna(x[1]),item.items()))
action = {
"_op_type": "index", // index update
"_index": "community_summary", // 索引名
"_id": item['id'], // 文档ID
"_source": filted_item // 文档值
}
actions.append(action)
if len(actions) == 1000:
// 批量写入
helpers.bulk(es12_client.elastic_client, actions)
count += len(actions)
print(count)
actions.clear()
if len(actions) > 0:
helpers.bulk(es12_client.elastic_client, actions)
count += len(actions)
print(count)
actions.clear()
7.2 批量更新
批量更新只需要改动action的以下内容即可
action = {
'_op_type': 'update', // 此处改为update
'_index': item['index'],
'_id': item_['_id'],
'doc': {'estate_type': item['映射物业类型']} // key值改为doc即可,有的需要改成_source,暂不知原因
}
8. OpenJDK时区未设置带来的隐患及上海时区设置
最近项目中发现入库ES与MONGO时间超前8小时导致ES中入库的数据无法立即可见及MONGO中日期不统一问题
在没有更改时区的情况下,尝试入库时日期减去8小时,OK,入库显示时间正常
这明显是我们采用了默认GMT时间导致:在new Date()时,会根据TimeZone.getDefaultRef()获取时区信息
private static synchronized TimeZone setDefaultZone() {
TimeZone tz;
// get the time zone ID from the system properties
String zoneID = AccessController.doPrivileged(
new GetPropertyAction("user.timezone"));
// if the time zone ID is not set (yet), perform the
// platform to Java time zone ID mapping.
if (zoneID == null || zoneID.isEmpty()) {
String javaHome = AccessController.doPrivileged(
new GetPropertyAction("java.home"));
try {
zoneID = getSystemTimeZoneID(javaHome);
if (zoneID == null) {
zoneID = GMT_ID;
}
} catch (NullPointerException e) {
zoneID = GMT_ID;
}
}
// Get the time zone for zoneID. But not fall back to
// "GMT" here.
tz = getTimeZone(zoneID, false);
if (tz == null) {
// If the given zone ID is unknown in Java, try to
// get the GMT-offset-based time zone ID,
// a.k.a. custom time zone ID (e.g., "GMT-08:00").
String gmtOffsetID = getSystemGMTOffsetID();
if (gmtOffsetID != null) {
zoneID = gmtOffsetID;
}
tz = getTimeZone(zoneID, true);
}
assert tz != null;
final String id = zoneID;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
System.setProperty("user.timezone", id);
return null;
}
});
defaultTimeZone = tz;
return tz;
}
8.1 解决方案一
由于项目是微服务,我们修改Dockerfile如下
ENTRYPOINT ["java" ,"-javaagent:/skywalking/skywalking-agent.jar=agent.service_name=MyService", "-Duser.timezone=GMT+8", "-jar", "MyService.jar"]
通过-Duser.timezone=GMT+8
,我们把当前时区设置为上海时区
8.2 解决方案二
由于Dockerfile中继承JDK8,所以我们重新打包该镜像新项目就不需要额外添加时区信息
FROM alpine-jdk8-uft8:latest
编写新Dockerfile文件:
FROM alpine-jdk8-uft8:fj1
RUN apk add tzdata
RUN echo "Asia/Shanghai" > /etc/timezone
RUN apk del tzdata
打包为镜像alpine-jdk8-uft8:fj2
docker build -t alpine-jdk8-uft8:fj2 .
验证时区是否生效:
首先运行进入容器
docker run -it alpine-jdk8-uft8:fj2
编写Test.java文件
import java.util.TimeZone;
public class Test {
public static void main(String[] args) {
System.out.println(TimeZone.getDefault());
}
}
javac Test.java
java Test
sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
// 以下为alpine-jdk8-uft8:fj1的输出
sun.util.calendar.ZoneInfo[id="GMT",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
OK,成功,现在我们可以安心的把tag更改为latest分别push到仓库中去
docker tag alpine-jdk8-uft8:fj2 alpine-jdk8-uft8:latest
注意,如果K8S中该依赖存在,Jenkins发布时即使修改了labels及配置了拉取策略也不会重新拉取Dockerfile中的alpine-jdk8-uft8:latest,原因未知,并且latest不能体现具体版本信息,我们改为alpine-jdk8-uft8:fj2即可
9. 基于Helm快速部署Mysql8
之前我们已在本地安装过helm,假设读者已事先安装好
9.1 添加bitnami仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
9.2 编写install.sh
请查看最新文档,版本不一致可能会无法启动
# 如果是第一次安装,则使用install, 如果安装过则upgrade
helm install mysql bitnami/mysql \
--set image.tag=8.0 \
--set auth.rootPassword=suanfaxiaosheng \
--set mysqlUser=shenjian \
--set mysqlPassword=suanfaxiaosheng \
--set mysqlAllowEmptyPassword=false \
--set mysqlRootAuthenticationPlugin=mysql_native_password \
--set service.type=LoadBalancer
在窗口执行,Windows我是在Git窗口运行的多行命令,如果遇到错误,如下所示,则执行helm repo update
更新Chart仓库即可
Error: no cached repo found. (try 'helm repo update'): open C:\Users\ADMINI~1\AppData\Local\Temp\helm\repository\bitnami-index.yaml: The system cannot find the file specified
9.3 检验安装是否成功
部署状态查看,OK
$ kubectl get pods -w --namespace default
NAME READY STATUS RESTARTS AGE
mysql-0 1/1 Running 0 3m41s
创建验证客户端
# 密码修改为刚才设置的密码
kubectl run mysql-client --rm -it --restart='Never' --image docker.io/bitnami/mysql:8.0 --namespace default --env MYSQL_ROOT_PASSWORD=suanfaxiaosheng --command -- bash
输入密码后即可登录成功
mysql -h mysql.default.svc.cluster.local -u root -p
到目前为止,只能在K8S集群中访问,如果本地Navicat访问的话需要改为NodePort[尝试过部署直接改为NodePort,未成功,如果你有好方法欢迎联系我更正]
9.4 服务端口放开,供工具访问
# K8S端口范围30000-32767,请不要指定33306超过
kubectl patch service mysql --type='json' -p '[{"op": "replace", "path": "/spec/type", "value": "NodePort"}, {"op": "replace", "path": "/spec/ports/0/nodePort", "value": 31306}]'
服务查看
kubectl get services mysql
10. 基于Helm快速部署私有云盘NextCloud
10.1 添加源
helm repo add nextcloud https://nextcloud.github.io/helm/
10.2 编写values.yaml
为了解决通过不被信任的域名访问。请联系您的管理员。如果您就是管理员,请参照 config.sample.php 中的示例编辑 config/config.php 中的“trusted_domains”设置。
nextcloud:
configs:
domains.config.php: |-
<?php
$CONFIG = array (
'trusted_domains' =>
array (
0 => '10.12.19.*',
1 => 'nextcloud.kube.home',
)
);
10.3 编写install.sh
我目前用的mysql,请先创建数据库nextcloud,脚本中会用到
helm install nextcloud nextcloud/nextcloud -f values.yaml \
--set image.tag=27.0.1-fpm \
--set nginx.enabled=true \
--set nextcloud.username=admin,nextcloud.password=suanfaxiaosheng \
--set internalDatabase.enabled=false \
--set externalDatabase.enabled=true \
--set externalDatabase.type=mysql \
--set externalDatabase.host=IP:PORT \
--set externalDatabase.database=nextcloud \
--set externalDatabase.user=nextcloud \
--set externalDatabase.password=suanfaxiaosheng \
--set persistence.enabled=true \
--set persistence.nextcloudData.enabled=true \
--set service.type=NodePort \
--set service.nodePort=31080
我是在git bash中执行的,如果github访问不通,配置下代理,其中代理的端口可在系统代理中查看
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy http://127.0.0.1:7890
执行完后【可能需要多试几次,有时间好几次才会成功下载】,耐心等待数据库中表创建成功
更多配置请查看文档
若想删除直接helm delete nextcloud
即可
10.4 访问
通过本机IP地址访问http://10.12.19.4:31080/即可
登录进去后,我们可以进行创建用户,略
10.5 下载nextcloud客户端
我们可以直接浏览器访问地址,输入新创建的用户名密码进行登录
然后我们下载nextcloud客户端登录账号,这样数据会实时与服务器双向同步,多台设备数据共享,再也不用来回拷贝数据了
10.6 常见问题
- “The Username is already being used” Nextcloud Issue
把nextcloud.username=admin配置中admin换成其他用户名称,重新uninstall后install即可
11. CSDN低质量分文章自动化获取
11.1 背景
最近粉丝终于达到了5K,可是仍然无法通过优质作者申请,原来是平均质量分较低,优化了一些文章后分数提高仍然较慢,所以需要批量获取低质量文章,重点优化
11.2 目标效果
11.3 核心代码
其中的Cookie可以根据浏览器最新的覆盖
@Service
public class CsdnScoreServiceImpl implements CsdnScoreService {
private final String getArticleUrl = "https://blog.csdn.net/community/home-api/v1/get-business-list";
private final String getArticlesScoreUrl = "https://bizapi.csdn.net/trends/api/v1/get-article-score";
@Override
public List<ArticleDetails> getAllTheArticles(String username, String businessType) {
List<ArticleDetails> articleDetails = new ArrayList<>();
int index = 0;
Map<String, String> headers = new HashMap<>(6);
headers.put("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36");
headers.put("Host", "blog.csdn.net");
headers.put("Cookie", "uuid_tt_dd=10_8463471280-1718794507759-204350; FCNEC=%5B%5B%22AKsRol88-fqTmGBYN7YDi9U4ygbkj1JCa7LGB_Eh5oqBMYbxPawawYP1R9HQMitznfEzHTdvP9Hq03iYunTjc6fz30rEUuagbA7rMA4utrG6MGIAyOONuiP8vf-cK8ohqxRwGbzFu1tQjTY70B_-6QJ_4lXJEycdOA%3D%3D%22%5D%5D; loginbox_strategy=%7B%22taskId%22%3A349%2C%22abCheckTime%22%3A1718794512762%2C%22version%22%3A%22exp11%22%2C%22blog-threeH-dialog-exp11tipShowTimes%22%3A1%2C%22blog-threeH-dialog-exp11%22%3A1718794512763%7D; fpv=9660f121e827956d613e4a2657299e01; UserName=SJshenjian; UserInfo=01a1f47bd59046a493a9edaec4a94fa2; UserToken=01a1f47bd59046a493a9edaec4a94fa2; UserNick=%E6%B2%88%E5%81%A5_%E7%AE%97%E6%B3%95%E5%B0%8F%E7%94%9F; AU=7C8; UN=SJshenjian; BT=1718794604114; p_uid=U010000; management_ques=1718794735632; dc_session_id=10_1719017672988.334311; c_first_ref=cn.bing.com; c_segment=14; Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1718794511,1719017674; https_waf_cookie=36c96ef2-eb7d-4e1c43281137a82485add8b511c3873a1823; dc_sid=32f7e92e29922c6bb7f590ee5319d105; creative_btn_mp=3; _clck=1mb890v%7C2%7Cfmu%7C0%7C1631; __gads=ID=97831c72e8a5d544:T=1718794504:RT=1719017718:S=ALNI_Mage4T_L7_PIttVEUlqKDn0abcd5w; __gpi=UID=00000e54e5f01fb5:T=1718794504:RT=1719017718:S=ALNI_MavHywZwa13TawErwQWLQ9HE9HRrg; __eoi=ID=6830a1239ddbaed9:T=1718794504:RT=1719017718:S=AA-AfjZp5nJb-rONHn2kDzA4InqR; yd_captcha_token=ycvu6kdAqCo7T6q8n1U4eqGfUPYDwODSOQZOk3NhHGK92gwCahTTz1bXx3O7an8OGojofgLc9HzheEv565VgOQ%3D%3D; c_first_page=https%3A//blog.csdn.net/wtyuong/article/details/136683702; c_dsid=11_1719018073483.620679; creativeSetApiNew=%7B%22toolbarImg%22%3A%22https%3A//img-home.csdnimg.cn/images/20230921102607.png%22%2C%22publishSuccessImg%22%3A%22https%3A//img-home.csdnimg.cn/images/20240229024608.png%22%2C%22articleNum%22%3A320%2C%22type%22%3A2%2C%22oldUser%22%3Atrue%2C%22useSeven%22%3Afalse%2C%22oldFullVersion%22%3Atrue%2C%22userName%22%3A%22SJshenjian%22%7D; c_pref=https%3A//blog.csdn.net/wtyuong/article/details/136683702; c_ref=https%3A//mp.csdn.net/mp_blog/manage/article%3Fspm%3D1001.2101.3001.5448; c_page_id=default; log_Id_pv=179; Hm_lpvt_6bcd52f51e9b3dce32bec4a3997715ac=1719018084; log_Id_view=9359; waf_captcha_marker=dc58a87b3ede410a37684476ff6946f2a1c1a6c1a952b5f088d359f3bb04f73b; log_Id_click=214; _clsk=1hosmqe%7C1719018094985%7C4%7C0%7Cx.clarity.ms%2Fcollect; dc_tos=sfgivk");
while (true) {
index++;
String msg = HttpUtil.createGet(getArticleUrl).headerMap(headers, true).body("size=20&page=" + index + "&businessType="+businessType
+"&username="+username+"&noMore=false").execute().body();
if (ObjectUtil.isEmpty(msg)) {
break;
}
JSONObject data = JSONUtil.parseObj(msg);
ArticleResponse articleResponse = JSONUtil.toBean(data, ArticleResponse.class);
if (ObjectUtil.isNotEmpty(articleResponse)
&& ObjectUtil.isNotEmpty(articleResponse.getData())
&& ObjectUtil.isNotEmpty(articleResponse.getData().getList())
) {
articleDetails.addAll(articleResponse.getData().getList());
} else {
break;
}
}
return articleDetails;
}
@Override
public Score getArticlesScore(String url) {
Map<String, String> headers = new HashMap<>(6);
headers.put("X-Ca-Key", "203930474");
headers.put("X-Ca-Signature", "+fkC/Z91B8FRai2qZutPI0OyQCX7IsfVFcS7rPZk+YM=");
headers.put("X-Ca-Nonce", "86970a2f-f385-4427-a40b-c90cb17c00b9");
headers.put("X-Ca-Signature-Headers", "x-ca-key,x-ca-nonce");
headers.put("X-Ca-Signed-Content-Type", "multipart/form-data");
headers.put("Accept", "application/json, text/plain, */*");
String body = HttpUtil.createPost(getArticlesScoreUrl).headerMap(headers, true)
.body("url=" + url).execute().body();
if (ObjectUtil.isNotEmpty(body)) {
ScoreResponse scoreResponse = JSONUtil.toBean(body, ScoreResponse.class);
if (ObjectUtil.isNotEmpty(scoreResponse)) {
return scoreResponse.getData();
}
}
return null;
}
@Override
public void exportExcel(String filePath, List<Map<String, Object>> rows) {
// 通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter(filePath);
// 默认的,未添加alias的属性也会写出,如果想只写出加了别名的字段,可以调用此方法排除之
writer.setOnlyAlias(true);
// 合并单元格后的标题行,使用默认标题样式
Integer columnTotal = rows.get(0).size() - 1;
writer.merge(columnTotal, "CSDN文章质量分");
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(rows, true);
// 设置第一列的自动调整列宽
writer.autoSizeColumn(0, true);
// 关闭writer,释放内存
writer.close();
}
}
11.4 测试
@SpringBootTest
class CsdnScoreApplicationTests {
@Resource
private CsdnScoreService scoreService;
@Test
void articleDetailsScore() {
List<Map<String, Object>> rows = new ArrayList<>();
List<ArticleDetails> allTheArticles = scoreService.getAllTheArticles("SJshenjian", "blog");
for (ArticleDetails articleDetails : allTheArticles) {
Score articlesScore = scoreService.getArticlesScore(articleDetails.getUrl());
System.out.println("-------文章质量分------");
System.out.println("文章名称:" + articleDetails.getTitle());
System.out.println("文章分数:" + articlesScore.getScore());
System.out.println("文章建议:" + articlesScore.getMessage());
System.out.println("------- 结束 ------");
Map<String, Object> row = new HashMap<>();
row.put("文章名称", articleDetails.getTitle());
row.put("文章阅读数", articleDetails.getViewCount());
row.put("文章分数", articlesScore.getScore());
row.put("文章建议", articlesScore.getMessage());
rows.add(row);
}
String absolutePath = "/home/shenjian/数据/CSDN文章分数" + DateUtil.currentSeconds() + ".xlsx";
scoreService.exportExcel(absolutePath, rows);
}
}
11.5 源码地址
更多推荐
所有评论(0)