技术栈:
https://share.note.youdao.com/s/LKfdB11d
主要使用到的技术包括:
springboot + spring cloud + mybatis + redis + ehcache + rabbit mq + AWS S3 + mysql
ehcache
EhCache直接在JVM中进行缓存,速度快,效率高。与Redis相比,操作简单、易用、高效,虽然EhCache也提供有缓存共享的方案,但对分布式集群的支持不太好,缓存共享实现麻烦。
| 12
 3
 4
 
 | @Cacheable(value = "serviceNameCache", key = "targetClass+methodName+#p0")@CachePut 用于新增
 @CacheEvict 用于删除
 @Caching 用于组合条件
 
 | 
redis:
缓存、分布式锁、事务、持久化存储
redis单线程为什么这么快 ?
redis 常见数据结构以及使用场景分析 
| 12
 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进行有序排列。
 举例: 在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜
 
 | 
删除机制
哨兵机制
缓存穿透 
| 12
 3
 4
 5
 6
 7
 
 | 简介:一般是黑客故意去请求缓存中不存在的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量
 请求而崩掉。
 
 解决办法:
 最常见的是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
 另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
 
 | 
缓存雪崩 
| 12
 3
 4
 5
 6
 7
 
 | 简介:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。 解决办法:
 事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
 
 事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
 
 事后:利用 redis 持久化机制保存的数据尽快恢复缓存
 
 | 
extends WebSecurityConfigurerAdapter
implements Filter
| 12
 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;
 
 }
 
 | 
| 12
 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
模式
| 12
 3
 4
 
 | Direct exchange:routingkey完全匹配Fanout exchange:订阅模式,exchange绑定的所有队列
 Topic exchange:routingkey通配
 Headers exchange:不通过routingkey,通过请求头信息
 
 | 
死信队列
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | DLX,全称为Dead-Letter-Exchange , 可以称之为死信交换机。当消息在一个队列中变成死信(dead message)之后,它能被重新发送到另一个交换机中,这个交换机就是DLX ,绑定DLX的队列就称之为死信队列。
 消息变成死信,可能是由于以下的原因:
 
 - 消息被拒绝
 - 消息过期
 - 队列达到最大长度
 
 DLX也是一个正常的交换机,和一般的交换机没有区别,它能在任何的队列上被指定,实际上就是设置某一个队列的属性。当这个队列中存在死信时,Rabbitmq就会自动地将这个消息重新发布到设置的DLX上去,进而被路由到另一个队列,即死信队列。
 
 | 
| 12
 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";
 }
 
 | 
定时任务
| 12
 
 | @Scheduled(cron = "${app.monitor.task.day}")@SchedulerLock(name = "SERVICENAME_SUBSCRIPTION_DAY", lockAtLeastFor = 30000, lockAtMostFor = 60000)
 
 | 
k8s
openfeign
| 12
 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