知了小站 - IT人的小站 https://izlzl.com/ 知了小站,分享开发经验,记录成长轨迹,帮助IT人士共同成长 总结国内加速拉取 Docker 镜像的几种方法 https://izlzl.com/archives/1749.html 2024-07-30T20:58:00+08:00 本文介绍多种方法加速Docker镜像拉取,包括利用Nginx、Cloudflare反向代理等方案,使国内服务器更高效获取海外Docker镜像。目前仍可用的镜像(随时可能失效)sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": [ "https://docker.m.daocloud.io", "https://huecker.io", "https://dockerhub.timeweb.cloud", "https://noohub.ru" ] } EOF sudo systemctl daemon-reload sudo systemctl restart docker使用 Nginx 反向代理需要有一台国外服务器, 按下面添加 Nginx 配置即可,配置完成后,在 /etc/docker/daemon.json 中修改成你的域名,具体参考上方配置,然后重启 dockerserver { listen 443 ssl; server_name 域名; ssl_certificate 证书地址; ssl_certificate_key 密钥地址; proxy_ssl_server_name on; # 启用SNI ssl_session_timeout 24h; ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256'; ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; location / { proxy_pass https://registry-1.docker.io; # Docker Hub 的官方镜像仓库 proxy_set_header Host registry-1.docker.io; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 关闭缓存 proxy_buffering off; # 转发认证相关的头部 proxy_set_header Authorization $http_authorization; proxy_pass_header Authorization; # 对 upstream 状态码检查,实现 error_page 错误重定向 proxy_intercept_errors on; # error_page 指令默认只检查了第一次后端返回的状态码,开启后可以跟随多次重定向。 recursive_error_pages on; # 根据状态码执行对应操作,以下为301、302、307状态码都会触发 #error_page 301 302 307 = @handle_redirect; error_page 429 = @handle_too_many_requests; } #处理重定向 location @handle_redirect { resolver 1.1.1.1; set $saved_redirect_location '$upstream_http_location'; proxy_pass $saved_redirect_location; } # 处理429错误 location @handle_too_many_requests { proxy_set_header Host 替换为在CloudFlare Worker设置的域名; # 替换为另一个服务器的地址 proxy_pass http://替换为在CloudFlare Worker设置的域名; proxy_set_header Host $http_host; } }使用 Cloudflare 反向代理登录到 Cloudflare 控制台, 新建 worker, 在 worker.js 文件中输入以下代码, 注意需要自行修改代码中的域名'use strict' const hub_host = 'registry-1.docker.io' const auth_url = 'https://auth.docker.io' const workers_url = 'https://你的域名' const workers_host = '你的域名' const home_page_url = 'https://qninq.cn/file/html/dockerproxy.html' /** @type {RequestInit} */ const PREFLIGHT_INIT = { status: 204, headers: new Headers({ 'access-control-allow-origin': '*', 'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS', 'access-control-max-age': '1728000', }), } /** * @param {any} body * @param {number} status * @param {Object<string, string>} headers */ function makeRes(body, status = 200, headers = {}) { headers['access-control-allow-origin'] = '*' return new Response(body, {status, headers}) } /** * @param {string} urlStr */ function newUrl(urlStr) { try { return new URL(urlStr) } catch (err) { return null } } addEventListener('fetch', e => { const ret = fetchHandler(e) .catch(err => makeRes('cfworker error:\n' + err.stack, 502)) e.respondWith(ret) }) /** * @param {FetchEvent} e */ async function fetchHandler(e) { const getReqHeader = (key) => e.request.headers.get(key); let url = new URL(e.request.url); if (url.pathname === '/') { // Fetch and return the home page HTML content with replacement let response = await fetch(home_page_url); let text = await response.text(); text = text.replace(/{workers_host}/g, workers_host); return new Response(text, { status: response.status, headers: response.headers }); } if (url.pathname === '/token') { let token_parameter = { headers: { 'Host': 'auth.docker.io', 'User-Agent': getReqHeader("User-Agent"), 'Accept': getReqHeader("Accept"), 'Accept-Language': getReqHeader("Accept-Language"), 'Accept-Encoding': getReqHeader("Accept-Encoding"), 'Connection': 'keep-alive', 'Cache-Control': 'max-age=0' } }; let token_url = auth_url + url.pathname + url.search return fetch(new Request(token_url, e.request), token_parameter) } url.hostname = hub_host; let parameter = { headers: { 'Host': hub_host, 'User-Agent': getReqHeader("User-Agent"), 'Accept': getReqHeader("Accept"), 'Accept-Language': getReqHeader("Accept-Language"), 'Accept-Encoding': getReqHeader("Accept-Encoding"), 'Connection': 'keep-alive', 'Cache-Control': 'max-age=0' }, cacheTtl: 3600 }; if (e.request.headers.has("Authorization")) { parameter.headers.Authorization = getReqHeader("Authorization"); } let original_response = await fetch(new Request(url, e.request), parameter) let original_response_clone = original_response.clone(); let original_text = original_response_clone.body; let response_headers = original_response.headers; let new_response_headers = new Headers(response_headers); let status = original_response.status; if (new_response_headers.get("Www-Authenticate")) { let auth = new_response_headers.get("Www-Authenticate"); let re = new RegExp(auth_url, 'g'); new_response_headers.set("Www-Authenticate", response_headers.get("Www-Authenticate").replace(re, workers_url)); } if (new_response_headers.get("Location")) { return httpHandler(e.request, new_response_headers.get("Location")) } let response = new Response(original_text, { status, headers: new_response_headers }) return response; } /** * @param {Request} req * @param {string} pathname */ function httpHandler(req, pathname) { const reqHdrRaw = req.headers // preflight if (req.method === 'OPTIONS' && reqHdrRaw.has('access-control-request-headers') ) { return new Response(null, PREFLIGHT_INIT) } let rawLen = '' const reqHdrNew = new Headers(reqHdrRaw) const refer = reqHdrNew.get('referer') let urlStr = pathname const urlObj = newUrl(urlStr) /** @type {RequestInit} */ const reqInit = { method: req.method, headers: reqHdrNew, redirect: 'follow', body: req.body } return proxy(urlObj, reqInit, rawLen, 0) } /** * * @param {URL} urlObj * @param {RequestInit} reqInit */ async function proxy(urlObj, reqInit, rawLen) { const res = await fetch(urlObj.href, reqInit) const resHdrOld = res.headers const resHdrNew = new Headers(resHdrOld) // verify if (rawLen) { const newLen = resHdrOld.get('content-length') || '' const badLen = (rawLen !== newLen) if (badLen) { return makeRes(res.body, 400, { '--error': `bad len: ${newLen}, except: ${rawLen}`, 'access-control-expose-headers': '--error', }) } } const status = res.status resHdrNew.set('access-control-expose-headers', '*') resHdrNew.set('access-control-allow-origin', '*') resHdrNew.set('Cache-Control', 'max-age=1500') resHdrNew.delete('content-security-policy') resHdrNew.delete('content-security-policy-report-only') resHdrNew.delete('clear-site-data') return new Response(res.body, { status, headers: resHdrNew }) }部署完成后,点击设置->触发器->添加自定义域,绑定自己的域名即可。配置完成后,在 /etc/docker/daemon.json 中修改成你的域名,具体参考上方配置,然后重启 docker。原文地址:https://blog.csun.site/posts/b3f62daf.html Java 模拟死锁以及如何避免死锁 https://izlzl.com/archives/1747.html 2024-02-29T22:30:00+08:00 模拟死锁死锁是多线程编程中常见的问题,它发生在两个或多个线程相互等待对方释放资源的情况下。以下是一个简单的Java死锁模拟示例:public class DeadlockExample { public static void main(String[] args) { // 创建两个共享资源 final Object resource1 = new Object(); final Object resource2 = new Object(); // 线程1尝试获取资源1,然后资源2 Thread thread1 = new Thread(() -> { synchronized (resource1) { System.out.println("Thread 1: Locked resource 1"); try { // 为了增加死锁的可能性,线程1休眠一段时间 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (resource2) { System.out.println("Thread 1: Locked resource 2"); } } }); // 线程2尝试获取资源2,然后资源1 Thread thread2 = new Thread(() -> { synchronized (resource2) { System.out.println("Thread 2: Locked resource 2"); try { // 为了增加死锁的可能性,线程2休眠一段时间 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (resource1) { System.out.println("Thread 2: Locked resource 1"); } } }); // 启动线程1和线程2 thread1.start(); thread2.start(); } }在这个例子中,两个线程分别尝试获取两个共享资源,但它们的获取顺序相反。如果这两个线程在不同的时刻开始执行,可能不会发生死锁,但如果它们同时开始执行,就有可能因为资源争夺而导致死锁。如何避免死锁避免死锁是多线程编程中非常重要的一个方面,以下是一些常见的避免死锁的策略:锁的顺序:定义一个全局的锁获取顺序,然后在所有线程中都按照相同的顺序获取锁。这样可以避免不同线程以不同的顺序获取锁而导致死锁。锁的超时机制:在获取锁的时候,设置一个超时机制。如果某个线程在一定时间内无法获取到所需的锁,就释放已经获取的锁,并重新尝试获取锁,或者执行其他逻辑来避免死锁。使用 tryLock() 方法:在Java中,Lock 接口提供了 tryLock() 方法,它可以尝试获取锁,但不会一直等待。通过使用这个方法,你可以在获取锁失败时执行一些逻辑,而不是一直等待锁。锁的粒度:设计时考虑锁的粒度。如果锁的范围太大,竞争会增加,容易导致死锁。将锁的范围缩小到最小必要范围,可以减少死锁的概率。使用事务:在数据库操作中,使用事务可以避免某些类型的死锁。数据库事务通常会自动处理资源的锁定和释放。死锁检测和处理:一些系统提供死锁检测机制,可以检测到死锁的发生,并采取一些措施,例如自动释放锁或终止某些线程。避免循环等待:设定一个全局的资源获取顺序,并要求所有线程按照相同的顺序获取资源,以避免循环等待的情况。使用高级同步工具:Java提供了一些高级的同步工具,如 java.util.concurrent 包中的 ReentrantLock、Semaphore 等,它们提供更灵活的控制和避免死锁的机制。避免死锁是一个复杂的问题,需要在设计和实现阶段考虑。以上策略可以根据具体情况进行选择和组合,以提高多线程程序的稳定性。 Debian 10/11 安装最新版 Nginx https://izlzl.com/archives/1740.html 2023-06-05T14:16:00+08:00 Debian 10 安装最新版的Nginx要在Debian 10 上安装最新版的Nginx,可以使用以下步骤1、更新系统sudo apt update sudo apt upgrade2、添加 Nginx 的存储库密钥wget http://nginx.org/keys/nginx_signing.key sudo apt-key add nginx_signing.key3、添加 Nginx 的存储库到apt源列表中echo "deb http://nginx.org/packages/mainline/debian/ $(lsb_release -cs) nginx" | sudo tee /etc/apt/sources.list.d/nginx.list4、更新存储库并安装 Nginxsudo apt update sudo apt install nginx # 启动 nginx sudo systemctl start nginx # 设置自启 sudo systemctl enable nginxDebian 11 安装最新版的Nginx在Debian 11 上安装最新版的Nginx,可以使用以下步骤1、更新系统sudo apt update sudo apt upgrade # 安装 Nginx 的依赖项 sudo apt install curl gnupg2 ca-certificates lsb-release2、添加 Nginx 的存储库密钥curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo gpg --dearmor -o /usr/share/keyrings/nginx-archive-keyring.gpg3、添加 Nginx 的存储库到apt源列表中echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/mainline/debian $(lsb_release -cs) nginx" | sudo tee /etc/apt/sources.list.d/nginx.list4、更新存储库并安装 Nginxsudo apt update sudo apt install nginx # 启动 nginx sudo systemctl start nginx # 设置自启 sudo systemctl enable nginx Jpa 持久层中使用自定义对象接收数据 https://izlzl.com/archives/1739.html 2023-05-12T16:32:00+08:00 在 JPA 持久层中,可以自定义接收数据的对象。这通常用于查询操作,其中查询结果不完全匹配现有的实体类,或者需要仅返回某些字段的结果。以下示例,展示如何在 JPA 持久层中自定义接收数据的对象假设有一个名为 Person 的实体类,包含 id、name 和 age 字段:@Entity @Table(name = "person") public class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private int age; // 省略构造函数、getter 和 setter 方法 }现在我们只想查询人员的姓名和年龄,并将结果封装到自定义的数据对象 PersonInfo 中:public class PersonInfo { private String name; private int age; // 省略构造函数、getter 和 setter 方法 }第一种方式@Repository public class PersonRepository { @PersistenceContext private EntityManager entityManager; public List<PersonInfo> getPersonInfo() { String query = "SELECT new com.example.PersonInfo(p.name, p.age) FROM Person p"; TypedQuery<PersonInfo> typedQuery = entityManager.createQuery(query, PersonInfo.class); return typedQuery.getResultList(); } }在上面的代码中,我们使用 SELECT new 关键字创建了一个 PersonInfo 对象,并将查询结果映射到该对象。通过使用构造函数,可以选择性地指定要接收的字段。第二种方式使用 Spring Data JPA 的 Repository 接口@Repository public interface PersonRepository extends JpaRepository<Person, Long> { @Query("SELECT new com.example.PersonInfo(p.name, p.age) FROM Person p") List<PersonInfo> getPersonInfo(); }在上面的示例中,我们使用了 @Query 注解,并指定了一个自定义的查询语句。在查询语句中,我们使用了 new 关键字创建了一个 PersonInfo 对象,并将查询结果映射到该对象。注意点在查询语句中,com.example.PersonInfo 是 PersonInfo 类的完全限定名,确保使用正确的包名,确保创建了对应的构造方法。 debian11 系统增加开机自启脚本 https://izlzl.com/archives/1736.html 2023-04-21T10:30:00+08:00 在 debian11 系统增加开机自启脚本时发现 /etc 目录里面没有 rc.local 文件,导致无法添加自启动脚本,这里记录下 debian11 增加开机自启脚本的全过程。修改 rc-local.service 文件cd /lib/systemd/system添加下面内容到 rc-local.service 文件最后[Install] WantedBy=multi-user.target创建 /etc/rc.local 文件创建 /etc/rc.local 文件,并写入下面内容#!/bin/sh -e # 在这里输入需要自启的脚本 exit 0创建完成后需要给其赋予运行权限sudo chmod +x /etc/rc.local启动 rc-local 服务即可sudo systemctl enable rc-local # 启用 sudo systemctl start rc-local.service # 开始运行 sudo systemctl status rc-local.service # 查看状态 使用 qshell 工具上传文件夹到七牛云 https://izlzl.com/archives/1709.html 2023-03-08T15:32:00+08:00 qshell 是利用七牛文档上公开的API实现的一个方便开发者测试和使用七牛API服务的命令行工具。下载地址: 直达 ,根据自己系统平台下载即可。使用教程下载后,随意放到哪都行。1、赋予qshell执行权限我这里是Linux系统,默认放到了 /root 目录,进入 /root 目录后赋予 qshell 执行权限chmod +x qshell2、配置七牛云账户需要鉴权的命令都需要依赖七牛账号下的 AccessKey 和 SecretKey, 点我直达 ,拿到 AccessKey 和 SecretKey 输入下面的命令./qshell account ak sk name此处操作后在当前用户主目录中生成 qshell 目录:ls ~/.qshell/ account.json3、上传同步文件夹./qshell qupload2 --src-dir=需要上传的文件夹 --bucket=对象存储桶的名称更多使用方式:https://developer.qiniu.com/kodo/1302/qshell Java 给 OkHttpClient 添加 Socks 代理 https://izlzl.com/archives/1708.html 2023-03-06T13:39:00+08:00 OkHttpClient 我使用的并不多,这里记录下怎么给 OkHttpClient 添加代理,首先还是添加 OkHttpClient 依赖<dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.10.0</version> </dependency>API接入package demo; import okhttp3.OkHttpClient; import okhttp3.Request; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Proxy; class ApiProxyJava { public static void main(String[] args) throws IOException { testHttpWithOkHttp(); testSocks5WithOkHttp(); } public static void testHttpWithOkHttp() throws IOException { String url = "https://www.google.com/"; Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(IP, PORT)); OkHttpClient client = new OkHttpClient().newBuilder().proxy(proxy).build(); Request request = new Request.Builder().url(url).build(); okhttp3.Response response = client.newCall(request).execute(); String responseString = response.body().string(); System.out.println(responseString); response.close(); } public static void testSocks5WithOkHttp() throws IOException { String url = "https://www.google.com/"; Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(IP, PORT)); OkHttpClient client = new OkHttpClient().newBuilder().proxy(proxy).build(); Request request = new Request.Builder().url(url).build(); okhttp3.Response response = client.newCall(request).execute(); String responseString = response.body().string(); System.out.println(responseString); response.close(); }账密接入package demo; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import java.io.IOException; import java.net.InetSocketAddress; import java.net.PasswordAuthentication; import java.net.Proxy; /** * compile 'com.squareup.okhttp3:okhttp:3.10.0' */ class AutProxyJava { public static void main(String[] args) throws IOException { testWithOkHttp(); testSocks5WithOkHttp(); } public static void testWithOkHttp() throws IOException { String url = "https://www.google.com/"; String gateIp = "" int gatePort = 1000 Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(gateIp, gatePort)); OkHttpClient client = new OkHttpClient().newBuilder().proxy(proxy).proxyAuthenticator((route, response) -> { String credential = Credentials.basic("账户", "密码"); return response.request().newBuilder() .header("Proxy-Authorization", credential) .build(); }).build(); Request request = new Request.Builder().url(url).build(); okhttp3.Response response = client.newCall(request).execute(); String responseString = response.body().string(); System.out.println(responseString); response.close(); } public static void testSocks5WithOkHttp() throws IOException { String url = "https://www.google.com/"; String gateIp = "" int gatePort = 2000 Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(gateIp, gatePort)); java.net.Authenticator.setDefault(new java.net.Authenticator() { private PasswordAuthentication authentication = new PasswordAuthentication("账户", "密码".toCharArray()); @Override protected PasswordAuthentication getPasswordAuthentication() { return authentication; } }); OkHttpClient client = new OkHttpClient().newBuilder().proxy(proxy).build(); Request request = new Request.Builder().url(url).build(); okhttp3.Response response = client.newCall(request).execute(); String responseString = response.body().string(); System.out.println(responseString); response.close(); } }原文地址:https://admin.rola-ip.co/v_manual.html#/api_java Docker容器 - 数据打包迁移全过程记录 https://izlzl.com/archives/1704.html 2023-02-23T18:09:00+08:00 记录下Docker容器打包迁移的一些命令:# 确认要迁移容器的名称 docker ps -a # 打包容器为新的镜像 docker commit 容器名 镜像名 # 把镜像保存为tar文件 docker save 镜像名 >文件名称.tar # 将文件传输到另一台服务器,用SCP命令 scp -P 22 /文件路径/文件名称.tar root@接收文件的服务器的IP:/存放文件的目录 # 恢复镜像 docker load < 文件名称.tar恢复镜像后,用同样的方法创建容器即可。 Java 开发之 BigDecimal 用法细节详解 https://izlzl.com/archives/1700.html 2022-12-01T10:03:00+08:00 一、BigDecimal 概述​ Java 在 java.math 包中提供的 API 类 BigDecimal,用来对超过 16 位有效位的数进行精确的运算。双精度浮点型变量 double 可以处理 16 位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理。一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用 Float 和 Double 处理,但是 Double.valueOf(String) 和 Float.valueOf(String) 会丢失精度。所以开发中,如果我们需要精确计算的结果,则必须使用 BigDecimal 类来操作。​BigDecimal所创建的是对象,故我们不能使用传统的 +、-、*、/ 等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是 BigDecimal 的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。二、BigDecimal 常用构造函数2.1、常用构造函数// 创建一个具有参数所指定整数值的对象 BigDecimal(int) // 创建一个具有参数所指定双精度值的对象 BigDecimal(double) // 创建一个具有参数所指定长整数值的对象 BigDecimal(long) // 创建一个具有参数所指定以字符串表示的数值的对象 BigDecimal(String)2.2、使用问题分析使用示例:BigDecimal a =new BigDecimal(0.1); System.out.println("a values is:"+a); System.out.println("====================="); BigDecimal b =new BigDecimal("0.1"); System.out.println("b values is:"+b);结果示例:a values is:0.1000000000000000055511151231257827021181583404541015625 ===================== b values is:0.1原因分析:1)参数类型为 double 的构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 newBigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。2)String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言, 通常建议优先使用String构造方法。3)当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。三、BigDecimal 常用方法详解3.1、常用方法// BigDecimal 对象中的值相加,返回 BigDecimal 对象 add(BigDecimal) // BigDecimal 对象中的值相减,返回 BigDecimal 对象 subtract(BigDecimal) // BigDecimal 对象中的值相乘,返回 BigDecimal 对象 multiply(BigDecimal) // BigDecimal 对象中的值相除,返回 BigDecimal 对象 divide(BigDecimal) // 将 BigDecimal 对象中的值转换成字符串 toString() // 将 BigDecimal 对象中的值转换成双精度数 doubleValue() // 将 BigDecimal 对象中的值转换成单精度数 floatValue() // 将 BigDecimal 对象中的值转换成长整数 longValue() // 将 BigDecimal 对象中的值转换成整数 intValue()3.2、BigDecimal大小比较java 中对 BigDecimal 比较大小一般用的是 bigdemical 的 compareTo 方法int a = bigdemical.compareTo(bigdemical2)返回结果分析:a = -1,表示bigdemical小于bigdemical2; a = 0,表示bigdemical等于bigdemical2; a = 1,表示bigdemical大于bigdemical2;举例:a大于等于bnew bigdemica(a).compareTo(new bigdemical(b)) >= 0四、BigDecimal 格式化由于 NumberFormat 类的 format() 方法可以使用 BigDecimal 对象作为其参数,可以利用 BigDecimal 对超出 16 位有效数字的货币值,百分值,以及一般数值进行格式化控制。以利用 BigDecimal 对货币和百分比格式化为例。首先,创建 BigDecimal 对象,进行 BigDecimal 的算术运算后,分别建立对货币和百分比格式化的引用,最后利用 BigDecimal 对象作为 format() 方法的参数,输出其格式化的货币值和百分比。NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用 NumberFormat percent = NumberFormat.getPercentInstance(); //建立百分比格式化引用 percent.setMaximumFractionDigits(3); //百分比小数点最多3位 BigDecimal loanAmount = new BigDecimal("15000.48"); //贷款金额 BigDecimal interestRate = new BigDecimal("0.008"); //利率 BigDecimal interest = loanAmount.multiply(interestRate); //相乘 System.out.println("贷款金额:\t" + currency.format(loanAmount)); System.out.println("利率:\t" + percent.format(interestRate)); System.out.println("利息:\t" + currency.format(interest)); 结果:贷款金额: ¥15,000.48 利率: 0.8% 利息: ¥120.00BigDecimal 格式化保留两位小数,不足则补 0:public class NumberFormat { public static void main(String[] s){ System.out.println(formatToNumber(new BigDecimal("3.435"))); System.out.println(formatToNumber(new BigDecimal(0))); System.out.println(formatToNumber(new BigDecimal("0.00"))); System.out.println(formatToNumber(new BigDecimal("0.001"))); System.out.println(formatToNumber(new BigDecimal("0.006"))); System.out.println(formatToNumber(new BigDecimal("0.206"))); } /** * @desc 1.0~1之间的BigDecimal小数,格式化后失去前面的0,则前面直接加上0。 * 2.传入的参数等于0,则直接返回字符串"0.00" * 3.大于1的小数,直接格式化返回字符串 * @param obj传入的小数 * @return */ public static String formatToNumber(BigDecimal obj) { DecimalFormat df = new DecimalFormat("#.00"); if(obj.compareTo(BigDecimal.ZERO)==0) { return "0.00"; }else if(obj.compareTo(BigDecimal.ZERO)>0&&obj.compareTo(new BigDecimal(1))<0){ return "0"+df.format(obj).toString(); }else { return df.format(obj).toString(); } } }结果为:3.44 0.00 0.00 0.00 0.01 0.21五、BigDecimal常见异常5.1、除法的时候出现异常java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result原因分析:​通过BigDecimal的divide方法进行除法时当不整除,出现无限循环小数时,就会抛异常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.解决方法:​divide 方法设置精确的小数点,如:divide(xxxxx,2)六、BigDecimal总结6.1、总结在需要精确的小数计算时再使用 BigDecimal,BigDecimal 的性能比 double 和 float 差,在处理庞大,复杂的运算时尤为明显。故一般精度的计算没必要使用 BigDecimal。尽量使用参数类型为 String 的构造函数。BigDecimal 都是不可变的(immutable)的, 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。6.2、工具类推荐package com.vivo.ars.util; import java.math.BigDecimal; /** * 用于高精确处理常用的数学运算 */ public class ArithmeticUtils { //默认除法运算精度 private static final int DEF_DIV_SCALE = 10; /** * 提供精确的加法运算 * * @param v1 被加数 * @param v2 加数 * @return 两个参数的和 */ public static double add(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.add(b2).doubleValue(); } /** * 提供精确的加法运算 * * @param v1 被加数 * @param v2 加数 * @return 两个参数的和 */ public static BigDecimal add(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.add(b2); } /** * 提供精确的加法运算 * * @param v1 被加数 * @param v2 加数 * @param scale 保留scale 位小数 * @return 两个参数的和 */ public static String add(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 提供精确的减法运算 * * @param v1 被减数 * @param v2 减数 * @return 两个参数的差 */ public static double sub(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.subtract(b2).doubleValue(); } /** * 提供精确的减法运算。 * * @param v1 被减数 * @param v2 减数 * @return 两个参数的差 */ public static BigDecimal sub(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.subtract(b2); } /** * 提供精确的减法运算 * * @param v1 被减数 * @param v2 减数 * @param scale 保留scale 位小数 * @return 两个参数的差 */ public static String sub(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 提供精确的乘法运算 * * @param v1 被乘数 * @param v2 乘数 * @return 两个参数的积 */ public static double mul(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.multiply(b2).doubleValue(); } /** * 提供精确的乘法运算 * * @param v1 被乘数 * @param v2 乘数 * @return 两个参数的积 */ public static BigDecimal mul(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.multiply(b2); } /** * 提供精确的乘法运算 * * @param v1 被乘数 * @param v2 乘数 * @param scale 保留scale 位小数 * @return 两个参数的积 */ public static double mul(double v1, double v2, int scale) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return round(b1.multiply(b2).doubleValue(), scale); } /** * 提供精确的乘法运算 * * @param v1 被乘数 * @param v2 乘数 * @param scale 保留scale 位小数 * @return 两个参数的积 */ public static String mul(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 * 小数点以后10位,以后的数字四舍五入 * * @param v1 被除数 * @param v2 除数 * @return 两个参数的商 */ public static double div(double v1, double v2) { return div(v1, v2, DEF_DIV_SCALE); } /** * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 * 定精度,以后的数字四舍五入 * * @param v1 被除数 * @param v2 除数 * @param scale 表示表示需要精确到小数点以后几位。 * @return 两个参数的商 */ public static double div(double v1, double v2, int scale) { if (scale < 0) { throw new IllegalArgumentException("The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 * 定精度,以后的数字四舍五入 * * @param v1 被除数 * @param v2 除数 * @param scale 表示需要精确到小数点以后几位 * @return 两个参数的商 */ public static String div(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException("The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v1); return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 提供精确的小数位四舍五入处理 * * @param v 需要四舍五入的数字 * @param scale 小数点后保留几位 * @return 四舍五入后的结果 */ public static double round(double v, int scale) { if (scale < 0) { throw new IllegalArgumentException("The scale must be a positive integer or zero"); } BigDecimal b = new BigDecimal(Double.toString(v)); return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 提供精确的小数位四舍五入处理 * * @param v 需要四舍五入的数字 * @param scale 小数点后保留几位 * @return 四舍五入后的结果 */ public static String round(String v, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b = new BigDecimal(v); return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 取余数 * * @param v1 被除数 * @param v2 除数 * @param scale 小数点后保留几位 * @return 余数 */ public static String remainder(String v1, String v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); } /** * 取余数 BigDecimal * * @param v1 被除数 * @param v2 除数 * @param scale 小数点后保留几位 * @return 余数 */ public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP); } /** * 比较大小 * * @param v1 被比较数 * @param v2 比较数 * @return 如果v1 大于v2 则 返回true 否则false */ public static boolean compare(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); int bj = b1.compareTo(b2); boolean res; if (bj > 0) res = true; else res = false; return res; } }原文地址:https://www.cnblogs.com/zhangyinhua/p/11545305.html 记 Docker 运行 Logstash out of memory 问题 https://izlzl.com/archives/1667.html 2022-08-06T15:50:00+08:00 最近在 Docker 容器上搭建 Zookeeper+Kafka+Logstash+Elasticsearch+Kibana 日志分析系统,在运行 Logstash 和 Elasticsearch 时遇到了如下错误:library initialization failed - unable to allocate file descriptor table - out of memorylibrary initialization failed - unable to allocate file descriptor table - out of memory在这里记录下解决方案。解决方案通过重写 Docker 的 ExecStart 的参数解决sudo systemctl edit docker进入后,添加或者修改对应参数[Service] ExecStart= ExecStart=/usr/bin/dockerd --default-ulimit nofile=65536:65536 -H fd://最后重启 Dockersudo systemctl daemon-reload sudo systemctl restart docker参考:https://stackoverflow.com/questions/68776387/docker-library-initialization-failed-unable-to-allocate-file-descriptor-tabl