RPC和本地JAVA调用的区别

  • RPC远程调用:一般是可以跨平台使用的,采用Socket技术,只要语言支持socket技术就可以进行互相通信。其实就是socket+反射实现的。
  • 本地调用:只能支持Java语言与Java语言开发,使用虚拟机进行通讯。

高并发服务的限流特技(解决高并发,请求多,用户等待时间长)

互联网雪崩效应的解决方案

(1)服务降级:在高并发的情况下,防止用户一直等待,直接返回一个友好的错误提示给客户端。
(2)服务熔断:在高并发的情况下,一旦达到了服务最大的承受极限(超过了线程池的最大缓存队列),直接拒绝访问,或者使用服务降级给用户返回一个错误提示。
(3)服务隔离:使用服务隔离方式解决服务雪崩效应,有计数器(不常用)和线程池(让每个接口都有自己独立的线程池,而不是一个线程池控制所有的接口)的方式
(4)服务限流:在高并发的情况下,一旦服务承受不了,就可以使用服务限流的机制(计时器,漏桶算法,令牌桶)

高并发的限流解决方案

限流算法的解决

(1)计数器

  • 他是限流算法中最简单最容易的一种算法,比如我们要求某一个接口,1分钟内的请求不能超过10次,我们可以在开始的时候设置一个计数器,每次请求过来,计数器就+1;如果该计数器的值大于10并且与第一次的请求时间间隔在一分钟以内,那么说明请求过多;如果该请求与第一次的请求间隔大于1分钟,并且该计数器的值还在限流范围内,那么重置其计数器。(就是超过1分钟,计数器清0)
  • 利用原子类来进行统计服务的实现次数
  • 注意:这里有临界的问题,如果一分钟以内在1-58秒没有请求到达,但是在59s的时候,直接访问了10次,然后过了60s即1分钟,计数器清0,又可以继续访问,61s的时候又有10个请求到达,此时下一分钟的请求量达到阈值,不能访问,知道120s之后,才可以继续访问,此时只是在2s内访问了20个请求,并不等于每分钟支持10个请求。还是直接2秒访问了20次,并不是我们定义的要求一分钟访问10次。

(2)滑动窗口计数器

  • 滑动窗口计数器有很多的使用场景,比如说限流防止系统雪崩。相比较计数器实现,滑动窗口实现会更加的平滑,能够自动的消除毛刺。
  • 滑动窗口原理是在每次有访问进来的时候,先判断前N个单位时间内的总访问量是否超过了设置的阈值,并对当前时间片上的请求数+1。
  • 每个格子都有自己的独立的原子计数器
  • 如上图所示,将一分钟之内分为6个格子,每个格子的访问时间是10s,每个格子中都有自己独立的计数器,60s之内只能有10个请求
  • 滑动窗口之所以能解决高并发的问题:滑动窗口分为10个格子,每隔10s滑动到下一个格子中,滑动会圈住6个格子(如图所示),6个格子表示1分钟之内访问的请求总数。这样就不会发生类似于计数器的临界现象,2s钟(59s和60s直接访问了20次)
  • 多少秒内创建多少个格子是自己随机创建的,我们举得例子是1分钟之内创建了6个格子。
  • 我们在多少秒内创建多少个格子,就会滑动多个个格子进行限制。
  • 滑动的时候从第一个格子开始,逐一滑动。就是说我们在第一个格子(0-9s)的时候执行完后(第一次执行的范围是0-59s),直接向右滑动一个格子,范围变为了10s-69s。就是每次都逐一滑动一个格子

(3) 令牌桶算法(限制平均流入速率)

  • 令牌桶是存放的token,就是所说的令牌
  • 令牌桶分为两个动作
    (1)以固定的速率,往桶中加入令牌-token(固定的速率其实是指的平均速度)
    (2)客户端想要访问请求,先从桶里面获取对应的token(令牌)
  • 令牌获取成功后,从桶中删除。
  • 在访问的时候开始放token,而不是服务启动的时候
  • 如果客户端发送请求的速度大于往桶中放入令牌(token)的速度,那么有的请求会直接拿不到token,此时就会直接拒绝访问服务(做服务降级或者服务熔断)
  • 如果客户端一直不访问,但是放入桶中的token是独立线程,此时桶中的token就会满。若桶中已经满了,则不会再接受新的令牌(token)
  • 令牌桶算法是一个存放固定容量令牌的桶,按照固定的速率往桶里面添加令牌,令牌桶算法的描述如下:
  • 假设限制2r/s,就是每秒钟加入2个令牌,取的是每秒的平均速率
  • 相当于开启了独立的线程,以一个固定的速率往桶中存入token
  • 桶中最多存放b个令牌,当桶满了的时候,新添加的令牌被丢弃或者拒绝
  • 当一个n个字节的数据包到达的时候,将从桶中删除n个令牌,接着数据包被发送到网络上;如果桶中的令牌不足n个,则不会删除令牌,且该数据包将被限流(要么丢弃,要么缓冲区等待)。
  • 说白了就是请求数大于桶中的令牌数,拒绝访问,做服务的降级或者熔断;如果请求数小于桶中的令牌数则允许访问,拿到令牌访问服务器;若桶满了则不会再接受新的令牌,不会将其放入桶中

基于ratelimter类实现令牌桶方式限流

  • 先做限流处理,再做业务逻辑处理
  • 通常情况下限流是放在网关中的,这样针对所有的接口对其限流,维护性不好,一般的限流是针对大流量接口的
  • Ratelimter.create方法中需要传入参数,传入的参数是速率值,如果传入1.0表示的是每秒钟往桶中存入1个令牌
  • Ratelimter是一个独立的线程,负责向桶中加入token
  • ratelimter.acquire()表示的是客户端从桶中获取对应的令牌,返回的结果是double,表示的是从桶中拿到令牌的等待时间。(开始准备拿token,一直到拿到了token的等待时间);如果获取不到令牌的话,就会一直等待(通常我们不能这么做,应该是超过了一定时间我们对其做服务的降级,因此需要传递别的参数做限制)
  • ratelimter.tryAcquire(时间,时间单位(ms,s)),返回的结果是boolean,true表示拿到了令牌:设置服务的降级处理,配置在规定时间内如果没有获取到令牌,直接走降级
  • java代码:
    (1)代码架构
  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.xiyou</groupId><artifactId>token</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.0.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>25.1-jre</version></dependency></dependencies></project>
  • 启动类:
package com.xiyou;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class TokenApplication {public static void main(String[] args) {SpringApplication.run(TokenApplication.class, args);}
}
  • OrderService
package com.xiyou.service;import org.springframework.stereotype.Service;@Service
public class OrderService {/*** 模拟添加订单操作* @return*/public boolean addOrder(){System.out.println("db......正在操作订单的数据库");return true;}}
  • IndexController
package com.xiyou.controller;import com.google.common.util.concurrent.RateLimiter;
import com.xiyou.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.concurrent.TimeUnit;/*** 使用令牌桶的方式实现限流*/
@RestController
public class IndexController {@Autowiredprivate OrderService orderService;/*** 创建独立线程* 默认传入一个参数,表示每秒往桶里放入多个令牌*/private RateLimiter rateLimiter = RateLimiter.create(1);@GetMapping("/addOrder")public String addOrder() {// 返回的结果是double类型,这个结果表示从桶中拿到令牌的时间// double acquire = rateLimiter.acquire();// System.out.println("从桶中获取的令牌的时间是: " + acquire);// 该方法表示如果500ms内没有获取到令牌的话,则直接走服务的降级// 返回的结果是一个boolean值,若返回true 则代表获取成功boolean tryAcquire = rateLimiter.tryAcquire(1, TimeUnit.MILLISECONDS);// 如果为false表示的是没有拿到 相当于做了服务降级if (!tryAcquire) {System.out.println("别抢了,再抢也是等待");return "没有拿到令牌";}// 业务逻辑处理boolean result = orderService.addOrder();if (result) {System.out.println("抢购成功");return "成功在500ms内拿去到令牌";}return "没有拿到令牌";}
}

传统方式ratelimiter的缺点:

  • 传统的方式整合RateLimiter有一个很大的缺点就是代码的重复量特别大,而且其本身是不支持注解的
  • 一般来说限流的代码是放到网关中,但是这就是针对了所有的接口都做了限流(如果接口的访问高并发不多的时候,不建议这么做,这么做的维护性不是很强 ,有时候还会影响整体的性能的),限流的代码其实最好还是放到高并发的接口中的。
  • 因此其实我们最好实现注解版本实现特定接口的限流,传统的rateLimiter是不支持注解的方式的。

rateLimiter注解版本技术点分析

(1)自己定义一个注解
(2)SpringBoot中整合SpringAOP,其AOP的作用就是定义一个切入点,拦截切入点的方法,拦截方法之前或者之后进行处理,其作用是简化代码,减少重复代码
(3)使用环绕通知

  1. 判断方法上是否有@ExtRateLimiter注解(自定义的注解)
  2. 如果方法上有该注解的话
  3. 使用反射技术获取注解方法上的参数
  4. 调用原生的RateLimiter代码创建令牌桶
  5. 如果获取令牌超时,直接调用服务降级的方法(服务降级的方法也需要自己定义)
  6. 如果能够获取令牌的话,直接进入实际的请求方法。
  • 注意:我们利用AOP实现令牌桶的时候,初始化令牌桶的时候即RateLimter.create(速率);调用该方法的时候,一定不能将其放在方法中,因为这样会造成一个请求多次访问会重复创建自己的令牌桶。需要保证其是单例的。一个请求只创建一个令牌桶。

  • java代码实现:
    (1)程序架构

    (2)pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.xiyou</groupId><artifactId>token-annotation</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.0.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>25.1-jre</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency></dependencies></project>

(3)启动类

package com.xiyou;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class TokenApplication {public static void main(String[] args) {SpringApplication.run(TokenApplication.class, args);}
}

(4)controller

package com.xiyou.controller;import com.xiyou.annotation.ExtRateLimiter;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class IndexController {/*** 测试自定义注解* @return* @throws InterruptedException*/@RequestMapping("/findOrder")@ExtRateLimiter(value = 1, timeOut = 1)public String findOrder() throws InterruptedException {System.out.println("findOrder");return "SUCCESS";}
}

(5)自定义注解

package com.xiyou.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定义注解类* 只能用在方法上* @author*/
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtRateLimiter {/*** 表示令牌桶一秒放入多少个令牌* @return*/double value();/*** 表示令牌桶的超时时间* @return*/long timeOut();
}

(6)重要,实现自定义注解(要加上@Component,否则无法加载)

package com.xiyou.aop;import com.google.common.util.concurrent.RateLimiter;
import com.xiyou.annotation.ExtRateLimiter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;/*** 自定义令牌桶注解的切面* 我们借由切面实现令牌桶的注解* @author*/
@Aspect
@Component
public class RateLimiterAop {/*** 该Map线程安全* 其key是请求的url value是各自请求的令牌桶对象*/private static ConcurrentHashMap<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<String, RateLimiter>();/*** 自定义切点,其切点是controller包下的所有方法,都会进入拦截*/@Pointcut("execution(public * com.xiyou.controller.*.*(..))")public void rlAop() {}/*** 环绕通知* @param proceedingJoinPoint* @return* @throws Throwable*/@Around("rlAop()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{// 先得到当前调用的方法的对象MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();// 看该方法上是否有@ExtRateLimter注解ExtRateLimiter extRateLimiter = signature.getMethod().getDeclaredAnnotation(ExtRateLimiter.class);if (extRateLimiter == null) {// 表示没有这个注解Object proceed = proceedingJoinPoint.proceed();return proceed;}// 有注解,要开始判断能否拿到令牌// 取令牌配置的value 即执行的速率(每秒放多少个令牌进去)double value = extRateLimiter.value();// 取令牌的等待时间long timeOut = extRateLimiter.timeOut();// 创建令牌桶RateLimiter rateLimiter = getRateLimiter(value, timeOut);// 判断其是否超时boolean tryAcquire = rateLimiter.tryAcquire(timeOut, TimeUnit.MILLISECONDS);if (!tryAcquire) {// 如果超时了 返回false// 调用服务降级的方法fallBack();return null;}// 获取到了令牌 开始执行Object proceed = proceedingJoinPoint.proceed();return proceed;}/*** 服务降级*/private void fallBack() throws Exception{System.out.println("令牌桶已经满了。");// 得到当前的请求参数,用来去response,写数据ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 得到响应对象HttpServletResponse response = attributes.getResponse();// 设置响应头response.setHeader("Content-type", "text/html;charset=UTF-8");// 往出写PrintWriter writer = response.getWriter();try {writer.println("执行了降级方法");}catch (Exception e) {} finally {writer.close();}}/*** 往map里面放置,去重* key是url value是RateLimter对象(令牌桶对象)*/private RateLimiter getRateLimiter(double value, long timeout){// 获取当前方法的urlServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();String requestURI = request.getRequestURI();// 令牌桶对象RateLimiter rateLimiter = null;if (!rateLimiterMap.containsKey(requestURI)) {// map中没有值// 创建独立的线程 创建令牌桶(速度是每秒放value个令牌)rateLimiter = RateLimiter.create(value);rateLimiterMap.put(requestURI, rateLimiter);} else {// 取值rateLimiter = rateLimiterMap.get(requestURI);}return rateLimiter;}
}

(4)漏桶算法(限制常量流出速率)

  • 以固定的速率从桶中流出,以任意的速率往桶中流入(客户端发送的请求),注意这里的流入流出并不是token,而是指的是不重复的标识
  • 漏桶算法作为计量工具时候,可以用于流量整形和流量控制,漏桶算法的描述如下:
  • 一个固定容量的漏桶,按照常量固定速率流出水滴
  • 如果桶是空的,则不需要流出水滴
  • 可以以任意的速率流入水滴到漏桶;
  • 如果流入水滴超出了桶的容量,则流入的水滴溢出了(被丢弃),而漏桶的容量是不变的。
  • 流入速率 > 流出速率,可能桶中的水滴会发生溢出,那么溢出的水滴请求都是拒绝访问,或者直接调用服务降级的方法。(水滴可以理解为一个不重复的唯一标识)
  • 令牌桶和漏桶的对比:(重要)
  • 令牌桶是按照固定的速率往桶中添加令牌,请求是否被处理需要看桶中的令牌是否足够,当令牌数减少为0的时候则拒绝新的请求。
  • 漏桶则是按照常量固定速率流出请求,流入请求的速率任意,当流入的请求数累计到漏桶容量的时候,则新流入的请求就会被拒绝。
  • 令牌桶限制的是平均的流入速率(允许突发请求,只要有令牌就可以处理,支持一次拿到3个令牌,4个令牌),并且允许一定程度突发流量, 因为我们是用线程放入令牌到令牌桶的,相当于是平均速度;
  • 漏桶则是限制的常量流出速率(就是流出的速率是一个固定的常量值,比如都是1的速率流出,而不是一会1 ,一会2的),从而平滑突发流入的速率;
  • 令牌桶允许一定程度上的突发(因为我们事先是用独立线程将令牌放入令牌桶),而漏桶主要的目的是平滑流入速率
  • 令牌桶和漏桶两个算法的实现其实是可以一样的,但是方向是反的,对于相同的参数得到的限流效果是一样的。
  • 另外我们有时候也可以使用计数器去实现限流,主要用于限制总并发数,比如数据库连接池,线程池,秒杀的并发数;只要全局总请求数或者一定时间段的总请求数设定的阈值则进行限流,是简单粗暴的总量限流,而不是平均速率的限流(不推荐使用计数器限流)

应用级限流

限流总并发/连接/请求数

  • 对于一个应用系统来说一定会有极限并发/请求数,就是说总是有一个TPS/QPS阈值,如果超过了阈值则系统就会不响应或者响应的特别慢用户的请求,因此我们最好进行过载保护,防止大量请求涌入击垮系统。
  • 如果使用过Tomcat,其Connector中有几个配置参数可以实现限制:
    (1)acceptCount:如果Tomcat的线程都忙于响应,新来的连接就会进入队列排队,如果超出了排队的大小,则会拒绝连接。
    (2)maxConnections:瞬时的最大连接数,超出的会排队等待
    (3)maxThreads:Tomcat能够启用来处理请求的最大的线程数,如果请求处理量一直远远大于最大线程数则可能僵死。

限流总资源数

  • 如果有的资源是稀缺资源(数据库连接,线程),而且可能有多个系统都会去使用它,那么就需要限制应用;可以使用池化技术限制总资源数:连接池,线程池。比如分配给每个应用的数据库连接是100 ,那么本应用最多可以使用100个资源,超出了可以等待或者抛出异常。

限流某个接口的总并发/请求数

  • 如果接口可能会有突发的访问情况,但是又担心访问量太大造成崩溃,如抢购业务;这个时候就会需要限制这个接口的总并发/请求数总请求数;因为粒度比较细,可以为每个接口都设置相对应的阈值,可以使用Java中的原子类进行限流(AtomicLong)
  • 适合对业务无损的服务或者需要过载保护的服务进行限流,如抢购业务,超出了大小要么让用户排队,要么告诉告诉用户已经没货了,对用户来说是可以接受的。而一些开放平台也会限制用户调用某个接口的试用请求量,也可以使用计数器限流的方式去实现。这种方式没有平滑的处理,是粗暴的限流,不推荐使用。

限制某个接口的时间窗请求数

  • 即第一个时间窗口内的请求数,如想限制某个接口/服务每秒/每分钟/每天的请求数/调用量。如一些基础服务会被很多其他的系统调用,比如商品详情页服务会调用基础商品服务调用,但是怕因为更新量较大将基础服务打挂,这时候我们将要对每秒/每分钟的调用量进行限速;
  • 平滑限流某个接口的请求数:
    之前的限流方式都不能很好的应对突发请求,即瞬间请求可能都被允许从而导致一些问题;因此在一些场景中需要对突发请求进行整形,整形为平均速率请求处理(如5r/s,则每隔200ms处理一个请求,平滑了速率)。这个时候有两种算法满足了我们的场景:
    (1)令牌桶
    (2)漏桶算法

接入层限流

互联网高并发解决方案(2)--高并发服务限流特技相关推荐

  1. 搞懂分布式技术30:高并发解决方案——提升高并发量服务器性能解决思路

    高并发解决方案--提升高并发量服务器性能解决思路 一个小型的网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构.性能的要求都很 ...

  2. Java 高并发_JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过!...

    JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过! 1.JPG (37.82 KB, 下载次数: 0) 2018-12-3 09:40 上传 2.JPG (28 ...

  3. 【java】 java 高并发解决方案和高负载优化方法

    [java] java 高并发解决方案和高负载优化方法 参考文章: (1)[java] java 高并发解决方案和高负载优化方法 (2)https://www.cnblogs.com/lonelywo ...

  4. 分布式技术与实战第七课 高并发下高可用的熔断、降级、限流和负载均衡、监控以及统一的日志系统

    第39讲:从双十一看高可用的保障方式 从这一课时开始,专栏内容进入最后一个模块,即分布式高可用系列,这部分的内容,我将以电商大促为背景,讲解系统限流.降级熔断.负载均衡.稳定性指标.系统监控和日志系统 ...

  5. 10分钟带你彻底搞懂服务限流和服务降级

    文章目录 十分钟搞懂系列 服务限流 计数器法 滑动窗口法 漏桶算法 令牌桶算法 服务降级 十分钟搞懂系列 序号 标题 链接 1 10分钟带你彻底搞懂企业服务总线 https://blog.csdn.n ...

  6. 秒杀项目之网关服务限流熔断降级分布式事务

    目录 一.网关服务限流熔断降级 二.Seata--分布式事务 2.1 分布式事务基础 2.1.1 事务 2.1.2 本地事务 2.1.3 分布式事务 2.1.4 分布式事务场景 2.2 分布式事务解决 ...

  7. 微服务架构服务限流方案详解

    话说在 Spring Cloud Gateway 问世之前,Spring Cloud 的微服务世界里,网关一定非 Netflix Zuul 莫属.但是由于 Zuul 1.x 存在的一些问题,比如阻塞式 ...

  8. Sentinel微服务限流、熔断、降级介绍(一)

    概述 在互联网应用中,会有很多突发性的高并发访问场景,比如双11大促.秒杀等.这些场景最大的特点就是访问量会远远超出系统所能够处理的并发数. 在没有任何保护机制的情况下,如果所有的流量都进入服务器,很 ...

  9. 亿级流量架构之服务限流思路与方法

    为什么要限流 日常生活中,有哪些需要限流的地方? 像我旁边有一个国家AAAA景区,平时可能根本没什么人前往,但是一到五一或者春节就人满为患,这时候景区管理人员就会实行一系列的政策来限制进入人流量, 为 ...

最新文章

  1. 收藏 | 有哪些相见恨晚的科研经验?
  2. Design Pattern - Adapter(C#)
  3. ubuntu 安装bcompare
  4. SQL语言之DQL语言学习(三)排序查询
  5. 【编程思想】【设计模式】【行为模式Behavioral】观察者模式Observer
  6. The Event System
  7. redis主从搭建和分片集群搭建
  8. 解决 SpringBoot 在 JDK8 中 LocalDateTime (反)序列化问题
  9. 混合云备份服务 > 产品简介 > 什么是混合云备份
  10. android MVP连接服务器,Android之MVP模式实现登陆和网络数据加载
  11. STC51入门笔记(郭天祥C语言)---第十一节:使用DS12C887时钟芯片设计高精度时钟
  12. 使用Windows批处理文件递归删除当前路径下的指定文件夹
  13. 在html中不是链接目标属性,在HTML中,()不是链接的目标属性。A、selfB、newC、blankD、Top - 扁豆云问答...
  14. IDEA方法名,javadoc神器
  15. 计算机二级c语言考点解析,2017年计算机二级C语言考点解析
  16. html渐变编织背景,css3编写浏览器背景渐变背景色
  17. git push 失败问题如何解决
  18. SQL数据库中日期函数
  19. R数据科学第三章读书笔记和习题
  20. PAT 1033 旧键盘打字 (20 分) C++

热门文章

  1. 百度字体编辑器/字体仓库
  2. 为什么Linux系统安全没有病毒?原因是“它”
  3. postgres数据库对比工具
  4. java udp nio_Java NIO UDP DEMO
  5. Python问题解决二:使用Radon变换进行线检测
  6. 创建AWS OrganizationsSCP
  7. 性能统计工具 sysstat 使用介绍
  8. Mesh Shader介绍
  9. FreeEIM 2.9 官方简体中文版发布
  10. 主板驱动导致电脑死机故障