唯's Blog

笔者是一个热爱编程的 Java 程序员。

0%

在 Dubbo 的核心领域模型中:

  • Protocol 是服务域,它是 Invoker 暴露和引用的主功能入口,它负责 Invoker 的生命周期管理。

  • Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠拢,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。

  • Invocation 是会话域,它持有调用过程中的变量,比如方法名,参数等

三个核心中心化组件

  • Backend
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

rm -rf docker/$PROJECT_NAME

mkdir -p docker/$PROJECT_NAME

cp -r target/$PROJECT_NAME-1.0.0.jar docker/$PROJECT_NAME/

cp -r certificate docker/$PROJECT_NAME/



cd docker



# 创建 Dockerfile

cat <<EOF > Dockerfile

FROM harbor.newegg.org/base/openjdk:8-jdk-alpine

RUN apk add --no-cache tini

ENTRYPOINT ["/sbin/tini", "--"]

VOLUME ["/usr/bin/mvn"]

MAINTAINER DAE

COPY $PROJECT_NAME /opt/$PROJECT_NAME

WORKDIR /opt/$PROJECT_NAME

RUN ls /usr/lib/jvm/java-1.8-openjdk/ | echo

RUN ls /opt/$PROJECT_NAME

Run cd /usr/lib/jvm/java-1.8-openjdk/jre/lib/security/ && echo yes | keytool -import -alias NeweggWebGateway -storepass changeit -keystore cacerts -file /opt/$PROJECT_NAME/certificate/PROXY.cer



EXPOSE 8080



CMD ["java", "-jar", "$PROJECT_NAME-1.0.0.jar", \

"--spring.profiles.active=gdev", \

"--server.port=8080", \

"--springfox.documentation.swagger.v2.host=gdev-services.newegg.space/$DISCOVER_ID", \

"--jasypt.encryptor.password=NAT@GDEV"]



EOF

  • Console
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

deploy_env=gdev



FROM harbor.newegg.org/base/nginx:stable-alpine



LABEL authors="Kent <kent.l.tan@newegg.com>"

MAINTAINER DAE



RUN mkdir /ng-alain

WORKDIR /ng-alain



# Just copy the dist directory and nginx config file

COPY ./dist ./dist

COPY _nginx/${deploy_env}.conf /etc/nginx/conf.d/nginx.conf



RUN rm -rf /usr/share/nginx/html/*



COPY /dist /usr/share/nginx/html



CMD [ "nginx", "-g", "daemon off;"]

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

deploy_env=prod



echo -e ">> $(date --rfc-3339=s) begin build app"

./node_modules/@angular/cli/bin/ng build --prod

node ./_rels/replace-html.js prd

echo -e ">> $(date --rfc-3339=s) end build app \n"



rm -f run.sh

touch run.sh

chmod 777 run.sh

echo '#!/bin/bash' >> run.sh

echo 'nginx -c /etc/nginx/nginx.conf' >> run.sh

echo 'nginx -s reload' >> run.sh

echo '/usr/bin/nginx-prometheus-exporter -nginx.scrape-uri http://127.0.0.1:8080/stub_status' >> run.sh

echo 'tail -f /dev/null' >> run.sh



# generate dockerfile

echo -e ">> $(date --rfc-3339=s) begin generate dockerfile"



FROM dev-harbor.newegg.org/sao/nginx-pe:0.0.1

USER root



LABEL authors="platform engineering <nesc-cd.mis.platformengineering@newegg.com>"

MAINTAINER DAE

COPY _nginx/${deploy_env}.conf /etc/nginx/conf.d/default.conf

COPY /dist /usr/share/nginx/html

COPY run.sh /usr/bin/run.sh



RUN chown nginx:nginx -R /etc/nginx/conf.d

RUN chown nginx:nginx -R /usr/share/nginx/html

RUN chown nginx:nginx -R /usr/bin/nginx-prometheus-exporter

RUN chown nginx:nginx /usr/bin/run.sh



USER nginx

EXPOSE 8080 9113

CMD ["/bin/sh", "/usr/bin/run.sh"]

代码层面

1
2
3

System.gc();

Java 应用层面

1
2
3

jmap -histo:live pid

  • 注:这个命令实际是用来统计展示当前堆内存区域存活的对象

  • jmap -dump:live 以及 jmap -histo:live 都会触发Full GC

核心原理

  • 底层依赖 Spring MVC ControllerAdvice
ControllerAdvice
  • 看名字就知道,类似 AOP Advice 功能,但是底层实现不是动态代理,依赖
三种功能
  • @ModelAttribute

    • 修改 Model 参数
  • @ExceptionHandler

    • 处理全局异常
  • @InitBinder

    • 全局参数的绑定
Problem 使用 @ExceptionHandler
  • 定义了 核心类 ProblemHandling , 该接口继承了定好的各种异常类型的ExceptionHandler 默认实现

  • 
    public interface ProblemHandling extends
    
            GeneralAdviceTrait,
    
            HttpAdviceTrait,
    
            IOAdviceTrait,
    
            RoutingAdviceTrait,
    
            SecurityAdviceTrait,
    
            ValidationAdviceTrait {
    
    
    
    }
    
    
    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



    * ```java

    public interface ProblemAdviceTrait extends AdviceTrait {



    @API(status = INTERNAL)

    @ExceptionHandler

    default ResponseEntity<Problem> handleProblem(

    final ThrowableProblem problem,

    final NativeWebRequest request) {

    return create(problem, request);

    }



    }

简介

  • 动态可观测线程池框架

  • Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池等功能,为业务系统提高线上运行保障能力。

⚡️ 动态变更 - 应用运行时动态变更线程池参数,包括不限于:核心、最大线程数、阻塞队列容量、拒绝策略等;

核心原理 (以 Nacos 为例)

  • 通过 Spring 的事件机制

  • NacosRefresherHandler (AbstractCoreThreadPoolDynamicRefresh) 使用 Spring Bean 生命周期的钩子方法 afterPropertiesSet 添加 nacos(configServer) 配置 Nacos 变更的监听器 -> Web容器线程池(TomcatWebThreadPoolHandler)/ThreadPoolExecutor 参数变更 发布事件 Hippo4jCoreDynamicRefreshEvent , 由监听器 WebExecutorListener/ExecutorsListener 监听事件,获取 Spring BeanFactory 中对应线程池实例 修改参数

ExecutorsListener

  • 利用 Spring BeanPostProcessor DynamicThreadPoolPostProcessor , postProcessAfterInitialization 生命周期方法向全局线程池管理工具 GlobalThreadPoolManage 和全局线程池核心参数配置 GlobalCoreThreadPoolManage 注册 对应的线程池 和 线程池参数,后续修改参数直接通过 poolId 拿取对应实例进行修改参数

该框架大量使用了 Spring 的特性,条件加载是springboot自动配置的刚需

MarkerConfiguration
  • starter 自动装配类 DynamicThreadPoolCoreAutoConfiguration, @ConditionalOnBean(MarkerConfiguration.Marker.class) 通过 ConditionalOnBean 主键实现开关式引入Hippo4j,@EnableDynamicThreadPool 通过 @Import({BeforeCheckConfiguration.class, MarkerConfiguration.class})导入 MarkerConfiguration 开关标志类

设计模式

策略模式

  • ConfigParserHandler -> ConfigParser
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

public Map<Object, Object> parseConfig(String content, ConfigFileTypeEnum type) throws IOException {

for (ConfigParser parser : PARSERS) {

if (parser.supports(type)) {

return parser.doParse(content);

}

}



return Collections.emptyMap();

}

tip

  • 利用 SPI 扩展机制来提供 ConfigParser 的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21



private ConfigParserHandler() {

ServiceLoader<ConfigParser> loader = ServiceLoader.load(ConfigParser.class);

for (ConfigParser configParser : loader) {

PARSERS.add(configParser);

}



PARSERS.add(new PropertiesConfigParser());

PARSERS.add(new YamlConfigParser());

}

建造者模式

Builder

运行模式

AC(默认)

只支持创建临时节点 -> 数据同步协议 Distro

CP

只支持创建持久化节点 -> Raft协议

MIXED

持久化节点与临时节点

临时节点: 注册实例 发送心跳信息,server端维护实例信息,心跳断了,server端摘除,实例重新上线,变化重新注册。

持久化节点:实例的注册会持久化节点信息,服务的下线,server端不会直接删除实例信息,而是维护实例状态为down

基本概念

  • 无状态服务

无状态服务不会在本地存储持久化数据.多个服务实例对于同一个用户请求的响应结果是完全一致的.这种多服务实例之间是没有依赖关系,比如web应用,在k8s控制器 中动态启停无状态服务的pod并不会对其它的pod产生影响.

  • 有状态服务

有状态服务需要在本地存储持久化数据,典型的是分布式数据库的应用,分布式节点实例之间有依赖的拓扑关系.比如,主从关系. 如果K8S停止分布式集群中任 一实例pod,就可能会导致数据丢失或者集群的crash.

通过使用 Headless Service + StatefulSet 来实现有状态服务,比如kafka集群,mysql集群,zk集群

https://zhuanlan.zhihu.com/p/390440336

大家对 DDD 应该都不陌生了,或多或少都听说过一点,我这里记录一些平常了解学习到关于 DDD 优秀的设计思想

防腐层(Anti-Corruption)

  • 简单说,就是应用不要直接依赖外域的信息,要把外域的信息转换成自己领域上下文(Context)的实体再去使用,从而实现本域和外部依赖的解耦。

  • 平常我们在代码 Coding 运用的一些设计模式,类似 门面模式 都跟防腐层的思想很像

  • 之前我们项目中在权限设计这块也借鉴了防腐层的思想,公司权限系统定义了三个模型:Group、Role、Function,系统在设计按钮功能权限,最初想的直接跟 Role 关联,类似 Save 按钮 开发,测试能够操作,接口设计上 校验 Dev-Role 和 Test-Role 。这样设计有个缺点便是 Save 按钮的权限一旦变更,就需要修改代码,如果我们使用 Function 承担我们系统这个功能的防腐层,则改动就很轻量。上面那个例子,我们定义一个 Save_Function ,它关联了 Dev-Role Test -Role 角色,变更成 TPM - Role ,我们只需要在公司权限管理系统修改 Function 关联关系就可以。

CQRS(查询与命令分离)

  • 是一种设计模式。DDD 中经常被用于分离领域模型命令修改功能与查询功能。

  • 在这一基础是上分析微服务的拆分,目前大型分布式系统,存在业务复杂、数据结构复杂。在富查询面前既要满足服务之间的隔离,又要满足查询功能的全面。为了不污染 DDD 模型保持其干净,

所以单独出来做查询服务。