diff --git a/pom.xml b/pom.xml index 9b21f5f..b7ab0a1 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,8 @@ shiro-springboot-admin-9010 shiro-springboot-admin-jwt-9011 shiro-sprinboot-proxy - + shiro-serialization + diff --git a/shiro-quickstart/src/test/java/test/thread/TestThreadLocal.java b/shiro-quickstart/src/test/java/test/thread/TestThreadLocal.java index 8a0e12a..03222c7 100644 --- a/shiro-quickstart/src/test/java/test/thread/TestThreadLocal.java +++ b/shiro-quickstart/src/test/java/test/thread/TestThreadLocal.java @@ -1,6 +1,7 @@ package test.thread; import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; import java.util.HashMap; import java.util.Map; @@ -13,57 +14,100 @@ import java.util.concurrent.*; * @date 2020-06-11 22:40 */ public class TestThreadLocal { - static ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, + ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); + static ThreadPoolExecutor executor2 = new ThreadPoolExecutor(2, 2, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); + + //sso + static ThreadLocal threadLocal = new InheritableThreadLocal<>(); public static void main(String[] args) { - ThreadLocal> threadLocal = new InheritableThreadLocalMap<>(); - for (int i = 0; i < 10; i++) { - Map map = new HashMap<>(); - map.put("key", i); - threadLocal.set(map); - executor.execute(()->{ - System.out.println(threadLocal.get().get("key")); + + TestThreadLocal testThreadLocal= new TestThreadLocal(); + + for (int i = 0; i < 2; i++) { + String finalI = "jdPin-" + i; + executor2.execute(() -> { + Thread.currentThread().setName(finalI); + testThreadLocal.sso(finalI); + + testThreadLocal.YeWuMethod(Thread.currentThread().getName()); }); } - } - public static void testInThreadLocal(){ - ThreadLocal> threadLocal = new InheritableThreadLocalMap<>(); - Map 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 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> extends InheritableThreadLocal> { - - /** - * This implementation was added to address a - * - * user-reported issue. - * @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 childValue(Map parentValue) { - if (parentValue != null) { - return (Map) ((HashMap) parentValue).clone(); - } else { - return null; - } + private void YeWuMethod(String name) { + +// executor = new ThreadPoolExecutor(2, 2, +// 0L, TimeUnit.MILLISECONDS, +// new LinkedBlockingQueue()); + + for (int j = 0; j < 10; j++) { + + +// Runnable task = new RunnableTask(); +// Runnable ttlRunnable = TtlRunnable.get(task); + +// executor.execute(ttlRunnable); + + 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> threadLocal = new InheritableThreadLocalMap<>(); +// +// new Thread(() -> { +// System.out.println(threadLocal.get().get("key")); +// }, "input thread name").start(); +// } +// +// public static void testThreadLocal() { +// ThreadLocal threadLocal = new ThreadLocal<>(); +// threadLocal.set("测试threadLocal"); +// new Thread(() -> { +// System.out.println(threadLocal.get()); +// }, "input thread name").start(); +// +// } + +// private static final class InheritableThreadLocalMap> extends InheritableThreadLocal> { +// +// /** +// * This implementation was added to address a +// * +// * user-reported issue. +// * @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 childValue(Map parentValue) { +// if (parentValue != null) { +// return (Map) ((HashMap) parentValue).clone(); +// } else { +// return null; +// } +// } +// } } diff --git a/shiro-serialization/pom.xml b/shiro-serialization/pom.xml new file mode 100644 index 0000000..bb2c232 --- /dev/null +++ b/shiro-serialization/pom.xml @@ -0,0 +1,49 @@ + + + + shiro + com.bjmashibing.shiro + 0.0.1-SNAPSHOT + + 4.0.0 + + shiro-serialization + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + org.projectlombok + lombok + true + + + + + + com.esotericsoftware + kryo + 5.0.0-RC5 + + + + \ No newline at end of file diff --git a/shiro-serialization/result1.obj b/shiro-serialization/result1.obj new file mode 100644 index 0000000..9e0b5cf Binary files /dev/null and b/shiro-serialization/result1.obj differ diff --git a/shiro-serialization/src/main/java/com/bjmashibing/config/RedisConfig.java b/shiro-serialization/src/main/java/com/bjmashibing/config/RedisConfig.java new file mode 100644 index 0000000..d221219 --- /dev/null +++ b/shiro-serialization/src/main/java/com/bjmashibing/config/RedisConfig.java @@ -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; + +/** + *

redis配置

+ * + * @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; + } + +} \ No newline at end of file diff --git a/shiro-serialization/src/main/java/com/bjmashibing/entity/User.java b/shiro-serialization/src/main/java/com/bjmashibing/entity/User.java new file mode 100644 index 0000000..5e61cb6 --- /dev/null +++ b/shiro-serialization/src/main/java/com/bjmashibing/entity/User.java @@ -0,0 +1,36 @@ +package com.bjmashibing.entity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + *

+ * 用户信息表 + *

+ * + * @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"; + +} diff --git a/shiro-serialization/src/main/java/com/bjmashibing/entity/User1.java b/shiro-serialization/src/main/java/com/bjmashibing/entity/User1.java new file mode 100644 index 0000000..5016633 --- /dev/null +++ b/shiro-serialization/src/main/java/com/bjmashibing/entity/User1.java @@ -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; + +/** + *

+ * + * @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); + + } + + +} diff --git a/shiro-serialization/src/main/java/com/bjmashibing/entity/UserSuper.java b/shiro-serialization/src/main/java/com/bjmashibing/entity/UserSuper.java new file mode 100644 index 0000000..b9e622a --- /dev/null +++ b/shiro-serialization/src/main/java/com/bjmashibing/entity/UserSuper.java @@ -0,0 +1,20 @@ +package com.bjmashibing.entity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + *

+ * + * @author sunzhiqiang23 + * @date 2020-06-16 21:08 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class UserSuper { + + private String superName; + +} diff --git a/shiro-serialization/src/main/java/com/bjmashibing/util/BeanUtil.java b/shiro-serialization/src/main/java/com/bjmashibing/util/BeanUtil.java new file mode 100644 index 0000000..00d0663 --- /dev/null +++ b/shiro-serialization/src/main/java/com/bjmashibing/util/BeanUtil.java @@ -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; + +/** + *

Bean (POJO) 相关工具

+ * + * @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 + * @return + */ + public static T copyDeeply(Object fromValue, Class toValueType) { + return beanConvertor.convert(fromValue, toValueType); + } + + /** + * 拷贝列表,需要指定目标列表的元素类型 + * + * @param sources 原始 Bean 的列表 + * @param target 目标列表元素的类型 + * @param + * @return + */ + public static List copyTo(List sources, Class target) { + if (sources == null) { + return null; + } + if (sources.isEmpty()) { + return Collections.emptyList(); + } + + List list = new ArrayList<>(sources.size()); + for (Object source : sources) { + if (source == null) { + list.add(null); + } else { + list.add(copyDeeply(source, target)); + } + } + return list; + } + +} diff --git a/shiro-serialization/src/main/java/com/bjmashibing/util/JacksonBeanConvertor.java b/shiro-serialization/src/main/java/com/bjmashibing/util/JacksonBeanConvertor.java new file mode 100644 index 0000000..281dcf7 --- /dev/null +++ b/shiro-serialization/src/main/java/com/bjmashibing/util/JacksonBeanConvertor.java @@ -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 convert(Object fromValue, Class toValueType) { + return objectMapper.convertValue(fromValue, toValueType); + } + + public T convert(Object fromValue, Type typeOfT) { + JavaType toValueType = objectMapper.getTypeFactory().constructType(typeOfT); + return objectMapper.convertValue(fromValue, toValueType); + } + +} diff --git a/shiro-serialization/src/main/java/com/bjmashibing/util/KryoUtil.java b/shiro-serialization/src/main/java/com/bjmashibing/util/KryoUtil.java new file mode 100644 index 0000000..01e9aae --- /dev/null +++ b/shiro-serialization/src/main/java/com/bjmashibing/util/KryoUtil.java @@ -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; + +/** + *

+ * + * @author sunzhiqiang23 + * @date 2020-06-19 9:50 + */ +public class KryoUtil { + + private static final String DEFAULT_ENCODING = "UTF-8"; + private static Map registrationMap = new ConcurrentHashMap(); + + //每个线程的 Kryo 实例 + private static final ThreadLocal kryoLocal = new ThreadLocal() { + @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 对象的类型 + * @return 序列化后的字节数组 + */ + public static byte[] writeToByteArray(T obj) { + Class clazz = (Class) 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 原对象的类型 + * @return 原对象 + */ + @SuppressWarnings("unchecked") + public static T readObjectFromByteArray(byte[] data, Class 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; + } + +} \ No newline at end of file diff --git a/shiro-serialization/src/main/java/com/bjmashibing/util/TimeTool.java b/shiro-serialization/src/main/java/com/bjmashibing/util/TimeTool.java new file mode 100644 index 0000000..bb6f635 --- /dev/null +++ b/shiro-serialization/src/main/java/com/bjmashibing/util/TimeTool.java @@ -0,0 +1,42 @@ +package com.bjmashibing.util; + +import lombok.extern.slf4j.Slf4j; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + *

计算方法执行时间工具类-粗略测试批量导入导出考勤报表用

+ * + * @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("-------------------------"); + + } + + +} diff --git a/shiro-serialization/src/main/resources/application.yml b/shiro-serialization/src/main/resources/application.yml new file mode 100644 index 0000000..4f28ad4 --- /dev/null +++ b/shiro-serialization/src/main/resources/application.yml @@ -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 \ No newline at end of file diff --git a/shiro-serialization/src/main/resources/logback-spring.xml b/shiro-serialization/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..18e03ea --- /dev/null +++ b/shiro-serialization/src/main/resources/logback-spring.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/shiro-serialization/src/test/java/com/bjmashibing/ApplicationTests.java b/shiro-serialization/src/test/java/com/bjmashibing/ApplicationTests.java new file mode 100644 index 0000000..b86517f --- /dev/null +++ b/shiro-serialization/src/test/java/com/bjmashibing/ApplicationTests.java @@ -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() { + + } + +} diff --git a/shiro-serialization/src/test/java/com/bjmashibing/deepcopy/DeepCopyBeanUtils.java b/shiro-serialization/src/test/java/com/bjmashibing/deepcopy/DeepCopyBeanUtils.java new file mode 100644 index 0000000..c5aeed1 --- /dev/null +++ b/shiro-serialization/src/test/java/com/bjmashibing/deepcopy/DeepCopyBeanUtils.java @@ -0,0 +1,32 @@ +package com.bjmashibing.deepcopy; + +import java.io.*; + +/** + *

+ * + * @author sunzhiqiang23 + * @date 2020-06-16 22:12 + */ +public class DeepCopyBeanUtils { + public static 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; + } +} diff --git a/shiro-serialization/src/test/java/com/bjmashibing/jdk/ExternalizableTest.java b/shiro-serialization/src/test/java/com/bjmashibing/jdk/ExternalizableTest.java new file mode 100644 index 0000000..7e7599f --- /dev/null +++ b/shiro-serialization/src/test/java/com/bjmashibing/jdk/ExternalizableTest.java @@ -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.*; + +/** + *

+ * + * @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()); + } +} diff --git a/shiro-serialization/src/test/java/com/bjmashibing/jdk/SerializableTest.java b/shiro-serialization/src/test/java/com/bjmashibing/jdk/SerializableTest.java new file mode 100644 index 0000000..0c49441 --- /dev/null +++ b/shiro-serialization/src/test/java/com/bjmashibing/jdk/SerializableTest.java @@ -0,0 +1,91 @@ +package com.bjmashibing.jdk; + +import com.bjmashibing.entity.User; +import org.junit.jupiter.api.Test; + +import java.io.*; + +/** + *

+ * + * @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()); + } +} diff --git a/shiro-serialization/src/test/java/com/bjmashibing/performance/PerformanceTest.java b/shiro-serialization/src/test/java/com/bjmashibing/performance/PerformanceTest.java new file mode 100644 index 0000000..8509681 --- /dev/null +++ b/shiro-serialization/src/test/java/com/bjmashibing/performance/PerformanceTest.java @@ -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; + +/** + *

+ * + * @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); + + } + +} diff --git a/资料/ELK流程.png b/资料/ELK流程.png new file mode 100644 index 0000000..28de3ba Binary files /dev/null and b/资料/ELK流程.png differ diff --git a/资料/java-日志体系.md b/资料/java-日志体系.md new file mode 100644 index 0000000..0b86cb8 --- /dev/null +++ b/资料/java-日志体系.md @@ -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 Logging(JCL) + + ``` +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 JAVA,java简单日志门面。类似于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 +举例子: + +组件1:mybatis---jcl+log4j +组件2:springboot----sjlf+logback +组件2:shiro----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-log,spring5之后,采用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/目录下, +过往日志格式为: {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。 +正例: + + case: + + + + +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.818(Async) | + +#### 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之间,加入Cache,CPU首先访问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输出(mysql,es) + +​ 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 + +E:Elasticsearch ---存储 + +L:LogStash ---收集 + +K:Kibana ---展示 + +流程 + +![1589351638767](D:\shiro\资料\ELK流程.png) \ No newline at end of file diff --git a/资料/java-日志体系.pdf b/资料/java-日志体系.pdf new file mode 100644 index 0000000..952b3dd Binary files /dev/null and b/资料/java-日志体系.pdf differ diff --git a/资料/log4j2-loagack架构对比.png b/资料/log4j2-loagack架构对比.png new file mode 100644 index 0000000..30d9ef0 Binary files /dev/null and b/资料/log4j2-loagack架构对比.png differ diff --git a/资料/shiro-高级-孙志强.emmx b/资料/shiro-高级-孙志强.emmx index 20b0345..a82a3b5 100644 Binary files a/资料/shiro-高级-孙志强.emmx and b/资料/shiro-高级-孙志强.emmx differ diff --git a/资料/shiro-高级2-孙志强.emmx b/资料/shiro-高级2-孙志强.emmx new file mode 100644 index 0000000..771a6b4 Binary files /dev/null and b/资料/shiro-高级2-孙志强.emmx differ diff --git a/资料/sleuth基本概念.png b/资料/sleuth基本概念.png new file mode 100644 index 0000000..ca345c4 Binary files /dev/null and b/资料/sleuth基本概念.png differ diff --git a/资料/sleuth架构图.png b/资料/sleuth架构图.png new file mode 100644 index 0000000..6a553cb Binary files /dev/null and b/资料/sleuth架构图.png differ diff --git a/资料/sleuth流程图.png b/资料/sleuth流程图.png new file mode 100644 index 0000000..909a96f Binary files /dev/null and b/资料/sleuth流程图.png differ diff --git a/资料/slfj桥接器.png b/资料/slfj桥接器.png new file mode 100644 index 0000000..937d726 Binary files /dev/null and b/资料/slfj桥接器.png differ diff --git a/资料/slfj适配器.png b/资料/slfj适配器.png new file mode 100644 index 0000000..59d1951 Binary files /dev/null and b/资料/slfj适配器.png differ diff --git a/资料/全链路背景.png b/资料/全链路背景.png new file mode 100644 index 0000000..41ffd90 Binary files /dev/null and b/资料/全链路背景.png differ diff --git a/资料/序列化.md b/资料/序列化.md new file mode 100644 index 0000000..c529e24 --- /dev/null +++ b/资料/序列化.md @@ -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会出现问题,一些集合Map,List的转换出现问题。 +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协议 +序列化方式:hessian,jdk,json,soap,kryo,fst,Protobuf(新版本支持),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类库,更适用于业务场景的开发,类的拷贝复制,不适合网络传输,性能低于其他类别的序列化方式 + +其他的类库,更倾向于中间件的设计,网络传输 + + diff --git a/资料/序列化.pdf b/资料/序列化.pdf new file mode 100644 index 0000000..bc07ec8 Binary files /dev/null and b/资料/序列化.pdf differ