完成序列化反序列化+java日志体系

master
sunzhiqiang23 4 years ago
parent 04540bb7b4
commit 4ca616c77e

@ -33,7 +33,8 @@
<module>shiro-springboot-admin-9010</module> <module>shiro-springboot-admin-9010</module>
<module>shiro-springboot-admin-jwt-9011</module> <module>shiro-springboot-admin-jwt-9011</module>
<module>shiro-sprinboot-proxy</module> <module>shiro-sprinboot-proxy</module>
</modules> <module>shiro-serialization</module>
</modules>

@ -1,6 +1,7 @@
package test.thread; package test.thread;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -13,57 +14,100 @@ import java.util.concurrent.*;
* @date 2020-06-11 22:40 * @date 2020-06-11 22:40
*/ */
public class TestThreadLocal { public class TestThreadLocal {
static ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2,
0L, TimeUnit.MILLISECONDS, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()); new LinkedBlockingQueue<Runnable>());
static ThreadPoolExecutor executor2 = new ThreadPoolExecutor(2, 2,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
//sso
static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) { public static void main(String[] args) {
ThreadLocal<Map<Object, Object>> threadLocal = new InheritableThreadLocalMap<>();
for (int i = 0; i < 10; i++) { TestThreadLocal testThreadLocal= new TestThreadLocal();
Map<Object, Object> map = new HashMap<>();
map.put("key", i); for (int i = 0; i < 2; i++) {
threadLocal.set(map); String finalI = "jdPin-" + i;
executor.execute(()->{ executor2.execute(() -> {
System.out.println(threadLocal.get().get("key")); Thread.currentThread().setName(finalI);
testThreadLocal.sso(finalI);
testThreadLocal.YeWuMethod(Thread.currentThread().getName());
}); });
} }
}
public static void testInThreadLocal(){
ThreadLocal<Map<Object, Object>> threadLocal = new InheritableThreadLocalMap<>();
Map<Object, Object> map = new HashMap<>();
map.put("key", "value");
threadLocal.set(map);
new Thread(() -> {
System.out.println(threadLocal.get().get("key"));
}, "input thread name").start();
} }
public static void testThreadLocal(){
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("测试threadLocal");
new Thread(() -> {
System.out.println(threadLocal.get());
}, "input thread name").start();
private void sso(String finalI) {
threadLocal.set(finalI);
} }
private static final class InheritableThreadLocalMap<T extends Map<Object, Object>> extends InheritableThreadLocal<Map<Object, Object>> { private void YeWuMethod(String name) {
/** // executor = new ThreadPoolExecutor(2, 2,
* This implementation was added to address a // 0L, TimeUnit.MILLISECONDS,
* <a href="http://jsecurity.markmail.org/search/?q=#query:+page:1+mid:xqi2yxurwmrpqrvj+state:results"> // new LinkedBlockingQueue<Runnable>());
* user-reported issue</a>.
* @param parentValue the parent value, a HashMap as defined in the {@link #initialValue()} method. for (int j = 0; j < 10; j++) {
* @return the HashMap to be used by any parent-spawned child threads (a clone of the parent HashMap).
*/
@SuppressWarnings({"unchecked"}) // Runnable task = new RunnableTask();
protected Map<Object, Object> childValue(Map<Object, Object> parentValue) { // Runnable ttlRunnable = TtlRunnable.get(task);
if (parentValue != null) {
return (Map<Object, Object>) ((HashMap<Object, Object>) parentValue).clone(); // executor.execute(ttlRunnable);
} else {
return null; executor.execute(() -> {
} System.out.println("=========="+name+"==="+threadLocal.get());
});
} }
// for (int i = 0; i < 10; i++) {
// new Thread(() -> {
// System.out.println(name+"==="+threadLocal.get());
// }, "input thread name").start();
// }
} }
// public static void testInThreadLocal() {
// InheritableThreadLocalMap<Map<Object, Object>> threadLocal = new InheritableThreadLocalMap<>();
//
// new Thread(() -> {
// System.out.println(threadLocal.get().get("key"));
// }, "input thread name").start();
// }
//
// public static void testThreadLocal() {
// ThreadLocal<String> threadLocal = new ThreadLocal<>();
// threadLocal.set("测试threadLocal");
// new Thread(() -> {
// System.out.println(threadLocal.get());
// }, "input thread name").start();
//
// }
// private static final class InheritableThreadLocalMap<T extends Map<Object, Object>> extends InheritableThreadLocal<Map<Object, Object>> {
//
// /**
// * This implementation was added to address a
// * <a href="http://jsecurity.markmail.org/search/?q=#query:+page:1+mid:xqi2yxurwmrpqrvj+state:results">
// * user-reported issue</a>.
// * @param parentValue the parent value, a HashMap as defined in the {@link #initialValue()} method.
// * @return the HashMap to be used by any parent-spawned child threads (a clone of the parent HashMap).
// */
// @SuppressWarnings({"unchecked"})
// protected Map<Object, Object> childValue(Map<Object, Object> parentValue) {
// if (parentValue != null) {
// return (Map<Object, Object>) ((HashMap<Object, Object>) parentValue).clone();
// } else {
// return null;
// }
// }
// }
} }

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>shiro</artifactId>
<groupId>com.bjmashibing.shiro</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shiro-serialization</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/com.esotericsoftware/kryo -->
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.0.0-RC5</version>
</dependency>
</dependencies>
</project>

Binary file not shown.

@ -0,0 +1,34 @@
package com.bjmashibing.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* <p>redis</p>
*
* @author sunzhiqiang23
* @date 2020-04-27 19:47
*/
@Configuration
public class RedisConfig {
/**
* shiro redis使
* RedisTemplate
* @return
*/
@Bean("shiroRedisTemplate")
public RedisTemplate shiroRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}

@ -0,0 +1,36 @@
package com.bjmashibing.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-04-13
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class User extends UserSuper implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
private Long id;
/**
*
*/
private String username;
public static String realName ="static";
}

@ -0,0 +1,55 @@
package com.bjmashibing.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* <p></p>
*
* @author sunzhiqiang23
* @date 2020-06-16 20:23
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class User1 extends UserSuper implements Externalizable {
private static final long serialVersionUID = 2L;
/**
*
*/
private Long id;
/**
*
*/
private String username;
public static String realName ="static";
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(id);
out.writeObject(username);
out.writeObject(realName);
out.writeObject(super.getSuperName());
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
id = (Long) in.readObject();
username = (String) in.readObject();
realName = (String) in.readObject();
String superName = (String) in.readObject();
setSuperName(superName);
}
}

@ -0,0 +1,20 @@
package com.bjmashibing.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p></p>
*
* @author sunzhiqiang23
* @date 2020-06-16 21:08
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class UserSuper {
private String superName;
}

@ -0,0 +1,61 @@
package com.bjmashibing.util;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* <p>Bean (POJO) </p>
*
* @author sunzhiqiang23
* @date 2020/1/6 12:22
*/
@Slf4j
public class BeanUtil extends BeanUtils {
private static JacksonBeanConvertor beanConvertor = new JacksonBeanConvertor();
/**
* Bean copyProperties
*
* @param fromValue Bean
* @param toValueType
* @param <T>
* @return
*/
public static <T> T copyDeeply(Object fromValue, Class<T> toValueType) {
return beanConvertor.convert(fromValue, toValueType);
}
/**
*
*
* @param sources Bean
* @param target
* @param <E>
* @return
*/
public static <E> List<E> copyTo(List<?> sources, Class<E> target) {
if (sources == null) {
return null;
}
if (sources.isEmpty()) {
return Collections.emptyList();
}
List<E> list = new ArrayList<>(sources.size());
for (Object source : sources) {
if (source == null) {
list.add(null);
} else {
list.add(copyDeeply(source, target));
}
}
return list;
}
}

@ -0,0 +1,48 @@
package com.bjmashibing.util;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.Map;
/**
* Bean - Jackson
*
* @author sunzhiqiang
* @version 1.0
* @date 2017/4/14
*/
@Slf4j
public class JacksonBeanConvertor {
private static ObjectMapper objectMapper;
static {
objectMapper = new ObjectMapper();
//指定时间格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//检测所有字段,包括私有的
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//序列化时忽略空属性
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//反序列化时忽略不一致成员变量
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
public <T> T convert(Object fromValue, Class<T> toValueType) {
return objectMapper.convertValue(fromValue, toValueType);
}
public <T> T convert(Object fromValue, Type typeOfT) {
JavaType toValueType = objectMapper.getTypeFactory().constructType(typeOfT);
return objectMapper.convertValue(fromValue, toValueType);
}
}

@ -0,0 +1,135 @@
package com.bjmashibing.util;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Registration;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy;
import org.objenesis.strategy.StdInstantiatorStrategy;
import java.io.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* <p></p>
*
* @author sunzhiqiang23
* @date 2020-06-19 9:50
*/
public class KryoUtil {
private static final String DEFAULT_ENCODING = "UTF-8";
private static Map<Class, Registration> registrationMap = new ConcurrentHashMap<Class, Registration>();
//每个线程的 Kryo 实例
private static final ThreadLocal<Kryo> kryoLocal = new ThreadLocal<Kryo>() {
@Override
protected Kryo initialValue() {
Kryo kryo = new Kryo();
/**
*
* 线 Redis
*
*/
//支持对象循环引用(否则会栈溢出)
kryo.setReferences(true);
//不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;而且业务系统中大量的 Class 也难以一一注册)
kryo.setRegistrationRequired(false);
//Fix the NPE bug when deserializing Collections.
((DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy())
.setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());
return kryo;
}
};
/**
* 线 Kryo
*
* @return 线 Kryo
*/
public static Kryo getInstance() {
return kryoLocal.get();
}
/**
*
*
* @param obj
* @param <T>
* @return
*/
public static <T> byte[] writeToByteArray(T obj) {
Class<T> clazz = (Class<T>) obj.getClass();
Kryo kryo = KryoUtil.getInstance();
if (!registrationMap.containsKey(clazz)) {
Registration registration = kryo.register(clazz);
registrationMap.put(clazz, registration);
}
ByteArrayOutputStream outputStream = null;
Output output = null;
byte[] bytes;
try {
outputStream = new ByteArrayOutputStream();
output = new Output(outputStream);
kryo.writeObject(output, obj);
output.flush();
bytes = outputStream.toByteArray();
return bytes;
} finally {
try {
if (output != null) {
output.close();
}
if (outputStream != null) {
outputStream.close();
}
} catch (Exception ignore) {
}
}
}
/**
*
*
* @param data writeToByteArray
* @param <T>
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T readObjectFromByteArray(byte[] data, Class<T> clazz) {
Kryo kryo = KryoUtil.getInstance();
Registration registration = registrationMap.get(clazz);
if (registration == null) {
registration = kryo.register(clazz);
registrationMap.put(clazz, registration);
}
T object = null;
ByteArrayInputStream byteArrayInputStream = null;
Input input;
try {
byteArrayInputStream = new ByteArrayInputStream(data);
input = new Input(byteArrayInputStream);
object = (T) kryo.readObject(input, registration.getType());
input.close();
} finally {
try {
if (byteArrayInputStream != null) {
byteArrayInputStream.close();
}
} catch (IOException ignore) {
}
}
return object;
}
}

@ -0,0 +1,42 @@
package com.bjmashibing.util;
import lombok.extern.slf4j.Slf4j;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* <p>-</p>
*
* @author sunzhiqiang23
* @date 2019/12/25 11:22
*/
@Slf4j
public class TimeTool {
private static final SimpleDateFormat FMT = new SimpleDateFormat("HH::mm:ss.SSS");
public interface Task {
void execute();
}
public static void check(String title, Task task) {
if (task == null) {
return;
}
title = (title == null) ? "" : ("【" + title + "】");
System.out.println(title);
long begin = System.currentTimeMillis();
task.execute();
long end = System.currentTimeMillis();
System.out.println("结束:" + FMT.format(new Date()));
double delta = (end - begin) / 1000.0;
System.out.println("耗时:" + delta + "秒");
System.out.println("-------------------------");
}
}

@ -0,0 +1,23 @@
# Tomcat
server:
tomcat:
uri-encoding: UTF-8
max-threads: 1000
min-spare-threads: 30
port: 8002
spring:
mvc:
static-path-pattern: /static/**
freemarker:
suffix: .html
request-context-attribute: request
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
# 集群配置
redis:
expireSeconds: 60
timeout: 5000ms #redis操作的超时时间
database: 0
host: 192.168.254.201
port: 6379

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Logback configuration. See http://logback.qos.ch/manual/index.html -->
<configuration scan="true" scanPeriod="10 seconds">
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="com.bjmashibing.shiro" level="debug"/>
</configuration>

@ -0,0 +1,14 @@
package com.bjmashibing;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class ApplicationTests {
@Test
void contextLoads() {
}
}

@ -0,0 +1,32 @@
package com.bjmashibing.deepcopy;
import java.io.*;
/**
* <p></p>
*
* @author sunzhiqiang23
* @date 2020-06-16 22:12
*/
public class DeepCopyBeanUtils {
public static <T extends Serializable> T clone(T obj) {
// 拷贝产生的对象
T clonedObj = null;
try {
// 读取对象字节数据
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
// 分配内存空间,写入原始对象,生成新对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
//返回新对象,并做类型转换
clonedObj = (T)ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return clonedObj;
}
}

@ -0,0 +1,91 @@
package com.bjmashibing.jdk;
import com.bjmashibing.entity.User;
import com.bjmashibing.entity.User1;
import org.junit.jupiter.api.Test;
import java.io.*;
/**
* <p></p>
*
* @author sunzhiqiang23
* @date 2020-06-15 22:08
*/
public class ExternalizableTest {
/**
*
* @throws Exception
*/
@Test
public void testSeri()throws Exception{
//初始化对象
User1 user = new User1();
user.setId(1L);
user.setUsername("序列化");
//序列化对象到文件中
File file = new File("result1.obj");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(user);
oos.close();
}
/**
*
* @throws Exception
*/
@Test
public void testDesSeri()throws Exception{
File file = new File("result1.obj");
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
User1 newUser = (User1) ois.readObject();
System.out.println(newUser.toString());
}
/**
* static
* @throws Exception
*/
@Test
public void testStaticSeri()throws Exception{
//初始化对象
User1 user = new User1();
user.setId(1L);
user.setUsername("序列化");
System.out.println(user);
//序列化对象到文件中
File file = new File("result1.obj");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(user);
oos.close();
//反序列化
User1.realName = "update";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
User1 newUser = (User1)ois.readObject();
System.out.println(newUser.realName);
}
/**
* static
* @throws Exception
*/
@Test
public void testExtendsSeri()throws Exception{
//初始化对象
User1 user = new User1();
user.setId(1L);
user.setUsername("序列化");
user.setSuperName("父亲");
System.out.println(user);
//序列化对象到文件中
File file = new File("result.obj");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(user);
oos.close();
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
User1 newUser = (User1)ois.readObject();
System.out.println(newUser.getSuperName());
}
}

@ -0,0 +1,91 @@
package com.bjmashibing.jdk;
import com.bjmashibing.entity.User;
import org.junit.jupiter.api.Test;
import java.io.*;
/**
* <p></p>
*
* @author sunzhiqiang23
* @date 2020-06-15 22:08
*/
public class SerializableTest {
/**
*
* @throws Exception
*/
@Test
public void testSeri()throws Exception{
//初始化对象
User user = new User();
user.setId(1L);
user.setUsername("序列化");
//序列化对象到文件中
File file = new File("result.obj");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(user);
oos.close();
}
/**
*
* @throws Exception
*/
@Test
public void testDesSeri()throws Exception{
File file = new File("result.obj");
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
User newUser = (User)ois.readObject();
System.out.println(newUser.toString());
}
/**
* static
* @throws Exception
*/
@Test
public void testStaticSeri()throws Exception{
//初始化对象
User user = new User();
user.setId(1L);
user.setUsername("序列化");
System.out.println(user);
//序列化对象到文件中
File file = new File("result.obj");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(user);
oos.close();
//反序列化
User.realName = "update";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
User newUser = (User)ois.readObject();
System.out.println(newUser.realName);
}
/**
* static
* @throws Exception
*/
@Test
public void testExtendsSeri()throws Exception{
//初始化对象
User user = new User();
user.setId(1L);
user.setUsername("序列化");
user.setSuperName("父亲");
System.out.println(user);
//序列化对象到文件中
File file = new File("result.obj");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(user);
oos.close();
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
User newUser = (User)ois.readObject();
System.out.println(newUser.getSuperName());
}
}

@ -0,0 +1,27 @@
package com.bjmashibing.performance;
import com.bjmashibing.entity.User;
import com.bjmashibing.entity.UserSuper;
import com.bjmashibing.util.KryoUtil;
import org.junit.jupiter.api.Test;
/**
* <p></p>
*
* @author sunzhiqiang23
* @date 2020-06-17 21:08
*/
public class PerformanceTest {
@Test
public void testKryo() {
UserSuper user = new UserSuper();
user.setSuperName("测试");
byte[] bytes = KryoUtil.writeToByteArray(user);
UserSuper user1 = KryoUtil.readObjectFromByteArray(bytes, UserSuper.class);
System.out.println(user1);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

@ -0,0 +1,420 @@
# 1.JAVA混乱的日志体系
---
换乱的java日志体系
case
SLF4J-JCL
LOG4J-CORE
LOGBACK
SLF4J-SIMPLE
JCL-OVER-SLF4J
LOGBACK-CORE
LOG4J
LOG4J-API
LOG4J-JUL
LOG4J-JCL
LOGBACK-ACCESS
LOGBACK-CLASSIC
SLF4-API
SLF4J-LOGJ12
LOGBACK-CLASSIC
LOG4J-SLF4J-IMPL
## 1.1 JAVA日志体系概述
问题:
常用的日志框架有哪些?
大家目前正在使用的
| 名称 | jar | 描述 |
| ----------------- | ------------------------------------------- | ---------------------- |
| log4j | log4j-1.2.17 | 早期常用的日志组件 |
| logback | logback-core,logback-classic,logback-access | 性能由于log4j |
| log4j2 | log4j,log4j-api,log4j-core | log4j升级 |
| Java.util.logging | jdk | jdk实现,tomcat默认实现 |
开发一个类似spring框架或者开发一个组件如何选择
选择任何一种实现,都不太好,不同的日志输出不一样,日志也会打印多份。如何解决,日志做抽象层
1.1.1 Apache Commons LoggingJCL
```
1-官网介绍
记录组件
在编写一个库时,记录信息是非常有用的。然而,外面有很多日志实现,一个库不能把某一个特定的日志实现强加给作为库一部分的整体应用。
Logging包是不同日志实现之间的一个超薄桥梁。一个使用commons-logging API的库可以在运行时与任何日志实现一起使用。Common-logging自带对许多流行的日志实现的支持为其他的日志实现编写适配器是一项相当简单的任务。
应用程序而不是库也可以选择使用commonons-logging。虽然日志记录实现的独立性对应用程序来说不像库那样重要但使用commons-logging确实允许应用程序在不重新编译代码的情况下改变到不同的日志实现。
请注意commons-logging不会尝试初始化或终止运行时使用的底层日志实现这是应用程序的责任。然而许多常用的日志实现都会自动初始化在这种情况下应用程序可以避免包含任何特定于所使用的日志实现的代码。
2-实现原理
org.apache.commons.logging.impl.LogFactoryImpl#discoverLogImplementation
3-缺点,只能实现一种,通过静态绑定实现,不易扩展,适配器模式。
```
1.1.2 SLFJ
```
slf4j全 称为Simple Logging Facade for JAVAjava简单日志门面。类似于Apache Common-Logging是对不同日志框架提供的一个门面封装(是接口而非实现)可以在部署的时候不修改任何配置即可接入一种日志实现方案。但是他在编译时静态绑定真正的Log库。使用SLF4J时如果需要使用某一种日志实现那么你必须选择正确的SLF4J的jar包的集合各种桥接包
----基于OSGI模块化框架详解
特点:
动态加载、更新、和卸载模块而不用停止服务
实现系统的模块化、版本化允许多版本bundule模块同时服务--注释很多三方jar中的MANIFEST.MF进行子描述就是一个例子
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: ceki
Build-Jdk: 1.8.0_121
Bundle-Description: The slf4j API
Bundle-Version: 1.7.30
Implementation-Version: 1.7.30
X-Compile-Source-JDK: 1.5
X-Compile-Target-JDK: 1.5
Implementation-Title: slf4j-api
Bundle-ManifestVersion: 2
Bundle-SymbolicName: slf4j.api
Bundle-Name: slf4j-api
Bundle-Vendor: SLF4J.ORG
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Automatic-Module-Name: org.slf4j
Export-Package: org.slf4j;version=1.7.30, org.slf4j.spi;version=1.7.30
, org.slf4j.helpers;version=1.7.30, org.slf4j.event;version=1.7.30
Import-Package: org.slf4j.impl;version=1.6.0
Service model允许模块、插件相互依赖但松耦合分享服务更简单
```
## 1.2 常用的日志组成方案与应用场景
### 1.2.1 jcl
### 1.2.2 slfj
![slfj桥接器](D:\shiro\资料\slfj桥接器.png)
```
桥接包
slfj-log4j12.jar(桥接log4j)
slf4j-jdk14.jar(桥接jdk Logging)
slf4j-jcl.jar(桥接jcl)
log4j-slf4j-impl.jar(桥接log4j2)
logback-classic.jar(桥接logback)
参考项目log-slfj
```
### 1.2.3 日志切换,适配器
![slfj适配器](D:\shiro\资料\slfj适配器.png)
```
如果当前系统之中再用jcl打印日志比如spring4但这是想加入slfj来打印日志就会出现两类日志输出如何既觉
只要classpath当中指定了slfj适配器即可无缝江源日志输出转移到slfj上来
jcl-over-slfj:转移 jcl日志至slf4j
举例子:
组件1mybatis---jcl+log4j
组件2springboot----sjlf+logback
组件2shiro----jcl+logback
默认jcl+jul
期望结果slfj+logback
1:slfj+桥接器+实现
log4j-over-slfj:转移log4j日志至slf4j
jul-over-slfj:转移jul日志至slf4j
```
| 名称 | 描述 | 相关JAR包 |
| -------- | ------------------------------- | ------------------------------------------------------------ |
| 门面 | slf4jAPI接口 | slf4j-api.jar |
| 桥接 | 用于slf4j连接对应日志实现 | slfj-log4j12.jar,slfj-jdk14.jar,log4j-slf4j-impl,logback-classic,slf4j-jcl.jar |
| 适配器 | 用于将原日志输出无缝转移到slf4j | cl-over-slf4j.jar,log4j-over-slfj,jul-over-slfj, |
| 具体实现 | 日志的具体实现 | log4j.jar,,logback,log4j2,java.util.logging |
### 1.2.4 循环依赖
如果clappsth中既有桥接器也有适配器日志会被踢来踢去陷入死循环
## 1.3.slfj+log4j2 统一系统应用日志
由于系统组件中可能采用了不同的日志体系spring5之前,spring采用的是apache-common-logspring5之后采用spring-jcl
## 1.4 日志规范
```
1. 【强制】应用中不可直接使用日志系统Log4j、Logback中的 API而应依赖使用日志框架
SLF4J、JCL--Jakarta Commons Logging中的 API使用门面模式的日志框架有利于维护和
各个类的日志处理方式统一。
说明日志框架SLF4J、JCL--Jakarta Commons Logging的使用方式推荐使用 SLF4J
使用 SLF4J
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class);
使用 JCL
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
private static final Log log = LogFactory.getLog(Test.class);
2. 【强制】所有日志文件至少保存 15 天,因为有些异常具备以“周”为频次发生的特点。对于
当天日志,以“应用名.log”来保存保存在/home/admin/应用名/logs/</font>目录下,
过往日志格式为: {logname}.log.{保存日期}日期格式yyyy-MM-dd
说明:以 mppserver 应用为例,日志保存在/home/admin/mppserver/logs/mppserver.log历史日志
名称为 mppserver.log.2016-08-01
3. 【强制】应用中的扩展日志(如打点、临时监控、访问日志等)命名方式:
appName_logType_logName.log。logType:日志类型,如 stats/monitor/access 等logName:日志描
述。这种命名的好处:通过文件名就可知道日志文件属于什么应用,什么类型,什么目的,也有利于归类查
找。
说明:推荐对日志进行分类,如将错误日志和业务日志分开存放,便于开发人员查看,也便于通过日志对系
统进行及时监控。
正例mppserver 应用中单独监控时区转换异常mppserver_monitor_timeZoneConvert.log
4. 【强制】在日志输出时,字符串变量之间的拼接使用占位符的方式。
说明:因为 String 字符串的拼接会使用 StringBuilder 的 append()方式,有一定的性能损耗。使用占位符仅
是替换动作,可以有效提升性能。
正例logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);
5. 【强制】对于 trace/debug/info 级别的日志输出,必须进行日志级别的开关判断。
说明:虽然在 debug(参数)的方法体内第一行代码 isDisabled(Level.DEBUG_INT)为真时Slf4j 的常见实现
Log4j 和 Logback就直接 return但是参数可能会进行字符串拼接运算。此外如果 debug(getName())
这种参数内有 getName()方法调用,无谓浪费方法调用的开销。
正例:
// 如果判断为真,那么可以输出 trace 和 debug 级别的日志
if (logger.isDebugEnabled()) {
logger.debug("Current ID is: {} and name is: {}", id, getName());
}
6. 【强制】避免重复打印日志,浪费磁盘空间,务必在 log4j.xml 中设置 additivity=false。
正例:<logger name="com.taobao.dubbo.config" additivity="false">
case:
<logger name="com.jd" level="DEBUG">
<AppenderRef ref="console"/>
</logger>
7. 【强制】生产环境禁止直接使用 System.out 或 System.err 输出日志或使用
e.printStackTrace()打印异常堆栈。
说明:标准日志输出与标准错误输出文件每次 Jboss 重启时才滚动,如果大量输出送往这两个文件,容易
造成文件大小超过操作系统大小限制。
8. 【强制】异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过
关键字 throws 往上抛出。
正例logger.error(各类参数或者对象 toString() + "_" + e.getMessage(), e);
case:不允许记录日志后又抛出异常,因为这样会多次记录日志,只允许记录一次日志
9. 【强制】日志打印时禁止直接用 JSON 工具将对象转换成 String。
说明:如果对象里某些 get 方法被重写,存在抛出异常的情况,则可能会因为打印日志而影响正常业务流
程的执行。
正例:打印日志时仅打印出业务相关属性值或者调用其对象的 toString()方法。
10.【推荐】谨慎地记录日志。生产环境禁止输出 debug 日志;有选择地输出 info 日志;如果使用
warn 来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘撑
爆,并记得及时删除这些观察日志。
说明:大量地输出无效日志,不利于系统性能提升,也不利于快速定位错误点。记录日志时请思考:这些
日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来好处?
11.【推荐】可以使用 warn 日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适
从。如非必要,请不要在此场景打出 error 级别,避免频繁报警。
说明注意日志输出的级别error 级别只记录系统逻辑出错、异常或者重要的错误信息。
12.【推荐】尽量用英文来描述日志错误信息,如果日志中的错误信息用英文描述不清楚的话使用
中文描述即可,否则容易产生歧义。
说明:国际化团队或海外部署的服务器由于字符集问题,使用全英文来注释和描述日志错误信息。
if (logger.isDebugEnabled()) {
logger.debug();
}
```
## 1.5 性能测试
#### 1.5.1 单线程
| 名称 | 未开启缓存,立即刷出 | 开启缓存,不立即刷出 | 异步-appender-未开启缓存,立即输出 | 异步appender-开启缓存,不立即输出 |
| ------- | -------------------- | -------------------- | ---------------------------------- | -------------------------------------------------------- |
| log4j | 87.342秒 | 10.757秒 | 91.752 | 10.058秒 |
| logback | 81.617 | 5.547 | 100.245 | 10.69 |
| log4j2 | 5.272/5.614/5.196 | 5.502/5.53/5.453 | 5.423/5.378/4.953 | 5.063/4.74/5.246(AsyncRoot)==>5.348/5.712/4.818Async |
#### 1.5.2 多线程
| 名称 | 未开启缓存,立即刷出 | 开启缓存,不立即刷出 | 异步appender-未开启缓存,立即输出 | 异步appender-开启缓存,不立即输出 |
| ------- | -------------------- | -------------------- | --------------------------------- | --------------------------------- |
| log4j | 102.823 | 13.324 | 87.966 | 10.651 |
| logback | 100.853 | 8.238 | 111.272 | 55.813 |
| log4j2 | 8.164/6.94/7.073 | 8.02/6.597/8.009 | 8.11/7.485 | 8.178/8.226 |
机构图对比:
![1589351161247](D:\shiro\资料\log4j2-loagack架构对比.png)
log4j2-AsyncLogger
```
Log4j2中的AsyncLogger的内部使用了Disruptor框架。
参考https://www.cnblogs.com/yeyang/p/7944906.html
Disruptor简介
Disruptor是英国外汇交易公司LMAX开发的一个高性能队列基于Disruptor开发的系统单线程能支撑每秒600万订单。
目前包括Apache Strom、Log4j2在内的很多知名项目都应用了Disruptor来获取高性能。
Disruptor框架内部核心数据结构为RingBuffer其为无锁环形队列。
单线程每秒能够处理600万订单Disruptor为什么这么快
a.lock-free-使用了CAS来实现线程安全
ArrayBlockingQueue使用锁实现并发控制当get或put时当前访问线程将上锁当多生产者、多消费者的大量并发情形下由于锁竞争、线程切换等会有性能损失。
Disruptor通过CAS实现多生产者、多消费者对RingBuffer的并发访问。CAS相当于乐观锁其性能优于Lock的性能。
b.使用缓存行填充解决伪共享问题
计算机体系结构中内存的访问速度远远低于CPU的运行速度在内存和CPU之间加入CacheCPU首先访问Cache中的数据CaChe未命中才访问内存中的数据。
伪共享Cache是以缓存行cache line为单位存储的当多个线程修改互相独立的变量时如果这些变量共享同一个缓存行就会无意中影响彼此的性能。
关于伪共享的深度分析,可参考《伪共享,并发编程的性能杀手》这篇文章。
日志输出方式
sync 同步打印日志,日志输出与业务逻辑在同一线程内,当日志输出完毕,才能进行后续业务逻辑操作
Async Appender 异步打印日志内部采用ArrayBlockingQueue对每个AsyncAppender创建一个线程用于处理日志输出。
Async Logger 异步打印日志采用了高性能并发框架Disruptor创建一个线程用于处理日志输出。
```
## 1.6 日志的全链路追踪 简介
#### 1.6.1 全链路追踪的解决方案
全链路追踪的背景
![全链路背景.png](D:\shiro\资料\全链路背景.png)
上图是一个典型的微服务调用链路,面对的场景问题如下:
1-如果D服务是一个关键服务返回结果错误无论是日志还是监控平台并不能很快捷的定位问题出现在了那里因为不能串联整个调用链路的流程
2-当对某一个服务架构升级或者改造的时候,不好评估影响范围,不明确服务之间的依赖关系,给技术决策带来了困难
3-性能瓶颈,整个调用链路那个环节耗时比较久
4-当一次请求结束后,不好确定执行顺序,都给业务逻辑上的理解带来了困难
需要解决问题:
1-串用调用链,快速定位问题
2-厘清服务依赖关系
3-进行各个服务接口的性能分析
4-跟踪业务流的处理顺序
已有方案
1-Google Dapper
2-Twitter Zipkin
3-Spring Cloud Sleuth
3-1 与springboot及spring组件无缝集成
3-2支持Zipkin输出mysqles
3-3 支持MQ和HTTP方式传输
#### 1.6.2 Spring Cloud Sleuth 介绍
##### 基本概念:
Trace链路
Span跨度
Annotation标注CS发送请求SR接受请求SS相应发送CR相应被客户端接收
##### 架构图
![1589347288854](D:\shiro\资料\sleuth架构图.png)
##### 流程图
![1589347457880](D:\shiro\资料\sleuth流程图.png)
##### 基本概念
![1589347636646](D:\shiro\资料\sleuth基本概念.png)
#### 1.6.3 分布式日志检索解决方案-ELK
EElasticsearch ---存储
LLogStash ---收集
KKibana ---展示
流程
![1589351638767](D:\shiro\资料\ELK流程.png)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

@ -0,0 +1,168 @@
### 1.序列化简介
---
#### 1.1 定义
序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据
#### 1.2 序列化的目的
通过序列化以字节流的形式使对象在网络中进行传递和接收
永久性保存对象,保存对象的字节序列到本地文件或者数据库中
通过序列化在进程间传递对象
深拷贝对象
跨语言传递数据
#### 1.3 序列化常见的方式
是否跨语言:
生成的中间文件二进制流不同:
### 2.常见的序列化方式特点
#### 2.1 JDK
参考脑图
#### 2.2 JSON
Jackson
```
相比json-lib框架Jackson所依赖的jar包较少简单易用并且性能也要相对高些。
而且Jackson社区相对比较活跃更新速度也比较快。
Jackson对于复杂类型的json转换bean会出现问题一些集合MapList的转换出现问题。
Jackson对于复杂类型的bean转换Json转换的json格式不是标准的Json格式
```
Gson
```
Gson是目前功能最全的Json解析神器Gson当初是为因应Google公司内部需求而由Google自行研发而来
但自从在2008年五月公开发布第一版后已被许多公司或用户应用。
Gson的应用主要为toJson与fromJson两个转换函数无依赖不需要例外额外的jar能够直接跑在JDK上。
而在使用这种对象转换之前需先创建好对象的类型以及其成员才能成功的将JSON字符串成功转换成相对应的对象。
类里面只要有get和set方法Gson完全可以将复杂类型的json到bean或bean到json的转换是JSON解析的神器。
Gson在功能上面无可挑剔但是性能上面比FastJson有所差距。
```
FastJson
```
Fastjson是一个Java语言编写的高性能的JSON处理器,由阿里巴巴公司开发。
无依赖不需要例外额外的jar能够直接跑在JDK上。
FastJson在复杂类型的Bean转换Json上会出现一些问题可能会出现引用的类型导致Json转换出错需要制定引用。
FastJson采用独创的算法将parse的速度提升到极致超过所有json库。
```
#### 2.3 kryo
```
1.速度快,序列化后体积小
2.跨语言支持较复杂
```
#### 2.4 Protobuf
```
1.结构化数据存储格式xml,json等
2.高性能编解码技术
3.语言和平台无关,扩展性好
4.支持java,C++,Python三种语言。
```
#### 2.5 **hessian**
```
1.默认支持跨语言
2.较慢
```
#### 2.6 Thrift
```
1.Thrift支持多种语言C++,C#,Cocoa,Erlag,Haskell,java,Ocami,Perl,PHP,Python,Ruby,和SmallTalk
2.Thrift适用了组建大型数据交换及存储工具对于大型系统中的内部数据传输相对于Json和xml在性能上和传输大小上都有明显的优势。
3.Thrift支持三种比较典型的编码方式。通用二进制编码压缩二进制编码优化的可选字段压缩编解码
4.支持RPC
```
#### 2.7 **fst**
```
1.fst是完全兼容JDK序列化协议的系列化框架序列化速度大概是JDK的4-10倍大小是JDK大小的1/3左右。
```
#### 2.8 MsgPack
```
官网https://msgpack.org
redis中使用此标准
1.速度快
2.序列化手的数据占用空间小
3.中间转换的是字符数组,不方便存储到文件之中
4.性能不够稳定(要求字段顺序一致,删减字段都需要注意)
```
#### 2.9 Avro
```
hadoop研发序列化
支持RPC
```
### 3 各大框架序列化方式
#### 3.1 dobbo
```
通讯协议dobbo协议rmi协议hessian协议http协议webservice协议
序列化方式hessianjdkjsonsoapkryofstProtobuf(新版本支持),kryo
```
#### 3.2 springcloud
```
通讯协议http
序列化方式json
```
### 3 性能测试
#### 3.1 性能衡量指标
不同的序列化方式,本身设计就是满足不同的应用场景,可序列化的数据类型,比如是否支持 StringBuffer和StringBuilder 等,所以单独对比速度快慢,也不太合理,但大体上衡量标准可以参考如下
```
1.是否支持跨语言,支持语种是否丰富
2.编码后的码流
3.编解码的性能
4.类库是否小巧API使用是否方便
5.使用者开发的工作量和难度。
```
性能参考:
https://blog.csdn.net/Brady74/article/details/90983288?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
https://github.com/eishay/jvm-serializers
其中JSON类库更适用于业务场景的开发类的拷贝复制不适合网络传输性能低于其他类别的序列化方式
其他的类库,更倾向于中间件的设计,网络传输

Binary file not shown.
Loading…
Cancel
Save