之前我们了解了 Sentinel 集成 SpringBoot实现限流,也探讨了Sentinel的限流基本原理,那么接下去我们来学习一下Sentinel整合Dubbo及 Nacos 实现动态数据源的限流以及分布式限流。

  先来看一下我的工程目录:

单服务的限流:

  Provider :

  首先从 api 模块开始:

  其中只是定义了一个接口:

  1. public interface SentinelService {
  2. String sayHello(String txt);
  3. }

  接下去来编写服务端的代码。

1.首先需要添加我们需要的依赖:

  1. <dependency>
  2. <groupId>com.wuzz.demo</groupId>
  3. <artifactId>sentinel-dubbo-api</artifactId>
  4. <version>1.0-SNAPSHOT</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.apache.dubbo</groupId>
  8. <artifactId>dubbo</artifactId>
  9. <version>2.7.</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.apache.curator</groupId>
  13. <artifactId>curator-recipes</artifactId>
  14. <version>4.0.</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>com.alibaba.csp</groupId>
  18. <artifactId>sentinel-dubbo-adapter</artifactId>
  19. <version>1.6.</version>
  20. </dependency>
  1. <dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.6.3</version>
    </dependency>

2.我们需要编写接口的实现类并且发布成Dubbo服务:

  1. @Service//把当前服务发布成dubbo服务
  2. public class SentinelServiceImpl implements SentinelService {
  3.  
  4. @Override
  5. public String sayHello(String txt) {
  6. return "hello world :" + LocalDateTime.now();
  7. }
  8. }

3.添加 Dubbo相关配置,这里采用注解的方式:

  1. @Configuration
  2. @DubboComponentScan("com.wuzz.demo")
  3. public class DubboConfig {
  4.  
  5. @Bean
  6. public ApplicationConfig applicationConfig(){
  7. ApplicationConfig applicationConfig=new ApplicationConfig();
  8. applicationConfig.setName("sentinel-dubbo");
  9. applicationConfig.setOwner("wuzz");
  10. return applicationConfig;
  11. }
  12. @Bean
  13. public RegistryConfig registryConfig(){
  14. RegistryConfig registryConfig=new RegistryConfig();
  15. registryConfig.setAddress("zookeeper://192.168.1.101:2181");
  16. return registryConfig;
  17. }
  18. @Bean
  19. public ProtocolConfig protocolConfig(){
  20. ProtocolConfig protocolConfig=new ProtocolConfig();
  21. protocolConfig.setName("dubbo");
  22. protocolConfig.setPort();
  23. return protocolConfig;
  24. }
  25. }

4.配置文件 application.properties:

  1. server.port =

5.编写主启动类:

  1. @SpringBootApplication
  2. public class SentinelProviderApplication {
  3. public static void main(String[] args) throws IOException {
  4. initFlowRules();
  5. SpringApplication.run(SentinelProviderApplication.class, args);
  6. System.in.read();
  7. }
  8.  
  9. //初始化规则
  10. private static void initFlowRules() {
  11. List<FlowRule> rules = new ArrayList<>(); //限流规则的集合
  12. FlowRule flowRule = new FlowRule();
  13. flowRule.setResource("com.wuzz.demo.SentinelService:sayHello(java.lang.String)");//资源(方法名称、接口)
  14. flowRule.setCount();//限流阈值 qps=10
  15. flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);//限流阈值类型(QPS 或并发线程数)
  16. //流量控制手段(直接拒绝、Warm Up、匀速排队)
  17. flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
  18. flowRule.setLimitApp("sentinel-web");//流控针对的调用来源,若为 default 则不区分调用来源
  19. rules.add(flowRule);
  20. FlowRuleManager.loadRules(rules);
  21. }
  22. }

设置限流的基准:

  Service Provider 用于向外界提供服务,处理各个消费者的调用请求。为了保护 Provider 不被激增的流量拖垮影响稳定性,可以给 Provider 配置 QPS 模式的限流,这样当每秒的请求量超过设定的阈值时会自动拒绝多的请求。限流粒度可以是服务接口和服务方法两种粒度。若希望整个服务接口的 QPS 不超过一定数值,则可以为对应服务接口资源(resourceName 为接口全限定名)配置 QPS 阈值;若希望服务的某个方法的 QPS 不超过一定数值,则可以为对应服务方法资源(resourceName 为接口全限定名:方法签名)配置 QPS 阈值.

LimitApp:

  很多场景下,根据调用方来限流也是非常重要的。比如有两个服务 A 和 B 都向 Service Provider 发起调用请求,我们希望只对来自服务 B 的请求进行限流,则可以设置限流规则的 limitApp 为服务 B 的名称。Sentinel Dubbo Adapter 会自动解析 Dubbo 消费者(调用方)的 application name 作为调用方名称(origin),在进行资源保护的时候都会带上调用方名称。若限流规则未配置调用方(default),则该限流规则对所有调用方生效。若限流规则配置了调用方则限流规则将仅对指定调用方生效。

  注:Dubbo 默认通信不携带对端 application name 信息,因此需要开发者在调用端手动将 applicationname 置入 attachment 中,provider 端进行相应的解析。Sentinel Dubbo Adapter 实现了一个 Filter 用于自动从 consumer 端向 provider 端透传 application name。若调用端未引入 Sentinel DubboAdapter,又希望根据调用端限流,可以在调用端手动将 application name 置入 attachment 中,key 为dubboApplication.

ControlBehavior:

  当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的手段包括以下几种:直接拒绝、Warm Up、匀速排队。对应 FlowRule 中的 controlBehavior 字段

  1. 直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时
  2. Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式,当系统长期处于低并发的情况下,流量突然增加到qps的最高峰值,可能会造成系统的瞬间流量过大把系统压垮。所以warmup,相当于处理请求的数量是缓慢增加,经过一段时间以后,到达系统处理请求个数的最大值
  3. 匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法它的原理是,以固定的间隔时间让请求通过。当请求过来的时候,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过;否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过;反之,则马上抛出阻塞异常。可以设置一个最长排队等待时间: flowRule.setMaxQueueingTimeMs(5 * 1000); // 最长排队等待时间:5s这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

  Consumer :

1.添加依赖:

  1. <dependency>
  2.   <groupId>com.wuzz.demo</groupId>
  3.   <artifactId>sentinel-dubbo-api</artifactId>
  4.   <version>1.0-SNAPSHOT</version>
  5. </dependency>
  6. <dependency>
  7.   <groupId>org.apache.dubbo</groupId>
  8.   <artifactId>dubbo</artifactId>
  9.   <version>2.7.</version>
  10. </dependency>
  11. <dependency>
  12.   <groupId>org.apache.curator</groupId>
  13.   <artifactId>curator-recipes</artifactId>
  14.   <version>4.0.</version>
  15. </dependency>
  16. <dependency>
  17.   <groupId>org.apache.dubbo</groupId>
  18.   <artifactId>dubbo-spring-boot-starter</artifactId>
  19.   <version>2.7.</version>
  20. </dependency>

2.consumer主要是对外提供服务的,我们需要一个controller:

  1. @RestController
  2. public class SentinelController {
  3.  
  4. @Reference(timeout = ,check = false)
  5. SentinelService sentinelService;//proxy$0
  6.  
  7. @GetMapping("/say")
  8. public String sayHello(){
  9. RpcContext.getContext().setAttachment("dubboApplication","sentinel-web");
  10. return sentinelService.sayHello("test");
  11. }
  12.  
  13. @GetMapping("/say2")
  14. public String sayHello2(){
  15. return sentinelService.sayHello("test2");
  16. }
  17. }

3.主启动类:

  1. @SpringBootApplication
  2. public class SentinelConsumerApplication {
  3.  
  4. public static void main(String[] args) {
  5. SpringApplication.run(SentinelConsumerApplication.class, args);
  6. }
  7.  
  8. }

4.配置文件 application.properties :

  1. server.port =
  2. dubbo.registry.address=zookeeper://192.168.1.101:2181
  3. dubbo.scan.base-packages=com.wuzz.demo
  4. dubbo.application.name=sentinel-web

  服务端及客户端代码编写完毕,这个时候我们需要先启动Sentinel-Dashboard 以便直观的去看到限流的效果。然后启动服务端,并且在启动时加入 JVM 参数 -Dcsp.sentinel.dashboard.server=localhost:8080 指定控制台地址和端口。其他具体的参数如下:

  1. -server
  2. -XX:MaxHeapSize=128m
  3. -Xms256m
  4. -Xmx256m
  5. -XX:PermSize=128M
  6. -XX:MaxPermSize=256m
  7. -Dproject.name=app-demo
  8. -Dcsp.sentinel.dashboard.server=localhost:
  9. -Dcsp.sentinel.log.use.pid=true

  然后启动客户端,通过 JMeter 进行压测,结果如下:

  另一方面我们启动了 Sentinel-Dashboard 所以我们可以通过控制台查看:

如何实现分布式限流:

  为什么要使用集群流控呢?假设我们希望给某个用户限制调用某个 API 的总 QPS 为 50,但机器数可能很多(比如有 100 台)。这时候我们很自然地就想到,找一个 server 来专门来统计总的调用量,其它的实例都与这台 server 通信来判断是否可以调用。这就是最基础的集群流控的方式。

另外集群流控还可以解决流量不均匀导致总体限流效果不佳的问题。假设集群中有 10 台机器,我们给每台机器设置单机限流阈值为 10 QPS,理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。

  集群流控中共有两种身份:

  • Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
  • Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。

  要想使用集群流控功能,我们需要在应用端配置动态规则源,并通过 Sentinel 控制台实时进行推送。如下图所示:

搭建 Token-Server:

1.添加pom依赖:

  1. <dependency>
  2.   <groupId>com.alibaba.csp</groupId>
  3.   <artifactId>sentinel-cluster-server-default</artifactId>
  4.   <version>1.6.</version>
  5. </dependency>
  6. <dependency>
  7.   <groupId>com.alibaba.csp</groupId>
  8.   <artifactId>sentinel-datasource-nacos</artifactId>
  9.   <version>1.6.</version>
  10. </dependency>
  11. <dependency>
  12.   <groupId>com.alibaba.csp</groupId>
  13.   <artifactId>sentinel-transport-simple-http</artifactId>
  14.   <version>1.6.</version>
  15. </dependency>

2.dubbo中利用Nacos实现动态数据源要求实现  InitFunc 接口:

  1. public class NacosDataSourceInitFunc implements InitFunc {
  2.  
  3. private final String remoteAddress="localhost"; //nacos 配置中心的服务host
  4. private final String groupId="SENTINEL_GROUP";
  5. private final String FLOW_POSTFIX="-flow-rules"; //dataid(names+postfix)
  6.  
  7. //意味着当前的token-server会从nacos上获得限流的规则
  8. @Override
  9. public void init() throws Exception {
  10. ClusterFlowRuleManager.setPropertySupplier(namespace ->{
  11. ReadableDataSource<String, List<FlowRule>> rds=
  12. new NacosDataSource<List<FlowRule>>(remoteAddress,groupId,namespace+FLOW_POSTFIX,
  13. source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>(){}));
  14. return rds.getProperty();
  15. });
  16. }
  17. }

  然后需要在META-INF/services 下添加拓展点文件 com.alibaba.csp.sentinel.init.InitFunc 。其内容就是我们的实现:

  1. com.wuzz.demo.NacosDataSourceInitFunc

3.主启动类:

  1. public class ClusterServer {
  2.  
  3. public static void main(String[] args) throws Exception {
  4. ClusterTokenServer tokenServer=new SentinelDefaultTokenServer();
  5. ClusterServerConfigManager.loadGlobalTransportConfig(
  6. new ServerTransportConfig().setIdleSeconds().setPort());
  7. ClusterServerConfigManager.loadServerNamespaceSet(Collections.singleton("app-wuzz")); //设置成动态
  8. tokenServer.start();
  9. }
  10. }

  启动Sentinel-dashboard:

  1. java -Dserver.port= -Dcsp.sentinel.dashboard.server=localhost: -Dproject.name=sentinel-dashboard -XX:MaxHeapSize=128m -Xms256m -Xmx256m -XX:PermSize=128M -XX:MaxPermSize=256m -jar sentinel-dashboard-1.6..jar

  启动nacos以及增加配置:Data Id=app-wuzz-flow-rules,Group=SENTINEL_GROUP

  1. [
  2. {
  3. "resource":"com.wuzz.demo.SentinelService:sayHello(java.lang.String)",
  4. "grade":, //限流模式 qps
  5. "count":, // 限流总阈值
  6. "clusterMode":true, //集群模式 true
  7. "clusterConfig":{
  8. "flowId":,//全局唯一ID
  9. "thresholdType":,//阈值模式,全局阈值
  10. "fallbackToLocalWhenFail":true //client连接失败使用本地限流模式
  11. }
  12. }
  13. ]

  启动 Token-Server 并且添加以下JVM参数,将其加入到 Sentinel-Dashboard中进行管理:

  1. -server -Dproject.name=app-wuzz -Dcsp.sentinel.dashboard.server=localhost: -Dcsp.sentinel.log.use.pid=true

  电脑内存不足加入 -XX:MaxHeapSize=128m -Xms256m -Xmx256m -XX:PermSize=128M -XX:MaxPermSize=256m。服务启动之后,在$user.home$/logs/csp/ 可以找到sentinel-record.log.pid*.date文件,如果看到日志文件中获取到了远程服务的信息,说明token-server启动成功了,也可以通过Sentinel-Dashboard看到注册的列表:

Provider :

1.添加pom依赖:

  1. <dependency>
  2.   <groupId>com.alibaba.csp</groupId>
  3.   <artifactId>sentinel-cluster-client-default</artifactId>
  4.   <version>1.6.</version>
  5. </dependency>
  6. <dependency>
  7.   <groupId>com.alibaba.csp</groupId>
  8.   <artifactId>sentinel-datasource-nacos</artifactId>
  9.   <version>1.6.</version>
  10. </dependency>

  2.在META-INF/services 下添加拓展点文件 com.alibaba.csp.sentinel.init.InitFunc 。其内容就是我们的实现,刚刚哎token-server中配置的是直接从Nacos中获取。在服务端配置如下:

  1. public class NacosDataSourceInitFunc implements InitFunc {
  2.  
  3. private final String CLUSTER_SERVER_HOST="localhost"; //token-server的地址
  4. private final int CLUSTER_SERVER_PORT=;
  5. private final int REQUEST_TIME_OUT=; //请求超时时间
  6.  
  7. private final String APP_NAME="app-wuzz"; //namespace
  8.  
  9. //nacos的配置()
  10. private final String remoteAddress="localhost"; //nacos 配置中心的服务host
  11. private final String groupId="SENTINEL_GROUP";
  12. private final String FLOW_POSTFIX="-flow-rules"; //dataid(names+postfix)
  13.  
  14. //意味着当前的token-server会从nacos上获得限流的规则
  15. @Override
  16. public void init() throws Exception {
  17. //加载集群-信息
  18. loadClusterClientConfig();
  19.  
  20. registryClusterFlowRuleProperty();
  21. }
  22.  
  23. private void loadClusterClientConfig(){
  24. ClusterClientAssignConfig assignConfig=new ClusterClientAssignConfig();
  25. assignConfig.setServerHost(CLUSTER_SERVER_HOST);
  26. assignConfig.setServerPort(CLUSTER_SERVER_PORT);
  27. ClusterClientConfigManager.applyNewAssignConfig(assignConfig);
  28.  
  29. ClusterClientConfig clientConfig=new ClusterClientConfig();
  30. clientConfig.setRequestTimeout(REQUEST_TIME_OUT);
  31. ClusterClientConfigManager.applyNewConfig(clientConfig);
  32. }
  33.  
  34. //注册动态数据源
  35. private void registryClusterFlowRuleProperty(){
  36. ReadableDataSource<String, List<FlowRule>> rds=
  37. new NacosDataSource<List<FlowRule>>(remoteAddress,groupId,APP_NAME+FLOW_POSTFIX,
  38. source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>(){}));
  39. FlowRuleManager.register2Property(rds.getProperty());
  40. }
  41.  
  42. }

3.修改主启动类:

  1. @SpringBootApplication
  2. public class SentinelProviderApplication {
  3. public static void main(String[] args) throws IOException {//表示当前的节点是集群客户端
  4. ClusterStateManager.applyState(ClusterStateManager.CLUSTER_CLIENT);
  5. SpringApplication.run(SentinelProviderApplication.class, args);
  6. System.in.read();
  7. }
  8. }

  启动服务端并且将其加入到Sentinel-Dashboard中,另外这里再添加JVM参数的时候需要注意,这里的project-name要包含在token-server中配置的namespace中,token server 会根据客户端对应的 namespace(默认为 project.name 定义的应用名)下的连接数来计算总的阈值,我这里设置成app-wuzz.:

  1. -server
  2. -XX:MaxHeapSize=128m
  3. -Xms256m
  4. -Xmx256m
  5. -XX:PermSize=128M
  6. -XX:MaxPermSize=256m
  7. -Dproject.name=app-wuzz
  8. -Dcsp.sentinel.dashboard.server=localhost:
  9. -Dcsp.sentinel.log.use.pid=true

  服务启动之后,在$user.home$/logs/csp/ 可以找到sentinel-record.log.pid*.date文件,如果看到日志文件中获取到了token-server的信息,说明连接成功了。

  由于我们要实现分布式限流,也就是需要部署我们的局群服务,我们可以利用IDEA来实现:添加一个 SentinelProviderApplication。同时运行两个程序:

  这里 JVM 参数需要多增加一个 -Ddubbo.protocol.port=20881 才可以。然后我们启动两个服务及客户端。通过JMeter 压测就可以看到结果(多发几次请求),我们也可以直接看Sentinel-Dashboard:

  就这样实现了分布式限流。

Sentinel整合Dubbo限流实战(分布式限流)的更多相关文章

  1. sentinel整合dubbo

    <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-dubbo-a ...

  2. Spring Cloud Gateway 整合阿里 Sentinel网关限流实战!

    大家好,我是不才陈某~ 这是<Spring Cloud 进阶>第八篇文章,往期文章如下: 五十五张图告诉你微服务的灵魂摆渡者Nacos究竟有多强? openFeign夺命连环9问,这谁受得 ...

  3. 分布式限流组件-基于Redis的注解支持的Ratelimiter

    原文:https://juejin.im/entry/5bd491c85188255ac2629bef?utm_source=coffeephp.com 在分布式领域,我们难免会遇到并发量突增,对后端 ...

  4. 基于kubernetes的分布式限流

    做为一个数据上报系统,随着接入量越来越大,由于 API 接口无法控制调用方的行为,因此当遇到瞬时请求量激增时,会导致接口占用过多服务器资源,使得其他请求响应速度降低或是超时,更有甚者可能导致服务器宕机 ...

  5. 2流高手速成记(之八):基于Sentinel实现微服务体系下的限流与熔断

    我们接上回 上一篇中,我们进行了简要的微服务实现,也体会到了SpringCloudAlibaba的强大和神奇之处 我们仅改动了两个注释,其他全篇代码不变,原来的独立服务就被我们分为了provider和 ...

  6. Redis实现的分布式锁和分布式限流

    随着现在分布式越来越普遍,分布式锁也十分常用,我的上一篇文章解释了使用zookeeper实现分布式锁(传送门),本次咱们说一下如何用Redis实现分布式锁和分布限流. Redis有个事务锁,就是如下的 ...

  7. 限流(三)Redis + lua分布式限流

    一.简介 1)分布式限流 如果是单实例项目,我们使用Guava这样的轻便又高性能的堆缓存来处理限流.但是当项目发展为多实例了以后呢?这时候我们就需要采用分布式限流的方式,分布式限流可以以redis + ...

  8. 【分布式架构】--- 基于Redis组件的特性,实现一个分布式限流

    分布式---基于Redis进行接口IP限流 场景 为了防止我们的接口被人恶意访问,比如有人通过JMeter工具频繁访问我们的接口,导致接口响应变慢甚至崩溃,所以我们需要对一些特定的接口进行IP限流,即 ...

  9. Spring Cloud Gateway限流实战

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

随机推荐

  1. SpringBoot集成redis + spring cache

    Spring Cache集成redis的运行原理: Spring缓存抽象模块通过CacheManager来创建.管理实际缓存组件,当SpringBoot应用程序引入spring-boot-starte ...

  2. springboot+HttpInvoke 实现RPC调用

    开始用springboot2+hession4实现RPC服务时,发现第一个服务可以调用成功,但第二个就一直报 '<' is an unknown code.第一个服务还是可以调用的.参考网上的方 ...

  3. zookeeper之四 Curator客户端的使用

    Curator是一个开源的zookeeper客户端,解决了很多zookeeper原生客户端非常底层的细节开发工作,如连接重试.反复注册watcher等. public class CuratorOpe ...

  4. font-awesome样式只显示方框

    这是一个踩过的坑:使用font-awesome中的css样式库时,比如fa-user-circle-o,显示的不是一个用户图标,而是一个方框. 怎么回事呢? 进入css文件,发现: 咦,这些文件呢?我 ...

  5. spark-2.1.1 yarn(高可用)搭建

    一.概述 spark分布式搭建方式大致分为三种:standalone.yarn.mesos.三种分类的区别这里就不一一介绍了,不明白可自行了解.standalone是官方提供的一种集群方式,企业一般不 ...

  6. 【leetcode】1128. Number of Equivalent Domino Pairs

    题目如下: Given a list of dominoes, dominoes[i] = [a, b] is equivalent to dominoes[j] = [c, d] if and on ...

  7. 用vue构建项目同一局域网下通过ip访问

    在webpack配置文件下改为 host:'0.0.0.0' 改为后启动跳转不到登录页面 需手动修改浏览器上的0.0.0.0:8080为自己ip加上:8080 就可以在别的电脑上进行访问了 举一反三: ...

  8. 解决webstorm卡顿问题,下面详细设置方法,使得webstorm快速打开

    具体办法: 找到WebStorm.exe.vmoptions这个文件,路径如下 webstorm安装主目录>bin>WebStorm.exe.vmoptions 更改为 第二行:-Xms1 ...

  9. 3,ActiveMQ-入门(基于JMS发布订阅模型)

    一.Pub/Sub-发布/订阅消息传递模型 在发布/订阅消息模型中,发布者发布一个消息,该消息通过topic传递给所有的客户端.在这种模型中,发布者和订阅者彼此不知道对方,是匿名的且可以动态发布和订阅 ...

  10. vim插件cscope使用方法

    一.安装cscope 安装方式比较多样,可以在各自linux软件管理工具中安装,也可以去官网下载安装. sudo apt-get install cscope 二.插件安装 这里选择的是Vundle来 ...