终于整完了, 看了两天,中间还穿插各种面试和会议. 看了我的英语水平...或者说google的英译汉能力着实可以的.

看完之后有几个感受.

gateway本身分成三个组件

routes: 路由, 也是最小的颗粒组件

predicates: 断言, 就是满足什么样的条件

filter: 过滤器, 里面可以对请求做一些处理

application.yml

spring:cloud:gateway:routes:- id: after_routeuri: https://example.orgpredicates:- Cookie=mycookie,mycookievaluefilters:- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin

这就是一个最简单的标准的一个路由里面有自己的

id(唯一标识),

predicates用来标记哪些请求进来,

filter代表着请求进来以后你要做什么,

这里面他提供了大批量的类库;

包括, 时间, cookie, url, 参数, header, 基本你能想到的东西都可以放到predicates和filter里面.他也比较希望你用它的类库.

从1~6 其实他就是在各种介绍他的类库.....不过我说实话.....太多了,整的我都不想用了,不是侥幸, 纯粹习惯问题.

这幅图要从上往下读: 请求进来, 进来以后经过两个handle,然后经过filter逐层的返回, 应该用的是责任链, 这个我还没有细看,后续去解读源码时候看看.

Example 59. ExampleConfiguration.java

@Bean
public GlobalFilter customFilter() {return new CustomGlobalFilter();
}public class CustomGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {log.info("custom global filter");return chain.filter(exchange);}@Overridepublic int getOrder() {return -1;}
}

我喜欢这个:全局filter, 里面我们可以随意的写代码,各种搞事情,比如jwt.

这时候有个问题了,如果多个filter他究竟是咋执行的

7.10. Marking An Exchange As Routed

网关路由ServerWebExchange之后,通过将gatewayAlreadyRouted添加到交换属性来将交换标记为“已路由”。 将请求标记为已路由后,其他路由筛选器将不会再次路由请求,实质上会跳过该过滤器。 您可以使用多种便捷方法将交换标记为已路由,或者检查交换是否已路由。

  • ServerWebExchangeUtils.isAlreadyRouted takes a ServerWebExchange object and checks if it has been “routed”.

  • ServerWebExchangeUtils.setAlreadyRouted takes a ServerWebExchange object and marks it as “routed”

也就是是说一个路由以后他就关闭了.其他的就不执行了,注意这里说的并不包括全局的,因为我创建了两个globalFilter都是执行的.那globalFilter呢,他的执行顺序是按照order执行的, 然后每个请求都必须执行.

我目前呢写了两个globalFilter, 一个是用来打印日志的, 另外一个是用来做jwt鉴权的.还有就是直接加一个@Component标签就行,不用非得声明一个@Bean,他的意思大概是希望你写的更加显式一点. 这个在

17. Developer Guide 导读更加明显.

我们看一下他的建议

17.1. Writing Custom Route Predicate Factories 编写自定义路由工厂

MyRoutePredicateFactory.java

public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<HeaderRoutePredicateFactory.Config> {public MyRoutePredicateFactory() {super(Config.class);}@Overridepublic Predicate<ServerWebExchange> apply(Config config) {// grab configuration from Config objectreturn exchange -> {//grab the requestServerHttpRequest request = exchange.getRequest();//take information from the request to see if it//matches configuration.return matches(config, request);};}public static class Config {//Put the configuration properties for your filter here}}

17.2. Writing Custom GatewayFilter Factories

To write a GatewayFilter, you must implement GatewayFilterFactory. You can extend an abstract class called AbstractGatewayFilterFactory. The following examples show how to do so:

Example 76. PreGatewayFilterFactory.java

public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory<PreGatewayFilterFactory.Config> {public PreGatewayFilterFactory() {super(Config.class);}@Overridepublic GatewayFilter apply(Config config) {// grab configuration from Config objectreturn (exchange, chain) -> {//If you want to build a "pre" filter you need to manipulate the//request before calling chain.filterServerHttpRequest.Builder builder = exchange.getRequest().mutate();//use builder to manipulate the requestreturn chain.filter(exchange.mutate().request(builder.build()).build());};}public static class Config {//Put the configuration properties for your filter here}}

PostGatewayFilterFactory.java

public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory<PostGatewayFilterFactory.Config> {public PostGatewayFilterFactory() {super(Config.class);}@Overridepublic GatewayFilter apply(Config config) {// grab configuration from Config objectreturn (exchange, chain) -> {return chain.filter(exchange).then(Mono.fromRunnable(() -> {ServerHttpResponse response = exchange.getResponse();//Manipulate the response in some way}));};}public static class Config {//Put the configuration properties for your filter here}}

他把rotes,prediscates 和filter给分开了.而且可以prefilter和postFilter

下面我们结合我们立下的flag, 看看怎么搞重定向

现在我想把所有/action/*****的请求都变成/business

Example 8. application.yml

spring:cloud:gateway:routes:- id: path_routeuri: https://example.orgpredicates:- Path=/red/{segment},/blue/{segment}

巴巴的说了好久了,停了好几天.因为在忙别的业务.在这里说一下最终解决方案

cloud:gateway:locator:enabled: truedefault-filters:- AddResponseHeader=X-Response-Default-Foo, Default-Bar# 服务自动发现,取第一个截取词匹配consuldiscovery:locator:lower-case-service-id: trueenabled: true #开启根据微服务名称自动转发filters:- StripPrefix=1routes:- id: actionuri: lb://businesspredicates:- Path= /action/**

没错,就是加了这么一个路由,这里加了一个断言predicates: 所有 /action/ 的请求 都被转发到注册中心的 business服务上.

比如http://gateway.com/action/abcServer 会被转译为 http://gateway.com/business/action/abcServer

-----------------------------------------------------

上边解决了路由转发,我们在这里在从头梳理一下我的需求

1.我有一个gateway, 一个consul 和一个business的主项目

2.我有一堆历史的债务有个叫做static的项目需要被整合到business里面,原因是里面就只有两个接口,一个2B同事搞得,个人感觉就是拿公司的服务玩.

3.我有个php的老项目api.com,原来有公网域名,现在需要整合进geteway,我需要把原有域名也可以正常通过gateway能访问到服务

4.我需要做一个负载均衡实现灰度

-------------------------------------

1.业务合并,上边已经解决了

2.现在我们做负载均衡和灰度

那么我们首先设置一个负载均衡的flag:假设我有两台服务, (ip分别是83,84),我这里是本地用端口91和92代替.91和92都已经注册到了consul(注册中心)上面.

  • 实现所有的请求20%在91上, 80%在92上
  • 实现header中所有请求studentId=123的学员请求都在92上
  • 实现header中所有请求按照studentId分群,将20%的比例固定的分配到91上,其他在92上
  • 实现header中url=abc的请求按照studentId分群,将20%的比例固定的分配到91上,其他在92上

回过来我们梳理一下无非就是按照url,studentId两个维度将流量分配给不同的服务. 好了现在我们需求有了,开始做个设计,

这个明显的是一个策略的模式,而且策略间应该是有优先级的.比如一个我们策略一是按照studentId%100<20在91,上策略2是studentId=123的在91上, 1,2明显是冲突的. 所以涉及到了优先级.

这个策略明显是不定长度的, 意思就是说比如url=abc的,后面还有url=bcd的,而且以后还可能有其他的项目.

最后初步设计这里采用责任链的方式,因为最后我们可以很明确的抽象出来几种规则有几个共性, 入参是studentId和url,回参是服务地址.

话不多说直接上代码:

-----------------------------兜兜转转,写完这个代码半个月了才想起来博客还没收尾--------------------------

首先yml的配置

#负载均衡
mybalance:open : true#灰度grayscale:- order: -129id: ver等于2.0.0的queryPort请求,投射到9091,ver不等于2.0.0的queryPort分发到其他服ip: 192.168.0.225:9091ver: '=2.0.0'url: '/statistics/testServer/queryPort'exclusive : 'trueUrl'- order: -128id: ver>=2.4.0的所有请求,投射到9091,ver小于2.4.0分发到其他服ip: 192.168.0.225:9092ver: '>2.4.0'url: '*'exclusive : 'trueVer'- order: -127id: studentId=123的,url=queryPort 投射到9091,其他请求随机投放ip: 192.168.0.225:9091studentId: 123url: '/statistics/testServer/queryPort2'exclusive : trueUrl- order: -125id: studentId=123的,url=queryPort 投射到9091,其他请求随机投放ip: 192.168.0.225:9091studentId: 123url: '*'exclusive : '*'

其次我们看看这个策略的解析类

@Api("灰度策略")
public class Grayscale {@ApiParam("优先级,值越小优先级越高,当出现了高优先级的负载均衡以后低优先级的就不再执行")private Integer order;@ApiParam("策略的唯一标识")private String id;@ApiParam("策略的ip标识,正则  consul中的address  ip或者ip+端口")private String ip;@ApiParam("header中studentId,正则,如果是*就代表所有")private String studentId;@ApiParam("请求的url,正则,如果是*就代表所有")private String url;@ApiParam("强制,true如果找不到对应ip就抛出异常,false:如果找不到对应的ip就随机返回一台 ")private boolean enforce=false;@ApiParam("权重0~100 当ip 有内容的时候本字段不生效")private Integer weight;@ApiParam("排他 如果之前的条件没有完全命中,那么就会执行exclusive过滤, 比如 trueStu 代表如果stu判断是true就会从服务列表摘除, falseStu代表如果stu是false就从列表摘除  ")private String exclusive;@ApiParam("版本号")private String ver;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getIp() {return ip;}public void setIp(String ip) {this.ip = ip;}public String getStudentId() {return studentId;}public void setStudentId(String studentId) {this.studentId = studentId;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public boolean isEnforce() {return enforce;}public void setEnforce(boolean enforce) {this.enforce = enforce;}public Integer getWeight() {return weight;}public void setWeight(Integer weight) {this.weight = weight;}public Integer getOrder() {return order;}public void setOrder(Integer order) {this.order = order;}public String getExclusive() {return exclusive;}public void setExclusive(String exclusive) {this.exclusive = exclusive;}public String getVer() {return ver;}public void setVer(String ver) {this.ver = ver;}

}

@Component
@ConditionalOnProperty( matchIfMissing = true ,prefix = "mybalance",name="open",havingValue = "true" )
@Api("负载均衡")
@ConfigurationProperties(prefix = "mybalance")
public class MyBalanceEntity {@ApiParam("灰度")private List<Grayscale> grayscale;@Constructor@ApiParam("所有策略")public void sort() {if (grayscale != null && grayscale.size() > 1) {grayscale.sort((g1, g2) -> {if (g2.getOrder() > g1.getOrder()) return 1;if (g2.getOrder() < g1.getOrder()) return -1;return 0;});}}public List<Grayscale> getGrayscale() {return grayscale;}public void setGrayscale(List<Grayscale> grayscale) {this.grayscale = grayscale;}
}

----------------------------------------这个可以看出来策略就是先按照id升序然后相同的按照前后顺序

/*** https://blog.csdn.net/zhou1124/article/details/103773835*/
@Api("负载均衡,先执行MyLoadBalancerClientFilter,再执行MyLoadBalanceRule")
@Component
public class MyLoadBalancerClientFilter extends LoadBalancerClientFilter {public static ThreadLocal<ServerWebExchange> exchange = new ThreadLocal<>();private static Logger log = LoggerFactory.getLogger(DaishuCloudGatewayApplication.class);@Autowiredprivate MyBalanceEntity myBalanceEntity;public MyLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) {super(loadBalancer, properties);}@Overrideprotected ServiceInstance choose(ServerWebExchange exchange) {//如果没有任何策略就使用if(myBalanceEntity==null||myBalanceEntity.getGrayscale()==null||myBalanceEntity.getGrayscale().size()==0){return super.choose(exchange);}//这里可以拿到web请求的上下文,可以从header中取出来自己定义的数据。MyLoadBalancerClientFilter.exchange.set(exchange);//获得真实的请求路径lb://statistics/testServer/queryPortURI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);HttpHeaders httpHeaders = exchange.getRequest().getHeaders();BalanceDto balanceDto=new BalanceDto(httpHeaders,uri);balanceDto.setHttpHeaders(httpHeaders);balanceDto.setUri(uri);log.info("步骤1");//如果在已有的ThreadLocal中没有连接if (MyLoadBalanceRule.originHost.get() == null) {//获得所属ipList<String> originHostHeader = httpHeaders.get(MyLoadBalanceRule.originHostHeader);if (originHostHeader == null || originHostHeader.size() == 0) {String host = exchange.getRequest().getURI().getHost();//设置请求头exchange.getRequest().mutate().header(MyLoadBalanceRule.originHostHeader, host).build();//设置本机地址MyLoadBalanceRule.originHost.set(host);} else {MyLoadBalanceRule.originHost.set(originHostHeader.get(0));}}//开始路由if (this.loadBalancer instanceof RibbonLoadBalancerClient) {RibbonLoadBalancerClient client = (RibbonLoadBalancerClient) this.loadBalancer;String serviceId = ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost();//这里使用userId做为选择服务实例的key, 调用的是MyLoadBalanceRule的choose, balanceDto 就是那边接收到的keyreturn client.choose(serviceId, balanceDto);}return super.choose(exchange);}}
@Component
@Api("路由规则")
public class MyLoadBalanceRule extends BestAvailableRule {private static Logger log = LoggerFactory.getLogger(DaishuCloudGatewayApplication.class);@Autowired@Qualifier("grayscaleBalance")private Balance grayscaleBalance;public static ThreadLocal<String> originHost=new ThreadLocal<>();public static  String originHostHeader="originHost";@Autowiredprivate MyBalanceEntity myBalanceEntity;public Server choose(ILoadBalancer lb, Object key) {//log.info("步骤2"+key);if (lb == null) {log.error("MyLoadBalanceRule Exception no load balancer");return null;}if(myBalanceEntity==null||myBalanceEntity.getGrayscale()==null||myBalanceEntity.getGrayscale().size()==0){return  grayscaleBalance.loadRandomServer(lb.getReachableServers());}BalanceDto balanceDto=(BalanceDto) key;//consul 上的注册192.168.0.225:9091 192.168.0.225:9092 consul中服务对应的address项目List<Server> reachableServers = lb.getReachableServers();if(reachableServers==null ||reachableServers.size()==0){log.error("MyLoadBalanceRule Exception 没有可用的服务");return null;}balanceDto.setReachableServers(reachableServers);BalanceContext balanceContext=new BalanceContext(balanceDto);//进行负载均衡grayscaleBalance.chooseServer(balanceContext);//log.info("balanceContext:" + JSONObject.toJSONString(balanceContext));return  balanceContext.getServer();}@Overridepublic Server choose(Object key) {return choose(getLoadBalancer(), key);}@Overridepublic void initWithNiwsConfig(IClientConfig clientConfig) {// TODO Auto-generated method stub}}

//这两个类是负载均衡

先执行

MyLoadBalancerClientFilter

在执行

MyLoadBalanceRule
@Api("负载均衡的抽象规范,暂时没有其他用途")
public abstract class Balance {private static Logger log = LoggerFactory.getLogger(DaishuCloudGatewayApplication.class);@ApiParam("返回一台服务")public abstract void chooseServer(BalanceContext balanceContext);@ApiParam("随机返回一台服务")public abstract Server loadRandomServer(List<Server> serverList);@ApiParam("随机返回一台服务")protected void loadRandomServer(final @ApiParam("入参") BalanceContext balanceContext) {Server server= loadRandomServer(new ArrayList(balanceContext.getBalanceDto().getReachableServerMap().values()));balanceContext.setServer(server);balanceContext.setPolicyId("loadRandomServer");}@ApiParam("根据策略加载服务到context")protected void loadServer(final @ApiParam("灰度策略") Grayscale grayscale, final @ApiParam("入参") BalanceContext balanceContext) {if (Collections.isEmpty(balanceContext.getBalanceDto().getReachableServerMap())) {throw new DsException(10009);}MatchMap matchMap = new MatchMap();//判断是否命中effectiveStu(grayscale, balanceContext, matchMap);effectiveUrl(grayscale, balanceContext, matchMap);effectiveVer(grayscale, balanceContext, matchMap);//命中ipif (matchMap.isAllMatch()) {//构建contextbuildContextServer(grayscale, balanceContext);} else {//移除serverexclusiveContextServer(grayscale, balanceContext, matchMap);}}@ApiParam("移除sever")private void exclusiveContextServer(Grayscale grayscale, BalanceContext balanceContext, @ApiParam("匹配结果") MatchMap matchMap) {//如果只剩下一台服务并且是一个有效的移除策略才开始移除if (balanceContext.getBalanceDto().getReachableServerMap().size() > 1 && DsStringUtil.isNotEmpty(grayscale.getExclusive()) && !grayscale.getExclusive().equals("*")) {String[] exp = grayscale.getExclusive().split(",");for (String str : exp) {switch (str) {case "falseVer":if (!matchMap.isVerMatch())balanceContext.getBalanceDto().getReachableServerMap().remove(grayscale.getIp());break;case "trueVer":if (matchMap.isVerMatch())balanceContext.getBalanceDto().getReachableServerMap().remove(grayscale.getIp());break;case "trueUrl":if (matchMap.isUrlMatch())balanceContext.getBalanceDto().getReachableServerMap().remove(grayscale.getIp());break;case "falseUrl":if (!matchMap.isUrlMatch())balanceContext.getBalanceDto().getReachableServerMap().remove(grayscale.getIp());break;case "trueStu":if (matchMap.isStuMatch())balanceContext.getBalanceDto().getReachableServerMap().remove(grayscale.getIp());break;case "falseStu":if (!matchMap.isStuMatch())balanceContext.getBalanceDto().getReachableServerMap().remove(grayscale.getIp());break;default:}}}}@ApiParam("构建context")private void buildContextServer(Grayscale grayscale, BalanceContext balanceContext) {if (balanceContext.getBalanceDto().getReachableServerMap().get(grayscale.getIp()) != null) {balanceContext.setOrder(grayscale.getOrder());balanceContext.setPolicyId(grayscale.getId());balanceContext.setServer(balanceContext.getBalanceDto().getReachableServerMap().get(grayscale.getIp()));} else {if (grayscale.isEnforce()) {log.info("策略:" + grayscale.getId() + "没有找到服务==" + grayscale.getIp() + "强制执行失败");throw new DsException(10010, "策略id:" + grayscale.getId());} else {log.info("策略:" + grayscale.getId() + "没有找到服务==" + grayscale.getIp() + "跳过策略");}}}@ApiParam("匹配版本")private void effectiveVer(Grayscale grayscale, BalanceContext balanceContext, MatchMap matchMap) {//如果策略有但是header没有就不通过if (effective(grayscale.getVer()) && DsStringUtil.isEmpty(balanceContext.getBalanceDto().getHttpHeaders().getFirst("ver"))) {matchMap.setAllMatch(false);matchMap.setVerMatch(false);return;}if (effective(grayscale.getVer())) {DsHeader dsHeader = new DsHeader(balanceContext.getBalanceDto().getHttpHeaders());String ver = grayscale.getVer().replaceAll(">", "").replaceAll("=", "").replaceAll("<", "");//判断版本情况  header内容小于输入version版本返回-1    0 等于  header内容大于version返回1Integer i = dsHeader.afterVer(ver);//结果是小于if (i == -1 && !grayscale.getVer().startsWith("<")) {matchMap.setAllMatch(false);matchMap.setVerMatch(false);}//结果是大于if (i == 1 && !grayscale.getVer().startsWith(">")) {matchMap.setAllMatch(false);matchMap.setVerMatch(false);}//如果结果相等但是判断条件是不等于,或者不包含=if (i == 0 && (grayscale.getVer().startsWith("!=") || !grayscale.getVer().contains("="))) {matchMap.setAllMatch(false);matchMap.setVerMatch(false);}}}@ApiParam("判断地址匹配结果")private void effectiveUrl(Grayscale grayscale, BalanceContext balanceContext, MatchMap matchMap) {//如果需要检测urlif (effective(grayscale.getUrl()) && !match(grayscale.getUrl(), balanceContext.getBalanceDto().getUri().toString())) {matchMap.setAllMatch(false);matchMap.setUrlMatch(false);}}@ApiParam("判断学生匹配结果")private void effectiveStu(Grayscale grayscale, BalanceContext balanceContext, MatchMap matchMap) {//如果策略有但是header没有if (effective(grayscale.getStudentId()) && DsStringUtil.isEmpty(balanceContext.getBalanceDto().getStudentId())) {matchMap.setAllMatch(false);matchMap.setStuMatch(false);return;}if (effective(grayscale.getStudentId()) && !match(grayscale.getStudentId(), balanceContext.getBalanceDto().getStudentId().toString())) {matchMap.setAllMatch(false);matchMap.setStuMatch(false);}}@ApiParam("是否是有效字段")private boolean effective(String str) {if (DsStringUtil.isEmpty(str) || str.trim().equals("*")) {return false;}return true;}@ApiParam("正则表达式是否匹配")private boolean match(@ApiParam("正则表达式") String exp, @ApiParam("内容") String str) {// 忽略大小写的写法Pattern pattern = Pattern.compile(exp, Pattern.CASE_INSENSITIVE);Matcher matcher = pattern.matcher(str.replaceAll("lb://","/"));boolean rs = matcher.matches();return rs;}}
@Api("灰度负载均衡")
@Component
public class GrayscaleBalance extends Balance {@Autowiredprivate MyBalanceEntity myBalanceEntity;@Overridepublic void chooseServer(BalanceContext balanceContext) {if (myBalanceEntity.getGrayscale() != null && myBalanceEntity.getGrayscale().size() > 0) {for (Grayscale grayscale : myBalanceEntity.getGrayscale()) {//如果还没有产生有效策略if (balanceContext.getServer() == null) {//根据策略加载服务到contextsuper.loadServer(grayscale, balanceContext);} else {//因为本身就已经进行过break;}}//如果所有的都执行完了还没有拿到有效的策略if (balanceContext.getServer() == null) {super.loadRandomServer(balanceContext);}}}@ApiParam("随机返回一台服务")@Overridepublic Server loadRandomServer(List<Server> serverList) {Random random = new Random();int index = random.nextInt(serverList.size());return serverList.get(index);}}

//这两个类是实际的逻辑

这里面用了继承的方式倒不是说必须的,主要是考虑以后万一有扩展

通过这些配置就可以实现负载均衡的灰度, 其实如果需求不这么复杂的话还是建议用自带的断言和filter,或者自带的ribbon.可读性更好,性能也高.而且权重什么的也不用自己去做.主要还是看需求吧.

gateway官网文档解读(六) 汇总相关推荐

  1. mockito官网文档解读

    文章目录 ArgumentMatchers doReturn vs When Verify Stubbing Matchers Verify Invocation Times Stub void wi ...

  2. 大数据实战之hadoop生态概况和官网文档解读

    本小节 jacky 分享的是: Hadoop 核心的组成模块,依赖于 Hadoop 的其他的生态系统.随着jacky后面逐步的分享,会对本小节 Hadoop 概况中的这些概念有更好的加深与了解. (一 ...

  3. Go语言从入门到规范-1.1、Go语言官网文档大纲及环境搭建

    Go语言从入门到规范-1.1.Go语言官网文档大纲及环境搭建 文章目录 Go语言从入门到规范-1.1.Go语言官网文档大纲及环境搭建 前言 一.go简介及学习路径 二.安装go 1.下载Go发行版 2 ...

  4. 【VR】Leap Motion 官网文档 HandModel(手部模型)

    前言: 本系列译文是为迎合Unity VR的热潮与大家的学习需要,推出的针对Unity方向的Leap Motion官方文档中英对照翻译. 本篇为第六篇 <HandModel(手部模型)> ...

  5. 根据官网文档看Spark Streaming对接Kafka的两种方式, 以及如何实现Exactly Once语义

    注: 本文算是本人的学习记录, 中间可能有些知识点并不成熟, 不能保证正确性. 只能算是对官网文档作了个翻译和解读, 随时有可能回来更新和纠错 上一篇文章讨论了Spark Streaming的WAL( ...

  6. OpenHarmony 官网文档有哪些上新?上篇:应用开发文档上新

    随着 OpenAtom OpenHarmony(以下简称"OpenHarmony")系统能力持续升级,已具备支撑复杂带屏标准设备和应用开发的基础能力.相较于旧版本,OpenHarm ...

  7. redis过期机制(官网文档总结)

    官网地址:https://redis.io/commands/expire redis过期定义如下: Set a timeout on key. After the timeout has expir ...

  8. mybatis官网文档mybatis_doc

    在平时的学习中,我们可以去参考官网的文档来学习,这个文档有中文的,方便我们去阅读,而且这里的分类很详细. 官网文档链接:http://www.mybatis.org/mybatis-3/zh/inde ...

  9. vue-awesome-swiper官网文档

    vue-awesome-swiper官网文档 https://www.swiper.com.cn/api/index.html

最新文章

  1. python 获取脚本所在目录
  2. IBM Java多线程 - 6.其它线程 API 详细信息
  3. linux 安全相关
  4. Linux信号 五 信号挂起与信号掩码操作接口集
  5. 零基础java自学就业_java零基础到就业需要多长时间呢?
  6. 实现公司管理系统【C++实战】(53)
  7. 好的,没事,失败是成功之母
  8. c语言怎么判定结构体有无数据,C语言中什么是结构体,怎么定义结构体。
  9. linux执行python命令后permission denied
  10. 线性表之顺序存储结构
  11. glnxa64 matlab 什么版本_发现了一个MATLAB的小bug
  12. WordPress采集插件-WordPress文章自动采集发布
  13. 随机森林回归简单示例
  14. css 边框代码,边框代码大全
  15. 中文词典的扩充和组织
  16. IPTV和宽带网络融合
  17. 邵阳计算机学院高考班成绩,邵阳这个学校出了一个“牛班”,“火箭班”考生本科上线率100%!...
  18. 在linux下使用360随身wifi 2 | 李凡希的blog,在Linux下使用“360随身WiFi 2” | 李凡希的Blog...
  19. 计算反转录转座子插入时间一:计算原理
  20. 13.2 用Patsy创建模型

热门文章

  1. 算法(赛马问题)图解
  2. 58同城一面【前端】
  3. 用Python优雅地制作动态条形图
  4. 《奏爱》彩虹韩漫画风超甜第二话完整版
  5. 全球程序员收入出炉!北京收入排入全球第十
  6. python程序员收入-令人羡慕!33岁程序员晒出收入和待遇,网友望尘莫及
  7. 工作三年程序员收入到底多高?透露收入:网友:哇,真的好高呀!
  8. c语言实验内容.doc答案,C语言程序设计实验内容与答案.doc
  9. 一周侃 | 周末随笔
  10. 【AWS系列】第四讲:什么是 AWS Serverless