spring cloud gateway集成sentinel并扩展支持restful api进行url粒度的流量治理

2023-11-06

前言

sentinel作为开源的微服务、流量治理组件,在对restful接口的支持上,在1.7之后才开始友好起来,对于带有@PathVariable的restful接口未作支持,在sentinel中/api/{id}这样的接口,其中/api/1与/api/2会被当做两个不同的接口处理,因此很难去做类似接口的流量治理,但在之后,sentinel团队已经提供了响应的csp扩展依赖,下文将会逐步讲述如何通过sentinel扩展来支持相应的服务流量治理

使用网关进行总体流量治理(sentinel版本:1.8.6)

这里选型为spring cloud gateway,而sentinel也对spring cloud gateway做了特殊照顾

1、cloud gateway添加依赖:

 <!-- alibaba封装的sentinel的starter -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2021.1</version>
        </dependency>
        <!-- alibaba封装的sentinel支持网关的starter -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
            <version>2021.1</version>
        </dependency>
 <!-- 此包即为sentinel提供的扩展支持restful接口的依赖 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-webmvc-adapter</artifactId>
            <version>1.8.0</version>
        </dependency>

上述需要重点关注的是sentinel-spring-webmvc-adapter包,此依赖是支持restful接口的关键,不需要我们自己改造。

2、sentinel配置

spring:
  cloud:
    sentinel:
      transport:
      #sentinel控制台地址
        dashboard: 1.1.1.1:8600
        #sentinel通信端口,默认为8179,被占用会继续扫描,一般固定起来
        port: 8700
        #项目所在服务器ip
        client-ip: 2.2.2.2
        #心跳启动
      eager: true

client-ip在某些情况下不配置会出现sentinl控制台页面只有首页,服务一直注册不上去的情况,如果出现这种情况一定要配置上,如果没有这种情况,client-IP可以不配置,同时上述配置的这些ip端口都需要连通。

3、网关类型项目配置

/**
 * @classDesc:
 * @author: cyjer
 * @date: 2023/1/30 9:53
 */
@SpringBootApplication
@EnableCaching
@Slf4j
public class SiriusApplication {

    public static void main(String[] args) {
        System.getProperties().setProperty("csp.sentinel.app.type", "1");
        SpringApplication.run(SiriusApplication.class, args);
        log.info("<<<<<<<<<<启动成功>>>>>>>>>>");
    }

}

如果是网关类型的项目,需要配置csp.sentinel.app.type= 1,普通项目与网关项目,在控制台上所展示和可使用的功能是不同的

4、通过zk事件监听刷新上报api分组信息

通过将接口分组按照不同粒度,如controller粒度,和具体api接口粒度,通过zookeeper修改数据监听的方式,通过网关监听该事件,实现将api分组信息写入到sentinel中。

1、非网关项目上报api分组信息

/**
 * @classDesc: 扫描项目接口上报api
 * @author: cyjer
 * @date: 2023/2/10 13:46
 */
@Configuration
@Slf4j
@Order(1)
@RequiredArgsConstructor
public class ApiDefinitionReporter implements BeanPostProcessor, CommandLineRunner, Constraint {
    private final List<ApiSiriusDefinition> apiSiriusDefinitionList = new ArrayList<>();
    private final GatewayServiceProperties gatewayServiceProperties;
    private final Environment environment;
    private final static char JTR = '/';
    private final static String PASS = "/**";
    private final static String APPLICATION_NAME = "spring.application.name";
    private final static String CONTEXT_PATH = "server.servlet.context-path";
    private final static List<String> PASS_LIST = Arrays.asList("swaggerWelcome", "basicErrorController", "swaggerConfigResource", "openApiResource");
    private final ZookeeperService zookeeperService;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // url访问路径为:访问基地址basePath+classMappingPath+methodPath
        if (!gatewayServiceProperties.isAutoReportAndRegister() || PASS_LIST.contains(beanName)) {
            return bean;
        }
        Class<?> beanClass = bean.getClass();
        Class<?> targetClass = AopUtils.getTargetClass(bean);
        //判断类上有无controller注解 spring代理类需用spring的注解扫描工具类查找
        RestController restController = AnnotationUtils.findAnnotation(beanClass, RestController.class);
        Controller controller = AnnotationUtils.findAnnotation(beanClass, Controller.class);
        //没有注解直接跳过扫描
        if (null == controller && null == restController) {
            return bean;
        }
        String applicationName = this.getApplicationName();
        //项目访问基地址
        String basePath = this.getBasePath();

        //如果类上有controller注解再查找requestMapping注解
        RequestMapping requestMapping = AnnotationUtils.findAnnotation(beanClass, RequestMapping.class);
        String classMappingPath = this.getClassMappingPath(requestMapping);

        //按照controller分组上报
        if (StringUtils.isNotBlank(classMappingPath)) {
            String controllerGroupPath = basePath + classMappingPath + PASS;
            ApiSiriusDefinition controllerGroup = new ApiSiriusDefinition();
            controllerGroup.setGatewayId(gatewayServiceProperties.getGatewayId());
            controllerGroup.setResource("服务:" + applicationName + ",控制器:" + targetClass.getSimpleName() + ",路径:" + controllerGroupPath);
            controllerGroup.setUrlPath(controllerGroupPath);
            apiSiriusDefinitionList.add(controllerGroup);
        }

        //查找类中所有方法,进行遍历
        Method[] methods = targetClass.getMethods();
        for (Method method : methods) {
            //查找方法上RequestMapping注解
            String methodPath = "";
            String requestType = "";
            RequestMapping methodRequestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
            if (methodRequestMapping != null) {
                String[] value = methodRequestMapping.value();
                RequestMethod[] requestMethods = methodRequestMapping.method();
                if (value.length == 0) {
                    if (requestMethods.length == 0) {
                        return bean;
                    }
                    RequestMethod requestMethod = requestMethods[0];
                    requestType = requestMethod.name();
                    if (requestMethod.equals(RequestMethod.POST)) {
                        PostMapping postMapping = AnnotationUtils.findAnnotation(method, PostMapping.class);
                        methodPath = this.joinMethodPath(postMapping.value());
                    } else if (requestMethod.equals(RequestMethod.GET)) {
                        GetMapping getMapping = AnnotationUtils.findAnnotation(method, GetMapping.class);
                        methodPath = this.joinMethodPath(getMapping.value());
                    } else if (requestMethod.equals(RequestMethod.DELETE)) {
                        DeleteMapping deleteMapping = AnnotationUtils.findAnnotation(method, DeleteMapping.class);
                        methodPath = this.joinMethodPath(deleteMapping.value());
                    } else if (requestMethod.equals(RequestMethod.PATCH)) {
                        PatchMapping patchMapping = AnnotationUtils.findAnnotation(method, PatchMapping.class);
                        methodPath = this.joinMethodPath(patchMapping.value());
                    } else if (requestMethod.equals(RequestMethod.PUT)) {
                        PutMapping putMapping = AnnotationUtils.findAnnotation(method, PutMapping.class);
                        methodPath = this.joinMethodPath(putMapping.value());
                    }
                }

                ApiSiriusDefinition apiSiriusDefinition = new ApiSiriusDefinition();
                String urlPath = basePath + classMappingPath + methodPath;
                apiSiriusDefinition.setUrlPath(urlPath);
                apiSiriusDefinition.setRequestType(requestType);
                apiSiriusDefinition.setGatewayId(gatewayServiceProperties.getGatewayId());
                apiSiriusDefinition.setResource("服务:" + applicationName + ",请求类型:" + requestType + ",路径:" + urlPath);
                apiSiriusDefinitionList.add(apiSiriusDefinition);
            }

        }
        return bean;
    }

    private String joinMethodPath(String[] value) {
        if (value.length != 0) {
            String str = this.trimStrWith(value[0], JTR);
            return JTR + str;
        }
        return "";
    }

    private String getContextPath() {
        String contextPath = environment.getProperty(CONTEXT_PATH);
        contextPath = this.trimStrWith(contextPath, JTR);
        return StringUtils.isBlank(contextPath) ? "" : contextPath;
    }

    public String getApplicationName() {
        String applicationName = environment.getProperty(APPLICATION_NAME);
        applicationName = this.trimStrWith(applicationName, JTR);
        return StringUtils.isBlank(applicationName) ? "" : applicationName;
    }

    private String getBasePath() {
        String contextPath = this.getContextPath();
        String applicationName = this.getApplicationName();
        if (StringUtils.isBlank(contextPath)) {
            return JTR + applicationName;
        }
        return JTR + applicationName + JTR + contextPath;
    }

    private String getClassMappingPath(RequestMapping requestMapping) {
        if (null != requestMapping) {
            String requestMappingUrl = requestMapping.value().length == 0 ? "" : requestMapping.value()[0];
            requestMappingUrl = this.trimStrWith(requestMappingUrl, JTR);
            return JTR + requestMappingUrl;
        }
        return "";
    }

    public String trimStrWith(String str, char trimStr) {
        if (StringUtils.isBlank(str)) {
            return str;
        }
        int st = 0;
        int len = str.length();
        char[] val = str.toCharArray();
        while ((st < len) && (val[st] <= trimStr)) {
            st++;
        }
        while ((st < len) && (val[len - 1] <= trimStr)) {
            len--;
        }
        return ((st > 0) || (len < str.length())) ? str.substring(st, len) : str;
    }

    @Override
    public void run(String... args) {
        if (StringUtils.isBlank(this.getApplicationName())) {
            throw new RuntimeException(APPLICATION_NAME + " should not be null");
        }
        if (!apiSiriusDefinitionList.isEmpty()) {
            log.info("<<<<< start to report api information to api governance platform >>>>>");
            try {
                zookeeperService.create(API_DEFINITION + SPLIT + getApplicationName(), JSONArray.toJSONString(apiSiriusDefinitionList));
                zookeeperService.update(API_DEFINITION + SPLIT + getApplicationName(), JSONArray.toJSONString(apiSiriusDefinitionList));
            } catch (Exception e) {
                log.error("reported api information failed,stack info:", e);
            }
            log.info("<<<<< successfully reported api information >>>>>");
        }
    }

}

通过扫描项目下的controller和相应的mapping注解中的属性拼接出url来,通过zk来更新节点数据

2、网关添加监听事件

zk操作查看另一篇文章zookeeper相关操作

/**
 * @classDesc: 网关核心应用
 * @author: cyjer
 * @date: 2023/1/30 9:53
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class GatewayApplication implements ApplicationListener<ContextRefreshedEvent> {
    private final GatewayServiceProperties properties;
    private final ApiDefinitionService apiDefinitionService;
    private final ZookeeperService zookeeperService;
    private final ApiGroupProcesser apiGroupProcesser;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        //拉取api governance platform 信息
        apiDefinitionService.refreshApiGovernanceInfo(properties.getGatewayId());
        log.info("<<<<<<<<<<刷新api分组信息完成>>>>>>>>>>");
        zookeeperService.create(Constraint.API_DEFINITION, "init");
        zookeeperService.addWatchChildListener(Constraint.API_DEFINITION, apiGroupProcesser);
        log.info("<<<<<<<<<<api上报监听器配置完成>>>>>>>>>>");

    }
}

通过事件处理,首先启动时刷新api信息,同时尝试初始化zk节点,然后注册监听watch。

3、网关监听事件处理

/**
 * @classDesc: api分组上报
 * @author: cyjer
 * @date: 2023/2/10 11:13
 */
@Slf4j
@Component
public class ApiGroupProcesser extends AbstractChildListenerProcess implements ApiDefinitionConstraint {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private GatewayServiceProperties gatewayServiceProperties;

    @Override
    public void process(CuratorFramework curatorFramework, PathChildrenCacheEvent cacheEvent) {
        ChildData data = cacheEvent.getData();
        if (Objects.nonNull(data) && cacheEvent.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)) {
            log.info("<<<<<<<<<<上报api分组到sentinel>>>>>>>>>>");
            String path = data.getPath();
            String content = new String(data.getData(), StandardCharsets.UTF_8);
            Set<ApiDefinition> definitions = GatewayApiDefinitionManager.getApiDefinitions();
            List<ApiSiriusDefinition> list = JSONArray.parseArray(content, ApiSiriusDefinition.class);
            for (ApiSiriusDefinition apiGroup : list) {
                ApiDefinition api = new ApiDefinition(apiGroup.getResource())
                        .setPredicateItems(new HashSet<ApiPredicateItem>() {
                            {
                                add(new ApiPathPredicateItem().setPattern(apiGroup.getUrlPath())
                                        .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                            }
                        });

                definitions.add(api);
            }
            GatewayApiDefinitionManager.loadApiDefinitions(definitions);
            redisTemplate.opsForHash().put(API_INFO_REDIS_PREFIX + gatewayServiceProperties.getGatewayId(), path, JSONArray.toJSONString(list));
            log.info("<<<<<<<<<<上报api分组到sentinel成功>>>>>>>>>>");
        }
    }
}

5、sentinel控制台启动

java -Dserver.port=8600 -Dcsp.sentinel.dashboard.server=localhost:8600 -Dproject.name=sentinel-dashboard -Xms512m -Xmx512m -Xmn256m -XX:MaxMetaspaceSize=100m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/oom/log -Dfile.encoding=UTF-8 -XX:+UseG1GC -jar sentinel-dashboard-1.8.6.jar

打开sentinel控制台,请求几次接口后
在这里插入图片描述

可以看到相应的api分组信息和url路径匹配都已加载,在进行流量治理的时候就可以支持restful接口和controller粒度的治理了

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

spring cloud gateway集成sentinel并扩展支持restful api进行url粒度的流量治理 的相关文章

  • 如何让 Spring 控制器从 POJO 返回 CSV? [复制]

    这个问题在这里已经有答案了 给定一个简单的 Java 对象 public class Pojo private String x private String y private String z getters setters 是否有一些
  • 如何找出导致 poi 损坏 xlsx / xlsm 文件的原因

    我遇到的问题是 Apache POI 仅通过读取和写入就 损坏 了 xlsm xlsx 文件 例如使用以下代码 public class Snippet public static void main String args throws
  • 添加@Named时@SessionScoped不起作用

    考虑以下支持 bean import javax faces bean ManagedBean import javax faces bean SessionScoped ManagedBean SessionScoped public c
  • 在 Java 中将日期从 UTC 转换为 PST

    我需要将日期从 Google App Engine 本地服务器时区转换为 Java 中的太平洋时间 我尝试使用 Calendar calstart Calendar getInstance calstart setTimeZone Time
  • setSize() 不起作用?

    我有一个程序 需要两个按钮 一个是常规按钮 另一个具有根据鼠标悬停而变化的图片 目前 由于图片很大 JButton自定义也很大 我可以更改自定义的大小并保持图像 和翻转图像 成比例吗 我尝试过 setSize 但它没有任何作用 对于任何反馈
  • Spring JSON序列化、Gson反序列化

    我目前在某些内部对象的反序列化方面遇到问题 在春天 我在使用输出之前初始化所有对象 ResponseBody 例如 这是一个响应 id 1 location id 1 extra location data id 2 location 1
  • Java ASN.1 编译器

    现在我正在使用二进制笔记 http bnotes sourceforge net 解析 ASN 1 文件以在 Java 项目中使用 它采用 ASN 1 定义并生成 Java 类 让我可以操作 ASN 1 文件 我用扩展标记碰壁了 因为它不支
  • 在这种情况下,我如何处理 Function 和省略号/可变参数?

    我的项目之一是抛出 lambda 表达式 https github com fge throwing lambdas 我的目标是简化潜在的使用 FunctionalInterfaces in Streams 其在流中使用的唯一 缺陷 是它们
  • 如何在线程和小程序中使用双缓冲

    我有一个关于何时调用绘制和更新方法的问题 我有游戏小程序 我想在其中使用双缓冲 但我无法使用它 问题是 在我的游戏中 有一个球在 run 方法内移动 我想知道如何使用双缓冲来交换屏幕外图像和当前图像 请有人帮忙 当同时存在 update 和
  • JP QL - 一对多关系中的过滤结果

    我在尝试构建 JPQL 查询时陷入困境 并希望比我拥有更多 JPA 经验的人能够提供帮助 考虑以下两个实体 class Author String name OneToMany mappedBy author Set
  • 我可以直接在 Maven 中使用 GitHub 项目吗?

    我有兴趣使用GitHub 上的项目 https github com toelen spymemcached jcache作为我的项目中的依赖项 GitHub 项目有一个pom文件 我可以修改我的pom文件来使用这个项目 如果是这样 怎么办
  • 如何在 jax-ws 客户端中隐藏(可能)由 jax-ws 库引起的警告

    我正在使用 netbeans 在我的应用程序中生成 Web 服务客户端 我的程序使用 jax ws 库来设置调用 Web 服务的超时 出现问题是因为每当我启动这个程序时它都会生成很多这样的警告消息 2010 年 12 月 13 日下午 4
  • 了解 Etc/GMT 时区

    Apple 在从 App Store 返回自动续订订阅收据时使用 Etc GMT 时区的理由是什么 Etc GMT 时区到底是什么 Java SDK 能理解这个时区吗 或者我是否必须使用其他第三方库 例如乔达时间 http www joda
  • JPA中如何连接多个数据库?

    我有一个 Spring Boot 应用程序 当前使用 JPA 连接到单个数据库 application properties 文件中的连接详细信息 spring datasource url jdbc oracle thin localho
  • 异步方法的同步版本

    在 Java 中创建异步方法的同步版本的最佳方法是什么 假设您有一个包含这两种方法的类 asyncDoSomething Starts an asynchronous task onFinishDoSomething Called when
  • 有没有办法让 SonarQube 只警告不完整的 Switch 语句?

    使用 Java SonarQube 抱怨枚举值上的 switch 语句没有default case 给出的推理是 最终默认条款的要求是防御性编程 该条款应采取适当的行动 或包含 关于为什么不采取行动的适当评论 当开关盖上时 枚举的所有当前值
  • 与 System.in.read() 一起使用的文件结尾/流键盘组合是什么

    如果这个小问题已经得到解答 我深表歉意 我无法在SO找到它 使用以下 Java 简单代码从 IDE 控制台读取行 Windows 7 和 Eclipse Kepler int v try while v System in read 1 S
  • Spring Boot 和安全性以及自定义 AngularJS 登录页面

    我正在为 Spring Security 实现一个自定义 AngularJS 登录页面 但遇到身份验证问题 遵循本教程 示例 以及他们的示例在本地运行良好 https github com dsyer spring security ang
  • Spring验证非空元素的字符串列表

    我有一个模型类 其中包含字符串列表 该列表可以为空 也可以包含元素 如果它有元素 这些元素不能为空 举个例子 假设我有一个名为 QuestionPaper 的类 它有一个 QuestionId 列表 其中每个都是一个字符串 class Qu
  • 指定不带组件的GridBagLayout的网格参数

    我试图帮助另一个用户 但遇到了一个问题 我想用 GridBagLayout 来做到这一点 c1 c2 c3 10 80 10 v v r1 B1

随机推荐

  • html给百度富文本编辑器editor设置默认值,设置固定高度

    通常在一些文章编辑或者内容编辑的应用场景下会用到百度编辑器设置默认值进行回显 百度到的问题代码 UE getEditor editor setHeight 500 UE getEditor editor setContent 内容 执行完浏
  • Linux系列之:9秒钟掌握多种实时查询日志的方法,再也不用cat命令了。

    实时查询日志 1 引言 2 less 3 tail 3 1 单文件实时查询 3 2 多文件实时查询 3 3 目标名称查询 3 4 跟踪日志 4 multitail 4 1 安装 4 2 用法 5 总结 1 引言 由于最近从某些企业毕业的同学
  • 查看当前SHELL的种类

    shell种类 shell种类常见的有sh bash csh zsh tcsh等 方法 echo 0
  • 【C语言程序设计】C语言渔夫打鱼晒网问题!

    如果一个渔夫从 2011 年 1 月 1 日开始每三天打一次渔 两天晒一次网 编程实现当输入 2011 1 月 1 日以后的任意一天 输出该渔夫是在打渔还是在晒网 实现过程 1 自定义函数 leap 用来判断输入的年份是否是闰年 2 自定义
  • 软件测试---用例篇

    软件测试 用例篇 测试用例 Test Case 概念 是为了实施测试而向被测试系统提供的一组集合 这组集合包括 测试环境 操作步骤 测试数据 预期结果等要素 好的测试用例是不熟悉业务的人也能根据用例很go准 用例表达清楚 无二义性 不能出项
  • 自增与自减运算符号&&赋值运算符

    自增运算符为 其功能是使变量的值自增1 自减运算符为 其功能是使变量值自减1 它们经常使用在循环中 自增自减运算符有以下几种形式 注意 无论是a 还是 a都等同于a a 1 在表达式执行完毕后a的值都自增了1 无论是a 还是 a都等同于a
  • Unsafe学习

    一 介绍 一个管理内存的类 Unsafe类是 final 的 不允许继承 且构造函数是private的 使用单列模式模式获取类对象 1 1 测试的类 public class UnsafeBean private static int st
  • 计算机程序存储在哪里,计算机正在运行的程序存放在哪里?

    计算机正在运行的程序存放在RAM 内存 里 RAM是与CPU进行沟通的桥梁 计算机中所有程序的运行都是在内存中进行的 因此内存的性能对计算机的影响非常大 计算机正在运行的程序存放在RAM 内存 里 RAM是与CPU进行沟通的桥梁 计算机中所
  • 使用Requests库来进行爬虫的详解

    Requests是用Python编写 基于urllib 采用Apache2 Licensed开源协议的HTTP库 它比urllib更方便 可以节约我们大量的工作 完全满足HTTP测试需求 安装 pip3 install requests 使
  • 【知识普及】神经网络架构搜索(Neural Architecture Search,NAS)

    文章目录 1 背景引入 2 网络架构搜索 NAS 2 1 NAS 搜索策略 2 1 1 基于强化学习 2 1 2 基于进化算法 2 1 3 基于梯度的方法 2 2 NAS 加速 2 2 1 层次化表示 2 2 2 权值共享 2 2 3 表现
  • 教你一招删除休眠文件hiberfil.sys节省大量C盘空间

    教你一招删除休眠文件hiberfil sys节省大量C盘空间首先分清SLEEP睡眠和HIBERNATE休眠两个概念 我们常用的是SLEEP功能 也就是电脑经过一定时间后 进入低功耗状态 工作态度保存在内存里 恢复时1 2秒就可以恢复原状态
  • 最新配置安装的centos7,解决xshell连接问题,安装docker

    配置初始的centos7 1 更新yum yum update y 2 安装vim yum install y vim 3 解决xshell连接显示警告 The remote SSH server rejected X11 forwardi
  • 用深度学习和CNN进行年龄识别

    DIP大作业 用深度学习和CNN进行年龄识别 基于深度学习的方法 基本步骤 深度学习方法 1 10 需求分析 系统设计 环境搭建 2 10 数据集及预处理 3 40 卷积神经网络模型设计 模型程序编写 模型训练 模型测试 4 30 实验结构
  • OpenWRT安装管理界面中文包

    如果刚刷的openwrt18点多的没有中文界面 用ssh连接路由后用opkg安装 root openWRT opkg install luci i18n base zh cn Unknown package luci i18n base z
  • 顺序栈——基础

    时间限制 1000ms 内存限制 256M 实验目的 编写代码 实现一个简单的顺序栈 实验要求 1 每个栈元素是一个union类型 例如 union unData 栈元素的数据类型为Union Union共用同一块存储空间 int d ch
  • 分布式任务 + 消息队列框架 go-queue

    为什么写这个库 应用场景有哪些 如何使用 总结 为什么要写这个库 在开始自研 go queue 之前 针对以下我们调研目前的开源队列方案 beanstalkd beanstalkd 有一些特殊好用功能 支持任务priority 延时 del
  • 文献学习-联合抽取C-Joint Extraction of Biomedical Entities and Relations based on Decomposition and Recombio

    论文信息 1 题目 Joint Extraction of Biomedical Entities and Relations based on Decomposition and Recombination Strategy 基于分解重组
  • QT 编码格式问题

    一 QT客户端与服务端通信 客户端先将数据进行序列化xml形式 然后利用SharpZipLib GZip Compress进行压缩 转换成Base64格式 源数据 Function GetSysTime IsMutipleReturn fa
  • 脑筋急转弯-3

    小明爱捉迷藏 却总被人找得到 为什么 答案 因为他总是喊着 我在这里 什么东西你越给它 它变得越短 答案 蜡烛 小华拿着空杯子站在雨中 为什么他的头发却没有湿 答案 因为小华是个雨伞销售员 他用雨伞遮住了头发 什么桥不能过 答案 音乐桥 因
  • spring cloud gateway集成sentinel并扩展支持restful api进行url粒度的流量治理

    sentinel集成网关支持restful接口进行url粒度的流量治理 前言 使用网关进行总体流量治理 sentinel版本 1 8 6 1 cloud gateway添加依赖 2 sentinel配置 3 网关类型项目配置 4 通过zk事