LXZ的原创知识库

记录,思考,成长,回忆


  • 首页

  • 分类

  • 归档

【笔记】markdown学习

发表于 2021-10-27 | 分类于 笔记

[笔记系列文章说明]: 该类型的文章是笔者学习过程中整理的学习笔记.

Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档.(文档格式语言)

语法规则

标题

一, 使用(=,-)标识一级,二级标题(数量大于等于1)

1
2
3
4
一级标题
=========
二级标题
---------

一, 使用(#1-6个)标识

1
2
3
4
5
6
# 一
## 二
### 三
#### 四
##### 五
###### 六
阅读全文 »

【笔记】对接接口定义标准化规范

发表于 2020-09-12 | 分类于 笔记

[笔记系列文章说明]: 该类型的文章是笔者学习过程中整理的学习笔记.

与不绝对信任的机构或企业交换数据的时候, 通常需要考虑严格的数据安全问题, 我在这里定义了一种, 完整版就不放了, 列一些关键的点

文档定义

一, 接口访问定义

请求方式: http请求post方式
请求类型: application/json;charset=utf-8
返回类型: application/json;charset=utf-8
域名:

二, 参数定义

  1. 请求头信息(Header)
参数 类型 说明 例
plantformCode String 商家授权码 如:
timeStamp Long 时间戳 如:1626255054672 (相当于2021-07-14 17:30:54)
sign String 签名 8d2472fac5a4a1879bde18075f7a879e
  1. 请求体内容(Body),具体内容依据具体接口
参数 类型 说明 例
messageId String 消息唯一标识
data String JSON字符串
timeStamp Long 时间戳
dataSource String 数据来源,供应商唯一标识 如BOLIAN
阅读全文 »

【笔记】项目管理工具(持续更新)

发表于 2020-04-10 | 分类于 笔记

[笔记系列文章说明]: 该类型的文章是笔者学习过程中整理的学习笔记.

开发工具

1
2
3
IDE: idea
idea插件: Lombok, SonarLint, Alibaba Java Coding Guidelines

项目管理工具

1
2
3
4
5
6
7
8
代码管理: git, gitlab
需求管理平台: 禅道
项目发布工具: jenkins
日志收集展示: logstash收集 + elasticsearch检索 + kibana展示 简称: ELK
接口API文档管理: YAPI
数据库平台查询: yearning
配置管理中心: necos, disconf, stcconfig
系统监控平台: CAT

框架

1
2
3
4
5
6
远程调用体系: 
1,SpringCloud(注册Eureka, 负载均衡ribbon, 服务注册Feign, Hystix服务治理熔断降级隔离监控, zuul网关过滤)
2,Dubbo(zookeeper/necos注册, mock降级, retry熔断, 配置最大并发数)
熔断降级框架: Sentinel, Hystrix, nginx(对你没有看错,可以控制http请求)
分布式事务框架: tx-lcn(停更), seata 原理: 托管事务管理,利用记录日志控制一致提交回滚
seata: https://www.cnblogs.com/leeego-123/p/12677124.html

【笔记】GIT代码管理&提交规范

发表于 2020-04-10 | 分类于 笔记

[笔记系列文章说明]: 该类型的文章是笔者学习过程中整理的学习笔记.

本篇整理一下项目中用到的管理代码的规范

一, 项目分支结构

分支环境:
开发环境:dev
测试环境:test
预生产环境:prev
生产环境:master

根据需要会分出test_2.0、test_bug等次级分支,测试到上线都会在这个分支进行。最终会同步到test、master分支。

二, 需求&BUG分支创建与命名

分支代码从prev拉取

分支命名规则: 项目名++日期++类型++开发名称/bug号/bug名称(++修复人标识)

需求开发分支命名:

1
2
3
4
5
stc_20210629_dev_version2

或

stc_20210629_dev_version2_lxz
阅读全文 »

【笔记】logback+MDC全解析

发表于 2019-05-15 | 分类于 笔记

[笔记系列文章说明]: 该类型的文章是笔者学习过程中整理的学习笔记.

logback的配置详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<?xml version="1.0" encoding="UTF-8"?>
<!-- 表达式一览 -->
<!-- -X号: X信息输出时左对齐;-->
<!-- %p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL,-->
<!-- %d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921-->
<!-- %r: 输出自应用启动到输出该log信息耗费的毫秒数-->
<!-- %c: 输出日志信息所属的类目,通常就是所在类的全名-->
<!-- %t: 输出产生该日志事件的线程名-->
<!-- %l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main (TestLog4.java:10)-->
<!-- %x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像Java servlets这样的多客户多线程的应用中。-->
<!-- %%: 输出一个”%”字符-->
<!-- %F: 输出日志消息产生时所在的文件名称-->
<!-- %L: 输出代码中的行号-->
<!-- %m: 输出代码中指定的消息,产生的日志具体信息-->
<!-- %n: 输出一个回车换行符,Windows平台为”\r\n”,Unix平台为”\n”输出日志信息换行-->

<!-- scan属性未true时,如果配置文档发生改变将会进行重新加载 -->
<!-- scanPeriod属性设置监测配置文件修改的时间间隔,默认单位为毫秒,在scan为true时才生效 -->
<!-- debug属性如果为true时,会打印出logback内部的日志信息 -->
<configuration scan="true" scanPeriod="5 seconds" debug="true">
<!--定义变量-->
<property name="app.name" value="app" />
<property name="log.level" value="debug" />
<property name="log.maxHistory" value="30" />
<property name="log.filePath" value="${catalina.base}/logs/webapps" />
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSSS} [%thread] %-5level %logger{50}-%msg%n" />
<property name="log.pattern2" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />

<!--每个appender代表一个输出-->
<!-- org.apache.log4j.ConsoleAppender(控制台)-->
<!-- org.apache.log4j.FileAppender(文件)-->
<!-- org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)-->
<!-- org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)-->
<!-- org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)-->
<!--控制台输出-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern2}</pattern>
</encoder>
</appender>

<appender name="FILE1" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<file>log/output.log</file>
<!-- 滚动策略-->
<!-- TimeBasedRollingPolicy - 基于时间-->
<!-- SizeAndTimeBasedRollingPolicy - 基于文件大小和时间-->
<!-- FixedWindowRollingPolicy - 基于自定义滚动触发 配合triggeringPolicy配置触发-->
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>rolllog/output.log.%i</fileNamePattern>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>1KB</MaxFileSize>
</triggeringPolicy>
</appender>

<appender name="FILE2" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d [%t] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!--获取java启动时的配置值,可根据环境不同利用启动区分,java -Dscheduler.manager.server.home=/path/to-->
<file>log/${app.name}.log</file>
<!--日志滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>rolllog/${app.name}.log.%i</fileNamePattern>
</rollingPolicy>
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">-->
<!--日志文件输出的文件名-->
<!-- <FileNamePattern>${LOG_HOME}/${LOG_NAME}.info.log.%d{yyyy-MM-dd-HH}.%i.log</FileNamePattern>-->
<!--日志文件保留天数-->
<!-- <MaxHistory>7</MaxHistory>-->
<!--日志文件的最大大小-->
<!-- <MaxFileSize>100MB</MaxFileSize>-->
<!-- </rollingPolicy>-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>1KB</MaxFileSize>
</triggeringPolicy>
</appender>
<!--为appender增加异步-->
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<!-- 添加附加的appender,最多只能添加一个 -->
<appender name="file.async" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>256</queueSize>
<includeCallerData>true</includeCallerData>
<appender-ref ref="FILE2" />
</appender>

  <!-- 使用异步来记录其他信息-->
<appender name="file.async.other" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>256</queueSize>
<includeCallerData>true</includeCallerData>
<appender-ref ref="FILE1" />
</appender>



<!--定义日志输出匹配的级别,包,默认root-->
<logger name="ch.qos" level="%{log.level}" additivity="false">
<appender-ref ref="file.async" />
<appender-ref ref="file.async.other" />
</logger>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE1" />
<appender-ref ref="FILE2" />
</root>
</configuration>

<!-- https://www.cnblogs.com/wenbronk/p/6529161.html-->
<!-- http://www.51gjie.com/javaweb/1108.html-->

MDC,NDC

什么是MDC?
Mapped Diagnostic Context
线程独立自定义Map存储上下文信息,子线程拷贝父线程Map信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.保存信息到上下文
MDC.put(key, value);

2.从上下文获取设置的信息
MDC.get(key);

3.清楚上下文中指定的key的信息
MDC.remove(key);

4.清除所有
clear()

5.输出模板,注意是大写[%X{key}]
log4j.appender.consoleAppender.layout.ConversionPattern = %-4r [%t] %5p %c %x - %m - %X{key}%n

什么是NDC?
Nested Diagnostic Context
线程独立自定义栈存储上下文信息,子线程拷贝父线程栈信息

1
2
3
4
5
6
7
8
9
10
11
1.开始调用
NDC.push(message);

2.删除栈顶消息
NDC.pop();

3.清除全部的消息,必须在线程退出前显示的调用,否则会导致内存溢出。
NDC.remove();

4.输出模板,注意是小写的[%x]
log4j.appender.stdout.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ssS}] [%x] : %m%n

logback中的MDC

单机MDC使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.ConsoleAppender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class SimpleMDC {
static public void main(String[] args) {
Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
programmaticConfiguration();
logger.info("今天的天气真不错");
logger.info("是的, 雨好大啊");
MDC.put("one", "one");
MDC.put("two", "two");
logger.info("今天的天气真不错");
logger.debug("是的, 雨好大啊");
MDC.put("one", "three");
MDC.put("two", "four");
logger.info("今天的天气真不错");
logger.info("是的, 雨好大啊");
// 实际使用,通常在finally中使用MDC.remove清除掉旧值
MDC.remove("one");
MDC.remove("two");
}

/**
* 定义日志输出格式
*/
static void programmaticConfiguration() {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.reset();
PatternLayoutEncoder layout = new PatternLayoutEncoder();
layout.setContext(loggerContext);
layout.setPattern("%X{one} %X{two} - %m%n");
layout.start();
ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
appender.setContext(loggerContext);
appender.setEncoder(layout);
appender.start();
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("root");
root.addAppender(appender);
}
}

跨服务MDC使用(web + dubbo)

  1. 使用Spring拦截器HandlerInterceptorAdapter初始化MDC trace

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /**
    * 日志跟踪
    */
    public class TraceLogInterceptor extends HandlerInterceptorAdapter {

    /* 追踪ID */
    private static final String TRACE_KEY = "traceId";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
    String a = UUID.randomUUID().toString().replace("-", "");
    MDC.put(TRACE_KEY, a);
    HttpLogLocalCache.setMsg( a);
    return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    MDC.clear();
    }
    }

  2. 使用Dubbo Filter拦截, RpcContext传递
    调用者设置值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class TraceConsumerFilter implements Filter {
    private static Logger logger = LoggerFactory.getLogger(TraceConsumerFilter.class);
    /* 追踪ID */
    private static final String TRACE_KEY = "traceId";
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    LoggerFactory.getLogger(invoker.getInterface().getName()).info(" [dubbo consumer] 服务调用-请求|method:{}|参数:{}| client[ip:{}-protocol:{}]", invocation.getMethodName(), invocation.getArguments(), RpcContext.getContext().getRemoteAddressString(), invoker.getUrl().getProtocol());
    try {
    logger.debug("MDC-traceId:" + MDC.get(TRACE_KEY));
    RpcContext.getContext().setAttachments(MDC.getCopyOfContextMap());
    } catch (Exception ex) {
    logger.error("dubbo consumer MDC-traceId:reset fail");
    }
    Result result = invoker.invoke(invocation);
    LoggerFactory.getLogger(invoker.getInterface().getName()).info(" [dubbo consumer] 服务调用-返回|method:{}|结果:{}| client[ip:{}-protocol:{}]", invocation.getMethodName(), result, RpcContext.getContext().getRemoteAddressString(), invoker.getUrl().getProtocol());
    return result;
    }

    }

消费者获取值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class TraceProviderFilter implements Filter {

@Override
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
RpcContext context = RpcContext.getContext();
MDC.setContextMap(context.getAttachments());
LoggerFactory.getLogger(invoker.getInterface().getName()).info(" [dubbo provider] 服务调用-接收|method:{}|参数:{}| client[ip:{}-protocol:{}]", inv.getMethodName(), inv.getArguments(), RpcContext.getContext().getRemoteAddressString(), invoker.getUrl().getProtocol());
long start = System.currentTimeMillis();
Result result = invoker.invoke(inv);
long time = System.currentTimeMillis() - start;
Object rval = result.getValue();
String logtxt = null;
if (rval != null) {
Class<?> clazz = rval.getClass();
if (List.class.isAssignableFrom(clazz)) {
int size = ((List) rval).size();
if (size > 5) {
logtxt = size + "(SIZE)";
} else {
logtxt = JSON.toJSONString(rval, new LogArgumentFilter());
}
} else if (Set.class.isAssignableFrom(clazz)) {
int size = ((Set) rval).size();
if (size > 5) {
logtxt = size + "(SIZE)";
} else {
logtxt = JSON.toJSONString(rval, new LogArgumentFilter());
}
} else {
logtxt = JSON.toJSONString(rval, new LogArgumentFilter());
}
}
LoggerFactory.getLogger(invoker.getInterface().getName()).info(" [dubbo provider] 服务调用-返回|method:{}|结果:{}| client[ip:{}-protocol:{}]", inv.getMethodName(), logtxt, RpcContext.getContext().getRemoteAddressString(), invoker.getUrl().getProtocol());
LoggerFactory.getLogger(invoker.getInterface().getName()).info(" [dubbo provider 服务性能] 统计|method:{}|耗时:{} ms|host:{}", inv.getMethodName(), time, RpcContext.getContext().getRemoteAddressString());
try {
MDC.clear();
} catch (Exception ex) {
LoggerFactory.getLogger(TraceProviderFilter.class).error("dubbo provider MDC-traceId:reset fail");
}
return result;
}
}

调用者配置 spring-dubbo-reference.xml

1
<dubbo:consumer filter="traceConsumerFilter"  version="1.0" timeout="3000" check="${dubbo.reference.check}"/>

提供者者配置 spring-dubbo-provider.xml

1
<dubbo:provider protocol="dubbo" filter="traceProviderFilter" version="1.0" timeout="3500" delay="-1" />

增加dubbo filter配置

1
2
3
4
路径: src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
内容:
traceConsumerFilter=com.qctrip.monitor.common.base.dubbo.spi.TraceConsumerFilter
traceProviderFilter=com.qctrip.monitor.common.base.dubbo.spi.TraceProviderFilter

【文档】mybatis文件生成自定义工具

发表于 2019-04-07 | 分类于 文档

[文档系列文章说明]: 该类型的文章是对项目使用进行说明.

代码地址: https://github.com/lvxingzhi/MybatisGeneratorMysqlUtil.git

快速构建项目自定义的实体映射相关文件

阅读全文 »

【方案】JDK实现DB连接池管理

发表于 2017-11-20 | 分类于 方案

[方案系列文章说明]: 该类型的文章是我在使用和学习中认为不错的解决办法,当然会有更好的方案,也一定会有, 所以文章具有一定的局限性, 请结合自己的思考辩证的看.

1,连接池接口定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package dbsource;

import java.sql.Connection;
import java.sql.SQLException;

/**
* 连接池接口
* @author lxz
*
*/
public interface DBSource {

Connection getConnection() throws SQLException;

void closeConnection(Connection con) throws SQLException;
}
阅读全文 »

【Design pattern】利用动态代理技术实现设计模式-代理模式

发表于 2017-11-07 | 分类于 Design pattern

[Design pattern]: 设计模式相关系列

什么时候可以使用代理模式?

需要在原有功能的基础上神不知鬼不觉的增加一些额外功能时,使用代理模式.

如何实现?

1,接口定义
1
2
3
4
5
6
package kooko.com.proxy;

public interface HelloWorld {
public void sayHelloWorld();
public void sayBye();
}
2,接口实现
1
2
3
4
5
6
7
8
9
10
11
12
package kooko.com.proxy;

public class HelloWorldImpl implements HelloWorld{

public void sayHelloWorld(){
System.out.println("say hello");
}

public void sayBye(){
System.out.println("say bye");
}
}
阅读全文 »

【方案】Oracle物化视图使用

发表于 2017-11-01 | 分类于 方案

[方案系列文章说明]: 该类型的文章是我在使用和学习中认为不错的解决办法,当然会有更好的方案,也一定会有, 所以文章具有一定的局限性, 请结合自己的思考辩证的看.

使用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
----删除
TRUNCATE TABLE mlog$_xxx_lxz_tmp;
DROP MATERIALIZED VIEW LOG ON xxx_lxz_tmp;

drop materialized view mv_xxx_lxz_tmp

----原表创建增量刷新日志
CREATE MATERIALIZED VIEW LOG ON xxx_lxz_tmp WITH PRIMARY KEY INCLUDING NEW VALUES;

----创建物化视图
CREATE MATERIALIZED VIEW mv_xxx_lxz_tmp
Build immediate //默认方式 创建即刷新 DEFERRED 下次刷新填入数据
REFRESH fast START WITH SYSDATE + 2/24 // fast刷新 开始时间 凌晨两点
NEXT round(sysdate+1) + 2/24 //下次刷新 明天两点
AS SELECT * FROM xxx_lxz_tmp; //语句

物化视图刷新

需创建定时任务

1
2
3
4
declare joblxz NUMBER;
begin
dbms_job.submit(job=>joblxz,what=>'mv_xxx_lxz_tmp;',next_date=>to_date('20191120 21:30:00','yyyyMMdd hh24:mi:ss'),interval=>'sysdate+1+30/(24*60)',no_parse=>false);
end;

启动

1
2
3
4
5
6
7
8
9
BEGIN
DBMS_JOB.RUN(23);
commit;
END;

23 就是定时器得索引 可根据下面方式查出
select * from user_jobs;——查看调度任务
select * from dba_jobs_running;——查看正在执行的调度任务
select * from dba_jobs;——查看执行完的调度任务
阅读全文 »

【方案】JDK实现线程池管理

发表于 2017-11-01 | 分类于 方案

[方案系列文章说明]: 该类型的文章是我在使用和学习中认为不错的解决办法,当然会有更好的方案,也一定会有, 所以文章具有一定的局限性, 请结合自己的思考辩证的看.

线程池的特点

  1. 迅速响应.
  2. 线程之间无优先级.
  3. 线程执行时间短,不阻塞其他任务.
  4. 线程不可绑定操作,不可被跟踪.

优点

  1. 对象线程不用重复的创建与销毁,节省时间,资源.
  2. 可以对线程的数量进行控制.

CODE

阅读全文 »
上一页123…6下一页

55 日志
5 分类
1 标签
GitHub E-Mail
© 2016 — 2022 吕兴志
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4