0%

Rust变量与数据类型

Rust 是一种静态类型的语言。 Rust 中的每个值都是某种数据类型。 编译器可以根据分配给它的值自动推断变量的数据类型。

let

使用let关键字声明变量

1
2
3
4
5
6
7
8
9
10
11
12
13
fn main() {
println!("Rust基础语法!");
let str_ = "String"; // string 类型
let f_ = 1.1; // float 类型
let bool_ = true; // boolean 类型
let char_ = 'a'; // unicode character
println!("string {}",str_);
println!("float {}",f_);
println!("boolean {}",bool_);
println!("icon {}",char_);


}
  • Console
1
2
3
4
5
Rust基础语法!
string String
float 1.1
boolean true
icon a
阅读全文 »

学习下Rust

为什么学习Rust

在知乎上看到一个人对Rust的评论。

首先,Rust 是有点反人类,否则不会一直都不火。然后,Rust 之所以反人类,是因为人类这玩意既愚蠢,又自大,破事还贼多。 你看 C++ 就很相信人类,它要求人类自己把自己 new 出来的东西给 delete 掉。 C++:“这点小事我相信你可以的!” 人类:“没问题!包在我身上!” 然后呢,内存泄漏、double free、野指针满世界飘…… C++:“……”

Java 选择不相信人类,但替人类把事办好。 Java:“别动,让我来,我有gc!” 人类:“你怎么做事这么慢呀?你怎么还 stop the world 了呀?你是不是不爱我了呀?” Java:“……”

Rust 发现唯一的办法就是既不相信人类,也不惯着人类。 Rust:“按老子说的做,不做就不编译!” 人类:“你反人类!” Rust:“滚!”

  • C/C++ 完全相信而且惯着程序员,让大家自行管理内存,所以可以编写很自由的代码,但一个不小心就会造成内存泄漏等问题导致程序崩溃。
  • Java/Golang 完全不相信程序员,但也惯着程序员。所有的内存生命周期都由 JVM 运行时统一管理。 在绝大部分场景下,你可以非常自由的写代码,而且不用关心内存到底是什么情况。 内存使用有问题的时候,我们可以通过 JVM 来信息相关的分析诊断和调整。
  • Rust 语言选择既不相信程序员,也不惯着程序员。 让你在写代码的时候,必须清楚明白的用 Rust 的规则管理好你的变量,好让机器能明白高效地分析和管理内存。 但是这样会导致代码不利于人的理解,写代码很不自由,学习成本也很高。
阅读全文 »

分布式系统

开发了几年的分布式系统,对其原理模模糊糊。大量的时间在研究场景下解决方案。并未深入总结过。本文梳理个人对分布式的认知理解。

  • 在没有系统学习分布式相关知识的情况,通常选择针对局部场景需求进行认知下局部最优解决。处理问题也没有十足的把握,在系统稳定性上只能通过case by case的方式处理。

  • 解决单机性能瓶颈。

  • 解决数据增加运维成本。

阅读全文 »

背景

权限管理是一个几乎所有大中型 B 端系统都会涉及的重要组成部分,其目的是对整个系统进行权限控制,避免造成误操作及数据泄露等风险问题。

权限与权限管理

名词定义

权限相关的基本概念:

  • 权限:用户可操作行为的最小单位。
  • 用户:每个用户都有唯一标识,并被授予一个或多个角色。
  • 角色:由不同的权限组合而成,最终分配给具体用户。
  • 权限管理:控制用户的权限,只能访问授权内容。

模型选择

  • ACL(Access Control List):基于用户级别的权限控制。
    • 将系统的各种权限直接授予具体的用户。抽象来说,为每个用户维护了单独的权限列表,当需要分配权限、收回权限时,需要修改对应用户的权限信息。
  • RBAC(Role Base Access Control):基于角色级别的权限控制。
    • 与 ACL 对比,RBAC不用给用户单个分配权限,权限与用户之前通过角色关联。通过给不同的角色分配不同的权限,只需要将用户指向对应的角色就会有对应的权限。分配权限、收回权限只需要通过修改用户的角色即可。
  • ABAC(Attribute Base Access Control):基于属性级别的权限控制。
    • 不同于常见的将用户通过某种方式直接关联到权限的方式,ABAC 是通过动态计算一个或一组属性来是否满足某种条件来进行权限判断。属性一般分为四类:用户属性(自然人属性,如年龄、性别等),环境属性(物理环境,如时间、地点、气候),操作属性(读、写)和对象属性(操作对象,如资金、某张图片、某个特定的页面,又称资源属性)。
  • 因此理论上能够实现灵活的权限控制、将在权限与用户之前通过一组或多组属性实现关联,几乎能满足所有类型的需求。

权限管控

抽象来看权限体系可以分为如下两类:功能权限 与 数据权限 两部分。

  • 功能权限指的是在系统中的功能可否使用,通常我们将功能权限分为查看、编辑、删除等,同时编辑、删除权限又包含了查看。通过小的权限点拆分更精细的赋予了员工能否进入某个页面查看信息、编辑信息的能力。
  • 数据权限指数据中存在的数据是否能查看,是一个更细粒度的权限。比如一个页面,不同角色查看不同的数据就需要通过数据权限控制。
    从管理对象维度又可以分为:企业能力 与 员工能力。
  • 企业能力店铺维度的权限,比如开通某服务,可以通过企业能力去体现。
  • 企业能力赋予用户的权限,比如收电子发票、资金管理等。
  • 企业能力优先级绝对高于员工能力,所有场景的权限判断,店铺能力必须先于员工能力。简单地说,企业能力决定了“企业能做什么”,员工能力决定了“用户能做什么”。

jvm调优是日常工作中经常会使用的技巧,整理下。

JVM调优

为什么要调优,当默认配置参数不能很好的发挥程序性能的时候。

  • Heap内存(老年代)持续上涨达到设置的最大内存值。
  • Full GC 次数频繁。
  • GC 停顿时间过长(超过1秒)。
  • 应用出现OutOfMemory 等内存异常。
  • 应用中有使用本地缓存且占用大量内存空间。
  • 系统吞吐量与响应性能不高或下降。

常用方式

  • 上线之前,应先考虑将机器的JVM参数设置到最优。(启动参数)
  • 大多数导致GC问题的原因是代码层面的问题导致的(代码层面)。
  • 减少创建对象的数量(代码层面)。
  • 减少使用全局变量和大对象(代码层面)。
  • 优先架构调优和代码调优,JVM优化是不得已的手段(代码、架构层面)。
  • 分析GC情况优化代码比优化JVM参数更好(代码层面)。

启动参数

调优的最终目的都是为了令应用程序使用最小的硬件消耗来承载更大的吞吐。jvm调优主要是针对垃圾收集器的收集性能优化,令运行在虚拟机上的应用能够使用更少的内存以及延迟获取更大的吞吐量。

  • 延迟:GC低停顿和GC低频率。
  • 低内存占用。
  • 高吞吐量。
  • 堆内存 = Old + Eden + S0 + S1
  • 年轻的 = Eden(新生代) + S0 + S1
  • 标准参数(-),所有JVM都必须支持这些参数的功能,而且向后兼容
  • 非标准参数(-X),默认JVM实现这些参数的功能,但是并不保证所有JVM实现都满足,且不保证向后兼容。
  • 非稳定参数(-XX),此类参数各个JVM实现会有所不同,将来可能会不被支持,需要慎重使用。
  • Heap 内存使用率 <= 70%;
  • Old generation内存使用率<= 70%;
  • avgpause <= 1秒;
  • Full gc 次数0 或 avg pause interval >= 24小时 ;
1
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC
  • 简单介绍下参数
1
2
3
4
5
6
7
8
9
-XX:MetaspaceSize=128m (元空间默认大小)
-XX:MaxMetaspaceSize=128m (元空间最大大小)
-Xms1024m (堆最大大小)
-Xmx1024m (堆默认大小)
-Xmn256m (新生代大小)
-Xss256k (棧最大深度大小)
-XX:SurvivorRatio=8 (新生代分区比例 8:2)
-XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器)
-XX:+PrintGCDetails (打印详细的GC日志)
  • 虽然有了介绍但是依然不清楚具体是干啥的。并且Java虚拟机提供了非常多的参数命令。下面代码可以输出支持的参数数量
1
2
3
4
5
java -XX:+PrintFlagsFinal -XX:+UnlockDiagnosticVMOptions -version | wc -l
openjdk version "1.8.0_275"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_275-b01)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.275-b01, mixed mode)
838
  • 堆内存大小配置

建议 -Xms = 最大内存 * [0.6. ~0.8] 这里需要考虑系统损耗内存、和实际物理内存。

  • 堆内存与堆外内存

    • 堆内存

    必须是1024的倍数,且不能低于2M。

    32位机器,最大1G/4G 64位机器最大可以超过 32G/64G

    • 堆外内存

    堆外内存一般指 Direct Memory ,不受GC控制,JVM、Netty都可能使用堆外内存。

1
-XX:MaxDirectMemorySize 限制
如何理解这些参数的含义?

首先我们需要理解java是如何运行的,为什么需要java虚拟机?

我们常用方式一般是安装java运行环境(jre)用命令行的方式启动或者直接双击jar运行。jre包含的java运行的必要环境。

Java 作为一门高级程序语言,它的语法非常复杂,抽象程度也很高。编译出来的也不是机器可以直接直接运行代码。所以使用面向Java语言的虚拟机运行Java编译以后的特定代码。这里的特定代码指的是Java字节指令码。

JVM 内存分配性能问题
  • 在应用服务的特定场景下,JVM 内存分配不合理带来的性能表现并不会像内存溢出问题这么突出。如果没有深入到各项性能指标中去,是很难发现其中隐藏的性能损耗。
  • JVM 内存分配不合理最直接的表现就是频繁的 GC,这会导致上下文切换等性能问题,从而降低系统的吞吐量、增加系统的响应时间。

分析 GC 日志

  • 在进行压测的时候,我们需要对GC日志进行分析。
1
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/log/heap.log
  • -XX:PrintGCTimeStamps:打印 GC 具体时间;
  • -XX:PrintGCDetails :打印出 GC 详细日志;
  • -Xloggc: path:GC 日志生成路径。
  • JVM 内存调优通常和 GC 调优是互补的,基于以上调优,可以对年轻代和堆内存的垃圾回收算法进行调优。
阅读全文 »

开发过程中经常会出现批量写入数据库的操作,特别是后台系统,在导入数据的场景下会对表性能造成一定影响。

Mybatis-ONE-SQL

SQL插入主要使用INSERT语句,有两种常见的用法。

逐条插入

  • 如果插入的记录过多,比如大于20条记录,性能损耗非常严重。
1
INSERT INTO `table_data` ('field1','field2') VALUES ('data1','data2');

批量插入

  • 在日常开发中使用最多的方式,但是数量大比如大于100条的时候插入性能非常差。
1
INSERT INTO `table_data` ('field1','field2') VALUES ('data1','data2'),('data1','data2');

Mybatis-Foreach-SQL

插入数据有两种实现,foreach、batchExecutor。

1
2
3
4
5
6
7
<insert id="batchInsert">
INSERT INTO `table_data`(`create_time`)
VALUES
<foreach collection="tableDataList" item="item" index="index" separator=",">
(#{item.createTime})
</foreach>
</insert>

Mybatis-Batch-SQL

  • 配合foreach执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Component
@Slf4j
public class BatchDao {

@Autowired
private SqlSessionTemplate sqlSessionTemplate;

public <T,M> boolean batchSave(Collection<T> entityList, Class<M> mapper, BiConsumer<M,Collection<T>> fuc) {
SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
M entityMapper = sqlSession.getMapper(mapper);
try {
fuc.accept(entityMapper, entityList);
sqlSession.flushStatements();
sqlSession.clearCache();
return true;
} catch (Exception e) {
log.error("{}",e.getMessage());
sqlSession.rollback();
} finally {
sqlSession.close();
}
return false;
}
}

测试结果

  • 10条数据 foreach > batch > insert
  • 100条数据 foreach > batch > insert
  • 1000条数据 foreach > batch > insert
  • 5000条数据 batch > foreach (数据量大,batch优势就出来了)
阅读全文 »

SpringBoot测试解决方案

  • mock

    • Mock 的意思是模拟,它可以用来对系统、组件或类进行隔离。
    • 验证组件级别正确性的一大难点在于关于组件与组件之间的依赖关系,这里就需要引出测试领域非常重要的一个概念,即 Mock(模拟)。

  • mvc-mock 测试Controller

  • service-mock 测试 Service

  • repository-mock 测试 Data

  • remote-mock 测试 远程接口

Spring-Boot-Start-Test

Spring Test & Spring Boot Test:为 Spring 和 Spring Boot 框架提供的测试工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[INFO] +- org.springframework.boot:spring-boot-starter-test:jar:2.4.5:test
[INFO] | +- org.springframework.boot:spring-boot-test:jar:2.4.5:test
[INFO] | +- org.springframework.boot:spring-boot-test-autoconfigure:jar:2.4.5:test
[INFO] | +- com.jayway.jsonpath:json-path:jar:2.4.0:test
[INFO] | | \- net.minidev:json-smart:jar:2.3:test
[INFO] | | \- net.minidev:accessors-smart:jar:1.2:test
[INFO] | | \- org.ow2.asm:asm:jar:5.0.4:test
[INFO] | +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:test
[INFO] | | \- jakarta.activation:jakarta.activation-api:jar:1.2.2:test
[INFO] | +- org.assertj:assertj-core:jar:3.18.1:test
[INFO] | +- org.hamcrest:hamcrest:jar:2.2:test
[INFO] | +- org.junit.jupiter:junit-jupiter:jar:5.7.1:test
[INFO] | | \- org.junit.jupiter:junit-jupiter-params:jar:5.7.1:test
[INFO] | +- org.mockito:mockito-core:jar:3.6.28:test
[INFO] | | +- net.bytebuddy:byte-buddy:jar:1.10.22:test
[INFO] | | +- net.bytebuddy:byte-buddy-agent:jar:1.10.22:test
[INFO] | | \- org.objenesis:objenesis:jar:3.1:test
[INFO] | +- org.mockito:mockito-junit-jupiter:jar:3.6.28:test
[INFO] | +- org.skyscreamer:jsonassert:jar:1.5.0:test
[INFO] | | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO] | +- org.springframework:spring-test:jar:5.3.6:test
[INFO] | \- org.xmlunit:xmlunit-core:jar:2.7.0:test
[INFO] +- org.junit.jupiter:junit-jupiter-api:jar:5.7.1:test
[INFO] | +- org.apiguardian:apiguardian-api:jar:1.1.0:test
[INFO] | +- org.opentest4j:opentest4j:jar:1.2.0:test
[INFO] | \- org.junit.platform:junit-platform-commons:jar:1.7.1:test
[INFO] \- org.junit.jupiter:junit-jupiter-engine:jar:5.7.1:test
[INFO] \- org.junit.platform:junit-platform-engine:jar:1.7.1:test
  • JUnit:JUnit 是一款非常流行的基于 Java 语言的单元测试框架,在我们的课程中主要使用该框架作为基础的测试框架。
  • JSON Path:类似于 XPath 在 XML 文档中的定位,JSON Path 表达式通常用来检索路径或设置 JSON 文件中的数据。
  • AssertJ:AssertJ 是一款强大的流式断言工具,它需要遵守 3A 核心原则,即 Arrange(初始化测试对象或准备测试数据)——> Actor(调用被测方法)——>Assert(执行断言)。
  • Mockito:Mockito 是 Java 世界中一款流行的 Mock 测试框架,它主要使用简洁的 API 实现模拟操作。在实施集成测试时,我们将大量使用到这个框架。
  • Hamcrest:Hamcrest 提供了一套匹配器(Matcher),其中每个匹配器的设计用于执行特定的比较操作。
  • JSONassert:JSONassert 是一款专门针对 JSON 提供的断言框架。
阅读全文 »

Redis 中不能直接使用布隆过滤器,Redis 4.0 版本之后提供的 modules(扩展模块)

布隆过滤器的原理

  • 数据结构使用一位的数组,每次存储键值的时候,不是直接把数据存储在数据结构中。而是经过hash运算。将此元素的 hash 值均匀的存储在位数组中。把这些位置设置成 1 就完成了添加操作。
  • 判断元素是否存在时,经过运算判断对应的位置是否为全部1即可。因此布隆过滤器查询此值存在时,此值不一定存在,但查询此值不存在时,此值一定不存在
  • 但是存在一定的误差,并且当位数组存储值比较稀疏的时候,查询的准确率越高,而当位数组存储的值越来越多时,误差也会增大。

  • 空间占用
    • 布隆过滤器有两个参数,第一个是预计元素的数量n,第二个是误判率p。公式根据这两个输入得到两个输出,第一个输出是位数组的长度m,也就是需要的存储空间大小(bit),第二个输出是hash函数的最佳数量k。hash函数的数量也会直接影响到误判率,最佳的数量会有最低的误判率。
  • 计算公式
1
2
k≈0.7*(m/n)    
p≈0.6185^(m/n)
  • 位数组m越长,误判率p就越低;
  • 位数组m越长,hash函数最佳数量k就越多,影响计算效率;

演示代码

安装Redis

演示情况直接使用docker来安装,免去下载编译过程。

1
docker run -p 6379:6379 -d redislabs/rebloom:latest
  • 使用redis-cli或者客户端工具进入
1
2
3
4
5
6
➜  docker-run redis-cli 
127.0.0.1:6379> bf.add 1 1
(integer) 0
127.0.0.1:6379> bf.add 1 2
(integer) 1
127.0.0.1:6379>
  • 如果没有提示错误则表示安装成功。
阅读全文 »

记录最近在Centos7上面部署jenkens。安装的方法有很多,下面采用最简单的方式安装。

参考文献

jenkins 是一个协调者的身份,管理和协调了代码库,代码仓库,代码运行环境等。

jenkins 简介

Jenkins是一个开源软件项目,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。持续集成(CI)已成为当前许多软件开发团队在整个软件开发生命周期内侧重于保证代码质量的常见做法。它是一种实践,旨在缓和和稳固软件的构建过程。并且能够帮助您的开发团队应对如下挑战:

  • 持续、自动地构建/测试软件项目。
  • 监控一些定时执行的任务。
  • 高度可扩展性、全球大量团队使用,并共享代码。
  • 基于插件、可以做出非常多有趣的事情。

安裝部署

阅读全文 »

WebClient

Spring 有两个web客户端的实现,一个是RestTemplate另一个是spring5的响应代替WebClient。

WebClient是一个以Reactive方式处理HTTP请求的非阻塞客户端。

  • RestTemplate是阻塞客户端

    • 它基于thread-pre-requset模型。
    • 这意味着线程将阻塞,直到 Web 客户端收到响应。阻塞代码的问题是由于每个线程消耗了一些内存和 CPU 周期。当出现慢速请求的时候,等待结果的线程会堆积起来,将导致创建更多的线程、消耗更多的资源。频繁切换CPU资源也会降低性能。
  • WebClient是异步、非阻塞的方案。

    • WebClient将为每个事件创建类似于“任务”的东西。在幕后,Reactive 框架会将这些“任务”排队并仅在适当的响应可用时执行它们。

    • WebClient是Spring WebFlux库的一部分。因此,我们还可以使用具有反应类型(Mono和Flux的功能性、流畅的 API 作为声明性组合来编写客户端代码。

    • 底层支持的库

      • Reactor Netty - ReactorClientHttpConnector
      • Jetty ReactiveStream HttpClient - JettyHttpConnector
  • 关于IDEA开启 Reactive Streams DEBUG

演示代码

阅读全文 »