主頁 > 後端開發 > 十二條后端開發經驗分享,純干貨,總有一條你不知道

十二條后端開發經驗分享,純干貨,總有一條你不知道

2022-12-01 06:47:08 後端開發

前言

本文是博主從事后端開發以來,對公司、個人專案的經驗總結,包含代碼撰寫、功能推薦、第三方庫使用及優雅配置等,希望大家看到都能有所識訓

  • 博主github地址: https://github.com/wayn111

一. 優雅的進行執行緒池例外處理

在Java開發中,執行緒池的使用必不可少,使用無回傳值 execute() 方法時,執行緒執行發生例外的話,需要記錄日志,方便回溯,一般做法是在執行緒執行方法內 try/catch 處理,如下:

@Test
public void test() throws Exception {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60,
            TimeUnit.SECONDS, new ArrayBlockingQueue<>(100000));
    Future<Integer> submit = threadPoolExecutor.execute(() -> {
        try {
            int i = 1 / 0;
            return i;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    });
}

但是當執行緒池呼叫方法很多時,那么每個執行緒執行方法內都要 try/catch 處理,這就不優雅了,其實ThreadPoolExecutor類還支持傳入 ThreadFactory 引數,自定義執行緒工廠,在創建 thread 時,指定 setUncaughtExceptionHandler 例外處理方法,這樣就可以做到全域處理例外了,代碼如下:

ThreadFactory threadFactory = r -> {
    Thread thread = new Thread(r);
    thread.setUncaughtExceptionHandler((t, e) -> {
        // 記錄執行緒例外
        log.error(e.getMessage(), e);
    });
    return thread;
};
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60,
        TimeUnit.SECONDS, new ArrayBlockingQueue<>(100000),
        threadFactory);
threadPoolExecutor.execute(() -> {
    log.info("---------------------");
    int i = 1 / 0;
});

二. 執行緒池決絕策略設定錯誤導致業務介面執行超時

先介紹下執行緒池得四種決絕策略

  • AbortPolicy:丟棄任務并拋出RejectedExecutionException例外,這是執行緒池默認的拒絕策略
  • DiscardPolicy:丟棄任務,但是不拋出例外,如果執行緒佇列已滿,則后續提交的任務都會被丟棄,且是靜默丟棄,
    使用此策略,可能會使我們無法發現系統的例外狀態,建議是一些無關緊要的業務采用此策略
  • DiscardOldestPolicy:丟棄佇列最前面的任務,然后重新提交被拒絕的任務,此拒絕策略,是一種喜新厭舊的拒絕策略,是否要采用此種拒絕策略,還得根據實際業務是否允許丟棄老任務來認真衡量,
  • CallerRunsPolicy:由呼叫執行緒處理該任務

如下是一個線上業務介面使用得執行緒池配置,決絕策略采用 CallerRunsPolicy

// 某個線上執行緒池配置如下
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                50, // 最小核心執行緒數
                50, // 最大執行緒數,當佇列滿時,能創建的最大執行緒數
                60L, TimeUnit.SECONDS, // 空閑執行緒超過核心執行緒時,回收該執行緒的最大等待時間
                new LinkedBlockingQueue<>(5000), // 阻塞佇列大小,當核心執行緒使用滿時,新的執行緒會放進佇列
            new CustomizableThreadFactory("task"), // 自定義執行緒名
                new ThreadPoolExecutor.CallerRunsPolicy() // 執行緒執行的拒絕策略
        );

在某些情況下,子執行緒任務呼叫第三方介面超時,導致核心執行緒數、最大執行緒數占滿、阻塞佇列占滿的情況下執行拒絕策略時,由于使用 CallerRunsPolicy 策略,導致業務執行緒執行子任務時繼續超時,進而導致介面執行例外,這種情況下,考慮到子執行緒任務得重要性,不是很重要得話,可以使用 DiscardPolicy 策略,要是很重要,可以發送到訊息佇列中持久化子執行緒任務資料待后續處理

三. 優雅的單例模式懶加載幫助類代碼實作

博主推薦通過靜態內部類實作單例模式,并實作懶加載效果,代碼如下

// 使用靜態內部類完成單例模式封裝,避免執行緒安全問題,避免重復初始化成員屬性  
@Slf4j  
public class FilterIpUtil {  
  
    private FilterIpUtil() {  
    }  
  
    private List<String> strings = new ArrayList<>();  
  
    // 代碼塊在FilterIpUtil實體初始化時才會執行  
    {  
    
        // 在代碼塊中完成檔案的第一次讀寫操作,后續不再讀這個檔案
        System.out.println("FilterIpUtil init");  
        try (InputStream resourceAsStream = FilterIpUtil.class.getClassLoader().getResourceAsStream("filterIp.txt")) {  
            // 將檔案內容放到string集合中  
            IoUtil.readUtf8Lines(resourceAsStream, strings);  
        } catch (IOException e) {  
            log.error(e.getMessage(), e);  
        }  
    }  
  
    public static FilterIpUtil getInstance() {  
        return InnerClassInstance.instance;  
    }  
    // 使用內部類完成單例模式,由jvm保證執行緒安全  
    private static class InnerClassInstance {  
        private static final FilterIpUtil instance = new FilterIpUtil();  
    }  
  
    // 判斷集合中是否包含目標引數  
    public boolean isFilter(String arg) {  
        return strings.contains(arg);  
    }  
  
}

四. 使用ip2region實作請求地址決議

在博主之前公司得專案中,ip決議是呼叫淘寶IP還有聚合IP介面獲取結果,通常耗時200毫秒左右,并且介面不穩定時而會掛,都會影響業務介面耗時,后來在 github 上了解到 ip2region 這個專案,使用本地ip庫查詢,查詢速度微秒級別, 精準度能達到90%,但是ip庫還是有少部分ip資訊不準,建議資料庫中把請求ip地址保存下來,簡介如下:

ip2region v2.0 - 是一個離線IP地址定位庫和IP定位資料管理框架,10微秒級別的查詢效率,提供了眾多主流編程語言的 xdb 資料生成和查詢客戶端實作基于 xdb 檔案的查詢,下面是一個 Spring 專案中 ip2region 幫助類來實作ip地址決議

/**
 * ip2region工具類
 */
@Slf4j
@Component
public class Ip2region {

    private Searcher searcher = null;

    @Value("${ip2region.path:}")
    private String ip2regionPath = "";

    @PostConstruct
    private void init() {
        // 1、從 dbPath 加載整個 xdb 到記憶體,
        String dbPath = ip2regionPath;

        // 1、從 dbPath 加載整個 xdb 到記憶體,
        byte[] cBuff;
        try {
            cBuff = Searcher.loadContentFromFile(dbPath);
            searcher = Searcher.newWithBuffer(cBuff);
        } catch (Exception e) {
            log.error("failed to create content cached searcher: {}", e.getMessage(), e);
        }
    }

    public IpInfoBean getIpInfo(String ip) {
        if (StringUtils.isBlank(ip)) {
            return null;
        }

        // 3、查詢
        try {
            long sTime = System.nanoTime();
            // 國家|區域|省份|城市|ISP
            String region = searcher.search(ip);
            long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime));
            log.info("{region: {}, ioCount: {}, took: {} μs}", region, searcher.getIOCount(), cost);
            if (StringUtils.isNotBlank(region)) {
                String[] split = region.split("\|");
                IpInfoBean ipInfo = new IpInfoBean();
                ipInfo.setIp(ip);
                if (!"".equals(split[0])) {
                    ipInfo.setCountry(split[0]);
                }
                if (!"".equals(split[2])) {
                    ipInfo.setProvince(split[2]);
                }
                if (!"".equals(split[3])) {
                    ipInfo.setCity(split[3]);
                }
                if (!"".equals(split[4])) {
                    ipInfo.setIsp(split[4]);
                }
                return ipInfo;
            }
        } catch (Exception e) {
            log.error("failed to search({}): {}", ip, e);
            return null;
        }

        // 4、關閉資源 - 該 searcher 物件可以安全用于并發,等整個服務關閉的時候再關閉 searcher
        // searcher.close();

        // 備注:并發使用,用整個 xdb 資料快取創建的查詢物件可以安全的用于并發,也就是你可以把這個 searcher 物件做成全域物件去跨執行緒訪問,
        return null;
    }
}

要注意得就是 ip2region v2.0 版本使用的xdb檔案不建議放在專案 resources 下一起打包,存在編碼格式問題,建議通過指定路徑加載得方式單獨放在服務器目錄下

五. 優雅得Springboot + mybatis配置多資料源方式

Springboot + mybatis 得專案中一般通過 @MapperScan 注解配置 dao 層包目錄,來實作 dao 層增強,其實專案中配置一個@MapperScan 是指定一個資料源,配置兩個@MapperScan就可以指定兩個資料源,通過不同得 dao 層包目錄區分,來實作不同資料源得訪問隔離,

比如下面代碼中,com.xxx.dao.master 目錄下為主資料源 dao 檔案,com.xxx.dao.slave 為從資料源 dao 檔案,這個方式比網上得基于 aop 加注解得方式更加簡潔好用,也沒有單個方法中使用不同資料源切換得問題,因此推薦這種寫法

/**
 * 主資料源
 */
@Slf4j
@Configuration
@MapperScan(basePackages = {"com.xxx.dao.master"},
        sqlSessionFactoryRef = "MasterSqlSessionFactory")
public class MasterDataSourceConfig {

    @Bean(name = "MasterDataSource")
    @Qualifier("MasterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource clickHouseDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "MasterSqlSessionFactory")
    public SqlSessionFactory getSqlSessionFactory(@Qualifier("MasterDataSource") DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/master/*.xml"));
        log.info("------------------------------------------MasterDataSource 配置成功");
        return sessionFactoryBean.getObject();
    }
}

/**
 * 從資料源
 */
@Slf4j
@Configuration
@MapperScan(basePackages = {"com.xxx.dao.slave"},
        sqlSessionFactoryRef = "SlaveSqlSessionFactory")
public class MasterDataSourceConfig {

    @Bean(name = "SlaveDataSource")
    @Qualifier("SlaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource clickHouseDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "SlaveSqlSessionFactory")
    public SqlSessionFactory getSqlSessionFactory(@Qualifier("SlaveDataSource") DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/slave/*.xml"));
        log.info("------------------------------------------SlaveDataSource 配置成功");
        return sessionFactoryBean.getObject();
    }
}

資料源yml配置

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    # 主庫資料源
    master:
      url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
      username: root
      password:
    slave:
      url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
      username: root
      password:

博主剛開始編碼一、兩年得時候一個專案中遇到了多資料源使用得問題,那時候題主便在網上搜索Spring 多資料源得帖子,大多數都是基于Spring提供得AbstractRoutingDataSource + AOP + 注解 來做動態切換,包括現在流行得 Mybatis plus 官方得多資料源解決方案也是這種做法,這種做法解決了博主當時得多資料源使用問題,后來加了一個需求,在一個定時任務中,查詢兩個資料源得資料,才發現動態切換在單個方法中不好用了,最后使用得原生jdbc資料源解決,多年后,博主在另一家公司得專案中又遇到了多資料源問題,但是這次博主在網上搜索得是Mybatis 多資料源,才發現了這個優雅得解決方案,進而推薦給大家

六. Spring Security專案中,使用MDC實作介面請求呼叫追蹤,以及用戶ID記錄

MDC介紹

MDC(Mapped Diagnostic Context,映射除錯背景關系)是 log4j 、logback及log4j2 提供的一種方便在多執行緒條件下記錄日志的功能,MDC 可以看成是一個與當前執行緒系結的哈希表,可以往其中添加鍵值對,MDC 中包含的內容可以被同一執行緒中執行的代碼所訪問,當前執行緒的子執行緒會繼承其父執行緒中的 MDC 的內容,當需要記錄日志時,只需要從 MDC 中獲取所需的資訊即可,

雖然MDC能夠方便得實作介面請求呼叫追蹤功能,但是它在子執行緒中會丟失父執行緒中添加得鍵值對資訊,解決方法是通過父執行緒中呼叫執行緒池前呼叫 MDC.getCopyOfContextMap() ,然后在子執行緒中第一個呼叫 MDC.setConextMap() 獲取鍵值對資訊,完整實作代碼如下:

/**
 * 自定義Spring執行緒池,解決子執行緒丟失reqest_id問題
 */
public class ThreadPoolExecutorMdcWrapper extends ThreadPoolTaskExecutor {

    @Override
    public void execute(Runnable task) {
        super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }

    @Override
    public Future<?> submit(Runnable task) {
        return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }
}

/**
 * MDC幫助類,添加reqest_id
 */
public class ThreadMdcUtil {


    public static final String REQUEST_ID = "request_id";

    /**
     * 設定請求唯一ID
     */
    public static void setTraceIdIfAbsent() {
        if (MDC.get(REQUEST_ID) == null) {
            MDC.put(REQUEST_ID, IdUtil.getUid());
        }
    }

    /**
     * 存在userId則添加到REQUEST_ID中
     * @param userId
     */
    public static void setUserId(String userId) {
        String s = MDC.get(REQUEST_ID);
        if (s != null) {
            MDC.put(REQUEST_ID, s + "_" + userId);
        }
    }

    public static void removeTraceId() {
        MDC.remove(REQUEST_ID);
    }

    public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {
        return () -> {
            if (context == null) {
                MDC.clear();
            } else {
                MDC.setContextMap(context);
            }
            setTraceIdIfAbsent();
            try {
                return callable.call();
            } finally {
                MDC.clear();
            }
        };
    }

    public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
        return () -> {
            if (context == null) {
                MDC.clear();
            } else {
                MDC.setContextMap(context);
            }
            // 設定traceId
            setTraceIdIfAbsent();
            try {
                runnable.run();
            } finally {
                MDC.clear();
            }
        };
    }
}

Spring Security 中添加 token 過濾器

/**
 * token過濾器 驗證token有效性
 *
 * @author ruoyi
 */
@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private TokenService tokenService;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        try {
            // 入口傳入請求ID
            ThreadMdcUtil.setTraceIdIfAbsent();
            LoginUserDetail loginUser = tokenService.getLoginUser(request);
            if (Objects.nonNull(loginUser) && Objects.isNull(SecurityContextHolder.getContext().getAuthentication())) {
                // 記錄userId
                ThreadMdcUtil.setUserId(String.valueOf(loginUser.getMember().getId()));
                tokenService.verifyToken(loginUser);
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
            chain.doFilter(request, response);
        } finally {
            // 出口移除請求ID
            ThreadMdcUtil.removeTraceId();
        }
    }

}

最后在 logback.xml 中添加 %X{request_id}

<property name="pattern"
          value="https://www.cnblogs.com/wayn111/archive/2022/11/30/%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{request_id}] [%thread] [%-5level] %logger{36}:%L %M - %msg%n"/>

日志列印效果如下:

2022-11-27 21:29:48.008 [86c76336100c414dbe9217aeb099ccd5_12] [http-nio-82-exec-2] [INFO ] c.w.m.a.s.impl.IHomeServiceImpl:56 getHomeIndexDataCompletableFuture - getHomeIndexDataCompletableFuture:com.wayn.common.util.R@701f7b8e[code=200,msg=操作成功,map={bannerList=[{"createTime":"2020-06-26 19:56:03","delFlag":false,"id":14,"imgUrl":"https://m.360buyimg.com/mobilecms/s700x280_jfs/t1/117335/39/13837/263099/5f291a83E8ba761d0/5c0460445cb28248.jpg!cr_1125x449_0_166!q70.jpg.dpg","jumpUrl":"http://82.157.141.70/mall/#/detail/1155015","sort":0,"status":0,"title":"hh2","updateTime":"2022-06-19 09:16:46"},{"createTime":"2020-06-26 19:56:03","delFlag":false,"id":15,"imgUrl":"https://m.360buyimg.com/mobilecms/s700x280_jfs/t1/202096/26/11652/265782/616acb67E4fcdc9ac/8d7cdfbb6c934e67.jpg!cr_1125x449_0_166!q70.jpg.dpg","jumpUrl":"#/detail/1155015","sort":0,"status":0,"title":"hh","updateTime":"2022-06-19 09:04:58"}], newGoodsList=[{"actualSales":0,"brandId":0,"brief":"酥脆奶香,甜酸回味","categoryId":1008015,"counterPrice":56.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1116011","id":1116011,"isHot":true,"isNew":true,"isOnSale":true,"keywords":"","name":"蔓越莓曲奇 200克","picUrl":"http://yanxuan.nosdn.127.net/767b370d07f3973500db54900bcbd2a7.png","retailPrice":36.00,"shareUrl":"","sort":5,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":0,"brandId":0,"brief":"粉彩色澤,記錄生活","categoryId":1012003,"counterPrice":49.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1127047","id":1127047,"isHot":false,"isNew":true,"isOnSale":true,"keywords":"","name":"趣味粉彩系列筆記本","picUrl":"http://yanxuan.nosdn.127.net/6c03ca93d8fe404faa266ea86f3f1e43.png","retailPrice":29.00,"shareUrl":"","sort":2,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":0,"brandId":0,"brief":"慢回彈海綿,時尚設計,","categoryId":1008002,"counterPrice":66.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1134030","id":1134030,"isHot":false,"isNew":true,"isOnSale":true,"keywords":"","name":"簡約知性記憶棉坐墊","picUrl":"http://yanxuan.nosdn.127.net/aa49dfe878becf768eddc4c1636643a6.png","retailPrice":46.00,"shareUrl":"","sort":12,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":0,"brandId":0,"brief":"慢回彈海綿的呵護,萌趣添彩,","categoryId":1008002,"counterPrice":69.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1134032","id":1134032,"isHot":false,"isNew":true,"isOnSale":true,"keywords":"","name":"趣味粉彩系列記憶棉坐墊","picUrl":"http://yanxuan.nosdn.127.net/8b30eeb17c831eba08b97bdcb4c46a8e.png","retailPrice":49.00,"shareUrl":"","sort":13,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":0,"brandId":0,"brief":"100%桑蠶絲,絲滑潤膚","categoryId":1008009,"counterPrice":2619.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1135002","id":1135002,"isHot":false,"isNew":true,"isOnSale":true,"keywords":"","name":"宮廷奢華真絲四件套","picUrl":"http://yanxuan.nosdn.127.net/45548f26cfd0c7c41e0afc3709d48286.png","retailPrice":2599.00,"shareUrl":"","sort":1,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":0,"brandId":0,"brief":"自由海軍領探索未來夢","categoryId":1020003,"counterPrice":89.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1135072","id":1135072,"isHot":false,"isNew":true,"isOnSale":true,"keywords":"","name":"經典海魂紋水手裙(嬰童)","picUrl":"http://yanxuan.nosdn.127.net/43e57d4208cdc78ac9c088f9b3e798f5.png","retailPrice":69.00,"shareUrl":"","sort":3,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":0,"brandId":0,"brief":"經典海魂紋自由海軍領","categoryId":1020003,"counterPrice":89.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1135073","id":1135073,"isHot":false,"isNew":true,"isOnSale":true,"keywords":"","name":"海魂紋哈衣水手服(嬰童)","picUrl":"http://yanxuan.nosdn.127.net/53052b04ae001d289c040e09ea92231c.png","retailPrice":69.00,"shareUrl":"","sort":4,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":5,"brandId":0,"brief":"差旅好伴侶","categoryId":1032000,"counterPrice":119.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1152031","id":1152031,"isHot":true,"isNew":true,"isOnSale":true,"keywords":"","name":"魔獸世界-伊利丹頸枕眼罩套裝","picUrl":"http://yanxuan.nosdn.127.net/fd6e78a397bd9e9804116a36f0270b0a.png","retailPrice":99.00,"shareUrl":"","sort":4,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":5,"brandId":0,"brief":"桌面整理神器","categoryId":1032000,"counterPrice":519.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1152095","id":1152095,"isHot":false,"isNew":true,"isOnSale":true,"keywords":"","name":"魔獸世界 聯盟·暴風城 堡壘收納盒","picUrl":"http://yanxuan.nosdn.127.net/c86b49f635fa141decebabbd0966a6ef.png","retailPrice":499.00,"shareUrl":"","sort":6,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":0,"brandId":0,"brief":"3重透氣,清爽柔滑","categoryId":1008009,"counterPrice":479.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1152161","id":1152161,"isHot":false,"isNew":true,"isOnSale":true,"keywords":"","name":"竹語絲麻印花四件套","picUrl":"http://yanxuan.nosdn.127.net/977401e75113f7c8334c4fb5b4bf6215.png","retailPrice":459.00,"shareUrl":"","sort":6,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10}], categoryList=[{"createTime":"2020-12-08 23:09:12","delFlag":false,"iconUrl":"http://cdn.wayn.xin/9fc3c52571aa38a1466f114c2dc892fc.png","id":2,"jumpType":0,"name":"大牌手機","picUrl":"http://cdn.wayn.xin/2545dd00ca4575759024af2811949a9d.jpg","sort":1,"status":0,"updateTime":"2020-12-12 19:26:48","valueId":5,"valueUrl":"http://baidu.com"},{"createTime":"2020-12-06 13:27:54","delFlag":false,"iconUrl":"http://82.157.141.70/upload/2022/02/21/a23fa32c8f4004a8c9fbbb1784462163.jpg","id":1,"jumpType":0,"name":"滋補保健2","picUrl":"http://cdn.wayn.xin/d4de7172eb7ae4178ae4dafd6a973d8f.jpg","sort":2,"status":0,"updateTime":"2022-06-19 09:17:20","valueId":2},{"createTime":"2020-12-08 23:26:15","delFlag":false,"iconUrl":"http://cdn.wayn.xin/6bc0f8a131d2d16b8fc2004d4aa4860c.png","id":3,"jumpType":1,"name":"不銹鋼鍋","picUrl":"http://cdn.wayn.xin/314d87257f7a2ff03d5f4c5183797912.jpg","sort":2,"status":0,"updateTime":"2020-12-12 19:27:24","valueId":1036000},{"createTime":"2020-12-12 19:28:08","delFlag":false,"iconUrl":"http://cdn.wayn.xin/5a90531d901529967885279d7dc826e1.png","id":14,"jumpType":0,"name":"進口牛奶","picUrl":"http://cdn.wayn.xin/0b1f6ab63d5e222c52c83a2d0581e44c.jpg","sort":3,"status":0,"valueId":5},{"createTime":"2020-12-12 19:28:33","delFlag":false,"iconUrl":"http://cdn.wayn.xin/33530951827ca7e59940d51cda537d84.png","id":15,"jumpType":0,"name":"量販囤貨","picUrl":"http://cdn.wayn.xin/bb6daee3b3e51c3008db97585249f513.jpg","sort":4,"status":0,"valueId":2},{"createTime":"2020-12-12 19:28:50","delFlag":false,"iconUrl":"http://cdn.wayn.xin/7d337f25111b263b29d5d12589015c46.png","id":16,"jumpType":0,"name":"清潔用品","picUrl":"http://cdn.wayn.xin/be8995bda39d03b17349b8ec0dcab3d5.jpg","sort":5,"status":0,"valueId":2},{"createTime":"2020-12-12 19:29:10","delFlag":false,"iconUrl":"http://cdn.wayn.xin/2e632ec0173bb477dcdb601495e0412a.png","id":17,"jumpType":0,"name":"洗護用品","picUrl":"http://cdn.wayn.xin/53fb88c9d1245caa882aa3fc29187d0b.jpg","sort":6,"status":0,"valueId":4},{"createTime":"2020-12-12 19:29:28","delFlag":false,"iconUrl":"http://cdn.wayn.xin/942323c0e74677cf2aa15f09a1e63bca.png","id":18,"jumpType":0,"name":"日用百貨","picUrl":"http://cdn.wayn.xin/8587f91db2edcb43e57da9835cc7ec76.jpg","sort":7,"status":0,"valueId":2},{"createTime":"2020-12-12 19:29:46","delFlag":false,"iconUrl":"http://cdn.wayn.xin/18d9d860ba9b8b28522e050f11a8a8e0.png","id":19,"jumpType":0,"name":"明星乳膠","picUrl":"http://cdn.wayn.xin/65273c7fb2273e90958e92626248a90a.jpg","sort":8,"status":0,"valueId":6},{"createTime":"2020-12-12 19:30:15","delFlag":false,"iconUrl":"http://cdn.wayn.xin/7c790577afda91eebc3c95586e190957.png","id":20,"jumpType":0,"name":"口碑好物","picUrl":"http://cdn.wayn.xin/210011b35be4ceee39e6a466b40b8e22.jpg","sort":9,"status":0,"updateTime":"2021-04-01 20:13:08","valueId":5}], expire_time=1669549170235, hotGoodsList=[{"actualSales":1,"brandId":1001045,"brief":"一級桑蠶絲,吸濕透氣柔軟","categoryId":1036000,"counterPrice":719.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1006013","id":1006013,"isHot":true,"isNew":false,"isOnSale":true,"keywords":"","name":"雙宮繭桑蠶絲被 空調被","picUrl":"http://yanxuan.nosdn.127.net/583812520c68ca7995b6fac4c67ae2c7.png","retailPrice":699.00,"shareUrl":"","sort":7,"unit":"件","updateTime":"2021-08-08 11:19:36","virtualSales":10},{"actualSales":1,"brandId":1001045,"brief":"雙層子母被,四季皆可使用","categoryId":1008008,"counterPrice":14199.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1006014","id":1006014,"isHot":true,"isNew":false,"isOnSale":true,"keywords":"","name":"雙宮繭桑蠶絲被 子母被","picUrl":"http://yanxuan.nosdn.127.net/2b537159f0f789034bf8c4b339c43750.png","retailPrice":1399.00,"shareUrl":"","sort":15,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":6,"brandId":1001000,"brief":"加大加厚,雙色精彩","categoryId":1036000,"counterPrice":219.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1011004","id":1011004,"isHot":true,"isNew":false,"isOnSale":true,"keywords":"","name":"色織精梳AB紗格紋空調被","picUrl":"http://yanxuan.nosdn.127.net/0984c9388a2c3fd2335779da904be393.png","retailPrice":199.00,"shareUrl":"","sort":2,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":6,"brandId":0,"brief":"共享親密2人時光","categoryId":1008008,"counterPrice":219.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1019002","id":1019002,"isHot":true,"isNew":false,"isOnSale":true,"keywords":"","name":"升級款護頸雙人記憶枕","picUrl":"http://yanxuan.nosdn.127.net/0118039f7cda342651595d994ed09567.png","retailPrice":199.00,"shareUrl":"","sort":10,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":6,"brandId":0,"brief":"健康保護枕","categoryId":1008008,"counterPrice":119.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1019006","id":1019006,"isHot":true,"isNew":false,"isOnSale":true,"keywords":"","name":"植物填充護頸夜交藤枕","picUrl":"http://yanxuan.nosdn.127.net/60c3707837c97a21715ecc3986a744ce.png","retailPrice":99.00,"shareUrl":"","sort":7,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":6,"brandId":0,"brief":"厚實舒適","categoryId":1008001,"counterPrice":59.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1021000","id":1021000,"isHot":true,"isNew":false,"isOnSale":true,"keywords":"被","name":"埃及進口長絨棉毛巾","picUrl":"http://yanxuan.nosdn.127.net/7191f2599c7fe44ed4cff7a76e853154.png","retailPrice":39.00,"shareUrl":"","sort":7,"unit":"條","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":6,"brandId":1001020,"brief":"浪漫毛線繡球,簡約而不簡單","categoryId":1008009,"counterPrice":319.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1022000","id":1022000,"isHot":true,"isNew":false,"isOnSale":true,"keywords":"","name":"意式毛線繡球四件套","picUrl":"http://yanxuan.nosdn.127.net/5350e35e6f22165f38928f3c2c52ac57.png","retailPrice":299.00,"shareUrl":"","sort":18,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":32,"brandId":1001000,"brief":"柔軟紗布,嬰童可用","categoryId":1036000,"counterPrice":269.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1027004","id":1027004,"isHot":true,"isNew":false,"isOnSale":true,"keywords":"","name":"色織六層紗布夏涼被","picUrl":"http://yanxuan.nosdn.127.net/6252f53aaf36c072b6678f3d8c635132.png","retailPrice":249.00,"shareUrl":"","sort":3,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":32,"brandId":0,"brief":"原生苦蕎,健康護頸","categoryId":1008008,"counterPrice":119.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1036002","id":1036002,"isHot":true,"isNew":false,"isOnSale":true,"keywords":"","name":"高山苦蕎麥枕","picUrl":"http://yanxuan.nosdn.127.net/ffd7efe9d5225dff9f36d5110b027caa.png","retailPrice":99.00,"shareUrl":"","sort":5,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10},{"actualSales":32,"brandId":0,"brief":"5cm記憶綿的親密包裹","categoryId":1008008,"counterPrice":619.00,"createTime":"2018-02-01 00:00:00","delFlag":false,"goodsSn":"1037011","id":1037011,"isHot":true,"isNew":false,"isOnSale":true,"keywords":"","name":"安睡慢回彈記憶綿床墊","picUrl":"http://yanxuan.nosdn.127.net/a03ea6f4509439acdafcb7ceba1debe0.png","retailPrice":599.00,"shareUrl":"","sort":22,"unit":"件","updateTime":"2018-02-01 00:00:00","virtualSales":10}]}]
2022-11-27 21:29:48.336 [9d919fb6d33c4652ba28ff87ae210809_12] [http-nio-82-exec-3] [DEBUG] c.w.c.c.m.s.G.selectGoodsListPage_mpCount:137 debug - ==>  Preparing: SELECT COUNT(*) AS total FROM shop_goods WHERE del_flag = 0 AND is_on_sale = 1
2022-11-27 21:29:48.387 [9d919fb6d33c4652ba28ff87ae210809_12] [http-nio-82-exec-3] [DEBUG] c.w.c.c.m.s.G.selectGoodsListPage_mpCount:137 debug - ==> Parameters: 
2022-11-27 21:29:48.426 [9d919fb6d33c4652ba28ff87ae210809_12] [http-nio-82-exec-3] [DEBUG] c.w.c.c.m.s.G.selectGoodsListPage_mpCount:137 debug - <==      Total: 1
2022-11-27 21:29:48.430 [9d919fb6d33c4652ba28ff87ae210809_12] [http-nio-82-exec-3] [DEBUG] c.w.c.c.m.s.G.selectGoodsListPage:137 debug - ==>  Preparing: select id, goods_sn, name, pic_url, counter_price, retail_price, actual_sales, virtual_sales from shop_goods WHERE del_flag = 0 and is_on_sale = 1 order by create_time desc LIMIT ?
2022-11-27 21:29:48.452 [9d919fb6d33c4652ba28ff87ae210809_12] [http-nio-82-exec-3] [DEBUG] c.w.c.c.m.s.G.selectGoodsListPage:137 debug - ==> Parameters: 6(Long)
2022-11-27 21:29:48.476 [9d919fb6d33c4652ba28ff87ae210809_12] [http-nio-82-exec-3] [DEBUG] c.w.c.c.m.s.G.selectGoodsListPage:137 debug - <==      Total: 6

最后分析上訴日志:通過86c76336100c414dbe9217aeb099ccd5實作介面呼叫追蹤,通過12用戶ID,實作用戶呼叫追蹤

七. alibaba excel匯出時自定義格式轉換優雅實作

官網介紹:EasyExcel 是一個基于Java的簡單、省記憶體的讀寫Excel的開源專案,在盡可能節約記憶體的情況下支持讀寫百M的Excel,

EasyExcelalibaba 出的一個基于 java poi 得excel通用處理類別庫,他的優勢在于記憶體消耗,對比 easypoi 方案,EasyExcel 在記憶體消耗、知名度(大廠光環)上更出眾些,

博主在使用程序中發現匯出excel,官網對自定義格式欄位提供了 converter 介面,但只簡單提供了CustomStringStringConverter 類代碼,達不到博主想要得優雅要求,如下:

public class CustomStringStringConverter implements Converter<String> {
    @Override
    public Class<?> supportJavaTypeKey() {
        return String.class;
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    /**
     * 這里讀的時候會呼叫
     *
     * @param context
     * @return
     */
    @Override
    public String convertToJavaData(ReadConverterContext<?> context) {
        return "自定義:" + context.getReadCellData().getStringValue();
    }

    /**
     * 這里是寫的時候會呼叫 不用管
     *
     * @return
     */
    @Override
    public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {
        return new WriteCellData<>(context.getValue());
    }

}

在以上代碼中,打個比方想要實作性別欄位得自定義格式轉換,就需要在convertToExcelData方法中,添加如下代碼

@Override
public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {
    String value = https://www.cnblogs.com/wayn111/archive/2022/11/30/context.getValue();
    if ("man".equals(value)) {
        return new WriteCellData<>("男");
    } else {
        return new WriteCellData<>("女");
    }
}

可以看到,非常得不優雅,對于這種型別欄位,博主習慣使用列舉類來定義欄位所有型別,然后將列舉類轉換為 map(value,desc) 結構,就可以優雅得實作這個自定義格式得需求

/**
 * 一、先定義int欄位抽象轉換類,實作通用轉換邏輯
 */
public abstract class AbstractIntConverter implements Converter<Integer> {
    abstract List<ConverterDTO> getArr();

    public WriteCellData<?> convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
        List<ConverterDTO> values = getArr();
        Map<Integer, String> map = values.stream().collect(toMap(ConverterDTO::getType, ConverterDTO::getDesc));
        String result = map.getOrDefault(value, "");
        return new WriteCellData<>(result);
    }

    static class ConverterDTO {
        private Integer type;
        private String desc;

        public Integer getType() {
            return type;
        }

        public void setType(Integer type) {
            this.type = type;
        }

        public String getDesc() {
            return desc;
        }

        public void setDesc(String desc) {
            this.desc = desc;
        }

        public ConverterDTO(Integer type, String desc) {
            this.type = type;
            this.desc = desc;
        }
    }
}

/**
 * 二、定義通用狀態欄位轉換類
 */
public class StatusConverter extends AbstractIntConverter {

    @Override
    List<ConverterDTO> getArr() {
        StatusEnum[] values = StatusEnum.values();
        return Arrays.stream(values).map(sexEnum -> new ConverterDTO(sexEnum.getType(), sexEnum.getDesc())).toList();
    }

    /**
     * 狀態列舉
     */
    enum StatusEnum {
        MAN(0, "啟用"),
        WOMAN(1, "禁用");

        private Integer type;
        private String desc;

        StatusEnum(Integer type, String desc) {
            this.type = type;
            this.desc = desc;
        }
        public Integer getType() {
            return type;
        }
        public String getDesc() {
            return desc;
        }
    }
}

最后再匯出 ExcelProperty 中甜膩加 StatusConverter ,就優雅得實作了自定義格式得需求

public class User extends BaseEntity {
    ...
    /**
     * 用戶狀態 0 啟用 1 禁用
     */
    @ExcelProperty(value = "https://www.cnblogs.com/wayn111/archive/2022/11/30/用戶狀態", converter = StatusConverter.class)
    private Integer userStatus;
    ...

}

八. Springboot 默認redis客戶端lettuce經常連接超時解決方案

不知道大家有沒有遇到這種情況,線上專案使用 lettuce 客戶端,當操作 redis 得介面一段時間沒有呼叫后(比如30分鐘),再次呼叫 redis 操作后,就會遇到連接超時得問題,導致介面例外,博主直接給出分析程序:

  1. 通過wireshark抓包工具,發現專案中 redis 連接創建后,一段時間未傳輸資料后,客戶端發送 psh 包,未收到服務端 ack 包,觸發tcp得超時重傳機制,在重傳次數重試完后,最終客戶端主動關閉了連接,

到這里我們就知道這個問題,主要原因在于服務端沒有回復客戶端(比如tcp引數設定、防火墻主動關閉等,都是針對一段時間內沒有資料傳輸得tcp連接會做關閉處理),造成了客戶端得連接超時

面對這個問題有三種解決方案:

  • redis操作例外后進行重試,這篇文章有介紹 生產環境Redis連接,長時間無回應被服務器斷開問題
  • 啟用一個心跳定時任務,定時訪問 redis,保持 redis 連接不被關閉,簡而言之,就是寫一個定時任務,定時呼叫 redisget 命令,進而保活 redis 連接
  • 基于Springboot 提供得 LettuceClientConfigurationBuilderCustomizer 自定義客戶端配置,博主這里主要針對第三種自定義客戶端配置來講解一種優雅得方式

Springboot 專案中關于 lettuce 客戶端得自動配置是沒有啟用保活配置得,要啟用得話代碼如下:

/**
 * 自定義lettuce客戶端配置
 *
 * @return LettuceClientConfigurationBuilderCustomizer
 */
@Bean
public LettuceClientConfigurationBuilderCustomizer lettuceClientConfigurationBuilderCustomizer() {
    return clientConfigurationBuilder -> {
        LettuceClientConfiguration clientConfiguration = clientConfigurationBuilder.build();
        ClientOptions clientOptions = clientConfiguration.getClientOptions().orElseGet(ClientOptions::create);
        ClientOptions build = clientOptions.mutate().build();
        SocketOptions.KeepAliveOptions.Builder builder = build.getSocketOptions().getKeepAlive().mutate();
        // 保活配置
        builder.enable(true);
        builder.idle(Duration.ofSeconds(30));
        SocketOptions.Builder socketOptionsBuilder = clientOptions.getSocketOptions().mutate();
        SocketOptions.KeepAliveOptions keepAliveOptions = builder.build();
        socketOptionsBuilder.keepAlive(keepAliveOptions);
        SocketOptions socketOptions = socketOptionsBuilder.build();
        ClientOptions clientOptions1 = ClientOptions.builder().socketOptions(socketOptions).build();
        clientConfigurationBuilder.clientOptions(clientOptions1);
    };
}

添加 lettuce 客戶端的自定義配置,在 KeepAliveOptions 中啟用 enable ,這樣 lettuce 客戶端就會在tcp協議規范上啟用 keep alive 機制自動發送心跳包

九. redis客戶端lettuce啟用epoll

直接給 官網連接,配置很簡單,添加一個 netty-all 得依賴,lettuce 會自動檢測專案系統是否支持 epolllinux 系統支持),并且是否有netty-transport-native-epoll依賴( netty-all 包含 netty-transport-native-epoll ),都滿足得話就會自動啟用 epoll 事件回圈,進一步提升系統性能

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
</dependency>

十. Springboot web專案優雅停機

web專案配置了優雅停機后,在重啟jar包,或者容器時可以防止正在活動得執行緒被突然停止( kill -9 無解,請不要使用這個引數殺線上行程,docker compose 專案盡量不要用 docker-compose down 命令關閉專案,使用 docker-compose rm -svf 可以觸發優雅停機),造成用戶請求失敗,在此期間允許完成現有請求但不允許新請求,配置如下:

server: shutdown: "graceful"

十一. nginx配置通用請求后綴

先說下這個配置產生得前提,博主公司pc客戶專案是基于 electron 打包得網頁專案,每次專案大版本更新時,為了做好兼容性,防止客戶端網頁快取等,會使用一個新網頁地址,打個比方:

老網頁地址,v1.1.0 版本網頁訪問地址:http://api.dev.com/pageV110

新網頁地址,v1.2.0 版本網頁訪問地址:http://api.dev.com/pageV120

那么專案得nginx配置則則需要新加一個 v1.2.0 得配置如下:

server {
   listen 80;
   server_name api.dev.com;
   client_max_body_size 10m;

   # 老網頁v1.1.0配置
   location ~ ^/pageV110 {
               alias  /home/wwwroot/api.dev.com/pageV110;
               index  index.html index.htm;
       }
       
   # 新網頁v1.2.0配置
   location ~ ^/pageV120 {
               alias  /home/wwwroot/api.dev.com/pageV120;
               index  index.html index.htm;
       }

}

那么博主在每次專案發布得時候就需要配合前端發版,配置一個新網頁,故產生了這個通用配置得需求,如下:

server {
   listen 80;
   server_name api.dev.com;
   client_max_body_size 10m;

   # 配置正則localtion
   location ~ ^/pageV(.*) {
               set $s $1; # 定義后綴變數
               alias  /home/wwwroot/api.dev.com/pageV$s;
               index  index.html index.htm;
       }

}

nginx 組態檔語法中,location 陳述句可以使用正則運算式,定義 set $s $1 變數,實作了通用配置

十二. 關于開發人員的自我提升和突破

博主這里主要總結了四點:

  1. 多和他人溝通,溝通能把復雜問題簡單化,有時候開發階段一個需求多問幾句,可以減少因為個人理解差異導致的需求不一致問題,進而減少開發時間
  2. 建立長短期目標,觀看技術視頻、書籍給自己充電,比如7天利用業余時間看完一本電子書,三十天從零開始一個新專案等
  3. 善于總結,對于專案中的疑難bug,踩坑點要有記錄,防止下次遇到再掉坑里
  4. 敢于嘗試、擔責,對專案、代碼里明確不合理的地方要敢于跟他人溝通,修改問題代碼,達到優化目的,對于自己造成的問題要承擔,不要推卸責任,對于線上問題要重視,優先解決線上問題,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/538819.html

標籤:其他

上一篇:重構后臺的django專案目錄、配置開發環境、添加環境變數

下一篇:eclipse真的落后了嘛?這幾點優勢其他IDE比不上

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more