<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
    <channel>
            <title>高大北博客</title>
            <link>https://www.sky12580.cn</link>
        <generator>Halo 1.5.3</generator>
        <lastBuildDate>Mon, 27 Oct 2025 16:26:08 CST</lastBuildDate>
                <item>
                    <title>
                        <![CDATA[Ubuntu 24.04 安装配置 Nginx 1.24]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/ubuntu2404-an-zhuang-pei-zhi-nginx124</link>
                    <description>
                            <![CDATA[<pre><code class="language-">wget http://nginx.org/download/nginx-1.24.0.tar.gz</code></pre><pre><code class="language-">tar -zxvf nginx-1.24.0.tar.gz</code></pre><pre><code class="language-">mkdir /var/temp/nginx -p</code></pre><pre><code class="language-">cd nginx-1.24.0</code></pre><pre><code class="language-">sudo apt update</code></pre><pre><code class="language-">sudo apt install -y build-essential libpcre3-dev zlib1g-dev libssl-dev</code></pre><pre><code class="language-">./configure --prefix=/usr/local/nginx --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --http-client-body-temp-path=/var/temp/nginx/client --http-proxy-temp-path=/var/temp/nginx/proxy --http-fastcgi-temp-path=/var/temp/nginx/fastcgi --http-uwsgi-temp-path=/var/temp/nginx/uwsgi --http-scgi-temp-path=/var/temp/nginx/scgi --with-http_ssl_module</code></pre><pre><code class="language-">make -j$(nproc)</code></pre><pre><code class="language-">make install</code></pre><pre><code class="language-">sudo /usr/local/nginx/sbin/nginxsudo /usr/local/nginx/sbin/nginx -s stopsudo /usr/local/nginx/sbin/nginx -s reload/usr/local/nginx/sbin/nginx -v</code></pre>]]>
                    </description>
                    <pubDate>Mon, 27 Oct 2025 16:23:40 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[proguard核心代码包混淆-java]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/proguard-he-xin-dai-ma-bao-hun-xiao--java</link>
                    <description>
                            <![CDATA[<pre><code class="language-">            &lt;plugin&gt;                &lt;groupId&gt;com.github.wvengen&lt;/groupId&gt;                &lt;artifactId&gt;proguard-maven-plugin&lt;/artifactId&gt;                &lt;version&gt;2.1.0&lt;/version&gt;                &lt;executions&gt;                    &lt;execution&gt;                        &lt;!-- 混淆时刻，这里是打包的时候混淆--&gt;                        &lt;phase&gt;package&lt;/phase&gt;                        &lt;goals&gt;                            &lt;!-- 使用插件的什么功能，当然是混淆--&gt;                            &lt;goal&gt;proguard&lt;/goal&gt;                        &lt;/goals&gt;                    &lt;/execution&gt;                &lt;/executions&gt;                &lt;configuration&gt;                    &lt;!-- 是否将生成的PG文件安装部署--&gt;                    &lt;attach&gt;true&lt;/attach&gt;                    &lt;!-- 是否混淆--&gt;                    &lt;obfuscate&gt;true&lt;/obfuscate&gt;                    &lt;!-- 指定生成文件分类 --&gt;                    &lt;attachArtifactClassifier&gt;pg&lt;/attachArtifactClassifier&gt;                    &lt;options&gt;                        &lt;!-- JDK目标版本1.8--&gt;                        &lt;option&gt;-target 1.8&lt;/option&gt;                        &lt;!-- 不做收缩（删除注释、未被引用代码）--&gt;                        &lt;option&gt;-dontshrink&lt;/option&gt;                        &lt;!-- 不做优化（变更代码实现逻辑）--&gt;                        &lt;option&gt;-dontoptimize&lt;/option&gt;                        &lt;!-- 不路过非公用类文件及成员--&gt;                        &lt;option&gt;-dontskipnonpubliclibraryclasses&lt;/option&gt;                        &lt;!--                        &lt;option&gt;-dontskipnonpubliclibraryclassmembers&lt;/option&gt;--&gt;                        &lt;!--不用大小写混合类名机制--&gt;                        &lt;option&gt;-dontusemixedcaseclassnames&lt;/option&gt;                        &lt;!-- 优化时允许访问并修改有修饰符的类和类的成员 --&gt;                        &lt;option&gt;-allowaccessmodification&lt;/option&gt;                        &lt;!-- 确定统一的混淆类的成员名称来增加混淆--&gt;                        &lt;option&gt;-useuniqueclassmembernames&lt;/option&gt;                        &lt;!-- 不混淆所有包名--&gt;                        &lt;!--                        &lt;option&gt;-keeppackagenames&lt;/option&gt;--&gt;                        &lt;option&gt;-ignorewarnings&lt;/option&gt;                        &lt;option&gt;-dontwarn&lt;/option&gt;                        &lt;!-- 需要保持的属性：异常，注解等：：：LocalVariable*Table,--&gt;                        &lt;option&gt;-keepattributes                            Exceptions,InnerClasses,Signature,Deprecated,SourceFile,*Annotation*,Synthetic,EnclosingMethod                        &lt;/option&gt;                        &lt;!-- 不混淆所有的set/get方法 --&gt;                        &lt;option&gt;-keepclassmembers public class * {void set*(***);*** get*();}&lt;/option&gt;                        &lt;!--不混淆包下的所有类名，且类中的方法也不混淆  --&gt;                        &lt;option&gt;-keep class com.huatuo.server.filter.**{*;}&lt;/option&gt;                        &lt;option&gt;-keep class com.huatuo.server.beans.**{*;}&lt;/option&gt;                        &lt;option&gt;-keep class com.huatuo.server.http.**{*;}&lt;/option&gt;                        &lt;!--                        &lt;option&gt;-keep class com.huatuo.server.arthas.**{*;}&lt;/option&gt;--&gt;                        &lt;option&gt;-keep class javax.annotation.**{*;}&lt;/option&gt;                        &lt;option&gt;-keep class org.apache.**{*;}&lt;/option&gt;                        &lt;option&gt;-keep class javax.servlet.**{*;}&lt;/option&gt;                        &lt;option&gt;-keep class com.sun.tools.attach.**{*;}&lt;/option&gt;                        &lt;option&gt;-keep class com.sun.tools.attach.spi.**{*;}&lt;/option&gt;                        &lt;option&gt;-keep class sun.tools.attach.**{*;}&lt;/option&gt;                        &lt;!--                        &lt;option&gt;-keep class com.huatuo.server.util.OsUtil&lt;/option&gt;--&gt;                    &lt;/options&gt;                    &lt;!--class 混淆后输出的jar包--&gt;                    &lt;outjar&gt;huatuo-probe-pg.jar&lt;/outjar&gt;                    &lt;!-- 添加依赖，这里你可以按你的需要修改，这里测试只需要一个JRE的Runtime包就行了 --&gt;                    &lt;libs&gt;                        &lt;lib&gt;${java.home}/lib/rt.jar&lt;/lib&gt;                        &lt;!--                        &lt;lib&gt;${java.home}/lib/jce.jar&lt;/lib&gt;--&gt;                    &lt;/libs&gt;                    &lt;!-- 对什么东西进行加载，这里仅有classes成功，毕竟你也不可能对配置文件及JSP混淆吧--&gt;                    &lt;injar&gt;classes&lt;/injar&gt;                    &lt;!-- 输出目录--&gt;                    &lt;outputDirectory&gt;${project.build.directory}&lt;/outputDirectory&gt;                &lt;/configuration&gt;            &lt;/plugin&gt;</code></pre>]]>
                    </description>
                    <pubDate>Tue, 14 Oct 2025 14:29:12 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[阿里云标准加密hls【支持苹果手机和安卓手机以及web】]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/a-li-yun-biao-zhun-jia-mi-hls-zhi-chi-ping-guo-shou-ji-he-an-zhuo-shou-ji-yi-ji-web</link>
                    <description>
                            <![CDATA[<p>先把demo代码占出来，改天有空写逻辑</p><pre><code class="language-">package com.cjbdi.test;import com.aliyuncs.DefaultAcsClient;import com.aliyuncs.exceptions.ClientException;import com.aliyuncs.kms.model.v20160120.GenerateDataKeyRequest;import com.aliyuncs.kms.model.v20160120.GenerateDataKeyResponse;import com.aliyuncs.profile.DefaultProfile;import com.aliyuncs.vod.model.v20170321.GenerateKMSDataKeyRequest;import com.aliyuncs.vod.model.v20170321.GenerateKMSDataKeyResponse;import com.aliyuncs.vod.model.v20170321.SubmitTranscodeJobsRequest;import com.aliyuncs.vod.model.v20170321.SubmitTranscodeJobsResponse;import com.alibaba.fastjson.JSONObject;import java.net.URLEncoder;import java.nio.charset.StandardCharsets;public class SubmitEncryptTranscodeJob {    private static DefaultAcsClient kmsClient;    private static DefaultAcsClient vodClient;    static {         String REGION_ID = &quot;cn-beijing&quot;; // 阿里云区域ID        String accessKeyId = &quot;&quot;;        String accessKeySecret =&quot;&quot;;        // 初始化KMS客户端        DefaultProfile kmsProfile = DefaultProfile.getProfile(REGION_ID, accessKeyId, accessKeySecret);        kmsClient = new DefaultAcsClient(kmsProfile);        // 初始化VOD客户端        DefaultProfile vodProfile = DefaultProfile.getProfile(REGION_ID, accessKeyId, accessKeySecret);        vodClient = new DefaultAcsClient(vodProfile);    }    /**     * 提交HLS加密转码任务     *      * @param videoId 视频ID（从上传回调获取）     * @param templateGroupId HLS加密模板组ID     * @param decryptServiceUrl 解密服务地址 (e.g., &quot;http://your-server:8099/decrypt&quot;)     * @return 转码任务响应     */    public static SubmitTranscodeJobsResponse submitHlsEncryptJob(            String videoId,             String templateGroupId,            String decryptServiceUrl) throws ClientException {        //        // 1. 生成KMS数据密钥//        GenerateDataKeyRequest keyRequest = new GenerateDataKeyRequest();//        // 设置密钥规格为AES_256（阿里云VOD要求）////        keyRequest.setKeySpec(&quot;AES_256&quot;);//        GenerateDataKeyResponse keyResponse = kmsClient.getAcsResponse(keyRequest);        GenerateKMSDataKeyRequest request = new GenerateKMSDataKeyRequest();        GenerateKMSDataKeyResponse keyResponse = kmsClient.getAcsResponse(request);        System.out.println(&quot;获取ciphertextBlob&quot;);        String ciphertextBlob = keyResponse.getCiphertextBlob();        System.out.println(&quot;CiphertextBlob Value: &quot; + ciphertextBlob); // &lt;-- 添加这行日志        // 2. 构建加密配置        JSONObject encryptConfig = new JSONObject();        encryptConfig.put(&quot;CipherText&quot;, keyResponse.getCiphertextBlob()); // 信封加密密钥(EDK)        System.out.println(&quot;EncryptConfig JSONObject: &quot; + encryptConfig.toJSONString()); // &lt;-- 添加这行日志        // 使用 UTF-8 编码进行 URL 编码        String encodedCipherText;        try {            // 使用 UTF-8 编码进行 URL 编码            encodedCipherText = URLEncoder.encode(ciphertextBlob, StandardCharsets.UTF_8.toString());        } catch (Exception e) {            throw new RuntimeException(&quot;Failed to encode CiphertextBlob for URL&quot;, e);        }        String fullDecryptKeyUri = decryptServiceUrl + &quot;?CipherText=&quot; + encodedCipherText;        System.out.println(&quot;Constructed Full DecryptKeyUri: &quot; + fullDecryptKeyUri);        // 3. 构建加密配置 (注意 CipherText 字段现在是可选的，因为 URI 已经包含了它)        // encryptConfig.put(&quot;CipherText&quot;, ciphertextBlob); // 可以保留，也可以不保留，取决于 VOD 要求        encryptConfig.put(&quot;DecryptKeyUri&quot;, fullDecryptKeyUri); // 使用拼接好的完整 URI        encryptConfig.put(&quot;KeyServiceType&quot;, &quot;KMS&quot;); // 加密类型                // 3. 提交转码任务        SubmitTranscodeJobsRequest transcodeRequest = new SubmitTranscodeJobsRequest();        transcodeRequest.setVideoId(videoId);        transcodeRequest.setTemplateGroupId(templateGroupId);        transcodeRequest.setEncryptConfig(encryptConfig.toJSONString());        System.out.println(&quot;Final EncryptConfig JSON String: &quot; + encryptConfig.toJSONString()); // &lt;-- 添加这行日志                return vodClient.getAcsResponse(transcodeRequest);    }    public static void main(String[] args) {        try {            // 示例参数 - 实际使用中应从配置或参数传入            String videoId = &quot;40bafb848b1171f080a77fb2780c0102&quot;;            String templateGroupId = &quot;610e0d1c7b9ed52d6974d72dd2075743&quot;;            //http://g5w6p0oa.dongtaiyuming.net/decrypt?CipherText=123.&amp;Token=123            String decryptServiceUrl = &quot;https://show.syntheticreality.cn/hd-api/decrypt&quot;;                        SubmitTranscodeJobsResponse response = submitHlsEncryptJob(                    videoId, templateGroupId, decryptServiceUrl);                        System.out.println(&quot;转码任务提交成功!&quot;);            System.out.println(&quot;请求ID: &quot; + response.getRequestId());            System.out.println(&quot;转码任务ID: &quot; + response.getTranscodeTaskId());                    } catch (ClientException e) {            System.err.println(&quot;转码任务提交失败:&quot;);            System.err.println(&quot;错误代码: &quot; + e.getErrCode());            System.err.println(&quot;错误消息: &quot; + e.getErrMsg());            System.err.println(&quot;请求ID: &quot; + e.getRequestId());        }    }}</code></pre><pre><code class="language-">package com.cjbdi.test;import com.aliyuncs.DefaultAcsClient;import com.aliyuncs.exceptions.ClientException;import com.aliyuncs.http.ProtocolType;import com.aliyuncs.profile.DefaultProfile;// 注意：导入的是 VOD 的 DecryptKMSDataKeyRequest/Responseimport com.aliyuncs.vod.model.v20170321.DecryptKMSDataKeyRequest;import com.aliyuncs.vod.model.v20170321.DecryptKMSDataKeyResponse;import com.sun.net.httpserver.Headers;import com.sun.net.httpserver.HttpExchange;import com.sun.net.httpserver.HttpHandler;import com.sun.net.httpserver.HttpServer;import org.apache.commons.codec.binary.Base64; // 确保引入了 commons-codecimport java.io.IOException;import java.io.OutputStream;import java.net.HttpURLConnection;import java.net.InetSocketAddress;import java.net.URI;import java.net.URLDecoder; // 导入 URLDecoderimport java.nio.charset.StandardCharsets; // 导入 StandardCharsetsimport java.util.regex.Matcher;import java.util.regex.Pattern;public class HlsDecryptServer {    private static final int PORT = 8099;    private static final String CONTEXT_PATH = &quot;/decrypt&quot;; // 定义一个明确的上下文路径    private static DefaultAcsClient client;    static {        // --- 配置 ---        // KMS的区域，必须与视频对应区域        // 访问KMS的授权AccessKey信息        // 强烈建议不要把AccessKey ID和AccessKey Secret保存到工程代码里。        // 本示例为了演示方便，直接写在代码里。生产环境请从环境变量或配置文件读取。        String region = &quot;cn-beijing&quot;; // 阿里云区域ID        String accessKeyId = &quot;LTAI5tFzxxuoLfUEF2UzrCsB&quot;;        String accessKeySecret =&quot;zTZkmMSc0WoGR6nhIh7cLbfYWsIQ8S&quot;;        // 检查凭据是否设置        if (accessKeyId == null || accessKeySecret == null || accessKeyId.isEmpty() || accessKeySecret.isEmpty()) {            System.err.println(&quot;错误: 请设置环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET。&quot;);            // System.exit(1); // 或者抛出异常阻止服务器启动            client = null; // 标记客户端初始化失败        } else {            try {                DefaultProfile profile = DefaultProfile.getProfile(region, accessKeyId, accessKeySecret);                client = new DefaultAcsClient(profile);                System.out.println(&quot;HlsDecryptServerNoToken: VOD KMS Client initialized for region: &quot; + region);            } catch (Exception e) {                System.err.println(&quot;HlsDecryptServerNoToken: Failed to initialize VOD KMS Client: &quot; + e.getMessage());                e.printStackTrace();                client = null;            }        }    }    /**     * 说明：     * 1、接收解密请求，获取密文密钥和令牌Token     * 2、调用VOD KMS decrypt接口获取明文密钥     * 3、将明文密钥base64decode返回     */    public class HlsDecryptHandler implements HttpHandler {        /**         * 处理解密请求         * @param httpExchange         * @throws IOException         */        @Override // 添加 @Override 注解        public void handle(HttpExchange httpExchange) throws IOException {            System.out.println(&quot;\n--- Received new request ---&quot;);            System.out.println(&quot;Request URI: &quot; + httpExchange.getRequestURI());            System.out.println(&quot;Request Method: &quot; + httpExchange.getRequestMethod());            Headers responseHeaders = httpExchange.getResponseHeaders();            // 设置 CORS 头            responseHeaders.add(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);            responseHeaders.add(&quot;Access-Control-Allow-Methods&quot;, &quot;GET, OPTIONS&quot;);            responseHeaders.add(&quot;Access-Control-Allow-Headers&quot;, &quot;*&quot;);            String requestMethod = httpExchange.getRequestMethod();            // 处理 OPTIONS 预检请求            if (&quot;OPTIONS&quot;.equalsIgnoreCase(requestMethod)) {                httpExchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, -1); // 204 No Content                httpExchange.close(); // 关闭交换                System.out.println(&quot;Handled OPTIONS preflight request.&quot;);                return;            }            if (&quot;GET&quot;.equalsIgnoreCase(requestMethod)) {                try {                    // 从URL中取得密文密钥                    String ciphertext = getCiphertext(httpExchange);                    System.out.println(&quot;Extracted CipherText: &quot; + ciphertext);                    if (ciphertext == null || ciphertext.isEmpty()) {                        sendErrorResponse(httpExchange, HttpURLConnection.HTTP_BAD_REQUEST, &quot;Missing or empty CipherText parameter.&quot;);                        return;                    }                    if (client == null) {                        sendErrorResponse(httpExchange, HttpURLConnection.HTTP_INTERNAL_ERROR, &quot;Server not properly configured (KMS client is null).&quot;);                        return;                    }                    // 从KMS中解密出来，并Base64 decode                    byte[] key = decrypt(ciphertext);                    if (key == null) {                        // decrypt 方法内部已打印错误，这里只需发送错误响应                        sendErrorResponse(httpExchange, HttpURLConnection.HTTP_INTERNAL_ERROR, &quot;Failed to decrypt key using KMS.&quot;);                        return;                    }                    // 设置header                    setHeader(httpExchange, key);                    // 返回base64decode之后的密钥                    try (OutputStream responseBody = httpExchange.getResponseBody()) { // 使用 try-with-resources 确保 OutputStream 关闭                        responseBody.write(key);                    }                    System.out.println(&quot;Decrypted key sent successfully.&quot;);                } catch (Exception e) {                    System.err.println(&quot;Internal server error during request handling: &quot; + e.getMessage());                    e.printStackTrace();                    // 即使发生异常，也要确保 HttpExchange 被关闭                    if (!httpExchange.getResponseHeaders().containsKey(&quot;Content-Type&quot;)) { // 检查是否已发送响应头                        sendErrorResponse(httpExchange, HttpURLConnection.HTTP_INTERNAL_ERROR, &quot;Internal Server Error: &quot; + e.getMessage());                    }                } finally {                    // 最终确保 HttpExchange 被关闭                    httpExchange.close();                    System.out.println(&quot;--- Request processing finished ---\n&quot;);                }            } else {                // 不支持的方法                sendErrorResponse(httpExchange, HttpURLConnection.HTTP_BAD_METHOD, &quot;Method Not Allowed. Only GET is supported.&quot;);                // sendErrorResponse 内部会关闭 httpExchange            }        }        /**         * 发送错误响应         * @param exchange HttpExchange 对象         * @param statusCode HTTP 状态码         * @param message 错误信息         */        private void sendErrorResponse(HttpExchange exchange, int statusCode, String message) {            try {                System.err.println(&quot;Sending error response (&quot; + statusCode + &quot;): &quot; + message);                byte[] response = message.getBytes(StandardCharsets.UTF_8);                Headers headers = exchange.getResponseHeaders();                headers.set(&quot;Content-Type&quot;, &quot;text/plain; charset=utf-8&quot;);                headers.set(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;); // 确保错误响应也有 CORS 头                exchange.sendResponseHeaders(statusCode, response.length);                try (OutputStream os = exchange.getResponseBody()) {                    os.write(response);                }            } catch (IOException e) {                System.err.println(&quot;Failed to send error response: &quot; + e.getMessage());                e.printStackTrace();            } finally {                // 确保交换被关闭                exchange.close();            }        }        private void setHeader(HttpExchange httpExchange, byte[] key) throws IOException {            Headers responseHeaders = httpExchange.getResponseHeaders();            responseHeaders.set(&quot;Content-Type&quot;, &quot;application/octet-stream&quot;);            responseHeaders.set(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);            // 可选：添加缓存控制头            // responseHeaders.set(&quot;Cache-Control&quot;, &quot;no-store&quot;);            httpExchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, key.length);        }        /**         * 调用VOD KMS decrypt接口解密，并将明文base64decode         * @param ciphertext Base64 encoded ciphertext         * @return Decrypted key bytes, or null if failed         */        private byte[] decrypt(String ciphertext) {            DecryptKMSDataKeyRequest request = new DecryptKMSDataKeyRequest();            request.setCipherText(ciphertext); // VOD SDK 期望接收 Base64 字符串            request.setProtocol(ProtocolType.HTTPS);            try {                System.out.println(&quot;Calling VOD KMS Decrypt API...&quot;);                DecryptKMSDataKeyResponse response = client.getAcsResponse(request);                String plaintextBase64 = response.getPlaintext(); // VOD SDK 返回 Base64 String                System.out.println(&quot;KMS Decrypted Plaintext (Base64): &quot; + plaintextBase64);                if (plaintextBase64 == null || plaintextBase64.isEmpty()) {                    System.err.println(&quot;Warning: KMS returned null or empty plaintext.&quot;);                    return null;                }                // 注意：需要base64 decode                byte[] keyBytes = Base64.decodeBase64(plaintextBase64); // 使用 commons-codec                System.out.println(&quot;Plaintext decoded to &quot; + keyBytes.length + &quot; bytes.&quot;);                return keyBytes;            } catch (ClientException e) {                System.err.println(&quot;KMS Decrypt API call failed:&quot;);                System.err.println(&quot;Error Code: &quot; + e.getErrCode());                System.err.println(&quot;Error Message: &quot; + e.getMessage());                System.err.println(&quot;Request ID: &quot; + e.getRequestId()); // 打印 Request ID 有助于排查                // e.printStackTrace(); // 可以选择打印完整堆栈                return null;            }        }        /**         * 从URL中获取密文密钥参数 (修正版)         * @param httpExchange         * @return Base64 encoded ciphertext, or null if not found         */        private String getCiphertext(HttpExchange httpExchange) {            URI uri = httpExchange.getRequestURI();            String queryString = uri.getQuery();            System.out.println(&quot;Raw Query String: &quot; + queryString);            if (queryString == null || queryString.isEmpty()) {                System.out.println(&quot;Query string is null or empty.&quot;);                return null;            }            // --- 修正 1: URL 解码 ---            try {                queryString = URLDecoder.decode(queryString, StandardCharsets.UTF_8.name());                System.out.println(&quot;URL Decoded Query String: &quot; + queryString);            } catch (Exception e) { // 包括 UnsupportedEncodingException                System.err.println(&quot;Failed to URL decode query string: &quot; + e.getMessage());                // 即使解码失败，也尝试用原始字符串匹配            }            // --- 修正 2: 更宽松的正则表达式 ---            // 匹配 CipherText= 后面的任何非 &amp; 字符 (包括 Base64 字符)            String pattern = &quot;CipherText=([^&amp;]*)&quot;;            Pattern r = Pattern.compile(pattern);            Matcher m = r.matcher(queryString);            if (m.find()) {                String cipherTextValue = m.group(1);                System.out.println(&quot;Matched CipherText value: &quot; + cipherTextValue);                return cipherTextValue; // 返回匹配到的值            } else {                System.out.println(&quot;CipherText parameter not found in query string using regex: &quot; + pattern);                return null;            }        }    }    /**     * 服务启动     *     * @throws IOException     */    public void serviceBootStrap() throws IOException {        if (client == null) {            throw new IOException(&quot;VOD KMS Client failed to initialize, cannot start server.&quot;);        }        // 监听端口可以自定义，能同时接受最多30个请求        HttpServer httpserver = HttpServer.create(new InetSocketAddress(PORT), 30);        httpserver.createContext(CONTEXT_PATH, new HlsDecryptHandler()); // 使用定义的路径        // 可以设置线程池        // httpserver.setExecutor(Executors.newFixedThreadPool(10));        httpserver.start();        System.out.println(&quot;===================================================&quot;);        System.out.println(&quot;HLS VOD KMS 解密服务已启动&quot;);        System.out.println(&quot;监听端口: &quot; + PORT);        System.out.println(&quot;解密端点: http://&lt;your-server-ip&gt;:&quot; + PORT + CONTEXT_PATH + &quot;?CipherText=BASE64_ENCODED_CIPHERTEXT&quot;);        System.out.println(&quot;===================================================&quot;);    }//    public static void main(String[] args) throws IOException {//        HlsDecryptServerNoToken server = new HlsDecryptServerNoToken();//        server.serviceBootStrap();//        System.out.println(&quot;服务器启动成功，按 Ctrl+C 停止服务...&quot;);//        // 可以添加一个关闭钩子来优雅地停止服务器//        Runtime.getRuntime().addShutdownHook(new Thread(() -&gt; {//            System.out.println(&quot;\n正在关闭服务器...&quot;);//            // 如果需要停止 HttpServer, 可以保存 httpserver 实例并调用 httpserver.stop(0);//            System.out.println(&quot;服务器已关闭。&quot;);//        }));//    }}</code></pre><p>前端</p><pre><code class="language-">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;zh-CN&quot;&gt;&lt;head&gt;    &lt;meta charset=&quot;UTF-8&quot;&gt;    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;    &lt;title&gt;艾达纪元&lt;/title&gt;    &lt;link rel=&quot;stylesheet&quot; href=&quot;https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/skins/default/aliplayer-min.css&quot;&gt;    &lt;script charset=&quot;utf-8&quot; src=&quot;https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/aliplayer-min.js&quot;&gt;&lt;/script&gt;    &lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css&quot;&gt;    &lt;style&gt;        * {            margin: 0;            padding: 0;            box-sizing: border-box;        }        body {            font-family: &#39;Segoe UI&#39;, Roboto, &#39;PingFang SC&#39;, &#39;Microsoft YaHei&#39;, sans-serif;            padding: 20px;            background: linear-gradient(135deg, #0c0e1a 0%, #1a1c2c 100%);            color: #e0e0ff;            min-height: 100vh;        }        .container {            max-width: 1200px;            margin: 0 auto;        }        .header {            margin-bottom: 30px;            text-align: center;            padding: 25px 0;            background: rgba(30, 32, 48, 0.6);            border-radius: 16px;            backdrop-filter: blur(10px);            box-shadow: 0 12px 24px rgba(0, 0, 0, 0.25);            border: 1px solid rgba(142, 231, 255, 0.15);        }        .header h1 {            font-size: 32px;            margin-bottom: 10px;            font-weight: 700;            letter-spacing: 1px;            background: linear-gradient(90deg, #8ee7ff, #6cb2ff);            -webkit-background-clip: text;            -webkit-text-fill-color: transparent;            text-shadow: 0 0 15px rgba(142, 231, 255, 0.6);        }        .subtitle {            font-size: 18px;            color: #a0a0d0;            max-width: 800px;            margin: 0 auto;            line-height: 1.6;            font-weight: 300;        }                /* 播放器网格容器 */        .players-grid {            display: grid;            grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));            gap: 25px;            margin-bottom: 30px;        }                /* 单个播放器容器 */        .player-container {            position: relative;            background: rgba(20, 22, 36, 0.8);            border-radius: 16px;            overflow: hidden;            box-shadow: 0 12px 30px rgba(0, 0, 0, 0.4);            transition: all 0.3s ease;            border: 1px solid rgba(142, 231, 255, 0.1);        }        .player-container:hover {            transform: translateY(-5px);            box-shadow: 0 15px 35px rgba(0, 0, 0, 0.5), 0 0 20px rgba(142, 231, 255, 0.3);            border-color: rgba(142, 231, 255, 0.3);        }                /* 播放器标题 */        .player-title {            padding: 15px 20px;            background: rgba(42, 44, 66, 0.7);            font-size: 18px;            font-weight: 600;            color: #e0e0ff;            display: flex;            align-items: center;            min-height: 60px;        }        .player-title i {            margin-right: 10px;            color: #8ee7ff;            font-size: 20px;        }                /* 播放器包装 */        .player-wrapper {            position: relative;            width: 100%;            height: 0;            padding-bottom: 56.25%; /* 16:9 宽高比 */        }        .player-wrapper-inner {            position: absolute;            top: 0;            left: 0;            width: 100%;            height: 100%;            background: #000;        }                /* 加载状态 */        .loading-state {            position: absolute;            top: 0;            left: 0;            width: 100%;            height: 100%;            display: flex;            flex-direction: column;            align-items: center;            justify-content: center;            background: rgba(10, 12, 20, 0.9);            z-index: 10;        }        .loading-spinner {            width: 40px;            height: 40px;            border: 4px solid rgba(142, 231, 255, 0.2);            border-radius: 50%;            border-top-color: #8ee7ff;            animation: spin 1s ease-in-out infinite;            margin-bottom: 15px;        }        .status-message {            font-size: 16px;            text-align: center;            color: #e0e0ff;            padding: 0 20px;        }                /* 控制区域 */        .controls {            display: flex;            justify-content: center;            gap: 20px;            padding: 15px 20px;            background: rgba(30, 32, 48, 0.6);            border-radius: 12px;            margin-bottom: 20px;            border: 1px solid rgba(142, 231, 255, 0.1);        }        .control-btn {            background: linear-gradient(135deg, #3c5aa0, #2a3d80);            color: white;            border: none;            padding: 10px 22px;            border-radius: 8px;            cursor: pointer;            font-weight: 600;            transition: all 0.3s ease;            display: flex;            align-items: center;            font-size: 16px;            min-width: 160px;            justify-content: center;        }        .control-btn i {            margin-right: 8px;        }        .control-btn:hover {            background: linear-gradient(135deg, #4a6cc0, #364b8a);            transform: translateY(-2px);            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);        }                /* 状态信息 */        .status-info {            text-align: center;            padding: 15px;            color: #8a8ac4;            font-size: 16px;            background: rgba(30, 32, 48, 0.5);            border-radius: 10px;            margin-top: 20px;            border: 1px solid rgba(142, 231, 255, 0.1);        }                /* 响应式设计 */        @media (max-width: 900px) {            .players-grid {                grid-template-columns: 1fr 1fr;            }        }        @media (max-width: 600px) {            .players-grid {                grid-template-columns: 1fr;            }            .header h1 {                font-size: 26px;            }            .subtitle {                font-size: 16px;            }            .control-btn {                min-width: 140px;                padding: 8px 16px;                font-size: 14px;            }        }                /* 动画 */        @keyframes spin {            to { transform: rotate(360deg); }        }        @keyframes fadeIn {            from { opacity: 0; transform: translateY(20px); }            to { opacity: 1; transform: translateY(0); }        }                .player-container {            animation: fadeIn 0.5s ease-out forwards;        }                /* 页脚 */        .footer {            text-align: center;            margin-top: 40px;            padding: 20px;            color: #8a8ac4;            font-size: 14px;            border-top: 1px solid rgba(142, 231, 255, 0.1);        }    &lt;/style&gt;&lt;/head&gt;&lt;body&gt;    &lt;div class=&quot;container&quot;&gt;        &lt;div class=&quot;header&quot;&gt;            &lt;h1&gt;全双工数字人案例&lt;/h1&gt;            &lt;!-- &lt;div class=&quot;subtitle&quot;&gt;探索人工智能与人类交互的未来，体验前沿数字人技术&lt;/div&gt; --&gt;        &lt;/div&gt;                &lt;!-- 控制区域 --&gt;        &lt;div class=&quot;controls&quot;&gt;            &lt;button class=&quot;control-btn&quot; id=&quot;pauseAllBtn&quot;&gt;                &lt;i class=&quot;fas fa-pause&quot;&gt;&lt;/i&gt; 暂停所有            &lt;/button&gt;            &lt;!-- &lt;button class=&quot;control-btn&quot; id=&quot;muteAllBtn&quot;&gt;                &lt;i class=&quot;fas fa-volume-mute&quot;&gt;&lt;/i&gt; 全部静音            &lt;/button&gt; --&gt;            &lt;button class=&quot;control-btn&quot; id=&quot;refreshBtn&quot;&gt;                &lt;i class=&quot;fas fa-sync-alt&quot;&gt;&lt;/i&gt; 刷新数据            &lt;/button&gt;        &lt;/div&gt;                &lt;!-- 播放器网格 --&gt;        &lt;div id=&quot;playersGrid&quot; class=&quot;players-grid&quot;&gt;            &lt;!-- 播放器将动态生成 --&gt;        &lt;/div&gt;                &lt;div id=&quot;statusInfo&quot; class=&quot;status-info&quot;&gt;            正在加载视频数据...        &lt;/div&gt;                &lt;div class=&quot;footer&quot;&gt;            © 2025 艾达纪元        &lt;/div&gt;    &lt;/div&gt;    &lt;script&gt;        // 全局变量        let players = [];        let videoData = [];                // 页面加载完成后初始化        document.addEventListener(&#39;DOMContentLoaded&#39;, function() {            // 加载播放器资源            loadPlayerResources();                        // 添加按钮事件监听            document.getElementById(&#39;pauseAllBtn&#39;).addEventListener(&#39;click&#39;, pauseAllPlayers);            document.getElementById(&#39;muteAllBtn&#39;).addEventListener(&#39;click&#39;, toggleMuteAll);            document.getElementById(&#39;refreshBtn&#39;).addEventListener(&#39;click&#39;, refreshData);        });          // 检测苹果设备        function isAppleDevice() {            const userAgent = navigator.userAgent;            return /iPhone|iPad|iPod|Macintosh|MacIntel|MacPPC|Mac68K/i.test(userAgent);        }        // 1. 加载播放器资源        function loadPlayerResources() {            updateStatus(&#39;正在初始化播放环境...&#39;);            // 检查是否已经加载了播放器脚本            if (typeof Aliplayer !== &#39;undefined&#39;) {                initPlayerData();                return;            }                        // 创建脚本加载监听            const script = document.createElement(&#39;script&#39;);            script.src = &#39;https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/aliplayer-min.js&#39;;            script.charset = &#39;utf-8&#39;;                        script.onload = function() {                console.log(&#39;播放器脚本加载完成&#39;);                initPlayerData();            };                        script.onerror = function() {                showError(&#39;播放器脚本加载失败，请检查网络连接&#39;);            };                        document.head.appendChild(script);        }        // 2. 初始化视频数据        function initPlayerData() {            updateStatus(&#39;正在获取视频数据...&#39;);                        // 获取视频数据            fetch(&#39;https://ip/api/open/tls/db&#39;)                .then(response =&gt; {                    if (!response.ok) {                        throw new Error(&#96;HTTP 错误! 状态码: ${response.status}&#96;);                    }                    return response.json();                })                .then(result =&gt; {                    if (result.code === 200 &amp;&amp; result.data &amp;&amp; result.data.length &gt; 0) {                        videoData = result.data;                        createPlayerContainers();                        initPlayers();                    } else {                        throw new Error(result.msg || &#39;接口返回数据格式异常&#39;);                    }                })                .catch(error =&gt; {                    console.error(&#39;获取视频数据失败:&#39;, error);                    showError(&#96;获取视频数据失败: ${error.message}&#96;);                });        }                // 3. 创建播放器容器        function createPlayerContainers() {            const playersGrid = document.getElementById(&#39;playersGrid&#39;);            playersGrid.innerHTML = &#39;&#39;;                        // 更新状态信息            updateStatus(&#96;已加载 ${videoData.length} 个视频&#96;);                        // 为每个视频创建容器            videoData.forEach((video, index) =&gt; {                const playerContainer = document.createElement(&#39;div&#39;);                playerContainer.className = &#39;player-container&#39;;                                playerContainer.innerHTML = &#96;                    &lt;div class=&quot;player-title&quot;&gt;                        &lt;i class=&quot;fas fa-video&quot;&gt;&lt;/i&gt; ${video.tittle || &#96;数字人视频 ${index + 1}&#96;}                    &lt;/div&gt;                    &lt;div class=&quot;player-wrapper&quot;&gt;                        &lt;div class=&quot;player-wrapper-inner&quot;&gt;                            &lt;div id=&quot;player-${index}&quot;&gt;&lt;/div&gt;                            &lt;div class=&quot;loading-state&quot;&gt;                                &lt;div class=&quot;loading-spinner&quot;&gt;&lt;/div&gt;                                &lt;div class=&quot;status-message&quot;&gt;加载视频中...&lt;/div&gt;                            &lt;/div&gt;                        &lt;/div&gt;                    &lt;/div&gt;                &#96;;                                playersGrid.appendChild(playerContainer);            });        }                // 4. 初始化播放器        function initPlayers() {            players = [];                        videoData.forEach((video, index) =&gt; {                try {                    // 创建新播放器                    const player = new Aliplayer({                        id: &#96;player-${index}&#96;,                        width: &#39;100%&#39;,                        height: &#39;100%&#39;,                        vid: video.vid,                        playauth: video.playauth,                        encryptType: 1, // 私有加密流                        qualitySort: &#39;asc&#39;,                        format: &#39;m3u8&#39;,                        mediaType: &#39;video&#39;,                        encryptType: 1,                        width: &#39;100%&#39;,                        autoplay: true,                        isLive: false,                        rePlay: false,                        videoHeight: undefined,                        isVBR: undefined,                        preload: true,                        controlBarVisibility: &#39;hover&#39;,                        useH5Prism: true                    }, function(playerInstance) {                        console.log(&#96;播放器 ${index} 创建成功&#96;);                                                // 隐藏加载状态                        const container = document.querySelector(&#96;#player-${index}&#96;).parentNode;                        const loadingState = container.querySelector(&#39;.loading-state&#39;);                        if (loadingState) {                            loadingState.style.display = &#39;none&#39;;                        }                                                // 监听播放器事件                        player.on(&#39;play&#39;, function() {                            console.log(&#96;播放器 ${index} 开始播放&#96;);                        });                                                player.on(&#39;pause&#39;, function() {                            console.log(&#96;播放器 ${index} 暂停播放&#96;);                        });                                                player.on(&#39;error&#39;, function(e) {                            console.error(&#96;播放器 ${index} 错误:&#96;, e);                            showError(&#96;播放器 ${index+1} 错误: ${e.paramData.errorMsg || &#39;未知错误&#39;}&#96;);                        });                    });                                        players.push(player);                } catch (e) {                    console.error(&#96;播放器 ${index} 创建失败&#96;, e);                    showError(&#96;播放器 ${index+1} 初始化失败: ${e.message}&#96;);                }            });        }                // 5. 控制功能        function pauseAllPlayers() {            players.forEach(player =&gt; {                if (player &amp;&amp; player.pause) {                    player.pause();                }            });        }                function toggleMuteAll() {            const btn = document.getElementById(&#39;muteAllBtn&#39;);            const isMuted = players.length &gt; 0 ? players[0].isMuted() : false;                        players.forEach(player =&gt; {                if (player &amp;&amp; player.mute) {                    player.mute(!isMuted);                }            });                        // 更新按钮文本            btn.innerHTML = isMuted ?                 &#39;&lt;i class=&quot;fas fa-volume-mute&quot;&gt;&lt;/i&gt; 全部静音&#39; :                 &#39;&lt;i class=&quot;fas fa-volume-up&quot;&gt;&lt;/i&gt; 取消静音&#39;;        }                function refreshData() {            players.forEach(player =&gt; {                if (player &amp;&amp; player.dispose) {                    player.dispose();                }            });            players = [];                        updateStatus(&#39;正在刷新数据...&#39;);            initPlayerData();        }                // 6. 状态管理        function updateStatus(message) {            const statusElement = document.getElementById(&#39;statusInfo&#39;);            if (statusElement) {                statusElement.textContent = message;            }        }                function showError(message) {            const statusElement = document.getElementById(&#39;statusInfo&#39;);            if (statusElement) {                statusElement.textContent = message;                statusElement.style.color = &#39;#ff7d9c&#39;;            }        }    &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre>]]>
                    </description>
                    <pubDate>Sun, 07 Sep 2025 00:44:40 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[最新可用的docker镜像列表]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/zui-xin-ke-yong-de-docker-jing-xiang-lie-biao</link>
                    <description>
                            <![CDATA[<pre><code class="language-">{  &quot;registry-mirrors&quot;: [    &quot;https://docker.xuanyuan.me&quot;,    &quot;https://docker.1ms.run&quot;,    &quot;https://hub.rat.dev&quot;,    &quot;https://docker.hpcloud.cloud&quot;,    &quot;https://docker.m.daocloud.io&quot;,    &quot;https://docker.tbedu.top/&quot;  ]}</code></pre>]]>
                    </description>
                    <pubDate>Mon, 25 Aug 2025 19:22:48 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[websocket如何携带header或参数]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/websocket-ru-he-xie-dai-header-huo-can-shu</link>
                    <description>
                            <![CDATA[<p>这尼玛就是哥伪命题，谁家好人这么玩？如果前后端都是自己人，让他放弃吧，换成别的方式比如query_parm形式。</p><p>如果是三方应用没办法怎么办？草！比如我这次对接的阿里云的ws服务，尼玛让我ws放token.我是Vue啊大哥，我怎么玩？</p><p>算了哥们直接给出方案吧。</p><p>通过nginx代理一层解决。</p><p>下面给出nginx的配置方案</p><h1 id="websocket%E4%BB%A3%E7%90%86%E9%85%8D%E7%BD%AE" tabindex="-1">WebSocket代理配置</h1><pre><code>location /api-ws/v1/inference {    proxy_pass https://dashscope.aliyuncs.com/api-ws/v1/inference;   # ws的真实地址    proxy_http_version 1.1;    proxy_set_header Upgrade $http_upgrade;    proxy_set_header Connection &quot;upgrade&quot;;    proxy_set_header Host dashscope.aliyuncs.com;   # 目标地址的主机头    proxy_set_header Authorization &quot;自定义key1&quot;;    proxy_set_header X-DashScope-DataInspection &quot;自定义key2&quot;;    # 超时和SSL配置    proxy_read_timeout 1000s;    proxy_ssl_server_name on;  # 启用SNI    proxy_ssl_protocols TLSv1.2;}</code></pre>]]>
                    </description>
                    <pubDate>Sat, 21 Jun 2025 18:17:45 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[历史文件压缩，且持续追加解决方案]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/li-shi-wen-jian-ya-suo--qie-chi-xu-zhui-jia-jie-jue-fang-an</link>
                    <description>
                            <![CDATA[<p>历史文件压缩，且持续追加解决方案</p><p>故事的发展是这样的，当linux系统持续的运行程序，会产生很多的数据日志，那么这部分日志删也不舍得，存着浪费空间，那么最好的方案就是压缩到本地咯，那么首先想到的就是使用find命令进行查找自己需要压缩的文件对不</p><p>find /your/directory/path -type f -ctime +150 -ctime -200</p><p>但是呢，这里面有个坑？什么坑呢，find这个命令是有长度限制的，那应该怎么办呢？那我们可以使用管道符的命令接不就行啦。</p><p>|</p><p>哈哈，那么如何实现，往压缩包里面追加文件呢？很简单啊</p><p>1:我们先创建一个压缩包</p><p>tar cvf var_lib_jenkins.tar</p><p>2:找到需要的文件往里面追加</p><p>find jobs/ -name config.xml | xargs tar rf var_lib_jenkins.tar<br />find jobs/ -name log | xargs tar rf var_lib_jenkins.tar</p><p>3:追加完成，哈哈我们还可以再压缩一轮(根据业务需要)</p><p>gzip var_lib_jenkins.tar</p><p>4: 完整追加的命令</p><p>find /your/directory/path -type f -ctime +150 -ctime -200 |  xargs tar rf var_lib_jenkins.tar</p>]]>
                    </description>
                    <pubDate>Sat, 12 Oct 2024 18:15:10 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[移动h5转微信小程序]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/yi-dong-h5-zhuan-wei-xin-xiao-cheng-xu</link>
                    <description>
                            <![CDATA[<p>可以使用hbuilder，创建一个uniapp模版，在pages/index.vue中添加/修改如下代码</p><p><web-view src="https://此处为h5的地址/"></web-view></p><p>这样写还不够，为什么？</p><p>你会发现，当这样以后在微信开发者工具是可以正常使用的，但是发布/体验版本会报错，提示:“不支持打开该页面等类型的错误”。</p><p>接下来，你应该登陆你的微信开发者平台，将业务域名配置好</p><p><strong>业务域名只能是企业账号配置</strong></p><p>在微信开发者平台下载描述文件，如果使用的是nginx，可以采用如下的方案，直接配置即可。</p><p>#微信业务域名校验<br />location  /xIkUPj1m30.txt {<br />default_type text/html;<br />return 200 ‘教研文件里面的内容粘贴到此’;<br />}</p>]]>
                    </description>
                    <pubDate>Thu, 12 Sep 2024 18:13:03 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[centos7安装mysql8.0（包含安装包）]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/centos7-an-zhuang-mysql80-bao-han-an-zhuang-bao-</link>
                    <description>
                            <![CDATA[<p>centos7安装mysql8.0（包含安装包）</p><h2 id="%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C" tabindex="-1">准备工作</h2><p>安装MySQL的话会和MariaDB的文件冲突，所以需要先卸载掉MariaDB。</p><h2 id="%E6%9F%A5%E7%9C%8B%E6%98%AF%E5%90%A6%E5%AE%89%E8%A3%85mariadb" tabindex="-1">查看是否安装mariadb</h2><p>rpm -qa|grep mariadb<br />卸载</p><p>rpm -e --nodeps 上一步的文件名</p><h2 id="%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8D%B8%E8%BD%BD%E5%AE%8C%E6%88%90" tabindex="-1">检查是否卸载完成</h2><p>rpm -qa|grep mariadb</p><h2 id="%E6%9F%A5%E7%9C%8B%E6%98%AF%E5%90%A6%E5%AE%89%E8%A3%85libaio" tabindex="-1">查看是否安装libaio</h2><p>rpm -qa|grep libaio<br />如果没有安装则执行</p><p>yum -y install libaio</p><h2 id="%E6%9F%A5%E7%9C%8B%E6%98%AF%E5%90%A6%E5%AE%89%E8%A3%85numactl" tabindex="-1">查看是否安装numactl</h2><p>rpm -qa|grep numactl<br />如果没有安装则执行</p><h2 id="yum--y-install-numactl" tabindex="-1">yum -y install numactl</h2><p>安装MySQL<br />这里我安装在/usr/local目录下<br />cd /usr/local/<br />wget <a href="https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.30-el7-x86_64.tar" target="_blank">https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.30-el7-x86_64.tar</a><br />如果下载较慢可以使用本地的</p><pre><code class="language-">链接: https://pan.baidu.com/s/1XyEuo3urMhcxLXnKvy2CfQ?pwd=irsf 提取码: irsf </code></pre><h2 id="%E4%B8%8B%E8%BD%BD%E5%AE%8C%E6%88%90%E5%90%8E%EF%BC%8C%E6%8B%86%E5%88%86tar%E5%8C%85" tabindex="-1">下载完成后，拆分tar包</h2><p>tar -xvf mysql-8.0.30-el7-x86_64.tar<br />解压安装包</p><p>tar -zxvf mysql-8.0.30-el7-x86_64.tar.gz<br />将解压后的文件夹重命名为mysql</p><p>mv mysql-8.0.30-el7-x86_64/ mysql<br />存储数据文件</p><h2 id="%E5%9C%A8%E9%87%8D%E5%91%BD%E5%90%8D%E5%90%8E%E7%9A%84mysql%E6%96%87%E4%BB%B6%E5%A4%B9%E4%B8%AD%E5%88%9B%E5%BB%BAdata%E6%96%87%E4%BB%B6%E5%A4%B9" tabindex="-1">在重命名后的mysql文件夹中创建data文件夹</h2><p>mkdir mysql/data<br />设置用户组并赋权<br />groupadd mysql<br />useradd -r -g mysql mysql<br />chown -R mysql:mysql /usr/local/mysql/<br />chmod -R 755 /usr/local/mysql/<br />初始化MySQL<br />cd /usr/local/mysql/bin/<br />./mysqld --initialize --user=mysql --datadir=/usr/local/mysql/data --basedir=/usr/local/mysql<br />注意：这里会打印一个初始密码，需要记录下来，后面用于登录：</p><h2 id="%E5%90%AF%E5%8A%A8mysql" tabindex="-1">启动MySQL</h2><p>/usr/local/mysql/support-files/mysql.server start<br />设置软连接</p><p>ln -s /usr/local/mysql/support-files/mysql.server /etc/init.d/mysql<br />ln -s /usr/local/mysql/bin/mysql /usr/bin/mysql<br />ln -s /usr/local/mysql/mysql.sock /var/mysql.sock<br />service mysql restart</p><h2 id="%E4%BF%AE%E6%94%B9%E5%AF%86%E7%A0%81" tabindex="-1">修改密码</h2><p>mysql -uroot -pt)k*3KuiMdr;<br />alter user ‘root’@‘localhost’ identified by ‘Iphone970129’;<br />grant all on <em>.</em> to root@‘localhost’ identified by ‘Iphone970129’;</p><h2 id="%E5%BC%80%E5%90%AF%E8%BF%9C%E7%A8%8B%E8%BF%9E%E6%8E%A5" tabindex="-1">开启远程连接</h2><p>use mysql;<br />update user set user.Host=‘%’ where user.User=‘root’;<br />flush privileges;<br />exit;<br />启动</p><p>service mysql start<br />service mysql stop<br />service mysql restart</p><h2 id="%E5%BC%80%E6%9C%BA%E8%87%AA%E5%90%AF%E5%8A%A8" tabindex="-1">开机自启动</h2><p>cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld<br />chmod +x /etc/init.d/mysqld<br />chkconfig --add mysqld<br />chkconfig --list<br />然后本地进行测试连接看是否可以连接。</p><p>如果是云服务器，则注意需要开启端口和防火墙即可！</p>]]>
                    </description>
                    <pubDate>Wed, 21 Aug 2024 18:05:25 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[unzip 解压大文件出现错误invalid zip file with overlapped components (possible zip bomb)]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/unzip-jie-ya-da-wen-jian-chu-xian-cuo-wu-invalidzipfilewithoverlappedcomponentspossiblezipbomb</link>
                    <description>
                            <![CDATA[<p>unzip 解压大文件出现错误invalid zip file with overlapped components (possible zip bomb)</p><p>添加环境变量即可</p><p>UNZIP_DISABLE_ZIPBOMB_DETECTION=TRUE<br />export UNZIP_DISABLE_ZIPBOMB_DETECTION</p>]]>
                    </description>
                    <pubDate>Fri, 28 Jun 2024 18:14:46 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[大量数据去重bitMap位图解决方案]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/da-liang-shu-ju-qu-zhong-bitmap-wei-tu-jie-jue-fang-an</link>
                    <description>
                            <![CDATA[<p>大量数据去重bitMap位图解决方案</p><h2 id="%E6%95%B0%E6%8D%AE%E5%8E%BB%E9%87%8Dbitmap%E4%BD%8D%E5%9B%BE%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88" tabindex="-1">数据去重bitMap位图解决方案</h2><p><strong>一个32g的内存操作系统，在20亿个整数，找出某个数x是否存在其中</strong></p><pre><code> - 方式一:假设是java int占4个字节，1个字节=8位(1byte=8bit)一个int 32*20亿 个bit 约等于7g - 方式二：不存储具体数据，而存储是否存在，如果存在则打上标签，采用bit存储20亿个数就是20亿位，空间就是0.2g。</code></pre><ul><li><p>什么是Bitmap<br />Bit-map就是用一个bit位来标记某个元素对应的Value（若元素存在bit位置为1，不存在则置为0）。可创建一个整型数组（如byte数组，int数组，long数组）来表示</p></li><li><p>Bitmap原理<br />在Java中，数据类型int占4字节，4字节=32位（1 byte = 8 bit）<br />数据类型byte占1字节，1字节=8位</p><ul><li>用byte数组来表示 集合 {1,2,4,6}，byte数组一个元素占一个字节，一个字节占8位</li><li>计算机内存分配的最小单位是字节，也就是8位，那如果要表示集合{1,2,4,6,12,13,15},需要在byte数组，增加一个元素，即增加8位的数据来表示</li></ul><ul><li><p>需求案例方式二解答</p><ul><li>每个int类型可标识32个整数，存储20亿个元素需要20亿个比特位，20亿/8/1024/1024  约上200多MB，省32倍空间</li></ul><pre><code class="language-">需要申请的数组大小array[0]:可表示0~31array[1]:可表示32~63array[2]可表示64~95…总的数组长度为20亿/32 +1</code></pre><ul><li>如何确定位置（给定任意整数M，那么M/32就得到下标，M%32就知道它在此下标的哪个位置）</li></ul><pre><code class="language-">比如元素存储 80，确定所在数组的bit位置1、数组index索引  80/32 = 2.5，即第3个数组的位置 arr[2]2、比特位index索引 80%32 = 16，索引下标为16的比特位，把比特位设置为1，即arr[2][16]</code></pre></li></ul></li></ul><ul><li><p>注意</p><ul><li><strong>位图适合对【数值类型】的海量数据进行查询统计、排序、去重 和  对两个集合做交集、并集运算</strong></li><li>bitmap在数据连续的时候，非常节省空间，但是在数据稀疏的时候，会有极大的浪费</li></ul></li><li><p>缺点</p><ul><li>数据碰撞：<ul><li>字符串映射到 <code>bitmap</code>会有碰撞问题，即可能映射到同个位置，即hash碰撞</li></ul></li><li>稀疏数据<ul><li>不连续的数据容易浪费空间，比如存入1和88两个数，需要构建长度89的数组</li><li>表示索引从1到88，所以需要构建一个长度为89的数组，存放1到88的元素，但实际只存储2个数字</li><li>如果用户的ID的数据类型是int32的话，那么最大值是2^32，需要用512MB的字节的位图来表示<ul><li>2^32bit=4294967296 比特(bit)=512 兆字节(MB)</li><li>如果只往bitmap存储一个最大值，那边需要申请512 兆字节(MB)，大大浪费空间</li></ul></li></ul></li></ul></li><li><p>业务应用：<strong>日活/月活UV统计、签到统计、用户点赞，用户签到，访问计数，在线用户数等</strong></p><h3 id="%E7%BC%96%E7%A0%81%E5%AE%9E%E7%8E%B01%E4%BA%BF%E4%B8%AA%E6%95%B0%E6%8D%AE%E6%89%BE%E4%B8%8D%E5%AD%98%E5%9C%A8%E7%9A%84%E9%9A%8F%E6%9C%BA%E6%95%B0" tabindex="-1">编码实现1亿个数据找不存在的随机数</h3><ul><li>题目需求</li><li>有1千万个随机数，随机数的范围在1到1亿之间，将1到1亿之间没有在随机数中的数求出来</li><li>前提条件：使用java现有数据结构或自定义数据结构，要求<strong>高效和省空间</strong></li></ul></li><li><p>位图在java里面的实现BitSet类</p><ul><li><p>是一个实现按需增长的位向量，位Set的每一个位置都有一个boolean值，默认初始值都是false</p></li><li><p>底层实现是使用long数组作为内部存储结构的，所以BitSet的大小为long类型大小(64位)的整数倍</p></li><li><p>如果指定了bitset的初始化大小，会规整到一个大于或者等于这个数字的64的整倍数（内存对齐）</p><ul><li>比如64位，bitset的大小是1个long，而65位时，bitset大小是2个long，即128位</li></ul></li><li><p>主要API</p><pre><code class="language-">void and(BitSet set) 对此目标位 set 和参数位 set 执行逻辑与操作。void or(BitSet set) 对此目标位集执行逻辑或操作void clear() 将此 BitSet 中的所有位设置为 falsevoid clear(int bitIndex)：将指定索引处的位设置为 falsevoid set(int index) 将指定索引处的位设置为 trueboolean get(int index) 返回指定索引处的位值int size()：返回此 BitSet 中的位数（按逻辑大小）【表示位值时实际使用空间的位数，值是64的整数倍】int length() 返回此 BitSet 的&quot;逻辑大小&quot;,BitSet 中最高设置位的索引加 1int cardinality() 返回此 BitSet 中设置为 true 的位数</code></pre></li><li><p>API测试</p><pre><code class="language-"> public static void testBitSet(){        BitSet bitSet = new BitSet();        bitSet.set(0);        bitSet.set(66);                System.out.println(bitSet.size());   // 128        System.out.println(bitSet.length());  // 67        System.out.println(bitSet.cardinality()); // 2        System.out.println(&quot;=====&quot;);         System.out.println(bitSet.get(0));  // true        System.out.println(bitSet.get(1));  // false}</code></pre></li></ul></li><li><p>解答思路</p><ul><li><p><strong>海量数据</strong> 里面查找是否存在，排序，交集，并集等，这类题目基本就是使用<strong>位图解决</strong></p></li><li><p>这类题目一般有两个面试形式</p><ul><li>方式一 口述问答形式<ul><li>给定X亿个不重复的 int的整数，再给一个数，如何快速判断这个数是否在那X亿个数当中</li><li>解法：遍历X亿个数字，映射到BitMap中，对于给出的数，直接判断指定的位上存在不存在即可</li></ul></li></ul></li><li><p>方式二 上机编码形式</p><pre><code class="language-"></code></pre></li></ul></li></ul><p>public class BitSetTest {<br />public static void main(String[] args) {<br />//        BitSet bitSet = new BitSet();<br />//        bitSet.set(0);<br />//        bitSet.set(66);<br />//        System.out.println(bitSet.size());<br />//        System.out.println(bitSet.length());<br />//        System.out.println(bitSet.cardinality());<br />//        System.out.println(bitSet.get(0));<br />//        System.out.println(bitSet.get(1));<br />testBitSetMap();<br />}</p><pre><code>public static void  testBitSetMap(){    //范围    int range=100000000;    //个数    int total=10000000;    //普通    ArrayList&lt;Object&gt; list = new ArrayList&lt;&gt;();    //声明一个位图    BitSet bitSet = new BitSet(range);    //产生随机数    for (int i = 0; i &lt; total; i++) {        int random = (int) (Math.random() * range);        bitSet.set(random);        list.add(random);    }    System.out.println(&quot;产生的随机数:&quot;+list);    System.out.println(&quot;bitSet是一的个数:&quot;+bitSet.cardinality());    System.out.println(&quot;bitSet的大小:&quot;+bitSet.size());    System.out.println(&quot;bitSet最高位加1 length:&quot;+bitSet.length());    //遍历bitst,没有出现的打印出来    for (int i = 0; i &lt;range; i++) {        if(!bitSet.get(i)) {            System.out.print(i+&quot;&quot;);        }    }}</code></pre><p>}<br />```</p><h3 id="%E9%99%90%E5%AE%9A%E4%BC%98%E6%83%A0%E5%8A%B5%E4%B8%9A%E5%8A%A1%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88-redis7%E7%9A%84bitmap%E5%BA%94%E7%94%A8" tabindex="-1">限定优惠劵业务解决方案-Redis7的BitMap应用</h3><p><strong>简介： 限定优惠劵业务解决方案-Redis的BitMap应用</strong></p><ul><li><p>需求背景</p><ul><li>互联网项目里多数离不开优惠劵，门槛优惠劵比较多</li><li>现在有一个B2C电商平台，总用户量10亿，日活用户5千万</li><li>每天都会发放几十到上百种不同类型的优惠劵，每类活动优惠劵领劵率到达20% （即1千万用户）</li><li>现在发一个门槛优惠劵，一个用户只能领取一张，禁止重复领取，（user_id是64位的Long类型）</li><li>针对”禁止重复领劵“，这个需求说下你的设计和思路</li></ul></li><li><p>题目条件</p><ul><li>用户量大，优惠劵类型多，领劵率高，日活高也说明存在高并发</li></ul></li><li><p>老王</p><ul><li>特别容易，新建一个coupon表，进行<strong>分库分表</strong>处理</li><li>领过的用户把user_id插入数据库表中，下次如果再次领取的查询是否重复领取</li><li>分析<ul><li>分库分表可以解决海量数据查询和存储问题</li><li>但存在高并发场景，频繁插入查询数据库不行，严重影响性能</li></ul></li></ul></li><li><p>老帆</p><ul><li>存在高并发场景，频繁插入查询数据库不行，那可以<strong>结合Redis</strong>，进行判断</li></ul><pre><code class="language-">docker部署redis7#部署docker run -itd --name xdclass-redis1 -p 6379:6379 -v /mydata/redis/data:/data redis:7.0.8 --requirepass 123456#进入容器内部docker exec -it 容器id /bin/bash#客户端连接./redis-cli#授权auth 123456</code></pre><ul><li>使用Redis的Set集合存储领取过的用户user_id，每个优惠劵创建一个set集合</li><li>领过的用户把user_id加入set集合中，下次如果再次领取的查询是否重复领取</li><li>分析<ul><li>Redis存储可以，解决了高并发场景避免了频繁查询数据库</li><li>但使用Redis的Set数据结构存储，存在内存空间问题</li><li>假如一个优惠劵有1千万用户领取，每个用户id占用空间64位</li><li>需要存储空间大小 64bit * 1千万 = 6.4亿bit = 76MB，空间占据比较多</li></ul></li></ul></li><li><p>冰冰</p><ul><li><p>存在高并发场景，频繁插入查询数据库不行，那可以<strong>结合Redis</strong>，进行判断</p></li><li><p>但数据结构应该采用bitmap数据结构，每个用户id占用空间只占用1位</p></li><li><p>领过的用户把user_id加入bitmap中，下次如果再次领取的查询是否重复领取</p></li><li><p>Redis的bitmap</p><pre><code class="language-">Redis中提供的BitMap命令：setbit,getbit,bitcount领劵：setbit coupon-id  user-uid 1  例子：setbit coupon-id:876 8888 1已经领券判断：getbit  coupon-id  user-uid  例子：getbit coupon-id:876 8888  如果未领取状态是0，如果已领就是1  统计该优惠券有多少个用户领取  bitcount  coupon-id:876 返回值为该key值中1的个数  </code></pre></li><li><p>分析</p><ul><li>假如一个优惠劵有1千万用户领取，每个用户id占用空间1位</li><li>需要存储空间大小 1bit * 1亿 = 1亿bit = 11.9MB，对比前面方案，省了7倍空间<ul><li>为啥是1亿，因为用户总量有1亿个，用户id顺序递增，最大到1亿的值，如果id值更大则需要更多空间</li></ul></li></ul></li></ul></li><li><p>注意</p><ul><li><p>但假如该网站每天的独立访问用户很少， 例如只有10万，那这时候使用Bitmaps就不太合适， 因为基本上大部分位都是0</p></li><li><p>Set存储使用的空间：64位 * 100000 = 800KB</p></li><li><p>BitMap存储使用的空间：1bit * 1亿 = 1亿bit = 11.9MB</p></li></ul></li></ul><h3 id="%E8%BF%9B%E9%98%B6%E7%89%88%E5%93%88%E5%B8%8C%E8%A1%A8bloomfilter" tabindex="-1">进阶版哈希表BloomFilter</h3><p><strong>简介：进阶版哈希表BloomFilter</strong></p><ul><li><p>背景需求</p><ul><li><p>海量数据去重需求解决方案</p><ul><li>如果用户的ID的数值类型是32位的话，如果有最大值是2^32，需要用512MB空间的【位图来表示】<ul><li>2^32bit=4294967296 比特(bit)=512  MB</li></ul></li><li>如果用户的ID的数值类型是64位的话，如果有最大值是2^64，需要用2048PB的位图来表示，硬件支撑不了<ul><li>2<sup>64bit=2</sup>61 Byte= 2048 PB</li></ul></li><li>所以Bitmap位图出现的问题<ul><li>好处：<strong>空间复杂度不随原始集合内元素的个数增加而增加</strong></li><li>坏处：<strong>空间复杂度随集合内【最大元素】增大而线性增大</strong></li></ul></li></ul></li></ul></li><li><p>什么是布隆过滤器</p><ul><li><p>1970年由布隆提出的一种空间效率很高的<strong>概率型数据结构</strong>，它可以用于检索一个元素是否在一个集合中</p></li><li><p>由只存0或1的位数组和多个hash算法, 进行判断数据   【<strong>一定不存在或者可能存在的算法</strong>】</p></li><li><p>如果这些bit数组 <strong>有任何一个0，则被判定的元素一定不在;  如果都是1则被检元素很可能在</strong></p></li><li><p><strong>对比bitmap位图，布隆过滤器适合更多类型元素，通过hash值转换</strong></p></li><li><p>原理</p><ul><li><p>将元素添加到一个bitmap数组中，每个散列函数将元素映射到bitmap数组中的一个位置</p></li><li><p>如果该位置已经被占用，则将该位置置为1，否则置为0</p></li><li><p>当要查询一个元素是否存在时，只需要计算该元素的散列值，并检查bitmap数组中对应的位置是否已经被置为1</p></li><li><p>如果都是1，则该元素可能存在，否则肯定不存在。</p></li><li><p>优点</p><ul><li>占用空间小，查询速度快，空间效率和查询时间都远远超过一般的算法</li></ul></li><li><p>缺点</p><ul><li>有一定的误识别率，有一定的误识别率，即某个元素可能存在，但实际上并不存在。</li><li>删除困难，因为无法确定某个位置是由哪个元素映射而来的</li></ul></li></ul></li><li><p>记住结论：<strong>不存在的一定不存在，存在的不一定存在</strong></p></li></ul></li><li><p>注意点</p><ul><li>布隆过滤器存在误判率，数组越小，所占的空间越小，误判率越高；如果要降低误判率，则数组越长，但所占空间越大</li><li>最大限度的避免误差, 选取的位数组应尽量大, hash函数的个数尽量多, 但空间占用的浪费和性能的下降</li><li>业务选择的时候， 需要误判率与bit数组长度和hash函数数量的平衡</li><li>布隆过滤器不能直接删除元素，因为所属的bit可能多个元素有使用</li><li>如果要删除则需要重新生成布隆过滤器，或者把布隆过滤器改造成带引用计数的方式</li></ul></li></ul><h3 id="%E7%88%AC%E8%99%ABurl%E5%8E%BB%E9%87%8D%E5%AE%9E%E6%88%98-springboot3.0%2Bguava%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8" tabindex="-1">爬虫URL去重实战-SpringBoot3.0+Guava布隆过滤器</h3><ul><li><p>前置环境准备</p><ul><li>本地 JDK17安装（SpringBoot3.0要求JDK17）</li></ul></li><li><p>项目开发</p><ul><li><p>快速创建 <a href="https://start.spring.io/" target="_blank">https://start.spring.io/</a></p></li><li><p>依赖包引入</p><pre><code class="language-">&lt;dependencies&gt;    &lt;dependency&gt;      &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;      &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;    &lt;/dependency&gt;    &lt;dependency&gt;      &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;      &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;      &lt;scope&gt;test&lt;/scope&gt;    &lt;/dependency&gt;    &lt;dependency&gt;      &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;      &lt;artifactId&gt;commons-lang3&lt;/artifactId&gt;      &lt;version&gt;3.12.0&lt;/version&gt;    &lt;/dependency&gt;    &lt;dependency&gt;      &lt;groupId&gt;com.google.guava&lt;/groupId&gt;      &lt;artifactId&gt;guava&lt;/artifactId&gt;      &lt;version&gt;31.1-jre&lt;/version&gt;    &lt;/dependency&gt;  &lt;/dependencies&gt;</code></pre></li><li><p>数据准备 (随机生成500万URL)</p><pre><code class="language-">    @Test    public void testGeneUrl() {        try{            File file = new File(&quot;/Users/xdclass/Desktop/dat.txt&quot;);            if (!file.exists()) {                file.createNewFile(); // 创建新文件,有同名的文件的话直接覆盖            }            FileOutputStream fos = new FileOutputStream(file, true);            OutputStreamWriter osw = new OutputStreamWriter(fos);            BufferedWriter bw = new BufferedWriter(osw);            StringBuilder builder = new StringBuilder();            for (int i = 0; i &lt; 5000000; i++) {                String name = RandomStringUtils.randomAlphabetic(5);                String fileName = &quot;https://www.&quot; + name + &quot;.com&quot; + i + &quot;\n&quot;;                builder.append(fileName);            }            bw.write(String.valueOf(builder));            bw.newLine();            bw.flush();            bw.close();            osw.close();            fos.close();        } catch (FileNotFoundException e1) {            e1.printStackTrace();        } catch (IOException e2) {            e2.printStackTrace();        }    }</code></pre></li><li><p>Guava包布隆过滤器介绍</p><pre><code class="language-">//参数一： 指定布隆过滤器中存的是什么类型的数据，有 IntegerFunnel，LongFunnel，StringCharsetFunnel//参数二： 预期需要存储的数据量//参数三： 误判率，默认是 0.03BloomFilter.create(Funnels.stringFunnel(Charset.forName(&quot;UTF-8&quot;)), 5000000, 0.01);</code></pre></li></ul></li><li><p>核心代码编写</p></li></ul><pre><code class="language-">  @Bean  public Set set() throws IOException {    Set&lt;String&gt; set = new LinkedHashSet&lt;&gt;();    FileInputStream inputStream = new FileInputStream(new File(&quot;/Users/xdclass/Desktop/dat.txt&quot;));    InputStreamReader streamReader = new InputStreamReader(inputStream);    BufferedReader reader = new BufferedReader(streamReader);    String line = null;    while (true) {      line = reader.readLine();      if (line != null) {        set.add(line);      } else {        break;      }    }    inputStream.close();    return set;  }  @Bean  public BloomFilter bloomFilter() throws IOException {    BloomFilter bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName(&quot;UTF-8&quot;)), 5000000, 0.01);    FileInputStream inputStream = new FileInputStream(new File(&quot;/Users/xdclass/Desktop/dat.txt&quot;));    InputStreamReader streamReader = new InputStreamReader(inputStream);    BufferedReader reader = new BufferedReader(streamReader);    String line = null;    while (true) {      line = reader.readLine();      if (line != null) {        bloomFilter.put(line);      } else {        break;      }    }    inputStream.close();    return bloomFilter;  }    @RestController@RequestMapping(&quot;/api&quot;)public class FilterController {    @Autowired    private BloomFilter&lt;String&gt; bloomFilter;    @Autowired    private Set set;    @GetMapping(&quot;/bloom&quot;)    public String list() throws IOException {        //判断是否包含这个内容        if (bloomFilter.mightContain(&quot;https://www.dhVrX.com5&quot;)) {            return &quot;命中了&quot;;        } else {            return &quot;没命中&quot;;        }    }    @GetMapping(&quot;/set&quot;)    public String set() {        if (set.contains(&quot;httssps://www.shncb.com999663&quot;)) {            return &quot;命中了&quot;;        } else {            return &quot;没命中&quot;;        }    }}</code></pre><ul><li><p>案例测试 （调整JVM参数分配内存：-Xms100m -Xmx100m）</p><ul><li>使用Set集合</li><li>使用布隆过滤器</li></ul></li></ul>]]>
                    </description>
                    <pubDate>Fri, 21 Jun 2024 18:12:32 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[centos7安装jdk17]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/centos7-an-zhuang-jdk17</link>
                    <description>
                            <![CDATA[<p>centos7安装jdk17</p><h2 id="%E8%BF%9B%E5%85%A5%E5%AE%89%E8%A3%85%E7%9B%AE%E5%BD%95" tabindex="-1">进入安装目录</h2><p>cd /usr/local/</p><h2 id="%E6%96%B0%E5%BB%BAjava%E5%AE%89%E8%A3%85%E7%9B%AE%E5%BD%95%EF%BC%8C%E5%B9%B6%E8%BF%9B%E5%85%A5%E6%AD%A4%E7%9B%AE%E5%BD%95" tabindex="-1">新建Java安装目录，并进入此目录</h2><p>mkdir java<br />cd java/</p><h2 id="%E4%B8%8B%E8%BD%BDjdk" tabindex="-1">下载JDK</h2><p>wget <a href="https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz" target="_blank">https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz</a><br />如果慢的话可以使用本地的</p><pre><code class="language-">链接: https://pan.baidu.com/s/1D3UAl7q8XH_Gvtfyu1WX2g?pwd=uuah 提取码: uuah </code></pre><h2 id="%E5%B0%86%E5%AE%89%E8%A3%85%E5%8C%85%E8%A7%A3%E5%8E%8B%E7%BC%A9%E5%88%B0java%E7%9B%AE%E5%BD%95" tabindex="-1">将安装包解压缩到java目录</h2><p>tar -zxvf /root/jdk-17_linux-x64_bin.tar.gz</p><h2 id="%E8%BF%9B%E5%85%A5%E7%9B%AE%E5%BD%95" tabindex="-1">进入目录</h2><p>cd jdk-17.0.4.1/</p><h2 id="%E9%85%8D%E7%BD%AE%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F" tabindex="-1">配置环境变量</h2><p>vim /etc/profile</p><h2 id="set-java-environment" tabindex="-1">set java environment</h2><p>JAVA_HOME=/usr/local/java/jdk-17.0.1<br />PATH=<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mi>A</mi><mi>T</mi><mi>H</mi><mo>:</mo></mrow><annotation encoding="application/x-tex">PATH:</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">A</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mord mathnormal" style="margin-right:0.08125em;">H</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">:</span></span></span></span>JAVA_HOME/bin<br />export JAVA_HOME PATH</p><h2 id="%E9%87%8D%E6%96%B0%E5%8A%A0%E8%BD%BDprofile" tabindex="-1">重新加载profile</h2><p>source /etc/profile</p><h2 id="%E6%9F%A5%E7%9C%8Bjava%E7%89%88%E6%9C%AC" tabindex="-1">查看Java版本</h2><p>java -version</p><p>我的博客即将同步至腾讯云开发者社区，邀请大家一同入驻：<a href="https://cloud.tencent.com/developer/support-plan?invite_code=2svi82dl2vy8c" target="_blank">https://cloud.tencent.com/developer/support-plan?invite_code=2svi82dl2vy8c</a></p>]]>
                    </description>
                    <pubDate>Mon, 17 Jun 2024 18:11:54 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[linux大文件压缩方案]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/linux-da-wen-jian-ya-suo-fang-an</link>
                    <description>
                            <![CDATA[<p>linux大文件压缩方案</p><p>已解决</p><p>(please check that you have transferred or created the zipfile in the appropriate BINARY mode and that you have compiled UnZip properly)</p><p>unzip智能压缩4g/2g大小的zip文件，根据版本不同，如果超大的文件的话，使用下面的方案p7zip</p><p>linux下大文件的压缩方法，使用p7zip,支持x86和arm</p><p>1: <a href="http://sourceforge.net/projects/p7zip/files/p7zip/" target="_blank">http://sourceforge.net/projects/p7zip/files/p7zip/</a></p><p>2: 下载源码包 p7zip_16.02_src_all.tar.bz2</p><p>3: tar -xjpf 解压后，进入bin目录使用 make &amp; make install 进行安装</p><p>如果你想解压缩到特定的目录，可以使用以下命令：<br />“<code>    7z x 文件名.7z -o 目标目录    “</code><br />将文件解压到指定目录下。</p><p>解压加密的7z文件：<br />如果7z文件加密了，你需要提供密码来解压缩文件。使用以下命令：<br />“<code>    7z x 文件名.7z -p    “</code><br />然后输入密码并按回车键。</p>]]>
                    </description>
                    <pubDate>Sat, 23 Mar 2024 18:16:23 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[arthas常用命令]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/arthas-chang-yong-ming-ling</link>
                    <description>
                            <![CDATA[<p>arthas常用命令</p><p>官方地址：<br /><a href="https://arthas.aliyun.com" target="_blank">https://arthas.aliyun.com</a></p><h1 id="%E7%AE%80%E4%BB%8B" tabindex="-1">简介</h1><h2 id="%E7%AE%80%E4%BB%8B-1" tabindex="-1">简介</h2><p><img src="https://arthas.aliyun.com/images/arthas.png" alt="" /><br />Arthas 是一款线上监控诊断产品，通过全局视角实时查看应用 load、内存、gc、线程的状态信息，并能在不修改应用代码的情况下，对业务问题进行诊断，包括查看方法调用的出入参、异常，监测方法执行耗时，类加载信息等，大大提升线上问题排查效率。</p><h2 id="%E8%83%8C%E6%99%AF" tabindex="-1">背景</h2><p>通常，本地开发环境无法访问生产环境。如果在生产环境中遇到问题，则无法使用 IDE 远程调试。更糟糕的是，在生产环境中调试是不可接受的，因为它会暂停所有线程，导致服务暂停。</p><p>开发人员可以尝试在测试环境或者预发环境中复现生产环境中的问题。但是，某些问题无法在不同的环境中轻松复现，甚至在重新启动后就消失了。</p><p>如果您正在考虑在代码中添加一些日志以帮助解决问题，您将必须经历以下阶段：测试、预发，然后生产。这种方法效率低下，更糟糕的是，该问题可能无法解决，因为一旦 JVM 重新启动，它可能无法复现，如上文所述。</p><p>Arthas 旨在解决这些问题。开发人员可以在线解决生产问题。无需 JVM 重启，无需代码更改。 Arthas 作为观察者永远不会暂停正在运行的线程。</p><p>Arthas（阿尔萨斯）能为你做什么？<br />Arthas 是 Alibaba 开源的 Java 诊断工具，深受开发者喜爱。</p><p>当你遇到以下类似问题而束手无策时，Arthas可以帮助你解决：</p><ol><li>这个类从哪个 jar 包加载的？为什么会报各种类相关的 Exception？</li><li>我改的代码为什么没有执行到？难道是我没 commit？分支搞错了？</li><li>遇到问题无法在线上 debug，难道只能通过加日志再重新发布吗？</li><li>线上遇到某个用户的数据处理有问题，但线上同样无法 debug，线下无法重现！</li><li>是否有一个全局视角来查看系统的运行状况？</li><li>有什么办法可以监控到 JVM 的实时运行状态？</li><li>怎么快速定位应用的热点，生成火焰图？</li><li>怎样直接从 JVM 内查找某个类的实例？</li><li>Arthas 支持 JDK 6+，支持 Linux/Mac/Windows，采用命令行交互模式，同时提供丰富的 Tab 自动补全功能，进一步方便进行问题的定位和诊断。</li></ol><h1 id="arthas-install" tabindex="-1">Arthas Install</h1><h2 id="%E4%BD%BF%E7%94%A8arthas-boot%EF%BC%88%E6%8E%A8%E8%8D%90%EF%BC%89" tabindex="-1">使用arthas-boot（推荐）</h2><p>下载<strong>arthas-boot.jar</strong>，然后用java -jar的方式启动：</p><pre><code class="language-">curl -O https://arthas.aliyun.com/arthas-boot.jarjava -jar arthas-boot.jar</code></pre><p>打印帮助信息：</p><pre><code class="language-">java -jar arthas-boot.jar -h</code></pre><ul><li>如果下载速度比较慢，可以使用 aliyun 的镜像：</li></ul><pre><code class="language-">java -jar arthas-boot.jar --repo-mirror aliyun --use-http</code></pre><h2 id="%E4%BD%BF%E7%94%A8as.sh" tabindex="-1"><a href="http://xn--as-os3c914l.sh" target="_blank">使用as.sh</a></h2><p>Arthas 支持在 Linux/Unix/Mac 等平台上一键安装，请复制以下内容，并粘贴到命令行中，敲 回车 执行即可：</p><pre><code class="language-">curl -L https://arthas.aliyun.com/install.sh | sh</code></pre><p>上述命令会下载启动脚本文件 <a href="http://as.sh" target="_blank">as.sh</a> 到当前目录，你可以放在任何地方或将其加入到 $PATH 中。</p><p>直接在 shell 下面执行./as.sh，就会进入交互界面。</p><p>也可以执行./as.sh -h来获取更多参数信息。</p><h1 id="%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8" tabindex="-1">快速入门</h1><h2 id="%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8-1" tabindex="-1">快速入门</h2><ol><li>执行一个jar包</li><li>通过arthas来粘附，并且进行几种常用的操作</li><li>通过一个案例快速入门</li></ol><p><strong>以下是一个简单的Java程序，每隔一秒生成一个随机数，再执行质因数分解，并打印出分解结果。代码<br />的内容不用理会这不是现在关注的点</strong></p><pre><code class="language-">package com.tiplus.api.web;import java.io.PrintStream;import java.util.ArrayList;import java.util.List;import java.util.Random;import java.util.concurrent.TimeUnit;public class MathGame {    private static Random random = new Random();    public int illegalArgumentCount = 0;    public List&lt;Integer&gt; primeFactors(int number) {        if (number &lt; 2) {            ++this.illegalArgumentCount;            throw new IllegalArgumentException(&quot;number is: &quot; + number + &quot;, need &gt;= 2&quot;);        }        ArrayList&lt;Integer&gt; result = new ArrayList&lt;Integer&gt;();        int i = 2;        while (i &lt;= number) {            if (number % i == 0) {                result.add(i);                number /= i;                i = 2;                continue;            }            ++i;        }        return result;    }    public static void main(String[] args) throws InterruptedException {        MathGame game = new MathGame();        do {            game.run();            TimeUnit.SECONDS.sleep(1L);            System.out.println(&quot;在main函数中循环体内&quot;);        } while (true);    }    public void run() throws InterruptedException {System.out.println(&quot;-- 计算中的函数 -- &quot;);        try {            int number = random.nextInt() / 10000;            List&lt;Integer&gt; primeFactors = this.primeFactors(number);            MathGame.print(number, primeFactors);        }        catch (Exception e) {            System.out.println(String.format(&quot;illegalArgumentCount:%3d, &quot;, this.illegalArgumentCount) + e.getMessage());        }    }    public static void print(int number, List&lt;Integer&gt; primeFactors) {        StringBuffer sb = new StringBuffer(number + &quot;=&quot;);        for (int factor : primeFactors) {            sb.append(factor).append(&#39;*&#39;);        }        if (sb.charAt(sb.length() - 1) == &#39;*&#39;) {            sb.deleteCharAt(sb.length() - 1);        }        System.out.println(sb);    }}</code></pre><p>选择应用 java 进程：</p><pre><code class="language-">$ $ java -jar arthas-boot.jar* [1]: 35542  [2]: 71560 math-game.jar</code></pre><p>查看 dashboard<br />输入dashboard，按回车/enter，会展示当前进程的信息，按ctrl+c可以中断执行。</p><pre><code class="language-">$ dashboardID     NAME                   GROUP          PRIORI STATE  %CPU    TIME   INTERRU DAEMON17     pool-2-thread-1        system         5      WAITIN 67      0:0    false   false27     Timer-for-arthas-dashb system         10     RUNNAB 32      0:0    false   true11     AsyncAppender-Worker-a system         9      WAITIN 0       0:0    false   true9      Attach Listener        system         9      RUNNAB 0       0:0    false   true3      Finalizer              system         8      WAITIN 0       0:0    false   true2      Reference Handler      system         10     WAITIN 0       0:0    false   true4      Signal Dispatcher      system         9      RUNNAB 0       0:0    false   true26     as-command-execute-dae system         10     TIMED_ 0       0:0    false   true13     job-timeout            system         9      TIMED_ 0       0:0    false   true1      main                   main           5      TIMED_ 0       0:0    false   false14     nioEventLoopGroup-2-1  system         10     RUNNAB 0       0:0    false   false18     nioEventLoopGroup-2-2  system         10     RUNNAB 0       0:0    false   false23     nioEventLoopGroup-2-3  system         10     RUNNAB 0       0:0    false   false15     nioEventLoopGroup-3-1  system         10     RUNNAB 0       0:0    false   falseMemory             used   total max    usage GCheap               32M    155M  1820M  1.77% gc.ps_scavenge.count  4ps_eden_space      14M    65M   672M   2.21% gc.ps_scavenge.time(m 166ps_survivor_space  4M     5M    5M           s)ps_old_gen         12M    85M   1365M  0.91% gc.ps_marksweep.count 0nonheap            20M    23M   -1           gc.ps_marksweep.time( 0code_cache         3M     5M    240M   1.32% ms)Runtimeos.name                Mac OS Xos.version             10.13.4java.version           1.8.0_162java.home              /Library/Java/JavaVir                       tualMachines/jdk1.8.0                       _162.jdk/Contents/Hom                       e/jre</code></pre><p>web:<a href="http://127.0.0.1:3658/" target="_blank">http://127.0.0.1:3658/</a></p><h1 id="%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E5%85%A5%E9%97%A8" tabindex="-1">常用命令入门</h1><h2 id="dashboard%E4%BB%AA%E8%A1%A8%E7%9B%98" tabindex="-1">dashboard仪表盘</h2><p>注：输入前面部分字母，按tab可以自动补全命令</p><ol><li>第一部分是显示JVM中运行的所有线程：所在线程组，优先级，线程的状态，CPU的占用率，是<br />否是后台进程等</li><li>第二部分显示的JVM内存的使用情况</li><li>第三部分是操作系统的一些信息和Java版本号<br /><img src="https://file.sky12580.cn/gaobei/1.png" alt="1.png" /></li></ol><h2 id="thread-%E6%9F%A5%E7%9C%8B%E5%BD%93%E5%89%8D%E7%BA%BF%E7%A8%8B%E4%BF%A1%E6%81%AF%EF%BC%8C%E6%9F%A5%E7%9C%8B%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%A0%86%E6%A0%88" tabindex="-1">thread 查看当前线程信息，查看线程的堆栈</h2><p><img src="https://file.sky12580.cn/gaobei/333.png" alt="333.png" /></p><h2 id="jad-%E5%8F%8D%E7%BC%96%E8%AF%91%E6%8C%87%E5%AE%9A%E5%B7%B2%E5%8A%A0%E8%BD%BD%E7%B1%BB%E7%9A%84%E6%BA%90%E7%A0%81" tabindex="-1">jad 反编译指定已加载类的源码</h2><p><img src="https://file.sky12580.cn/gaobei/44.png" alt="44.png" /></p><h2 id="watch-%E5%87%BD%E6%95%B0%E6%89%A7%E8%A1%8C%E6%95%B0%E6%8D%AE%E8%A7%82%E6%B5%8B-debug%E4%BD%BF%E7%94%A8%EF%BC%88%E7%A8%8B%E5%BA%8F%E5%AE%9E%E9%99%85%E8%BF%90%E8%A1%8C%E4%BD%BF%E7%94%A8%EF%BC%89" tabindex="-1">watch 函数执行数据观测-debug使用（程序实际运行使用）</h2><p>让你能方便的观察到指定函数的调用情况。能观察到的范围为：返回值、抛出异常、入参，通过编写 OGNL 表达式进行对应变量的查看。</p><p>watch demo.MathGame primeFactors returnObj</p><h1 id="%E5%9F%BA%E7%A1%80%E5%91%BD%E4%BB%A4" tabindex="-1">基础命令</h1><h2 id="help-%E6%9F%A5%E7%9C%8B%E5%91%BD%E4%BB%A4%E5%B8%AE%E5%8A%A9%E4%BF%A1%E6%81%AF%EF%BC%8C%E5%8F%AF%E4%BB%A5%E6%9F%A5%E7%9C%8B%E5%BD%93%E5%89%8D-arthas-%E7%89%88%E6%9C%AC%E6%94%AF%E6%8C%81%E7%9A%84%E6%8C%87%E4%BB%A4%EF%BC%8C%E6%88%96%E8%80%85%E6%9F%A5%E7%9C%8B%E5%85%B7%E4%BD%93%E6%8C%87%E4%BB%A4%E7%9A%84%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E3%80%82" tabindex="-1">help 查看命令帮助信息，可以查看当前 arthas 版本支持的指令，或者查看具体指令的使用说明。</h2><h2 id="cat-%E6%89%93%E5%8D%B0%E6%96%87%E4%BB%B6%E5%86%85%E5%AE%B9%EF%BC%8C%E5%92%8C-linux-%E9%87%8C%E7%9A%84-cat-%E5%91%BD%E4%BB%A4%E7%B1%BB%E4%BC%BC%E3%80%82" tabindex="-1">cat  打印文件内容，和 linux 里的 cat 命令类似。</h2><h2 id="grep-%E7%B1%BB%E4%BC%BC%E4%BC%A0%E7%BB%9F%E7%9A%84grep%E5%91%BD%E4%BB%A4%E3%80%82" tabindex="-1">grep 类似传统的grep命令。</h2><h2 id="pwd-%E8%BF%94%E5%9B%9E%E5%BD%93%E5%89%8D%E7%9A%84%E5%B7%A5%E4%BD%9C%E7%9B%AE%E5%BD%95%EF%BC%8C%E5%92%8C-linux-%E5%91%BD%E4%BB%A4%E7%B1%BB%E4%BC%BC" tabindex="-1">pwd 返回当前的工作目录，和 linux 命令类似</h2><h2 id="cls-%E6%B8%85%E7%A9%BA%E5%BD%93%E5%89%8D%E5%B1%8F%E5%B9%95%E5%8C%BA%E5%9F%9F%E3%80%82" tabindex="-1">cls 清空当前屏幕区域。</h2><h2 id="session-%E6%9F%A5%E7%9C%8B%E5%BD%93%E5%89%8D%E4%BC%9A%E8%AF%9D%E7%9A%84%E4%BF%A1%E6%81%AF%EF%BC%8C%E6%98%BE%E7%A4%BA%E5%BD%93%E5%89%8D%E7%BB%91%E5%AE%9A%E7%9A%84-pid-%E4%BB%A5%E5%8F%8A%E4%BC%9A%E8%AF%9D-id%E3%80%82" tabindex="-1">session 查看当前会话的信息，显示当前绑定的 pid 以及会话 id。</h2><h2 id="reset-%E9%87%8D%E7%BD%AE%E5%A2%9E%E5%BC%BA%E7%B1%BB%EF%BC%8C%E5%B0%86%E8%A2%AB-arthas-%E5%A2%9E%E5%BC%BA%E8%BF%87%E7%9A%84%E7%B1%BB%E5%85%A8%E9%83%A8%E8%BF%98%E5%8E%9F%EF%BC%8Carthas-%E6%9C%8D%E5%8A%A1%E7%AB%AFstop%E6%97%B6%E4%BC%9A%E9%87%8D%E7%BD%AE%E6%89%80%E6%9C%89%E5%A2%9E%E5%BC%BA%E8%BF%87%E7%9A%84%E7%B1%BB" tabindex="-1">reset 重置增强类，将被 Arthas 增强过的类全部还原，Arthas 服务端stop时会重置所有增强过的类</h2><h2 id="version-%E8%BE%93%E5%87%BA%E5%BD%93%E5%89%8D%E7%9B%AE%E6%A0%87-java-%E8%BF%9B%E7%A8%8B%E6%89%80%E5%8A%A0%E8%BD%BD%E7%9A%84-arthas-%E7%89%88%E6%9C%AC%E5%8F%B7" tabindex="-1">version 输出当前目标 Java 进程所加载的 Arthas 版本号</h2><h2 id="quit-%E9%80%80%E5%87%BA%E5%BD%93%E5%89%8D-arthas-%E5%AE%A2%E6%88%B7%E7%AB%AF%EF%BC%8C%E5%85%B6%E4%BB%96-arthas-%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%B8%8D%E5%8F%97%E5%BD%B1%E5%93%8D%E3%80%82%E7%AD%89%E5%90%8C%E4%BA%8Eexit%E3%80%81logout%E3%80%81q%E4%B8%89%E4%B8%AA%E6%8C%87%E4%BB%A4%E3%80%82" tabindex="-1">quit 退出当前 Arthas 客户端，其他 Arthas 客户端不受影响。等同于exit、logout、q三个指令。</h2><h2 id="stop-%E5%85%B3%E9%97%AD-arthas-%E6%9C%8D%E5%8A%A1%E7%AB%AF%EF%BC%8C%E6%89%80%E6%9C%89-arthas-%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%85%A8%E9%83%A8%E9%80%80%E5%87%BA%E3%80%82" tabindex="-1">stop 关闭 Arthas 服务端，所有 Arthas 客户端全部退出。</h2><h2 id="keymap-%E5%91%BD%E4%BB%A4%E8%BE%93%E5%87%BA%E5%BD%93%E5%89%8D%E7%9A%84%E5%BF%AB%E6%8D%B7%E9%94%AE%E6%98%A0%E5%B0%84%E8%A1%A8%EF%BC%9A" tabindex="-1">keymap 命令输出当前的快捷键映射表：</h2><h1 id="jvm%E7%9B%B8%E5%85%B3" tabindex="-1">jvm相关</h1><h2 id="dashboard-%E4%BB%AA%E8%A1%A8%E6%9D%BF" tabindex="-1">dashboard 仪表板</h2><p><img src="http://file.sky12580.cn/gaobei/6.png" alt="6.png" /></p><ul><li>ID: Java级别的线程ID，注意这个ID不能跟jstack中的nativeID一一对应</li><li>NAME: 线程名</li><li>GROUP: 线程组名</li><li>PRIORITY: 线程优先级, 1~10之间的数字，越大表示优先级越高</li><li>STATE: 线程的状态</li><li>CPU%: 线程消耗的cpu占比，采样100ms，将所有线程在这100ms内的cpu使用量求和，再算出每个线程的cpu使用占比。</li><li>TIME: 线程运行总时间，数据格式为 分：秒</li><li>INTERRUPTED: 线程当前的中断位状态</li><li>DAEMON: 是否是daemon线程</li></ul><h2 id="thread-%E7%BA%BF%E7%A8%8B" tabindex="-1">thread 线程</h2><p>查看当前线程信息，查看线程的堆栈</p><pre><code class="language-">参数名称参数说明id线程 id[n:]指定最忙的前 N 个线程并打印堆栈[b]找出当前阻塞其他线程的线程[i &lt;value&gt;]指定 cpu 使用率统计的采样间隔，单位为毫秒，默认值为 200[--all]显示所有匹配的线程</code></pre><h2 id="jvm-%E8%99%9A%E6%8B%9F%E6%9C%BA-%E6%9F%A5%E7%9C%8B%E5%BD%93%E5%89%8D-jvm-%E4%BF%A1%E6%81%AF" tabindex="-1">jvm 虚拟机 查看当前 JVM 信息</h2><ul><li>THREAD相关<ul><li>COUNT: JVM当前活跃的线程数</li><li>DAEMON-COUNT: JVM当前活跃的守护线程数</li><li>PEAK-COUNT: 从JVM启动开始曾经活着的最大线程数</li><li>STARTED-COUNT: 从JVM启动开始总共启动过的线程次数</li><li>DEADLOCK-COUNT: JVM当前死锁的线程数</li><li>指定采样时间间隔，每过1000毫秒采样，显示最占时间的3个线程<br />thread -i 1000 -n 3<br />1<br />2</li></ul></li><li>查看处于等待状态的线程<br />thread --state WAITING<br />1<br />2</li><li>文件描述符相关<ul><li>MAX-FILE-DESCRIPTOR-COUNT：JVM进程最大可以打开的文件描述符数</li><li>OPEN-FILE-DESCRIPTOR-COUNT：JVM当前打开的文件描述符数</li></ul></li></ul><h2 id="sysprop-%E7%B3%BB%E7%BB%9F%E5%B1%9E%E6%80%A7" tabindex="-1">sysprop 系统属性</h2><p>查看当前 JVM 的系统属性(System Property)</p><h2 id="sysenv-%E6%9F%A5%E7%9C%8B%E5%BD%93%E5%89%8Djvm%E7%9A%84%E7%8E%AF%E5%A2%83%E5%B1%9E%E6%80%A7(-system-environment-variables-)" tabindex="-1">sysenv 查看当前JVM的环境属性( System Environment Variables )</h2><h2 id="vmoption-%E6%9F%A5%E7%9C%8B%EF%BC%8C%E6%9B%B4%E6%96%B0vm%E8%AF%8A%E6%96%AD%E7%9B%B8%E5%85%B3%E7%9A%84%E5%8F%82%E6%95%B0" tabindex="-1">vmoption 查看，更新VM诊断相关的参数</h2><h2 id="getstatic-%E9%80%9A%E8%BF%87getstatic%E5%91%BD%E4%BB%A4%E5%8F%AF%E4%BB%A5%E6%96%B9%E4%BE%BF%E7%9A%84%E6%9F%A5%E7%9C%8B%E7%B1%BB%E7%9A%84%E9%9D%99%E6%80%81%E5%B1%9E%E6%80%A7" tabindex="-1">getstatic 通过getstatic命令可以方便的查看类的静态属性</h2><h2 id="ognl-%E6%89%A7%E8%A1%8C-ognl-%E8%A1%A8%E8%BE%BE%E5%BC%8F" tabindex="-1">ognl 执行 ognl 表达式</h2><h1 id="class%2Fclassloader%E7%9B%B8%E5%85%B3" tabindex="-1">class/classloader相关</h1><h2 id="sc-%E6%9F%A5%E7%9C%8B-jvm-%E5%B7%B2%E5%8A%A0%E8%BD%BD%E7%9A%84%E7%B1%BB%E4%BF%A1%E6%81%AF" tabindex="-1">sc 查看 JVM 已加载的类信息</h2><p>Search-Class” 的简写，这个命令能搜索出所有已经加载到 JVM 中的 Class 信息，这个命令支持的参数有[d]、[E]、[f] 和 [x:]。</p><h2 id="sm-%E6%9F%A5%E7%9C%8B%E5%B7%B2%E5%8A%A0%E8%BD%BD%E7%B1%BB%E7%9A%84%E6%96%B9%E6%B3%95%E4%BF%A1%E6%81%AF" tabindex="-1">sm 查看已加载类的方法信息</h2><p>“Search-Method” 的简写，这个命令能搜索出所有已经加载了 Class 信息的方法信息。<br />sm 命令只能看到由当前类所声明 (declaring) 的方法，父类则无法看到。</p><h2 id="jad-%E6%9F%A5%E7%9C%8B%E5%B7%B2%E5%8A%A0%E8%BD%BD%E7%B1%BB%E7%9A%84%E6%96%B9%E6%B3%95%E4%BF%A1%E6%81%AF-%E5%8F%8D%E7%BC%96%E8%AF%91" tabindex="-1">jad 查看已加载类的方法信息 反编译</h2><p>jad 命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码，便于你理解业务逻辑；<br />在 Arthas Console 上，反编译出来的源码是带语法高亮的，阅读更方便<br />当然，反编译出来的 java 代码可能会存在语法错误，但不影响你进行阅读理解</p><h2 id="mc-memory-compiler%2F%E5%86%85%E5%AD%98%E7%BC%96%E8%AF%91%E5%99%A8%EF%BC%8C%E7%BC%96%E8%AF%91.java%E6%96%87%E4%BB%B6%E7%94%9F%E6%88%90.class%E3%80%82" tabindex="-1">mc Memory Compiler/内存编译器，编译.java文件生成.class。</h2><h2 id="redefine-%E5%8A%A0%E8%BD%BD%E5%A4%96%E9%83%A8%E7%9A%84-.class-%E6%96%87%E4%BB%B6%EF%BC%8Credefine%E5%88%B0jvm%E9%87%8C" tabindex="-1">redefine 加载外部的 .class 文件，redefine到JVM里</h2><h2 id="retransform-%E5%8A%A0%E8%BD%BD%E5%A4%96%E9%83%A8%E7%9A%84.class%E6%96%87%E4%BB%B6%EF%BC%8Cretransform-jvm-%E5%B7%B2%E5%8A%A0%E8%BD%BD%E7%9A%84%E7%B1%BB%E3%80%82" tabindex="-1">retransform 加载外部的.class文件，retransform jvm 已加载的类。</h2><h2 id="dump-dump-%E5%B7%B2%E5%8A%A0%E8%BD%BD%E7%B1%BB%E7%9A%84-bytecode-%E5%88%B0%E7%89%B9%E5%AE%9A%E7%9B%AE%E5%BD%95" tabindex="-1">dump dump 已加载类的 bytecode 到特定目录</h2><h2 id="classloader-%E6%9F%A5%E7%9C%8B-classloader-%E7%9A%84%E7%BB%A7%E6%89%BF%E6%A0%91%EF%BC%8Curls%EF%BC%8C%E7%B1%BB%E5%8A%A0%E8%BD%BD%E4%BF%A1%E6%81%AF" tabindex="-1">classloader 查看 classloader 的继承树，urls，类加载信息</h2><p>classloader 命令将 JVM 中所有的 classloader 的信息统计出来，并可以展示继承树，urls 等。<br />可以让指定的 classloader 去 getResources，打印出所有查找到的 resources 的 url。对于ResourceNotFoundException比较有用。</p><h1 id="monitor%2Fwatch%2Ftrace%E7%9B%B8%E5%85%B3" tabindex="-1">monitor/watch/trace相关</h1><h2 id="monitor%E6%96%B9%E6%B3%95%E6%89%A7%E8%A1%8C%E7%9B%91%E6%8E%A7" tabindex="-1">monitor方法执行监控</h2><p>对匹配 class-pattern／method-pattern／condition-express的类、方法的调用进行监控。<br />monitor 命令是一个非实时返回命令.<br />实时返回命令是输入之后立即返回，而非实时返回的命令，则是不断的等待目标 Java 进程返回信息，直到用户输入 Ctrl+C 为止。<br />服务端是以任务的形式在后台跑任务，植入的代码随着任务的中止而不会被执行，所以任务关闭后，不会对原有性能产生太大影响，而且原则上，任何 Arthas 命令不会引起原有业务逻辑的改变。</p><h2 id="watch-%E5%87%BD%E6%95%B0%E6%89%A7%E8%A1%8C%E6%95%B0%E6%8D%AE%E8%A7%82%E6%B5%8B" tabindex="-1">watch 函数执行数据观测</h2><p>让你能方便的观察到指定函数的调用情况。能观察到的范围为：返回值、抛出异常、入参，通过编写 OGNL 表达式进行对应变量的查看。</p><h2 id="trace%E6%96%B9%E6%B3%95%E5%86%85%E9%83%A8%E8%B0%83%E7%94%A8%E8%B7%AF%E5%BE%84%EF%BC%8C%E5%B9%B6%E8%BE%93%E5%87%BA%E6%96%B9%E6%B3%95%E8%B7%AF%E5%BE%84%E4%B8%8A%E7%9A%84%E6%AF%8F%E4%B8%AA%E8%8A%82%E7%82%B9%E4%B8%8A%E8%80%97%E6%97%B6" tabindex="-1">trace方法内部调用路径，并输出方法路径上的每个节点上耗时</h2><p>trace 命令能主动搜索 class-pattern／method-pattern 对应的方法调用路径，渲染和统计整个调用链路上的所有性能开销和追踪调用链路。</p><h2 id="stack-%E8%BE%93%E5%87%BA%E5%BD%93%E5%89%8D%E6%96%B9%E6%B3%95%E8%A2%AB%E8%B0%83%E7%94%A8%E7%9A%84%E8%B0%83%E7%94%A8%E8%B7%AF%E5%BE%84" tabindex="-1">stack 输出当前方法被调用的调用路径</h2><p>很多时候我们都知道一个方法被执行，但这个方法被执行的路径非常多，或者你根本就不知道这个方法是从那里被执行了，此时你需要的是 stack 命令</p>]]>
                    </description>
                    <pubDate>Tue, 05 Mar 2024 18:13:32 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[春晚刘谦魔术java代码揭秘]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/chun-wan-liu-qian-mo-shu-java-dai-ma-jie-mi</link>
                    <description>
                            <![CDATA[<pre><code class="language-">package com.gtf;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.List;/** * @author gtf * @date 2024/2/10 02:24 */public class Demo {    public static void main(String[] args) {        //1。定义四张扑克牌也就是一个数组,假设这四张是我的扑克牌        String[] poker1 = {&quot;K&quot;, &quot;A&quot;, &quot;Q&quot;, &quot;J&quot;};        System.out.println(&quot;原始扑克牌顺序&quot; + Arrays.toString(poker1));        //2。随机打乱顺序        Collections.shuffle(Arrays.asList(poker1));        System.out.println(&quot;打乱扑克牌以后的顺序: &quot; + Arrays.toString(poker1));        //3。撕下来扑克牌，那也就是 打乱以后的数组克隆一份相加        String[] poker2 = new String[8];        for (int i = 0; i &lt; poker1.length; i++) {            poker2[i] = poker1[i];        }        for (int i = 0; i &lt; poker1.length; i++) {            poker2[poker1.length + i] = poker1[i];        }        System.out.println(&quot;撕开扑克牌以后的数据: &quot; + Arrays.toString(poker2));        //todo 在这个过程过你会发现，顺序是相同的，这是魔术的关键点1，必须相同否则会有问题        //魔术步骤1：你的名字几个字 就往下排几张 ok,我就写2个字吧，那就是把这个数组位置替换2个位置,第一个拿走的位置 就是下标1，第二个拿走的位置就是下标0        String temp1 = poker2[poker2.length - 2];        String temp2 = poker2[poker2.length - 1];        for (int i = poker2.length - 1; i &gt;= 2; i--) {            poker2[i] = poker2[i - 2];        }        poker2[0] = temp1;        poker2[1] = temp2;        System.out.println(&quot;我就两个字测试，根据名字紫薯打乱以后的: &quot; + Arrays.toString(poker2));        //todo 经过上面名字切牌的过程，你会发现魔术神奇的地方来了，不关你是几个字，顺序都是一摸一样的，四个一组。        //魔术步骤2：前面三张扑克牌，插入到中间的任意位置，我就将除第一个 最后一个随机打乱        String[] extracted = Arrays.copyOfRange(poker2, 0, 3);        String[] remaining = Arrays.copyOfRange(poker2, 3, poker2.length);        // 将取出的元素插入到剩余元素数组的指定位置        String[] poker3 = new String[extracted.length + remaining.length];        System.arraycopy(remaining, 0, poker3, 0, 1);        System.arraycopy(extracted, 0, poker3, 1, extracted.length);        System.arraycopy(remaining, 1, poker3, 4, remaining.length - 1);        System.out.println(&quot;前面三张扑克牌，插入到中间的任意位置:我插入的是下标为1的位置也就是第2个元素 &quot;                + Arrays.toString(poker3));        //todo 经过这一个步骤，聪明的你又看出来问题了，无论怎么操作，只要插入到非第一张和最后一张的位置，        // 第一张和最后一张永远是一样的        // 魔术步骤3：将第一张牌拿出去。        System.out.println(&quot;把第一张藏起来，拿出去的扑克牌  &quot; + poker3[0]);        String[] poker4 = new String[poker3.length - 1];        System.arraycopy(poker3, 1, poker4, 0, poker4.length);        System.out.println(&quot;男生拿出去一张扑克牌，拿出去扑克牌以后，现在的扑克牌  &quot; + Arrays.toString(poker4));        //魔术步骤4：男生丢掉1张，女生丢掉2张 ，这边无所谓的，保留6张就行。        for (int i = 0; i &lt; poker4.length - 1; i++) {            poker4[i] = poker4[i + 1];        }        String[] poker5 = new String[poker4.length - 1];        System.arraycopy(poker4, 0, poker5, 0, poker5.length);        System.out.println(&quot;男生丢完以后的扑克牌  &quot; + Arrays.toString(poker5));        //todo 经过这一个步骤，聪明的你又看出来了，无论男生还是女生，只要最后一张牌不丢都无所谓的。        //魔术步骤5：见证奇迹的时刻 ，7个字这边就定义7次循环了        // 循环7次，每次将最后一个元素移动到第一个位置，其他元素前移        for (int i = 0; i &lt; 7; i++) {            String temp = poker5[poker5.length - 1];            for (int j = poker5.length - 1; j &gt; 0; j--) {                poker5[j] = poker5[j - 1];            }            poker5[0] = temp;        }        System.out.println(&quot;见证奇迹的时刻循环7次: &quot; + Arrays.toString(poker5));        //todo 经过这一个步骤，聪明的你又看出来了，你想预留的那个扑克牌在第一个位置不变，        // 下面就是很随意的将第一个元素变成最后一个元素，删除掉原始的第二个元素，直到剩余最后一个元素        int numIterations = poker5.length - 1;        ArrayList&lt;String&gt; list = new ArrayList&lt;&gt;(Arrays.asList(poker5));        for (int i = 0; i &lt; numIterations; i++) {            String removedElement = list.remove(1);            list.add(removedElement);        }        String[] finalArr = new String[1];        finalArr[0] = list.get(0);        System.out.println(&quot;循环直到剩下最后一张扑克牌 &quot; + Arrays.toString(finalArr));    }}</code></pre>]]>
                    </description>
                    <pubDate>Thu, 29 Feb 2024 18:15:55 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[mac多个压缩包合并以及解压]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/mac-duo-ge-ya-suo-bao-he-bing-yi-ji-jie-ya</link>
                    <description>
                            <![CDATA[<p>mac多个压缩包合并以及解压</p><p>多个压缩包合并</p><p>cat xxxx.tar.zip.0** &gt;xxxx.tar.zip</p><p>解压 unzip xxxx.tar.zip<br />cat xxxx.tar.zip.0** &gt;xxxx.tar.zip</p>]]>
                    </description>
                    <pubDate>Sun, 31 Dec 2023 18:14:20 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[redis批量删除某个指定key的前缀]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/redis-pi-liang-shan-chu-mou-ge-zhi-ding-key-de-qian-zhui</link>
                    <description>
                            <![CDATA[<p>redis批量删除某个指定key的前缀</p><p>EVAL “local licenseKeys = redis.call(‘keys’, KEYS[1]) for i, v in ipairs(licenseKeys) do redis.call(‘del’, v) end” 1 group_*</p><p>redis-cli -h <a href="http://r-2zeyhq5t9be111.redis.rds.aliyuncs.com" target="_blank">r-2zeyhq5t9be111.redis.rds.aliyuncs.com</a> -p 6379 -a mima keys 删除的key* | xargs -r -t -n1 ./redis-cli -h <a href="http://r-2zeyhq5t9be111.redis.rds.aliyuncs.com" target="_blank">r-2zeyhq5t9be111.redis.rds.aliyuncs.com</a> -p 6379 -a mima del</p>]]>
                    </description>
                    <pubDate>Thu, 31 Aug 2023 18:13:57 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[Nginx代理，header数据丢失（带“_”下划线）]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/nginx-dai-li-header-shu-ju-diu-shi--dai--xia-hua-xian-</link>
                    <description>
                            <![CDATA[<p>通过request.getheader(''xx_xx) 发现只要包含_的都取不到，仔细回想了一下，代码中怎么看都没有问题，本地代理也没有问题，</p><p>最后回想起来，生产通过nginx进行了代理。那么问题就在nginx这里，来吧，给出答案</p><p>nginx.conf配置文件中的http部分中添加如下配置：<br />underscores_in_headers on; （默认 underscores_in_headers 为off）</p>]]>
                    </description>
                    <pubDate>Fri, 04 Aug 2023 18:16:47 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[最简单的linux挂载盘符]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/zui-jian-dan-de-linux-gua-zai-pan-fu</link>
                    <description>
                            <![CDATA[<ol><li>确定设备名称</li></ol><p>lsblk</p><h1 id="%E6%88%96%E8%80%85" tabindex="-1">或者</h1><p>sudo fdisk -l</p><ol start="2"><li>创建挂载点</li></ol><p>sudo mkdir -p /mnt/mydisk</p><ol start="3"><li>挂载设备</li></ol><p>sudo mount /dev/sdb1 /mnt/mydisk</p><ol start="4"><li>自动挂载（可选）[谨慎，如果操作失误，会开不开机的哦]</li></ol><p>sudo blkid /dev/sdb1</p><p>输出例子</p><p>/dev/sdb1: UUID=“1234-5678” TYPE=“ntfs”</p><p>然后，编辑 /etc/fstab 文件：</p><p>sudo vim /etc/fstab</p><p>在文件末尾添加一行，例如：</p><p>UUID=1234-5678  /mnt/mydisk  ntfs  defaults  0  2</p>]]>
                    </description>
                    <pubDate>Thu, 22 Jun 2023 18:17:15 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[java批处理]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/java-pi-chu-li</link>
                    <description>
                            <![CDATA[<pre><code class="language-java">@Resource    private SqlSessionFactory sqlSessionFactory;    @Override    public &lt;T&gt;  void saveBatch(List&lt;?&gt; saveList, Class&lt;? extends BaseMapper&lt;T&gt;&gt; mappers) {        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);        BaseMapper&lt;T&gt; mapper = sqlSession.getMapper(mappers);        for(int i = 0; i &lt;= saveList.size() - 1; i++){            mapper.insert((T) saveList.get(i));            if(i % 1000 == 0){                sqlSession.commit();                sqlSession.clearCache();            }        }        sqlSession.commit();        sqlSession.clearCache();    }</code></pre>]]>
                    </description>
                    <pubDate>Thu, 25 May 2023 13:34:14 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[devops[单节点版本]]]>
                    </title>
                    <link>https://www.sky12580.cn/archives/devops-dan-jie-dian-ban-ben-</link>
                    <description>
                            <![CDATA[<pre><code class="language-">资料下载地址链接：https://pan.baidu.com/s/12dQ-B82t5mVBwklKYI9ByA?pwd=ue0u 提取码：ue0u </code></pre><h3 id="%E7%AC%AC%E4%B8%80%E7%AB%A0-devops" tabindex="-1">第一章 DevOps</h3><h4 id="%E7%AC%AC1%E9%9B%86-%E7%8E%AF%E5%A2%83%E4%BA%86%E8%A7%A3" tabindex="-1">第1集 环境了解</h4><ul><li>基本要求<br />熟练使⽤CentOS <strong>7</strong> / <strong>8</strong> 或者其他Linux发现版<br />了解Docker是什么，不要求会⽤，但要知道容器化是怎么回事<br />最佳效果<br />掌握Docker的各种命令，在学习、⼯作中使⽤过Docker，理解什么是容器</li><li>涉及组件<br />CentOS 7、Docker、Gitlab、Jenkins、IDEA、Kubeode、Kubernetes、Helm、 Harbor</li><li>环境准备<br />4台2核8G物理机、虚拟机、云主机</li></ul><h4 id="%E7%AC%AC2%E9%9B%86-%E4%BB%80%E4%B9%88%E6%98%AFdevops" tabindex="-1">第2集 什么是devops</h4><p>DevOps 是 Development（开发）和 Operations（运维）的组合，是 ⼀种⽅法论，是⼀组过程、⽅法与系统的统称，⽤于促进应⽤开发、应2<br />⽤运维和质量保障（QA）部⻔之间的沟通、协作与整合，以期打破传 统开发和运营之间的壁垒和鸿沟<br />CI/CD 的主要概念是持续集成、持续交付和持续部署。 CI/CD 是解决集成新代码可能给开发和运营团队带来的问题（⼜名“集 成地狱”）的解决⽅案。</p><ul><li>CI/CD 中的“CD”指的是持续交付(Continuous Delivery)/持续部署<br />持续交付通常意味着开发⼈员对应⽤程序的更改会⾃动进⾏错误测试并 上传到存储库（如 GitHub 或容器注册表），然后运维团队可以将它们 部署到实时⽣产环境中。这是对开发团队和业务团队之间可⻅性和沟通 不佳问题的解决⽅案。为此，持续交付的⽬的是确保以最少的努⼒部署 新代码</li><li>CI/CD 中的“CI”<br />在现代应⽤程序开发中，⽬标是让多个开发⼈员同时开发同⼀个应⽤程 序的不同功能。但是，如果⼀个组织被设置为在某⼀天（称为“合并 ⽇”）将所有分⽀源代码合并在⼀起，那么最终的⼯作可能是乏味的、 ⼿动的和耗时的。这是因为当⼀个单独⼯作的开发⼈员对应⽤程序进⾏ 更改时，它可能会与其他开发⼈员同时进⾏的不同更改发⽣冲突。如果 每个开发⼈员都定制了他们⾃⼰的本地集成开发环境 (IDE)，⽽不是团 队就⼀个基于云的 IDE 达成⼀致，这个问题可能会更加复杂。 5<br />持续集成 (CI) 帮助开发⼈员更频繁地将他们的代码更改合并回共享分⽀ 或“主⼲”——有时甚⾄每天。合并开发⼈员对应⽤程序的更改后，将通 过⾃动构建应⽤程序并运⾏不同级别的⾃动化测试（通常是单元测试和 集成测试）来验证这些更改，以确保更改不会破坏应⽤程序。这意味着 测试从类和函数到构成整个应⽤程序的不同模块的所有内容。如果⾃动 化测试发现新代码和现有代码之间存在冲突，CI 可以更轻松地快速、频 繁地修复这些错误。</li></ul><h4 id="%E7%AC%AC3%E9%9B%86-docker%E5%AE%89%E8%A3%85" tabindex="-1">第3集  docker安装</h4><p>开发机-》gitlab-&gt;jenkins-&gt;生产服务器<br />80   8080</p><ul><li>准备两台服务器安装docker(jenkins-gitlab)</li></ul><p>27.129   130</p><pre><code class="language-">systemctl stop firewalldsystemctl stop firewalld.servicesystemctl disable firewalld.servicefirewall-cmd --zone=public --remove-port=80/tcp --permanent# 修改时区ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime# hwclock -w</code></pre><ul><li>安装docker</li></ul><ol><li>安装底层⼯具</li></ol><pre><code class="language-">sudo yum install -y yum-utils device-mapper-persistent-data lvm2</code></pre><ol start="2"><li>加⼊阿⾥云yum仓库提速docker下载过程</li></ol><pre><code class="language-">sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo</code></pre><ol start="3"><li>更新⼀下仓库的源信息</li></ol><pre><code class="language-">sudo yum makecache fast</code></pre><ol start="4"><li>⾃动安装下载Docker</li></ol><pre><code class="language-">sudo yum -y install docker-ce</code></pre><ol start="5"><li>启动Docker服务</li></ol><pre><code class="language-">sudo service docker start</code></pre><ol start="6"><li>验证docker是否启动成功</li></ol><pre><code class="language-">docker version</code></pre><ol start="7"><li>Aliyun加速镜像下载</li></ol><pre><code class="language-">https://cr.console.aliyun.com/cn-shanghai/instances/mirrors</code></pre><pre><code class="language-">sudo mkdir -p /etc/dockersudo tee /etc/docker/daemon.json &lt;&lt;-&#39;EOF&#39;{ &quot;registry-mirrors&quot;: [&quot;https://fskvstob.mirror.aliyuncs.com&quot;]}EOFsudo systemctl daemon-reloadsudo systemctl restart docker</code></pre><p>生产 安装jdk</p><ul><li>安装OpenJDK<strong>8</strong>+</li></ul><pre><code class="language-">yum -y install java-1.8.0-openjdk-devel.x86_64sudo cat &gt;&gt; /etc/profile &lt;&lt;-&#39;EOF&#39;export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdkexport JRE_HOME=$JAVA_HOME/jreexport CLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATHexport PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATHEOFsource /etc/profileecho $JAVA_HOME</code></pre><h4 id="%E7%AC%AC4%E9%9B%86-gitlab%E5%AE%89%E8%A3%85" tabindex="-1">第4集  gitlab安装</h4><p>docker服务器操作</p><ul><li>下载部署Gitlab容器（27.129）</li></ul><pre><code class="language-">    rm -rf /etc/gitlab    rm -rf /var/log/gitlab    rm -rf /var/opt/gitlabdocker rm -f gitlabmkdir -p /etc/gitlabmkdir -p /var/log/gitlabmkdir -p /var/opt/gitlabchmod -R 755 /etc/gitlabchmod -R 755 /var/log/gitlabchmod -R 755 /var/opt/gitlabdocker run --name gitlab \--hostname gitlab.example.com \--restart=always \-v /etc/gitlab:/etc/gitlab \-v /var/log/gitlab:/var/log/gitlab \-v /var/opt/gitlab:/var/opt/gitlab \-p 80:80 \-d gitlab/gitlab-ce</code></pre><p>查看gitlab 初始化账号密码</p><pre><code class="language-">docker logs -f gitlab </code></pre><p>但是你会发现gitlab日志一直在持续输出，不方便查看。</p><pre><code class="language-">sudo docker exec -it gitlab grep &#39;Password:&#39; /etc/gitlab/initial_root_password</code></pre><p><a href="http://192.168.27.129" target="_blank">http://192.168.27.129</a></p><p>⽤户头像-&gt;Preferences-&gt;Password修改初始密码</p><h4 id="%E7%AC%AC4%E9%9B%86-jenkins%E5%AE%89%E8%A3%85" tabindex="-1">第4集  Jenkins安装</h4><p><a href="https://www.jenkins.io/" target="_blank">https://www.jenkins.io/</a></p><ul><li>配置JDK&amp;Maven</li></ul><pre><code class="language-">cd /usr/localwget --no-check-certificate https://manongbiji.oss-cn-beijing.aliyuncs.com/ittailkshow/devops/download/jdk-8u341-linux-x64.tar.gzwget --no-check-certificate https://manongbiji.oss-cn-beijing.aliyuncs.com/ittailkshow/devops/download/apache-maven-3.8.6-bin.tar.gztar zxvf jdk-8u341-linux-x64.tar.gztar zxvf apache-maven-3.8.6-bin.tar.gzmv jdk1.8.0_341 jdkmv apache-maven-3.8.6 mavenrm -f jdk-8u341-linux-x64.tar.gzrm -f apache-maven-3.8.6-bin.tar.gzcd /usr/local/maven/conf# 使用别人配置好的mavenrm -f settings.xmlwget --no-check-certificate https://manongbiji.oss-cn-beijing.aliyuncs.com/ittailkshow/devops/download/settings.xml</code></pre><ul><li>部署Jenkins容器（27.131）</li></ul><pre><code class="language-">rm -rf /var/jenkins/docker rm -f jenkinsmkdir -p /var/jenkins/chmod -R 777 /var/jenkins/docker run --name jenkins \--restart=always \-v /var/jenkins/:/var/jenkins_home/ \-v /usr/local/jdk:/usr/local/jdk \-v /usr/local/maven:/usr/local/maven \-p 8080:8080 \-e JENKINS_UC=https://mirrors.cloud.tencent.com/jenkins/ \-e JENKINS_UC_DOWNLOAD=https://mirrors.cloud.tencent.com/jenkins/ \-d jenkins/jenkins:2.395docker logs -f jenkinsdocker exec -it jenkins bash</code></pre><ul><li><p>国内常⽤的Jenkins插件安装源</p><ul><li>tencent <a href="https://mirrors.cloud.tencent.com/jenkins/" target="_blank">https://mirrors.cloud.tencent.com/jenkins/</a></li><li>huawei <a href="https://mirrors.huaweicloud.com/jenkins/" target="_blank">https://mirrors.huaweicloud.com/jenkins/</a></li><li>tsinghua</li><li><a href="https://mirrors.tuna.tsinghua.edu.cn/jenkins/" target="_blank">https://mirrors.tuna.tsinghua.edu.cn/jenkins/</a></li><li>ustc <a href="https://mirrors.ustc.edu.cn/jenkins/" target="_blank">https://mirrors.ustc.edu.cn/jenkins/</a></li><li>bit <a href="http://mirror.bit.edu.cn/jenkins/" target="_blank">http://mirror.bit.edu.cn/jenkins/</a></li></ul></li><li><p><a href="http://192.168.27.131:8080/" target="_blank">http://192.168.27.131:8080/</a></p></li></ul><p>⾸⻚点击Manage Jenkins _&gt;管理插件</p><p>添加Git Parameter与Publish Over SSH 两款插件即可</p><h4 id="%E7%AC%AC5%E9%9B%86-jenkins%E2%BE%83%E5%8A%A8%E6%8B%89%E5%8F%96%E6%9E%84%E5%BB%BA%E4%BB%A3%E7%A0%81" tabindex="-1">第5集  Jenkins⾃动拉取构建代码</h4><p>首先将代码提交到gitlab(此步骤忽略)</p><ol><li><p>Jenkins新建任务myproject-ci，选择⾃由⻛格 -》源码管理-》git</p></li><li><p>构建：拉取完毕构建代码，选择执⾏Shell</p></li></ol><pre><code class="language-">sh /usr/local/maven/bin/mvn package</code></pre><ol start="3"><li>选择构建后操作：“Send build artifacts over SSH”向231服务器发布jar包并运⾏</li></ol><p>之前并没有配置⽬标服务器，点击主界⾯“系统配置”找到Public Over SSH，新增⼀个SSH Server</p><p>Name：Target-130</p><p>Hostname：192.168.27.130</p><p>Username：root</p><p>Remote Directory：/usr/local</p><p>Password: 123456</p><ol start="4"><li>回到myproject-ci的配置⾯板，找到构建后操作</li></ol><p>SSH Server：</p><p>name：Target-130</p><p>Source files：target/*.jar</p><p>Exec command</p><pre><code class="language-">pkill javanohup java -jar /usr/local/target/myproject.jar &amp; sleep 1 （一秒钟推出）</code></pre><p>一定记得点击高级，exec in pty</p><h4 id="%E7%AC%AC5%E9%9B%86-%E2%BE%83%E5%8A%A8%E6%9E%84%E5%BB%BAdocker%E9%95%9C%E5%83%8F" tabindex="-1">第5集 ⾃动构建Docker镜像</h4><ul><li>修改原有配置</li></ul><p>Dockerfile⽂件</p><pre><code class="language-">FROM openjdk:8-slimWORKDIR /usr/localCOPY myproject.jar .CMD java -jar myproject.jar</code></pre><p>回到Jenkins，找到构建后操作，删除Exec command所有内容</p><p>add transfer set：增加一个传输项</p><p>Source files：docker/*</p><p>Exec command</p><pre><code class="language-">echo 0</code></pre><p>点击运行发现生产服务器多了两个文件夹docker 和 target，现在将两个目录合并在一起</p><p>Remove prefix：target</p><p>Remove prefix：docker</p><ul><li>构建镜像(docker)<ul><li>Exec command</li></ul></li></ul><pre><code class="language-">docker build -t it/myproject:1.0 /usr/local/docker rm -f myprojectdocker run -d -p 80:80--name=myproject it/myproject:1.0</code></pre><p>需要pkill 掉之前的java进程</p><h4 id="%E7%AC%AC6%E9%9B%86-harbor%E5%AE%89%E8%A3%85" tabindex="-1">第6集 Harbor安装</h4><p>开发机-》gitlab-&gt;jenkins-&gt;Harbor-&gt;生产服务器</p><pre><code class="language-">cat &gt; /etc/sysctl.conf &lt;&lt;-&#39;EOF&#39;net.ipv4.ip_forward=1vm.max_map_count=655360EOFsysctl -psystemctl stop firewalld</code></pre><p>⾃动部署安装Docker-Compose</p><pre><code class="language-">不好下本地安装吧 sudo curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-&#96;uname -s&#96;-&#96;uname -m&#96; -o /usr/local/bin/docker-composesudo chmod +x /usr/local/bin/docker-composesudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-composedocker-compose --versionservice docker restart</code></pre><p>下载离线harbor安装包</p><p>下载链接：<a href="https://github.com/goharbor/harbor/releases" target="_blank">https://github.com/goharbor/harbor/releases</a></p><pre><code class="language-">cd /usr/local太大了，离线装吧 wget --no-check-certificate https://github.com/goharbor/harbor/releases/download/v1.10.14/harbor-offline-installer-v1.10.14.tgzwget https://ghproxy.com/https://github.com/goharbor/harbor/releases/download/v2.5.3/harbor-offline-installer-v2.5.3.tgztar xzvf harbor-offline-installer-v2.5.3.tgz </code></pre><p>修改Habor核⼼配置⽂件harbor.yml</p><p>hostname：192.168.27.132</p><p>注释掉https.*</p><p>默认密码：Harbor12345</p><p>安装Harbor</p><pre><code class="language-">./install</code></pre><p>Harbor由⼗多个容器组合构成，必须使⽤docker-compose才可以款速安装</p><p>卸载Harbor</p><pre><code class="language-">docker-compose down</code></pre><p><a href="http://192.168.27.132/" target="_blank">http://192.168.27.132/</a></p><p>⽤户名：admin</p><p>密码：Harbor12345</p><h4 id="%E7%AC%AC7%E9%9B%86-harbor%E6%96%B0%E5%BB%BA%E9%95%9C%E5%83%8F%E4%BB%93%E5%BA%93%E5%B9%B6%E6%8E%A8%E9%80%81" tabindex="-1">第7集  Harbor新建镜像仓库并推送</h4><p>Harbor新建镜像仓库</p><p>项⽬名称：public</p><p>访问级别：公开</p><p>在132Harbor服务器，修改daemon.json，增加harbor私有仓库地址，insecure-registries</p><pre><code class="language-">cat &gt; /etc/docker/daemon.json &lt;&lt;-&#39;EOF&#39;{ &quot;registry-mirrors&quot;: [&quot;https://fskvstob.mirror.aliyuncs.com&quot;], &quot;insecure-registries&quot;: [&quot;192.168.27.132:80&quot;]}EOFsystemctl daemon-reloadsystemctl restart docker</code></pre><p>生产服务器也同时添加上面的，进行测试</p><pre><code class="language-">docker imagesdocker tag b7909ddfe5d1 192.168.27.132:80/public/myproject:1.0 必须包含版本号（public仓库名称 myproject:1.0自定义）docker login -u admin -p Harbor12345 192.168.27.132:80docker push 192.168.27.132:80/public/myproject:1.0</code></pre><h4 id="%E7%AC%AC8%E9%9B%86-jenkins%E8%87%AA%E5%8A%A8%E5%AE%9E%E7%8E%B0ci%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90" tabindex="-1">第8集 Jenkins自动实现CI持续集成</h4><p>开发机-》gitlab-&gt;jenkins-&gt;Harbor-&gt;生产服务器</p><p>确保Harbor节点上登记了insecure-registries</p><pre><code class="language-">cat &gt; /etc/docker/daemon.json &lt;&lt;-&#39;EOF&#39;{ &quot;registry-mirrors&quot;: [&quot;https://fskvstob.mirror.aliyuncs.com&quot;], &quot;insecure-registries&quot;: [&quot;192.168.27.132:80&quot;]}EOFsystemctl daemon-reloadsystemctl restart docker</code></pre><pre><code class="language-">在系统设置中新增SSH Server： name：Harbor-132hostname：192.168.27.132username：root password：123456remote directory：/usr/local </code></pre><ul><li>在原有构建后操作生产服务器之前，新增Harbor节点操作</li></ul><p>向Harbor传输Jar⽂件 向Harbor节点传输Dockerfile，并构建、推送容器</p><pre><code class="language-">docker build -t 192.168.27.132:80/public/myproject:1.0 /usr/local/docker login -u admin -p Harbor12345 192.168.27.132:80docker push 192.168.27.132:80/public/myproject:1.0</code></pre><ul><li>立即构建测试</li><li>对原有的生产节点进⾏调整，只负责容器创建⼯作</li></ul><pre><code class="language-">docker rm -f myprojectdocker run -d -p 80:80 --name=myproject 192.168.27.132:80/public/myproject:1.0</code></pre><ul><li>再次重新构建，容器已经可以⾃动发布</li></ul><p>此处注意，生产节点也必须添加</p><pre><code class="language-">cat &gt; /etc/docker/daemon.json &lt;&lt;-&#39;EOF&#39;{ &quot;registry-mirrors&quot;: [&quot;https://fskvstob.mirror.aliyuncs.com&quot;], &quot;insecure-registries&quot;: [&quot;192.168.27.132:80&quot;]}EOFsystemctl daemon-reloadsystemctl restart docker</code></pre><h4 id="%E7%AC%AC9%E9%9B%86-jenkins%E5%8F%82%E6%95%B0%E5%8C%96%E6%9E%84%E5%BB%BA%E5%A4%9A%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83" tabindex="-1">第9集 Jenkins参数化构建多版本发布</h4><p>解决固定版本号问题</p><ul><li>添加git参数化设置</li></ul><p>名称：tag</p><p>描述：发布的版本号</p><p>默认值：orgin/main</p><ul><li>在构建部分(Build Steps)：增加构建步骤</li></ul><p>原本的package前新增Shell，现⾏checkout指定的版本，$tag引⽤选择的版本号</p><pre><code class="language-">git checkout $tag</code></pre><p>Harbor仓库Exec command，将所有1.0改为$tag进⾏引⽤</p><ul><li>在gitlab中</li></ul><p>点击repository-&gt;tag,进行打标签</p><p>开始构建</p><h4 id="%E7%AC%AC10%E9%9B%86-jenkins-pipeline%E6%B5%81%E6%B0%B4%E7%BA%BF%E4%BD%9C%E4%B8%9A" tabindex="-1">第10集 Jenkins Pipeline流水线作业</h4><p>Pipeline流⽔线提供了脚本化，按阶段步骤处理</p><p>配置过程</p><p>参照之前选择参数化构建</p><ul><li>先配置git参数化构建以及代码拉取</li></ul><pre><code class="language-">pipeline {    agent any    stages {        stage(&#39;Pull SourceCode&#39;){            steps{                这里取流水线代码生成checkout            }        }            }}</code></pre><ul><li>maven构建</li></ul><pre><code class="language-">        stage(&#39;Maven Build&#39;) {             steps {               这里取流水线代码生成 shell            }        }</code></pre><ul><li>将镜像推送到harbor</li></ul><pre><code class="language-">        stage(&#39;Publish Harbor Image&#39;) {             steps {               这里取流水线代码生成 sendbuild ssh            }        }</code></pre><ul><li>拉取镜像启动</li></ul><pre><code class="language-">     stage(&#39;Run Container&#39;) {             steps {               这里取流水线代码生成 sendbuild ssh            }        }</code></pre><p>完整脚本</p><pre><code class="language-">pipeline {    agent any    stages {        stage(&#39;Pull SourceCode&#39;){            steps{                checkout scmGit(branches: [[name: &#39;$tag&#39;]], extensions: [], userRemoteConfigs: [[credentialsId: &#39;5431ccf7-c145-454a-902a-86c9f0a31b22&#39;, url: &#39;http://192.168.27.129/root/cicd.git&#39;]])            }        }        stage(&#39;Maven Build&#39;) {             steps {               sh &#39;/usr/local/maven/bin/mvn package&#39;            }        }        stage(&#39;Publish Harbor Image&#39;) {             steps {    sshPublisher(publishers: [sshPublisherDesc(configName: &#39;Target-132&#39;, transfers: [sshTransfer(cleanRemote: false, excludes: &#39;&#39;, execCommand: &#39;&#39;, execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: &#39;[, ]+&#39;, remoteDirectory: &#39;&#39;, remoteDirectorySDF: false, removePrefix: &#39;target&#39;, sourceFiles: &#39;target/*.jar&#39;), sshTransfer(cleanRemote: false, excludes: &#39;&#39;, execCommand: &#39;&#39;&#39;docker build -t 192.168.27.132:80/public/myproject:$tag /usr/local/docker login -u admin -p Harbor12345 192.168.27.132:80docker push 192.168.27.132:80/public/myproject:$tag&#39;&#39;&#39;, execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: &#39;[, ]+&#39;, remoteDirectory: &#39;&#39;, remoteDirectorySDF: false, removePrefix: &#39;docker&#39;, sourceFiles: &#39;docker/*&#39;)], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])            }        }        stage(&#39;Run Container&#39;) {             steps {               sshPublisher(publishers: [sshPublisherDesc(configName: &#39;Target-130&#39;, transfers: [sshTransfer(cleanRemote: false, excludes: &#39;&#39;, execCommand: &#39;&#39;&#39;docker rm -f myprojectdocker run -d -p 80:80 --name=myproject 192.168.27.132:80/public/myproject:$tag&#39;&#39;&#39;, execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: &#39;[, ]+&#39;, remoteDirectory: &#39;&#39;, remoteDirectorySDF: false, removePrefix: &#39;&#39;, sourceFiles: &#39;&#39;)], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])            }        }            }}</code></pre><h4 id="%E7%AC%AC11%E9%9B%86-gitlab%E6%89%98%E7%AE%A1jenkinsfile" tabindex="-1">第11集 Gitlab托管Jenkinsfile</h4><p>Repository URL: <a href="http://192.168.27.129/root/cicd.git" target="_blank">http://192.168.27.129/root/cicd.git</a></p><p>在源码根路径下新增Jenkinsfile，内容与直接使⽤Script完全相同，然</p><p>后上传⾄master分⽀即可。</p><p>Jenkins Pipeline会先从Gitlab下载Jekinsfile，在读取脚本执⾏</p><h3 id="%E7%AC%AC%E4%BA%8C%E7%AB%A0-%E5%BC%95%E5%85%A5kubernetes" tabindex="-1">第二章 引入Kubernetes</h3><h4 id="%E7%AC%AC1%E9%9B%86-%E5%BC%95%E2%BC%8Akubernetes" tabindex="-1">第1集 引⼊Kubernetes</h4><p>开发机-》gitlab-&gt;jenkins-&gt;Harbor-&gt;k9s-&gt;生产服务器</p><p>随着系统可部署组件的数量增⻓，把它们都管理起来会变得越来越困难。需要⼀个更好的⽅式来部署和</p><p>管理这些组件，并⽀持基础设施的全球性伸缩，⾕歌可能是第⼀个意识到这⼀点的公司。⾕歌等全球少</p><p>数⼏个公司运⾏着成千上万的服务器，⽽且在如此海量规模下，不得不处理部署管理的问题。这推动着</p><p>他们找出解决⽅案使成千上万组件的管理变得有效且成本低廉。</p><p>Kubernetes是⼀个软件系统，它允许你在其上很容易地部署和管理容器化的应⽤。它依赖于Linux容器的</p><p>特性来运⾏异构应⽤，⽽⽆须知道这些应⽤的内部详情，也不需要⼿动将这些应⽤部署到每台机器。因</p><p>为这些应⽤运⾏在容器⾥，它们不会影响运⾏在同⼀台服务器上的其他应⽤，当你是为完全不同的组织</p><p>机构运⾏应⽤时，这就很关键了。这对于云供应商来说是⾄关重要的，因为它们在追求⾼硬件可⽤率的</p><p>同时也必须保障所承载应⽤的完全隔离。</p><h4 id="%E7%AC%AC2%E9%9B%86-%E5%BF%AB%E9%80%9F%E5%AE%89%E8%A3%85kubernetes" tabindex="-1">第2集 快速安装Kubernetes</h4><ul><li><p>利⽤Kubeode快速部署单节点K8S</p></li><li><p>下载2022-04-24版本</p></li><li><p>将压缩包上传⾄/usr/local⽬录</p></li><li><p>解压缩，并执⾏安装</p></li></ul><pre><code class="language-">tar xf k8s-2022-04-24.tarcd k8s-2022-04-24bash install.sh</code></pre><ul><li>单机部署</li></ul><p>选择第一个</p><p>安装成功，⽣成各种⽤户名密码</p><ul><li><p><a href="http://192.168.31.233:30080" target="_blank">http://192.168.31.233:30080</a></p><p>⽤户名：admin</p><p>密码：Kuboard123</p></li><li><p>添加集群</p></li></ul>]]>
                    </description>
                    <pubDate>Sat, 18 Mar 2023 18:22:09 CST</pubDate>
                </item>
    </channel>
</rss>