技术栈:
https://share.note.youdao.com/s/LKfdB11d
主要使用到的技术包括:
springboot + spring cloud + mybatis + redis + ehcache + rabbit mq + AWS S3 + mysql
ehcache
EhCache直接在JVM中进行缓存,速度快,效率高。与Redis相比,操作简单、易用、高效,虽然EhCache也提供有缓存共享的方案,但对分布式集群的支持不太好,缓存共享实现麻烦。
1 2 3 4
| @Cacheable(value = "serviceNameCache", key = "targetClass+methodName+#p0") @CachePut 用于新增 @CacheEvict 用于删除 @Caching 用于组合条件
|
redis:
缓存、分布式锁、事务、持久化存储
redis单线程为什么这么快 ?
redis 常见数据结构以及使用场景分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 1. String:key-value类型 常规key-value缓存应用; 常规计数:微博数,粉丝数等。
2. Hash 是一个 string 类型的 field 和 value 的映射表。 hash 特别适合用于存储对象,后续操作的时候,可以直接仅仅修改这个对象中的某个字段的值。 比如存储用户信息,商品信息等等
3. List 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作 微博的关注列表,粉丝列表, 消息列表等功能都可以用Redis的 list 结构来实现。
4. Set 是可以自动排重的。 Redis可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程
5. Sorted Set 增加了一个权重参数score,使得集合中的元素能够按score进行有序排列。 举例: 在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜
|
删除机制
哨兵机制
缓存穿透
1 2 3 4 5 6 7
| 简介: 一般是黑客故意去请求缓存中不存在的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量 请求而崩掉。
解决办法: 最常见的是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。 另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
|
缓存雪崩
1 2 3 4 5 6 7
| 简介:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。 解决办法:
事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
事后:利用 redis 持久化机制保存的数据尽快恢复缓存
|
extends WebSecurityConfigurerAdapter
implements Filter
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class User implements BaseEntity { private Long id; private Group group; private String role; private Long creatorId; private String name; private String email; private String password; private Status status; private Date createTime; private Date updateTime;
}
|
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
| @EnableWebSecurity @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
System.arraycopy(baseUrls, 0, urls, excludeUrls.size(), baseUrls.length); http.csrf().disable() .authorizeRequests() .antMatchers(urls).permitAll() .antMatchers("/api/admin/users").hasAnyRole("ADMIN") .anyRequest() .authenticated();
http.exceptionHandling() .authenticationEntryPoint((request, response, authException) -> { Map<String, String> message = new HashMap<>(4); message.put("message", I18nContext.getMessage("AUTH_ERROR_403")); message.put("url", ContextConfig.getConf("WEB_OAUTH_LOGIN_URL")); response.setStatus(HttpStatus.UNAUTHORIZED.value()); MessageUtils.respMsg(response, message); }) .accessDeniedHandler((request, response, accessDeniedException) -> MessageUtils.respStringMsg(HttpStatus.FORBIDDEN));
http.addFilterBefore(new OauthLoginFilter(sysUserService), UsernamePasswordAuthenticationFilter.class);
} }
|
rabbit-mq
模式
1 2 3 4
| Direct exchange:routingkey完全匹配 Fanout exchange:订阅模式,exchange绑定的所有队列 Topic exchange:routingkey通配 Headers exchange:不通过routingkey,通过请求头信息
|
死信队列
1 2 3 4 5 6 7 8 9
| DLX,全称为Dead-Letter-Exchange , 可以称之为死信交换机。当消息在一个队列中变成死信(dead message)之后,它能被重新发送到另一个交换机中,这个交换机就是DLX ,绑定DLX的队列就称之为死信队列。
消息变成死信,可能是由于以下的原因:
- 消息被拒绝 - 消息过期 - 队列达到最大长度
DLX也是一个正常的交换机,和一般的交换机没有区别,它能在任何的队列上被指定,实际上就是设置某一个队列的属性。当这个队列中存在死信时,Rabbitmq就会自动地将这个消息重新发布到设置的DLX上去,进而被路由到另一个队列,即死信队列。
|
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
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LogOperation {
String module();
String operation() default "";
String[] fields();
String [] unionFields() default {};
String operationField();
String operator() default "@userServiceImpl.getCurrentUser().getId()";
String originExpression() default "";
String currentExpression() default "";
String condition() default "true"; }
|
定时任务
1 2
| @Scheduled(cron = "${app.monitor.task.day}") @SchedulerLock(name = "SERVICENAME_SUBSCRIPTION_DAY", lockAtLeastFor = 30000, lockAtMostFor = 60000)
|
k8s
openfeign
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
@FeignClient(name = "${openfeign.serviceName.accountClient}", url = "${openfeign.serviceName.accountDomain}", fallbackFactory = FeignHacBackService.class, configuration = FeignConfiguration.class) @Service public interface FeignHacService { @RequestMapping(value = "${openfeign.serviceName.userApi}", method = RequestMethod.GET) HacMessage<HacUser> syncUsers(); }
|
spring cloud