0%

文章字数:251,阅读全文大约需要1分钟

java native interface JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架

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
29
30
31
32
33
34
35
36
37

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import com.sun.jna.win32.StdCallLibrary;

public class JNADllCall {

/**
* DLL动态库调用方法
* stdCall模式,调用完内存清除
*/
public interface StdCallDll extends StdCallLibrary {
// DLL文件默认路径为项目根目录,若DLL文件存放在项目外,请使用绝对路径
StdCallDll INSTANCE = (StdCallDll) Native.loadLibrary("dllName",StdCallDll.class);// 加载动态库文件

// 声明将要调用的DLL中的方法(可以是多个方法)
void printf(String format, Object... args);
}

/**
* DLL动态库调用方法2
* cdecl模式,调用完内存信息保留,数据由调用者清除
*/
public interface CLibrary extends Library {
// 做个判断,是windos下就加载msvcrt.dll ,否则是 c.so
CLibrary INSTANCE = (CLibrary) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
CLibrary.class);

void printf(String format, Object... args);
}

public static void main(String[] args) {
StdCallDll.INSTANCE.printf("Hello, World!\n");
CLibrary.INSTANCE.printf("Hello, World\n");
}
}

文章字数:251,阅读全文大约需要1分钟

简介

JMHJDK9自带的JVM基准测试套件,用于定位代码性能,执行时间、吞吐量等

构建

  1. 通过maven依赖引入
1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.23</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.23</version>
</dependency>
  1. 直接生成maven工程(官方推荐)
1
2
3
4
5
6
7
mvn archetype:generate 
-DinteractiveMode=false
-DarchetypeGroupId=org.openjdk.jmh
-DarchetypeArtifactId=jmh-java-benchmark-archetype
-DgroupId=com.test.jmh
-DartifactId=jmh
-Dversion=1.0.0-SNAPSHOT

使用

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
// 预热5次,每次1s
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
// 循环运行5次,总共5秒
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
// 1个线程运行基准测试
@Fork(1)
// 平均时间
@BenchmarkMode(Mode.AverageTime)
// 纳秒,和上面的组合就是每次操作的纳秒单位的平均时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {

@GenerateMicroBenchmark
public List<Integer> testMethod() {
int cardCount = 54;
List<Integer> cardList = new ArrayList<Integer>();
for (int i=0; i<cardCount; i++){
cardList.add(i);
}
// 洗牌算法
Random random = new Random();
for (int i=0; i<cardCount; i++) {
int rand = random.nextInt(cardCount);
Collections.swap(cardList, i, rand);
}
return cardList;
}
}

Mean + Units就是每次操作的毫秒数

1
2
Benchmark                        Mode   Samples         Mean   Mean error    Units
c.a.j.MyBenchmark.testMethod avgt 5 1056.677 30.809 ns/op

@BenchmarkMode(Mode.Throughput)@OutputTimeUnit(TimeUnit.MILLISECONDS)组合,就是每毫秒吞吐量

943.437 ops/ms

1
2
Benchmark                        Mode   Samples         Mean   Mean error    Units
c.a.j.MyBenchmark.testMethod thrpt 5 943.437 44.060 ops/ms

文章字数:250,阅读全文大约需要1分钟

jni(java native interface)允许java和本地方法交互,但是会丧失平台的可移植性。

实现步骤

  1. 编写java native方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloWorld {
    public native void displayHelloWorld();//所有native关键词修饰的都是对本地的声明
    static {
    System.loadLibrary("hello");//载入本地库
    }
    public static void main(String[] args) {
    //调用
    new HelloWorld().displayHelloWorld();
    }
    }
  2. 使用javac编译java类
    1
    javac HelloWorld.java
  3. 使用javah生成头文件
    1
    javah HelloWord
  4. 本地方法实现
    编写和javah生成的头文件声明方法相同的方法。
    HelloWorldImpl.cpp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # include "jni.h"
    # include "HelloWorld.h"
    # include "stdio.h"
    JNIEXPORT void JNICALL
    Java_HelloWorld_displayHelloWorld(JNIEnv*env,jobject obj)
    {
    printf("hello");
    return;
    }
    生成dll,重命名为hello.dll移动到java目录下。
    调用
    1
    2
    3
    static {
    System.loadLibrary("hello");//载入本地库
    }

文章字数:1076,阅读全文大约需要4分钟

java内存模型(java Memory Model)是一种符合内存模型规范,屏蔽硬件及操作系统的差异,保证java程序在不同平台下的内存访问效果一致性的机制及规范。

本文整理自-深入理解JVM-内存模型(jmm)和GC的上半部分


一、电脑内存模型

  1. 背景

随着cpu的发展,内存的读写速度远远赶不上cpu。因此,cpu的厂商在每颗cpu上加上了高速缓存,作为cpu和内存间的缓冲区域。

2.带来的问题

每个cpu各自的一套缓存(一级缓存、二级缓存、三级缓存),如何保证多处理器运算到同一个内存区域时的数据一致性?

  1. 解决方法

为了解决这个问题,各个处理器需要遵循缓存一致性协议与主存交互

在cpu层面,内存屏障(Memory Barrier)提供了支持(硬件层支持):

  1. 内存屏障的类型

硬件层的内存屏障分为:Load Barrier(读屏障)和Store Barrier(写屏障)

  1. 内存屏障作用

1.cpu执行指令可能是;无序的,内存屏障可以组织屏障两侧进行指令重排。
2.强制把写缓冲区/高速缓存中的数据失效,从而强制从主存中获取。

  1. java中的生成jvm内存屏障

使用关键词volatitle修饰变量

相当于变量读写加锁

1
2
3
4
5
6
7
8
9
10
public class VolatitleValue {
private String value;
public synchronized String get(){
return value;
}

public synchronized void set(String value){
this.value = value;
}
}
  1. volatile变量特性

1.可见性,该变量的读一定可以看到读之前最后的写
2.原子性,对于该变量的读写具有原子性。

java内存区域

jvm主要有 sun的HotSpot/JRockit和IBM的IBMJVM,其中HotSpot为主流,在此探讨HotSpot虚拟机。

  1. Java内存模型(Java Memory Model ,JMM)

如开题所说,jmm是jvm的一种规范。使语言不能直接访问硬件内存,作为中转以解决各硬件和操作系统的差异问题。

虚拟机五大数据区域

1
2
方法区、堆是线程共享的
栈、本地方法栈、计数器是线程私有的
  1. 计数器

线程执行java方法时指向字节码,方便cpu线程切换时切换回正确位置

注:内存中唯一没有OutOfMemoryError的区域

2 虚拟机栈

由栈帧组成,每个方法执行的时候都会创建栈帧存储变量表,操作栈,动态链接,方法出入口等。

注:方法调用入栈,调用完出栈。局部变量表大小在方法运行前就完全确定了

  1. 本地方法栈

虚拟机栈调用的是java方法(字节码文件),本地方法栈则是调动native方法(c、c++)的实现方法


以下为线程共有的

一般来说对象实例及数组都是在堆上被分配内存。堆作为最大的内存区域也是GC(内存回收机制)主要管理的区域。根据规范,堆可以存在与物理上不连续的内存空间,可设定固定大小也可扩展。(-Xmx和-Xms)。

注:没有内存可分配会报OOM(OutOfMemory)

  1. 方法区

存储已被虚拟机加载的类信息、常量、静态变量、静态方法、静态代码块。


再回顾一下
线程私有的:

区域名 作用
计数器 cpu线程切换后找到正确位置
jvm栈 引用java方法
native栈 引用本地c/c++方法及功能

线程共享的:

区域名 作用
对象实例、数组
方法区 类的静态信息
  1. 对象在内存中的结构
    1
    2
    3
    1.对象头//
    2.数据实体//对象信息
    3.对齐填充//占位

对象头(markword)说明:

  1. 64操作系统下16字节(开启指针压缩12)
  2. synchronized的锁信息就存储在这里
  3. 根据锁位偏移有四种锁状态无锁态偏向锁轻量级锁重量级锁

文章字数:1244,阅读全文大约需要4分钟

一、性能调优目的

1. 减少`minor gc`的频率,将转移到老年代的对象数量降低到最新
2. 减少`full gc`次数
3. 找到并提升性能瓶颈

二、软件调优大致过程

  1. 查看内存和GC日志
1
2
3
4
5
6
7
-verbose.gc:显示GC的操作内容。打开它,可以显示最忙和最空闲收集行为发生的时间、收集前后的内存大小、收集需要的时间等。
-XX:+PrintGC #输出GC日志
-XX:+PrintGCDetails #输出GC的详细日志
-XX:+PrintGCTimeStamps #输出GC时间戳(以基准时间的形式)
-XX:+PrintHeapAtGC #在进行GC的前后打印出堆的信息
-Xloggc:/path/gc.log #日志文件的输出路径
-XX:+PrintGCApplicationStoppedTime #打印由GC产生的停顿时间
  1. 调整新生代和老年代比例
1
2
3
4
5
6
7
8
9
10
11
# 新生代和老年代一般内存比例为 1:2
-Xms # 初始堆大小
-Xmx # 最大堆大小
-Xmn # 新生代大小
-XX:NewRatio=n # 新生代和老年代的比例,一般为2,新生代1:老年代2
-XX:PermSize=n # 非堆内存大小,一般为物理内存1/64
-XX:MaxPermSize=n # 非堆内存最大值,默认是物理内存的1/4
# 新生代中 Eden和Survivor区设置(Survivor还分为to Survivor和from Survivor)
-XX:SurvivorRatio=n:新生代中Eden区与两个Survivor区的比值
-XX:PretenureSizeThreshold:直接进入老年代中的对象大小
-XX:MaxTenuringThreshold:进入老年代的年龄

性能分析

  1. 性能达标条件
  • Minor GC执行时间不超过50ms
  • Minor GC执行频率在10s一次以上
  • Full GC执行时间不到1s
  • Full GC执行频率在10分钟一次以上

常用工具列表

  1. 内存监控和故障处理
  • jps: JVM Process Status Tool, 显示指定系统内所有的HotSpot虚拟机进程。
  • jstat: JVM statistics Monitoring, 是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
  • jmap: JVM Memory Map用于生成heap dump文件。
  • jhat: JVM Heap Analysis Tool, 与jmap搭配使用,用来分析jmap生成的dumpjhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。
    jstack: 用于生成java虚拟机当前时刻的线程快照。
  • jinfo: JVM Configuration info实时查看和调整虚拟机运行参数。
  • javap: 查看经javac之后产生的JVM字节码代码,自动解析.class文件, 避免了去理解class文件格式以及手动解析class文件内容。
  • jcmd: 几乎集合了jpsjstatjinfojmapjstack所有功能,一个多功能工具, 可以用来导出堆, 查看Java进程、导出线程信息、 执行GC、查看性能相关数据等。
  1. jdk自带调优工具
  • jconsole: Java Monitoring and Management Console, 监控内存,线程和类
  • jvisualvm: 全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等
  1. 第三方工具
  • MAT: Memory Analyzer Tool, Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗, 基于Eclipse
  • GChisto: 分析gc日志的工具

JPS

  • 查看基于HotSpotJVM里,具有访问权限的java进程的具体状态
1
2
3
4
5
6
7
jps [options] [hostid]
-q #禁止输出类名,JAR文件名和传递给main方法的参数,只产生一个本地JVM标识符列表。
-m #显示传递给主方法的参数。 嵌入式JVM的输出可能为空。输出JVM启动时传递给main()的参数。
-l #显示应用程序主类的完整程序包名称或应用程序JAR文件的完整路径名称。
-v #显示传递给JVM的参数。
-V #禁止输出类名,JAR文件名和传递给main方法的参数,只产生一个本地JVM标识符列表(.hotspotrc文件,或者是通过参数-XX:Flags=指定的文件)。
-Joption #将选项传递给JVM,其中选项是Java应用程序启动器参考页上描述的选项之一。 例如,-J -Xms48m将启动内存设置为48 MB。

Jstat

  • 另开一篇

Jmap

  • 用于生成heap dump文件,如果不使用这个命令,可以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM时自动生成dunp文件

文章字数:476,阅读全文大约需要1分钟

SpringBoot中可以使用jpa整合ElasticSearch

环境

  • jdk 1.8
  • ElasticSearch 2.4需要和springBoot版本匹配
  • com.sun.jna

继承依赖

1
2
3
spring-boot-starter-data-elasticsearch
com.sun.jna
org.springframework.boot:spring-boot-starter-web

配置实体

data-elasticsearch的依赖自带了ES的jar,如果不配置ES实例SpringBoot会自动生成一个。但是性能不如自己搭建的。

1
2
3
4
5
spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: localhost:9300

实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Data  //lombok
@Document(indexName = "shop", type = "user", refreshInterval = "0s")
public class User {

@Id
private Long id;

private String username;

private String realname;

private String password;

private Integer age;

//这三个注解是为了前台序列化java8 LocalDateTime使用的,需要引入jsr310的包才可以使用
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime birth;

}
  • @Document加在类上
类型 属性名 默认值 说明
String indexName 索引库的名称,建议以项目的名称命名
String type “” 类型,建议以实体的名称命名
short shards 5 默认分区数
short replica 1 每个分区默认的备份数
String refreshInterval “1s” 刷新间隔
String indexStoreType “fs” 索引文件存储类型
  • @Field属性上加的,相当于@Column,可以不写,默认全部添加到ES中。主键上是@Id
类型 属性名 默认值 说明
FieldType type FieldType.Auto 自动检测属性的类型
FieldIndex index FieldIndex.analyzed 默认情况下分词
boolean store false 默认情况下不存储原文
String searchAnalyzer “” 指定字段搜索时使用的分词器
String indexAnalyzer “” 指定字段建立索引时指定的分词器
String[] ignoreFields {} 如果某个字段需要被忽略

DAO

1
2
3
@Repository
public interface UserRepository extends ElasticsearchRepository<UserInfo,String> {
}

文章字数:1807,阅读全文大约需要7分钟

JSTAT

  • 用于监视虚拟机运行时的状态信息,可以显示虚拟机进程类装载、内存、垃圾收集、JIT编译等运行数据
1
2
3
jstat [ generalOption | outputOptions vmid [ interval[s|ms] [ count ] ]
# 查看1111的gc数据,两秒一次,共一次
jstat -gc 1111 2 1
  • -statOption统计参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class   #显示有关类加载器行为的统计信息。
    compiler #显示有关Java HotSpot VM即时编译器行为的统计信息。
    gc #显示有关垃圾回收堆的行为的统计信息。
    gccapacity #各个垃圾回收代容量(young,old,perm)和他们相应的空间统计。
    gccause #垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因。
    gcnew #显示新生代行为的统计信息。gcnewcapacity #显示有关新生代及其相应空间大小的统计信息。
    gcold #显示有关老年代和metaspace统计信息的统计信息。
    gcoldcapacity #年老代行为统计。
    gcmetacapacity #显示有关元空间大小的统计信息。
    gcutil #显示关于垃圾收集统计信息的摘要。
    printcompilation #显示Java HotSpot VM编译方法统计信息。

其它参数

1
2
3
 -h n  #每n个样本(输出行)显示一个列标题,其中n是一个正整数。 默认值是0,它显示列标题的第一行数据。
-t #显示一个时间戳列作为输出的第一列。 时间戳是从目标JVM开始时间开始的时间。
-JjavaOption #将javaOption传递给Java应用程序启动器。

gcutil选项

名称 简介
S0 幸存者0空间利用率占空间当前容量的百分比。
S1 幸存者1空间利用率占空间当前容量的百分比。
E Eden空间利用率占空间当前容量的百分比。
O 旧空间利用率占空间当前容量的百分比。
M Metaspace利用率占空间当前容量的百分比。
CCS 压缩类空间利用率,以百分比表示。
YGC 年轻一代GC事件的数量。
YGCT 年轻一代的垃圾收集时间(S)。
FGC 完整的GC事件的数量。
FGCT 完整的垃圾收集时间(S)。
GCT 垃圾收集总时间(S)。

gcnew选项

名称 简介
S0C 当前幸存者空间0容量(kB)。
S1C 当前幸存者空间1容量(kB)。
S0U 幸存者空间0利用率(kB)。
S1U 幸存者空间1利用率(kB)。
TT 任期阀值
MTT 最大任期阀值
DSS 所需的幸存者大小(kB)。
EC 当前eden空间容量(kB)。
EU Eden空间利用率(kB)。
YGC 年轻一代GC事件的数量。
YGCT 年轻一代的垃圾收集时间(S)。

gcoldcapacity

名称 简介
OGCMN 最小老年代容量(kB)。#从上图可以看出是670MB
OGCMX 最大老年代容量(kB)。#从上如可以看出最大内存是2730MB
OGC 当前的老年代容量(kB)。 #当前老年代是2730MB
OC 老年代大小(kB)。
YGC 年轻一代GC事件的数量。
FGC full GC事件的数量。
FGCT 完整的垃圾收集时间(S)。
GCT 垃圾收集总时间(S)。

class

名称 简介
Loaded 加载class的数量
Bytes class字节大小
Unloaded 卸载的类数。
Bytes 卸载的千字节数。
Time 执行类加载和卸载操作的时间。

compiler

名称 简介
Compiled 执行的编译任务数。
Failed 编译任务的失败数量。
Invalid 无效的编译任务数。
Time 执行编译任务的时间。
FailedType 编译最后一次失败编译的类型。
FailedMethod 上次失败编译的类名称和方法。

gc

名称 简介
S0C 当前survivor0区容量(kB)。 #大概是136MB
S1C 当前survivor1区容量(kB)。 #大概是136MB
S0U survivor0区已使用的容量(KB) #当前使用了17MB
S1U survivor1区已使用的容量(KB)
EC Eden区的总容量(KB) #Eden区的大小现在是1092MB
EU 当前Eden区已使用的容量(KB) #当前Eden区使用了1006MB
OC Old空间容量(kB)。 #当前老年代是2730MB
OU Old区已使用的容量(KB) #当前使用了748MB
MC Metaspace空间容量(KB) #在jdk1.7的版本MC是PC,也就是256MB
MU Metacspace使用量(KB) #也就是jdk1.7版本永久代使用了140MB
CCSC 压缩类空间容量(kB)。
CCSU 压缩类空间使用(kB)。
YGC 新生代垃圾回收次数
YGCT 新生代垃圾回收时间
FGC 老年代 full GC垃圾回收次数
FGCT 老年代垃圾回收时间
GCT 垃圾回收总消耗时间

gccapacity

名称 简介
NGCMN 年轻代(young)中初始化(最小)的大小(KB)
NGCMX 年轻代(young)的最大容量 (KB)
NGC 年轻代(young)中当前的容量 (KB)
S0C 年轻代中第一个survivor(幸存区)的容量 (KB)
S1C 年轻代中第二个survivor(幸存区)的容量 (KB)
EC 年轻代中Eden(伊甸园)的容量 (KB)
OGCMN old代中初始化(最小)的大小 (KB)
OGCMX old代的最大容量(KB)
OGC old代当前新生成的容量 (KB)
OC Old代的容量 (KB)
PGCMN perm代中初始化(最小)的大小 (KB) ,jdk1.8改为了MCMN
PGCMX perm代的最大容量 (KB),jdk1.8改为了MCMX
PGC perm代当前新生成的容量 (KB)
PC Perm(持久代)的容量 (KB)
YGC 从应用程序启动到采样时年轻代中gc次数
FGC 从应用程序启动到采样时old代(全gc)gc次数
MC Metaspace空间(KB)
CCSMN 压缩类空间最小容量(kB)。
CCSMX 压缩类空间最大容量(kB)。
CCSC 压缩类空间容量(kB)。

gcnewcapacity

名称 简介
NGCMN 年轻代(young)中初始化(最小)的大小(kb)
NGCMX 年轻代(young)的最大容量 (kb)
NGC 年轻代(young)中当前的容量 (kb)
S0CMX 年轻代中第一个survivor(幸存区)的最大容量 (kb)
S0C 年轻代中第一个survivor(幸存区)的容量 (kb)
S1CMX 年轻代中第二个survivor(幸存区)的最大容量 (kb)
S1C 年轻代中第二个survivor(幸存区)的容量 (kb)
ECMX 年轻代中Eden(伊甸园)的最大容量 (kb)
EC 年轻代中Eden(伊甸园)的容量 (kb)
YGC 从应用程序启动到采样时年轻代中gc次数
FGC 从应用程序启动到采样时old代(全gc)gc次数

gcold

名称 简介
PC Perm(持久代)的容量 (kb) #jdk1.8是MC Metaspace容量
PU Perm(持久代)目前已使用空间 (kb) #jdk1.8是MU Metaspace目前的使用量
OC Old代的容量 (kb)
OU Old代目前已使用空间 (kb)
YGC 从应用程序启动到采样时年轻代中gc次数
FGC 从应用程序启动到采样时old代(全gc)gc次数
FGCT 从应用程序启动到采样时old代(full gc)gc所用时间
GCT 垃圾收集总时间

gcoldcapacity

名称 简介
OGCMN old代中初始化(最小)的大小 (kb)
OGCMX old代的最大容量(kb)
OGC old代当前的容量 (kb)
OC Old代的容量 (kb)
YGC 从应用程序启动到采样时年轻代中gc次数
FGC 从应用程序启动到采样时old代(全gc)gc次数
FGCT 从应用程序启动到采样时old代(full gc)gc所用时间
GCT 垃圾收集总时间。

文章字数:564,阅读全文大约需要2分钟

记一次查找问题的过程

  1. 做一个用jpa从数据库中查找数据,修改再保存到数据库的简单功能。

  2. 查找出来后使用BeanUtil将修改的信息放入jpa的对象中,结果报了错误
    Could not copy property 'id' from source to target

  3. 查看BeanUtil的源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}

Object value = readMethod.invoke(source);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}

writeMethod.invoke(target, value);
} catch (Throwable var15) {
throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var15);
}
}

发现在反射调用失败后统一返回这个异常,于是打断点。果然是反射报的异常。

  1. 检查属性是否一一对应,是否有公共的getset,是否有基础类型(有的话传入空会空指针)

  2. 都没发现有问题,于是手动赋值又运行了一遍。

1
a.setId(b.getId());
  1. 期初只是为了测试一下方法是否被私有化,属性名是否一致。结果抛出异常LazyInitializationException

  2. 这才是导致反射出错的原因,异常信息的意思大致是session被关闭。在stackoverflow上找到解决方法:方法上加上@Transactional

  3. 最后成功解决问题,回想了一下。当元素或者元素的lazy属性为true时执行find()得到的只是一个代理对象,只有执行getId()时才会从数据库中取。然后取的时候事务已经关闭了。(可能是因为find操作是在Stream流中进行的,流关闭事务也关闭了)加上@Transactional注解可以使代码块事务统一。


文章字数:569,阅读全文大约需要2分钟

Keytool是一个Java数据证书的管理工具。

keystore

  • Keytool将秘钥(key)和证书(certificates)存在名为keystore的文件中。
  • keystore中有两种数据
  1. 秘钥实体(key entity),私钥+公钥
  2. 可信的证书实体(trusted certificate entries),公钥
  • alias别名,不区分大小写。keystore下又若干别名条目

常用命令

  • -genkey: 在用户主目录中创建一个默认文件.keystore,还会产生一个mykey的别名,mykey中包含用户的公钥、私钥和证书。没有指定目录,会生成在默认目录下。
  • -alias: 产生别名
  • -keystore: 指定秘钥库的名称。加上此命令,生成的各类信息将不在.keystore文件中
  • -keyalg: 指定秘钥的算法,默认DSA,可以设置成RSA
  • -validity: 创建的证书有效期是多少天
  • -keysize: 指定秘钥长度
  • -storepass: 指定秘钥库的访问密码(查看秘钥库内容需要)
  • -keypass: 指定别名条目的密码(私钥的密码)
  • -dname: 指定证书拥有者的信息CN=名字与姓氏,OU=组织单位名称,O=组织名称,L=城市或区域名称,ST=州或省份名称,C=单位的两字母国家代码
  • -list: 显示密钥库中的证书信息,例如keytool -list -v -keystore, -storepass指定密码
  • -v: 显示秘钥库中的证书详细信息
  • -export: 将别名指定的证书导出到文件,-alias指定导出的别名,-keystore指定keystore,-file指定导出证书的位置及证书名称,-storepass密码
  • -file: 指定导出文件的文件名
  • -delete: 删除秘钥库中某条目,keytool -delete -alias指定删除的别名
  • -printcert: 查看导出的证书信息keytool -printcert -file xxx.crt查看导出的证书信息
  • -keypasswd: 修改秘钥库中指定条目口令,-keypasswd -alias指定需要修改的别名,-keypass 旧密码 -new 新密码 -storepass keystore密码 -keystore xxx
  • -storepasswd: 修改keystore口令,-storepasswd -keystore e:\sture.keystore(需修改口令的keystore) -storepass 123456(原始密码) -new 321(新密码)
  • -import: 将签名数字证书导入秘钥库keytool -import -alias指定导入条目的别名 -keystore-file指定导入的证书

例子

  • 生成keystore
    keytool -genkey -v -alias broker -keyalg RSA -keystore broker.keystore -storepass brokerPwd -keypass brokerKeyPwd

  • 查看keystore
    keytool -list -v -keystore broker.keystore

  • keystore导出成证书
    keytool -export -alias broker -keystore broker.keystore -file borker_cert -storepass brokerPwd

  • 导入证书到truststore
    keytool -import -v -file borker_cert -keystore client.truststore -storepass clientTrustPwd

  • jksp12

keytool -importkeystore -srckeystore keyStore.jks -srcstoretype JKS -deststoretype PKCS12 -destkeystore keyStore.p12


文章字数:546,阅读全文大约需要2分钟

QOSQuality of Service服务质量,有发布者的QOS和订阅者的QOS

等级和作用

  1. level0: 最多传输一次
  2. level1: 至少传输一次
  3. level2: 只有一次的传输

交互过程

qos0

1
生产者---》中间件---》消费者

只会发送一次,不管有没有收到。适合不是很重要的数据,比如传感器温度。反正很快会有下一次的数据。

qos1

1
2
生产者《------》中间件《-------》消费者
发送 确认 发送 确认

接受者需要发送确认信息,确认自己收到了。发送者如果没有收到确认信息就会再次发送。这个保证了发送的信息一定会到达,但是如果确认的信息没有被收到,可能导致重复发送。

qos2

1
2
3
发送者发送信息+信息编号--》接受者接收,保存编号--》确认收到+信息编号
--》发送者收到确认消息后发送请求删除编号--》接受者删除编号,并发请求删除编号
--》接受者删除编号

增加了信息编号的传递,可以有效防止重复信息,但是多了删除编号的步骤。
并且假如接受者的删除请求没有到达(即没收到删除请求就有新的message)还会多次发送删除请求。
信息发送的次数过多。