-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path201908.txt
515 lines (455 loc) · 31.9 KB
/
201908.txt
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
AOP, 拦截器, 过滤器:
public interface Filter {
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOExecption, ServletException;
public void destroy();
}
/* Workflow interface that allows for customized handler execution chains.
Applications can register any number of existing or custom interceptors for certain groups
of handlers, to add common preprocessing behavior without needing to modify eache handler implementation.
A HandlerInterceptor gets called before the appropriate HandlerAdapter triggers the execution of
the handler itself. This mechanism can be used for a large field of preprocessing aspects, e.g.
for authorization checks, or common handler behavior like locale or theme changes. Its main purpose
is to allow for factoring out repetitive handler code.
In an asynchronous processing scenario, the handler may be executed in a separate thread while the main
thread exits without rendering or invoking the #postHandle and #afterCompletion callbacks. When concurrent
handler execution completes, the request is dispatched back in order to proceed with rendering the model
and all methods of this contract are invoked again. For further options and details
see #AsyncHandlerInterceptor.
Typically an interceptor chain is defined per HandlerMapping bean, sharing its granularity. To be able to
apply a certain interceptor chain to a group of handlers, one needs to map the desired handlers via one
HandlerMapping bean. The interceptors themselves are defined as beans in the application context,
referenced by the mapping bean definition via its "interceptors" property(in XML).
HandlerInterceptor is basically similar to a Servlet Filter, but in contrast to the latter it just allows
custom pre-processing with the option of prohibiting the execution of the handler itself, and custom
post-processing. Filters are more powerful, for example they allow for exchanging the request and response
objects that are handed down the chain. Note that a filter gets configured in web.xml, a HandlerInterceptor
in the application context.
As a basic guideline, fine-grained handler-related preprocessing tasks are
candidates for HandlerInterceptor implementations, especially for factored-out common handler code and
authorization checks. On the other hand, a Filter is well-suited for request content and view content
handling, like multipart forms and GZIP commpression. This typically shows when one needs to map the
filter to certain content types(e.g. images), or to all requests.
*/
public interface HandlerInterceptor {
default boolean preHandler(HttpServletRequest request, HttpServletResponse response, Object Handler)
throws Exception {
return true;
}
default void postHandler(HttpServeltRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {}
}
Filter过滤器:拦截web访问url地址。 这个比拦截器范围广,过滤器是大集合,拦截器是大集合中的小集合。而且任何url是先经过过滤器后才进入拦截器的。
Interceptor拦截器:拦截url以action结尾或者没有后缀的,没有后缀拦截器会认为是.action结尾。。 如:struts2拦截器、spring拦截器
AOP拦截器:只能拦截Spring管理Bean的访问(业务层Service),就是说执行某个bean容器中方法时进行拦截,而不是对url。
---------------------------
Spring cloud组件:
Spring Cloud Stream
Spring Cloud Azure
Spring Cloud Alibaba
Spring Cloud for Amazon Web Services
Spring Cloud Bus
Spring Cloud CLI
Spring Cloud for Cloud Foundry
Spring Cloud - Cloud Foundry Service Broker
Spring Cloud Cluster
Spring Cloud Commons
Spring Cloud Config
Spring Cloud Connectors
Spring Cloud Consul
Spring Cloud Contract
Spring Cloud Function
Spring Cloud Gateway
Spring Cloud GCP
Spring Cloud Netflix
Spring Cloud Open Service Broker
Spring Cloud Pipelines
Spring Cloud Schema Registry
Spring Cloud Security
Spring Cloud Sleuth
Spring Cloud Stream Applications
Spring Cloud Stream App Starters
Spring Cloud Task
Spring Cloud Task App Starters
Spring Cloud Vault
Spring Cloud Zookeeper
Spring Cloud App Broker
Spring Cloud Circuit Breaker
Spring Cloud Kubernetes
Spring Cloud OpenFeign
---------------------------
微服务改造考虑点:
1. 业务模块拆分和重建;
2. 重构不等于重写
3. 通用服务/模块的下沉;
4. 相关支撑系统/中间件:分布式调度,分布式缓存,消息中间件,搜索引擎,日志系统,配置;
5. 循序渐进:适配器模式,门面模式,先共用DB后期单独DB;
6. 测试驱动开发;
7. 面向失败设计:服务降级/流量控制/接口熔断/分布式事务;
8. 灰度发布;
9. 日志聚合和全链路监控
---------------------------
MySQL优化的几个维度:硬件,系统配置,数据库表结构,SQL及索引。
不使用swap分区:/proc/sys/vm/swappiness内容改成0(永久),/etc/sysctl.conf上添加vm.swappiness=0(永久)
修改MySQL的配置参数innodb_flush_method,开启O_DIRECT模式:这种情况下,InnoDB的buffer pool会直接绕过文件系统cache来访问磁盘,但是redo log依旧会使用文件系统cache。
值得注意的是,Redo log是覆写模式,即使使用了文件系统的cache,也不会占用太多。
thread_concurrency: #并发线程数量个数
sort_buffer_size: #排序缓存
read_buffer_size: #顺序读取缓存
read_rnd_buffer_size: #随机读取缓存
key_buffer_size: #索引缓存
thread_cache_size: #
innodb_flush_log_at_trx_commit: #1最安全,0性能最高,2折中
innodb_max_dirty_pages_pct #达到百分之75的时候刷新内存脏页到磁盘
1. explain
2. SQL语句中in包含的值不应过多
3. select语句务必指定字段名称;
4. 当只需要一条数据时,使用limit 1
5. 如果排序字段没有用到索引,就尽量少排序或应用层来排序;
6. 如果限制条件中其他字段没有索引,尽量少用or;
7. 尽量用union all替换union;
8. 不使用order by rand();
9. 区分in, exists, not in和not exists;
10. 使用合理的分页方式以提高分页的效率;
11. 分段查询;
12. 避免在where子句中对字段进行null值判断;
13. 不建议使用%前缀模糊查询;
14. 避免在where子句对字段进行表达式操作;
15. 避免隐式类型转换;
16. 对于联合索引来说,遵守最左前缀法则;
17. 必要时,可以使用force index来强制查询走某个索引;
18. 注意范围查询语句;
19. join优化;
---------------------------
区分in和exists主要造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问。如果是in,那么先执行子查询。所以in适合于外表大而内表小的情况;exists适合于外表小而内表大的情况。
关于not in和not exists,推荐使用not exists,不仅仅效率问题,not in可能存在逻辑问题。
straight_join来强制连接顺序,在straight_join左边的表名是驱动表,右边则是被驱动表。在使用straight_join有一个前提,也就是inner join,其他连接不推荐使用straight_join,否则可能造成查询结果不准确。
InnoDB的意向锁的作用:
1. 提高了是否可以加锁的判断效率;
2. 实现了行锁和表锁的多粒度锁机制,使得行锁和表锁可以共存。且满足事务隔离性的要求。
不可重复读重点在同一个事务多次读取同一条记录的时候,出现读到的数据不一致的情况。InnoDB通过MVCC的方式避免了不可重复读,即一致性的非锁定读。
幻读重点在同一个事务多次执行相同的SQL,可能返回之前不存在的行,或者之前存在的行之后不存在了。InnoDB使用Next-key Lock算法避免了幻读,即一致性的锁定读。
默认情况下,InnoDB使用一致性的非锁定读,即读取不会被阻塞。然后有些情况下用户希望通过锁定读取的方式保证数据的一致性,可以通过lock in share mode或for update主动对读取进行加锁操作,这种方式为一致性的锁定读。
锁相关的运维操作:
1. show engine innodb status;
2. select r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting thread, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread, b.trx_query blocking_query from information_schema.innodb_lock_waits w inner join information_schema.innodb_trx b on b.trx_id = w.blocking_trx_id inner join information_schema.innodb_trx r on r.trx_id = w.requesting_trx_id;
间隙锁(Gap Lock)是InnoDB在可重复读提交下为了解决幻读问题时引入的锁机制。
public calss DefaultSingletonBeanRegistry extends SimpleAliaisRegistry implements SingletonBeanRegistry {
// Cache of singleton objects : bean name to bean instance.
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// Cache of singleton factories: bean name to ObjectFactory.
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// Cache of early singleton objects: bean name to bean instance.
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// Set of registered singletons, containing the bean names in registration order.
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
// Names of beans that are currently in creation.
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
// Names of beans currently excluded from in creation checks.
private final Set<String> inCreationCheckExclusions =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
// List of suppressed Exceptions, available for associating related causes.
@Nullable
private Set<Exception> suppressedExceptions;
// Flag that indicates wheher we're currently within destroySingletons.
private boolean singletonsCurrentlyInDestruction = false;
// Disposable bean instances: bean name to disposable instance.
private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
// Map between containing bean names: bean name to Set of bean names that the bean contains.
private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
// Map between dependent bean names: bean name to Set of dependent bean names.
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
// Map between depending bean names: bean name to Set of bean names for the bean's dependencies.
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
...
}
Spring不能解决构造器的循环依赖:Spring解决循环依赖主要依赖三级缓存,但是在调用构造器之前还未将其放入三级缓存之中,因此后续的依赖调用构造方法的时候,并不能从三级缓存中获取到依赖的Bean,因此不能解决;
Spring为什么不能解决Prototyp的循环依赖:多实例Bean是每次调用一次getBean都会执行一次构造器并且为属性赋值,根本没有三级缓存,因此无法解决三级缓存。
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport
implements ConfigurableBeanFactory {
...
// Names of beans that are currently in creation.
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<>("Prototype beans currently in creation");
...
}
java堆外内存回收:
涉及java.lang.ref.Reference<T>, java.nio.DirectByteBuffer, java.nio.DirectByteBuffer$Deallocator,sun.misc.Cleaner
java NIO包是通过sun.misc.Cleaner和PhantomReference来实现堆外内存的自动释放。
Cleaner.create()需要两个参数:需要监控的内存对象和程序释放资源的回调。当JVM进行GC时,如果发现监控的对象,不存在强引用(Cleaner对象对他的引用是幽灵引用),就会调用第二个参数的run()方法逻辑,执行完run()方法时(此时已经释放了堆外内存),JVM会自动释放堆内存中我们监控的对象。
JDK中使用DirectByteBuffer对象来表示堆外内存,可以通过-XX:MaxDirectMemorySize来指定最大的堆外内存,每个DirectByteBuffer对象在初始化时,都会创建一个对应的Cleaner对象,在Cleaner对象回收的时候回收这部分堆外内存。
Cleaner对象是PhantomReference的子类,当GC发现除了PhantomReference外已不可达(持有它的DirectByteBuffer失效了),就会把它放进Reference类的pending list静态变量里。有另外一个ReferenceHandler线程,名字叫做"Reference Handler",关注着这个pending list,如果看到有对象类型是Cleaner,就会执行它的clean(),最终在里面执行Unsafe的free接口来释放DirectByteBuffer对应的堆外内存。
public class sun.misc.Cleaner extends java.lang.ref.PhantomReference {
...
}
// Access to bits, native and otherwise.
class Bits {
private Bits() {} // package-private
...
// -- Direct memory management --
// A user-settable upper limit on the maximum amount of allocatable
// direct buffer memory. This value may be changed during VM
// initialization if it is launched with "-XX:MaxDirectMemorySize=<size>".
private static volatile long maxMemory = VM.maxDirectMemory();
private static final AtomicLong reservedMemory = new AtomicLong();
private static final AtomicLong totalCapacity = new AtomicLong();
private static final AtomicLong count = new AtomicLong();
private static volatile boolean memoryLimitSet = false;
// max. number of sleeps during try-reserving with exponentially
// increasing delay before throwing OutOfMemoryError:
// 1, 2, 4, 8, 16, 32, 64, 128, 256 (total 511 ms ~ 0.5 s)
// which means that OOME will be thrown after 0.5 s of trying
private static final int MAX_SLEEPS = 9;
// These methods should be called whenever direct memory is allocated or
// freed. They allow the user to control the amount of direct memory
// which a process may access. All sizes are specified in bytes.
static void reserveMemory(long size, int cap) {
if (!memoryLimitSet && VM.isBooted()) {
maxMemory = VM.maxDirectMemory();
memoryLimitSet = true;
}
// optimist!
if (tryReserveMemory(size, cap)) {
return;
}
final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess();
// retry while helping enqueue pending Reference objects
// which includes executing pending Cleaner(s) which includes
// Cleaner(s) that free direct buffer memory
while (jlra.tryHandlePendingReference()) {
if (tryReserveMemory(size, cap)) {
return;
}
}
// trigger VM's Reference processing
System.gc();
// a retry loop with exponential back-off delays
// (this gives VM some time to do it's job)
boolean interrupted = false;
try {
long sleepTime = 1;
int sleeps = 0;
while (true) {
if (tryReserveMemory(size, cap)) {
return;
}
if (sleeps >= MAX_SLEEPS) {
break;
}
if (!jlra.tryHandlePendingReference()) {
try {
Thread.sleep(sleepTime);
sleepTime <<= 1;
sleeps++;
} catch (InterruptedException e) {
interrupted = true;
}
}
}
// no luck
throw new OutOfMemoryError("Direct buffer memory");
} finally {
if (interrupted) {
// don't swallow interrupts
Thread.currentThread().interrupt();
}
}
}
...
}
负载均衡
集群容错
服务路由
Dubbo中,先通过路由,从多个Provider中按照路由规则,选出一个子集。再根据负载均衡从子集中选出一个Provider进行本次调用。如果调用失败了,根据集群容错策略,进行重试或定时重发或快速失败等。可以看出Dubbo中的路由,负载均衡和集群容错发生在一次RPC调用的不同阶段。最先是路由,然后是负载均衡,最后是集群容错。
Dubbo内置了4种负载均衡策略:
1. RandomLoadBalance:随机负载均衡。随机的选择一个。是Dubbo的默认负载均衡策略。
2. RoundRobinLoadBalance:轮询负载均衡。轮询选择一个;
3. LeastActiveLoadBalance:最小活跃调用数,相同活跃数的随机。活跃数指调用前后计数差。使慢的Provider收到更少请求,因为越慢的Provider的调用前后计数差会越大。
4. ConsistentHashLoadBalance: 一致性哈希负载均衡。相同参数的请求总是落在同一台机器上。
Dubbo中的随机负载均衡策略是基于权重的负载均衡算法。
轮询负载均衡,就是依次的调用所有的Provider。和随机负载均衡策略一样,轮询负载均衡策略也有权重的概念。轮询负载均衡算法可以让RPC调用严格按照我们设置的比例来分配。不管是少量的调用还是大量的调用。但是轮询负载均衡算法也有不足的地方,存在慢的Provider累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那里,久而久之,所有请求都卡在第二台上,导致整个系统变慢。
最少活跃调用数:相同活跃的随机,活跃数指调用前后计数差,使慢的机器收到更少。
Dubbo: 如果不指定负载均衡,默认使用随机负载均衡。我们也可以根据自己的需要,显式指定一个负载均衡。 可以在多个地方类来配置负载均衡,比如 Provider 端,Consumer端,服务级别,方法级别等。
Spring Boot引用的配置文件参数除了@Value还有哪些方式可以获取:Property values can be injected directly into your beans using the @Value annotation, accessed via Spring's Environment abstraction or bound to structured objects via @ConfigurationProperties.
Spring Boot uses a very particular #ProperySource order that is designed to allow sensible overriding of values.
The RandomValuePropertySource is useful for injecting random values(e.g. into secrets or test cases). It can produce integers, longs, uuids or strings, e.g.
Spring Framework provides two convenient classes that can be used to load YAML documents. The YamlPropertiesFactoryBean will load YAML as Properties and the YamlMapFactoryBean will load YAML as a Map.
Exposing YAML as properties in the Spring Environment.
The YamlPropertySourceLoader class can be used to expose YAML as a PropertySource in the Spring #Environment. This allows you to use the familiar @Value annotation with placeholders syntax to access YAML properties.
-------------------------------
@ConfigurationProperties PK @Value
@Value is a core container feature and it does not provide the same features as type-safe Configuration Properties. 下表总结了两者的Features:
Feature @ConfigurationProperties @Value
Relaxed binding Yes No
Meta-data support Yes No
SpEL evaluation No Yes
If you define a set of configuration keys for your own components, we recommend you to group them in a POJO annotated with @ConfigurationPropertis. Please also be aware that since @Value does not support relaxed binding, it isn't a candidate if you need to provide the value using environment variables.
Finally, while you can write a SpEL expression in @Value, such expressions are not processed from Applicatin property files.
-------------------------------
/*BeanFactoryPostProcessor used for bootstrapping processing of @Configuration classes.
Registered by default when using <context:annotation-config/> or <context:component-scan/>. Otherwise, may be declared manually as with any other BeanFactoryPostProcessor.
This post processor is Ordered#HIGHEST_PRECEDENCE as it is important that any #Bean methods declared in Configuration classes have their respective bean definitions registered before any other BeanFactoryPostProcessor executes.
*/
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
...
/* Prepare the Configuration classes for servicing bean requests at runtime by
replacing them with CGLIB-enhanced subclasses.
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factriesPostProcessed.add(factoryId);
if (!this.registerPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDifinitionRegistry) beanFactory);
}
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
...
}
/* Parses a #Configuration class definition, populating a collection of #ConfigurationClass objects (parsing a single Configuration class may result in any number of ConfigurationClass objects because one Configuration class may import another using #Import annotation).
This class helps separate the concern of parsing the structure of a Configuration class from the concern of registering BeanDefinition objects based on the content of that model (with the exception of @ComponentScan annotations which need to be registered immediately).
This ASM-based implementation avoids reflection and eager class loading in order to interoperate effectively with lazy class loading in a Spring ApplicationContext.
*/
class ConfigurationClassParser {
...
/* Apply processing and build a complete #ConfigurationClass by reading the annotations, members and methods from the source class. This method can be called multiple times as relevant sources are discovered
*/
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass,
SourceClass sourceClass) throws IOExecption {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
} else {
logger.warn("Ignoring @ProperySource annotation on [" + sourceClas.getMetadata()
.getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment");
}
}
...
}
...
}
ConfigurationClassBeanDefinitionReader
Dubbo目前所支持的协议包括Dubbo, Rest, Thrift, gRPC, JsonRPC, Hessian等,基本涵盖了主流的RPC通信协议。
Dubbo官方目前支持的注册中心实现有:consul, default, etcd3, eureka, multicast, multiple, nacos, redis,sofa, zookeeper。
当前Dubbo的服务注册/发现模型是以接口为粒度的,而从2.7.5版本开始,Dubbo新引入了应用粒度的服务注册/发现模型。这一方面有助于优化Dubbo当前服务发现机制、提升服务容量,另一方面对于连通以SpringCloud为代表的微服务体系也非常重要。
HTTP2的特性:保留了Http/1的所有语义,在保持兼容的同时,在通信模型和传输效率上做了很大的改进。
1. 支持单条链路上的Multiplexing,相比于Request-Response独占链路,基于Frame实现更高效利用链路;
2. Request-Stream语义,原生支持Server Push和Stream数据传输;
3. Flow Control,单条Stream粒度和整个链路粒度的流量控制;
4. 头部压缩HPACK;
5. Binary Frame;
6. 原生TLS支持;
-----------------------------------
相比于Dubbo构建于TCP传输层之上,Google选择将gRPC直接定义在HTTP/2协议之上。gRPC设计目的的特性如下:
1. Coverage & Simplicity, 协议设计和框架实现要足够通用和简单,能运行在任何设备之上,甚至一些资源如IoT, Mobile等设备;
2. Interoperablity & Reach,要构建在更通用的协议之上,协议本身要能被网络上几乎所有的基础设施所支持;
3. General Purpose & Performant, 要在场景和性能间做好平衡,首先协议本身适用于各种场景,同时也要尽量有高的性能;
4. Payload Agnostic, 协议上传输的负载要保持语言和平台中立;
5. Streaming,要支持Request-Response, Request-Stream, Bi-Stream等通信模型;
6.Flow Control,协议自身具备流量感知和限制的能力;
7. Metadata Exchange,在RPC服务定义之外,提高额外附加数据传输的能力。
总的来说,在这种设计理念下,gRPC最终被设计为一个跨语言、跨平台、通用、高性能、基于HTTP/2的RPC协议和框架。
-----------------------------------
dto.domain.model: 用来做数据传输的轻量级领域对象
dto.domain.event: 用来做数据传输的领域事件
Bounded Context之间的协作:如何实现不同域之间的协作,同时又要保证各自领域的概念的完整性是有一套方法论的。总体来说,大概有两种方式:共享内核(Shared Kernel)和防腐层(ACL, Anti-Corruption Layer)。
-----------------------------------
微服务拆分原则:
1. 康威定律;
2. 领域模型;
3. 伸缩需求;
4. 修改相关性;
5. 部署频率;
6. 避免分布式事务;
康威定律简单来说就是系统设计(产品结构)等同组织形式,每个设计系统的组织,其产生的设计等同于组织之间的沟通结构。如果单个服务由不同组织管理,需求无法达成统一,面临着令出多头、需求干扰的风险。
伸缩需求:同一个进程之内的不同业务功能,有时在业务量方面会出现较大的差异,具体要求的进程数量会有较大差别,这样的模块锁定在同一个进程之内,势必会造成资源的浪费。
部署频率:同一个交付物内不同的组件有着不同的上线频率,会大大的提高上线的发生频率,会造成较大的人员浪费。
修改相关性:如果同一个交付物内的不同组件,经常会被同步修改,这可能说明,如果发生拆分,这两个模块应该是在一起的。
领域建模:针对业务领域,引入限界上下文(Bounded Context)和上下文映射(Context Map)对业务领域进行合理的分解,识别出核心领域(Core Domain)与子领域(Sub Domain),并确定领域的边界以及它们之间的关系。根据核心领域和子域划分微服务边界。
对于一个单体应用,拆分过程应该是循序渐进、逐步拆分、有简到繁、由粗到细,是一个渐进的过程。例如先将有明显边界的业务拆分为独立服务,无法明细边界的先混在一起,等业务需求逐步清晰后再拆。拆分时先拆分为几个相对较粗粒度的服务,根据业务需求情况,逐步将粗粒度的服务中相对稳定,可以沉淀的业务拆分为独立服务。在这个过程中,原有的单体应用也可以承担部分兼容能力,在改造完成前,不对外系统造成过大的影响。
微服务的拆分是跟业务需求强相关,如果业务需求变更不多、相对稳定,处理的请求并发量不高,单体应用的稳定性和可维护性更好,更加适用。
-----------------------------------
Seata: Simple Extensible Autonomous Transaction Architecture
在实际开发中会进行取舍,对于更多的金融核心以上的业务系统可以采用补偿事务,补偿事务处理在30年前就提出了Saga理论,随着微服务的发展,逐步受到关注。目前业界比较公认Saga是作为长事务的解决方案。
社区和业界的Saga方案:
1. Apache Camel Saga;
2. Eventuate Tram Saga;
3. Apache ServiceComb Saga;
4. Seata Saga;
补偿事务的方案,基本上是两种:
1. 一种是当一个服务在失败时需要"重试"或"补偿"时,在执行服务前在数据库插入一条记录,记录状态,当异常时通过定时任务去查询数据库记录并进行"重试"或"补偿",当业务流程执行成功则删除记录;
2. 另一种设计一个状态机引擎和简单的DSL,编排业务流程和记录业务状态,状态机引擎可以定义"补偿服务",当异常时由状态机引擎反向调用"补偿服务"进行回滚,同时还会有一个"差错守护"平台,监控那些执行失败或补偿失败的业务流水,并不断进行"补偿"或"重试"。
社区和业界的解决方案一般有两种,一种基于状态机或流程引擎通过DSL方式编排流程和补偿定义,一种是基于Java注解+拦截器实现补偿,优缺点如下:
1. 状态机+DSL
优点:
1. 可以用可视化工具来定义业务流程,标准化,可读性高,可实现服务编排的功能;
2. 提高业务分析人员与程序开发人员的沟通效率;
3. 业务状态管理:流程本质就是一个状态机,可以很好的反应业务状态的流转;
4. 提高异常处理灵活性:可以实现宕机恢复后的"向前重试"或"向后补偿";
5. 天然可以使用Actor模型或SEDA架构等异步处理引擎来执行,提高整体吞吐率;
缺点:
1. 业务流程实际是由JAVA与DSL配置组成,程序与配置分离,开发起来比较繁琐;
2. 如果是改造现有业务,对业务侵入性高;
3. 引擎实现成本高;
2. 拦截器+java注解:
优点:
1. 程序与注解在一起,开发简单,学习成本低;
2. 方便接入现有业务;
3. 基于动态代理拦截器,框架实现成本低;
缺点:
1. 框架无法提供Actor模型或SEDA架构等异步处理模式来提高系统吞吐量;
2. 框架无法提供业务状态管理;
3. 难以实现宕机恢复后的"向前重试",因为无法恢复线程上下文;
阿里内部有安全生产三板斧概念:可灰度、可观测、可回滚。必须要掌握发布系统的灰度、观测和回滚功能如何使用。
灰度发布的几种策略:
1. 蓝绿发布:通过部署两套环境来解决新老版本的发布问题。如果新版本( New Version )发生问题要进行回滚的时候,直接通过切流将流量全部切到老版本( Old Version )上。
优点:升级切换和回退比发布回滚迅速;
缺点:成本较高,需要部署两套环境。如果新版本中基础服务出现问题,会瞬间影响全网用户;如果新版本有问题也会影响全网用户;
2. 金丝雀发布:
优点:灵活,策略自定义,可以按照流量或具体的内容进行灰度(比如不同账号、不同参数),出现问题不会影响全网用户;
缺点:没有覆盖到所有的用户导致出现问题不好排查;
3. 滚动发布:金丝雀发布的一种变化。通过分批发布的方式进行多批发布(比如一共 9 个实例,分 3 批,每次 3 个实例发布),适合大规模应用发布。
优点:出现问题不会影响全网用户,适合大规模应用发布;
缺点:发布和回滚周期较长。
Advice, Interceptor, ConstructorInterceptor, MethodInterceptor, JoinPoint, Invocation, MethodInvocation, ConstructorInvocation;
public interface AopProxyFactory {
AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
}
Spring AOP核心接口和类:
TargetClassAware
Advised
TargetSource
ProxyConfig
AdvisedSupport
ProxyCreatorSupport
ProxyFactory
AopProxy
CglibAopProxy
JDKDynamicAopProxy
ObjenesisCglibAopProxy
AopProxyFactory
DefaultAopProxyFactory
AdvisorChainFactory
DefaultAdvisorChainFactory
Advisor
PointcutAdvisor
Pointcut
ClassFilter
MethodMatcher