Giter Site home page Giter Site logo

practice's People

Watchers

 avatar

practice's Issues

[practice-mistake] part5

OOM

  1. 获取OOM时刻的内存分布: *.hprof
    -ea -Xmx100m -Xms100m -XX:+HeapDumpOnOutOfMemoryError
    -ea -Xmx200m -Xms200m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=. -XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M 
  2. 分析该文件1
    // jdk 自带的: 不是很好用
    jhat -port 7401 -J-Xmx4G java_pid85580.hprof
  3. 分析该文件2: MAT
    • software || bak
    • histogram[直方图] - 找到自己的类[show retained set] - 就可以看到那个对象栈多大的空间
    • Shallow heap 是一个对象本身占用的堆内存大小
    • Retained set: 对于某个对象X来说, 如果X被垃圾收集器回收了, 那么这个集合中的对象都会被回收
    • Retained heap: 对象X的保留内存大小
  4. 分析快照文件的思路
    • 内存占用过大的对象是什么: histogram - 自己的类 - [show retained set]
    • 这个对象是被谁引用: dominator_tree 查看该对象被谁引用[找到对应的Thread]
    • 定位到具体的代码: thread_overview 查看上一步的 thread_overview, 根据内存最大的开始看[该线程从下向上看]
  5. oom 场景
    • 我们的程序确实需要超出 JVM 配置的内存上限的内存: 内存不合理, 框架重复处理[相同的数据占多分空间], 大量缓存, 导出, 文件下载等 容量评估是需要注意
    • 出现内存泄露: 我们认为盖被回收的对象没有被回收, 缓存[weak reference 的k-v相互引用]
    • 不合理的资源需求配置: 比如 header-size
  6. oom 分类
    • 堆 oom
    • 站 oom
    • gc 评率oom
    • direct memory oom
    • native thread oom
    • Metaspace oom
  7. OOM 示例: oom 一般都是因为第三方插件引起的
    • 用户搜索功能[下拉框提示补全用户名的功能]: 软引用: 新增用户这部分是定时重做的
      1. 如果有两个用户 aa 和 ab,那么 Key 就有三个,分别是 a、aa 和 ab
      2. 从数据库内查询所有用户, 根据 name 构建 Map#computeIfAbsent[这一步构建VO对象]
      3. 从数据库内查询所有用户, 构建 Vo 对象, 之后再构建 Map#computeIfAbsent
      4. 也可以构建 Trie Tree
    • 数据导出: 在进行容量评估时,我们不能认为一份数据在程序内存中也是一份
      0. 这些数据在数据库中占用 100M 内存,但是 1GB 的 JVM 堆却无法完成导出操作
      1. 100M 的数据加载到程序内存中,变为 Java 的数据结构就已经占用了 200M 堆内存
      2. 这些数据经过 JDBC、MyBatis 等框架其实是加载了 2 份
      3. 然后领域模型、DTO 再进行转换可能又加载了 2 次
      4. 最终,占用的内存达到了 200M*4=800M
    • tomcat oom: 并发场景下[undertow 是不会的]
      1. Mat 看到内存中有大量的 1.7GB 的 byte, 找到的引用线程是 Tomcat 的工作线程, 分配了两个 10M 的数组: Http11InputBuffer/Http11OutputBuffer [request/response]
      2. Http11InputBuffer 大小的初始化: headerBufferSize[MaxHttpHeaderSize, 自己手动配了]+readBuffer[8192byte]
      3. server.max-http-header-size=10000000
      4. 修改参数一定要注意: 一定要根据实际需求来修改参数配置,可以考虑预留 2 到 5 倍的量
      5. 容量类的参数背后往往代表了资源,设置超大的参数就有可能占用不必要的资源,在并发量大的时候因为资源大量分配导致 OOM
    • 架构师一开始定义了这么一个 SayService 抽象类,其中维护了一个类型是 ArrayList 的字段 data,用于保存方法处理的中间数据。每次调用 say 方法都会往 data 加入新数据,可以认为 SayService 是有状态,如果 SayService 是单例的话必然会 OOM:

[practice-mistake] part3

10. List

  1. RandomAccess & Deque

    image

  2. Arrays

    • 不能直接使用 Arrays.asList 来转换基本类型数组
    • Arrays.asList 返回的 List 不支持增删操作: 但是可以修改
    • 对原始数组的修改会影响到我们获得的那个 List
  3. List

    • subList 会导致被切割对象无法被回收, 可能导致oom
    • SubList 本身没有存储元素, 是 ArrayList 的视图
    • 修改值会彼此影响, 各自有 modCount, 但是操作时两个值必须是一样的
    • 要对大 List 进行单值搜索的话, 可以考虑使用 HashMap:
      • 其中 Key 是要搜索的值, Value 是原始对象,
      • 会比使用 ArrayList 有非常明显的性能优势
    • LinkedList 性能不太好, 可以考虑使用 ArrayDeque; 除非使用具体位置[ArrayDeque不适应]{ArrayList适应}
    • ArrayList 遍历时删除会出现 ConcurrentModificationException: 考虑使用迭代器去修改

[practice-mistake] part4

15. Serialize & Enum

  1. redis serialize
  2. spring 中的 ObjectMapper 尽量不去覆盖之前的 Bean
    • FAIL_ON_UNKNOWN_PROPERTIES: 反序列化时忽略未知字段
    // 或者 spring.jackson.serialization.write_enums_using_index=true
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizer(){
        return builder -> builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
    }
  3. 默认情况下,在反序列化的时候,Jackson 框架只会调用无参构造方法创建对象
    • 如果走自定义的构造方法创建对象, 需要通过 @JsonCreator 来指定构造方法
    • 并通过 @JsonProperty 设置构造方法中参数对应的 JSON 属性名
  4. 枚举 enum
    • 客户端和服务端的枚举定义不一致时, 会出异常
    • 枚举序列化反序列化实现自定义的字段非常麻烦,会涉及 Jackson 的 Bug
  5. 使用枚举的字段交互 API
    • 设置全局的 enum 未知转默认配置: read_unknown_enum_values_using_default_value
    • 定义 enum 默认值: @JsonEnumDefaultValue UNKNOWN(-1, "未知");
    • 配置 enum 中要使用的交互字段: @JsonValue
    • 配置 restTemplate 中的 jackson converter
    • 反序列化没有使用 @JsonValue 字段: 自定义一个 EnumDeserializer, 并注册到 jackson 配置中
    • deatil code
      @Slf4j
      @Getter
      @AllArgsConstructor
      public enum StatusEnumClient {
          CREATED(1, "已创建"),
          PAID(2, "已支付"),
          DELIVERED(3, "已送到"),
          FINISHED(4, "已完成"),
          @JsonEnumDefaultValue
          UNKNOWN(-1, "未知");
      
          @JsonValue private final int status;
          private final String desc;
      }
      
       @Bean
       public Jackson2ObjectMapperBuilderCustomizer customizer() {
           return builder -> {
               builder.locale(Locale.CHINA);
               builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
               builder.simpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);
               builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
               // spring.jackson.deserialization.read_unknown_enum_values_using_default_value=true
               builder.featuresToEnable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
      
               SimpleModule module = new SimpleModule();
               module.addDeserializer(Enum.class, new EnumDeserializer());
               builder.modules(module);
           };
       }
      
       @Bean
       public RestTemplate restTemplate(MappingJackson2HttpMessageConverter converter) {
           return new RestTemplateBuilder().additionalMessageConverters(converter).build();
       }
      
       @Slf4j
       @NoArgsConstructor
       @AllArgsConstructor
       public class EnumDeserializer extends JsonDeserializer<Enum> implements ContextualDeserializer {
           private Class<Enum> targetClass;
      
           @Override
           public Enum deserialize(JsonParser p, DeserializationContext ctxt) {
               // 找枚举中带有@JsonValue注解的字段,这个字段是我们反序列化的基准字段
               Optional<Field> valueFieldOpt = Arrays.asList(targetClass.getDeclaredFields()).stream()
                       .filter(m -> m.isAnnotationPresent(JsonValue.class)).findFirst();
      
               if (valueFieldOpt.isPresent()) {
                   Field valueField = valueFieldOpt.get();
                   if (!valueField.isAccessible()) {
                       valueField.setAccessible(true);
               }
               // 遍历枚举项,查找字段的值等于反序列化的字符串的那个枚举项
               return Arrays.stream(targetClass.getEnumConstants())
                       .filter(e -> valueField.get(e).toString().equals(p.getValueAsString()))
                       .findFirst()
                       .orElseGet(
                           () -> Arrays.stream(targetClass.getEnumConstants())
                               .filter(e -> {
                                   // 如果找不到,那么就需要寻找默认枚举值来替代,同样遍历所有枚举项,查找@JsonEnumDefaultValue注解标识的枚举项
                                   return targetClass.getField(e.name()).isAnnotationPresent(JsonEnumDefaultValue.class);
                               })
                               .findFirst().orElse(null));
              }
      
              return null;
          }
      
          @Override
          public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
              targetClass = (Class<Enum>) ctxt.getContextualType().getRawClass();
              return new EnumDeserializer(targetClass);
          }
      
      }

[practice-mistake] part6

18.反射 Reflect & 注解 annotation & OOP

  1. 反射
    • 反射调用方法不是以传参决定重载
  2. 泛型: 代码复用的有效手段
    • 泛型擦除
  3. 反射+泛型 有天坑啊
    • getDeclaredMethods 获取自己所有可以获取的方法, 如果是泛型继承, 则会获取该类的方法和Object 参数的方法: method.isBridge 可以区分
    • 原因: 子类 String 类型, 父类是 Object, 参数不匹配, 所以jdk 为子类生成了一个 Object 参数的方法[内部调用String 参数] 实现重载
  4. 注解
    • @inherited: 打这个 Annotation 的类表示具有继承性. 如果某个类使用, 则其子类将自动具有该注解
      1. 该继承性值发生于类上, 方法等其他的地方都不行[即使是 override]
      2. AnnotatedElementUtils.findMergedAnnotation(child.getClass(), MyAnnotation.class))); 可以在没有 @inherited 时也能获取父类的注解信息和方法上的注解信息
      3. AnnotatedElementUtils 可以帮助我们找出父类和接口、父类方法和接口方法上的注解,并可以处理桥接方法,实现一键找到继承链的注解

[practice-mistake] 并发工具类库

1. Juc

  1. ThreadLocal: 线程池中使用之后没有 remove, 导致用户信息错乱, 且会导致内存泄漏
  2. ConcurrentHashMap 只保证提供原子性的读写操作是线程安全的
    • containsKey/size/isEmpty/containsValue/putAll 等都有可能获取中间态数据, 不能作为流程控制
    • sample: 统计大文本每个key出现的次数{CHM+LongAdder+putInAbsent+多线程}
  3. CopyOnWriteArrayList 在大量写的情况下会有性能问题
    • 修改时会先复制出来一份
  4. Map 方法
    • putIfAbsent: HashMap 可以KV 为null, CHM 不可以为null会直接报错
    • computeIfAbsent: Functional 为null 则不会真正的put 进去

[common-actuator] actuator module

  1. actuator admin
  2. actuator client
  3. docs
  4. yml
version: '3.0'
services:
  actuator-server:
    image: registry.cn-shanghai.aliyuncs.com/alice52/practice-actuator-server:20210608.09ec081
    restart: 'on-failure:3'
    container_name: dev-actuator-server
    ports:
      - 8010:8010
    environment:
      TZ: Asia/Shanghai

  project-worklow:
    image: registry.cn-shanghai.aliyuncs.com/alice52/practice-custom-test:20210608.09ec081
    restart: 'on-failure:3'
    container_name: dev-project-worklow
    volumes:
      - /root/project-name/logs:/log
    ports:
      - 8010:8080
    environment:
      - TZ=Asia/Shanghai
      - spring.profiles.active=cloud
      - spring.boot.admin.client.instance.prefer-ip=true
      # This is client server public ip
      - spring.boot.admin.client.instance.service-base-url=http://xxx:8010

[common-core] redis cache

  1. should config redis as cache management
    /**
     * Notice: if donnot config redis as CacheManager, it will use memory as CacheManager.
     *
     * @param factory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {

        RedisCacheConfiguration config =
                RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ZERO)
                        .serializeKeysWith(
                                RedisSerializationContext.SerializationPair.fromSerializer(
                                        stringRedisSerializer))
                        .serializeValuesWith(
                                RedisSerializationContext.SerializationPair.fromSerializer(j2jrs))
                        .disableCachingNullValues();

        return RedisCacheManager.builder(factory).cacheDefaults(config).build();
    }
  1. yml config
spring:
  redis:
    host: ENC(7KovIsPRdqwCfirn4hqyvyImWNCj0p/ag5rLhqKqMjiZ9HcC54uBFalQrr9h00lq)
    password: ENC(JIczcGuavcI1nX9NbM0YcDyHiQ834QTPhqmczAw+Qr8Nm6wPeq5p5M076HzPSbFF)
    port: ENC(AcXQfYO9iFqD0RYGjek03cmHP3k9y7fl9HEy41cg/mr0xoT4ApjXUbtlpKNK8OCp)
    database: 13
    enable: true
    redisson: classpath:redisson-dev.yml
#  cache:
#    # fix error: Cannot find cache named 'xx' for Builder Or Config Redis as cache management
#    type: simple

[common-oss] oss & cos

Strategy

  • 策略模式

Aliyun OSS

  • OSS
  • cdn
  • 防盗链

Tencent COS

  • OSS
  • cdn
  • 防盗链
  • 涉黄

[common-api] boot starter

requirement

  1. this jar can be reference by any boot project

  2. version:

    • swagger: 3.0.0
    • spring boot: 2.3.0 and at least 2.2.2
  3. default global Unified exception handler

  4. base exception: based on Assert + Enum

  5. task

    • integration swagger
    • swagger error response definition
    • base exception
    • exception handler
  6. diagram

    image

  7. expectation

    • swagger, common response and common utils can be import alone, which is no business with spring boo version
    • swagger can be controlled by config: swagger properties
    • common response should be satisfied follow condition: can enable or disable this feature
      1. HTTP's status should be hold, such as 200, 401, 400, 500, 404
      2. Common response struct
        // version 1: failed api status can be 200 or 400 by configuring
        {
          "code": 200,
          "message": "success",
          "data": {
              "name": "zack"
          }
        }
        
        {
          "code": 50002,
          "message": "User is not exist!",
          "data": null
        }
        
        {
          "code": 40001,
          "message": "Not Found!",
          "data": null
        }
        
        
        // version 2, outdate, and not implemnt
        {
          "name": "zack"
        }
        
        {
          "code": 50002,
          "message": "User is not exist!",
        }
        
        {
          "code": 40001,
          "message": "Not Found!",
        }

[practice-mistake] part2

6. Spring Transaction

  1. 事务没生效
    • 除非特殊配置[比如使用 AspectJ 静态织入实现 AOP], 否则只有定义在 public 方法上的 @Transactional 才能生效
    • 必须通过代理过的类从外部调用目标方法才能生效
    • Spring Transaction 的实现有 jdk 和 cglib 两种{实现类上使用, spring 默认切换}
  2. 异常回滚失效
    • 只有异常传播标记了 @Transactional 注解的方法, 事务才能回滚
    • 默认情况下, 出现 RuntimeException[非受检异常] 或 Error 的时候, Spring 才会回滚事务
  3. 事务没提交
    • 子方法内需要父方法的数据, 可以考虑传值或者 @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMI) [事件]; 而不是去修改事务的隔离级别
    • 注册一个用户, 附带着会创建一个子用户[目标是单独单独的事务{不影响主用户的流程}]
    • 正确的实现方式是: propagation = Propagation.REQUIRES_NEW
    • 无法提交: 但是当使用默认传播级别, 且字用户创建使用 try...catch 时, 且子用户中throw异常, 则主用户事务是无法提交的{在同一个事务中, 子用户创建异常会标记事务rollback}
    @Transactional
    public void createUserWrong2(UserEntity entity) {
       createMainUser(entity);
       try{
           subUserService.createSubUserWithExceptionWrong(entity);
       } catch (Exception ex) {
           // 虽然捕获了异常,但是因为没有开启新事务,而当前事务因为异常已经被标记为rollback了,所以最终还是会回滚。
           log.error("create sub user error:{}", ex.getMessage());
       }
    }
    
    private void createMainUser(UserEntity entity) {
       userRepository.save(entity);
       log.info("createMainUser finish");
    }
    
    @Transactional
    public void subUserService#createSubUserWithExceptionWrong(UserEntity entity) {
       log.info("createSubUserWithExceptionWrong start");
       userRepository.save(entity);
       throw new RuntimeException("invalid status");
    }

[common-redis] 使用场景

  1. Alice52/database#68
  2. Alice52/system-design#5 签到

uv 统计

  1. 数据结构的选型: set || HyperLogLog
  2. set 存放用户唯一标识准确, 且可以看到是具体的人
    • 访问人很多时, 需要很大的存储空间, 且会变成 bigkey,也会一定程度上浪费时间与性能和并发度
  3. HyperLogLog: 伯努利实验
    • 节约空间, 时间, 性能: 12k 的内存就可以统计2^64个数据[误差率0.81%]
    • 统计的数据不是精确的[有一定的误差], 但是此业务是允许的
    • explian
      image
      image

签到系统

  1. Alice52/system-design#5

使用场景

  • 1. N/A

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.