全栈工程师工作干货总结(一)

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 源码地址

https://github.com/SJshenjian/csdn-core

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法小生Đ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值