Spring Boot + Spring Cloud 实现权限管理系统 配置中心(Config、Bus)
技术背景
如今微服务架构盛行,在分布式系统中,项目日益庞大,子项目日益增多,每个项目都散落着各种配置文件,且随着服务的增加而不断增多。此时,往往某一个基础服务信息变更,都会导致一系列服务的更新和重启,运维也是苦不堪言,而且还很容易出错。于是,配置中心便由此应运而生了。
目前市面上开源的配置中心有很多,像Spring家族的Spring Cloud Config, Apache的Apache Commons Configuration,淘宝的diamond, 百度的disconf, 360的QConf等等,都是为了解决这类问题。当下Spring体系大行其道,我们当然也优先选择Spring Cloud Config了。
Spring Cloud Config
Spring Cloud Config 是一套为分布式系统中的基础设施和微服务应用提供集中化配置的管理方案,它分为服务端与客户端两个部分。服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息。客户端则是微服务架构中的各个微服务应用或基础设施,它们通过指定的配置中心来管理服务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。
Spring Cloud Config对服务端和客户端中的环境变量和属性配置 实现了抽象映射,所以它除了适用于 Spring 应用,也是可以在任何其他语言应用中使用的。Spring Cloud Config 实现的配置中心默认采用 Git 来存储配置信息,所以使用 Spring Cloud Config 构建的配置服务器,天然就支持对微服务应用配置信息的版本管理,并且可以通过 Git 客户端工具非常方便的管理和访问配置内容。当然它也提供了对其他存储方式的支持,比如:SVN 仓库、本地化文件系统等。
实现案例
准备配置文件
首先在GIT下,新建config-repo目录,用来存放配置文件,如下图所示,分别模拟了三个环境的配置文件。
分别编辑三个文件,配置 comsumer.hello 属性的值为 comsumer.hello=hello, this is xx configurations.
服务端实现
新建工程
新建 kitty-conifg 工程,作为配置中心的服务端,负责把GIT仓库的配置文件发布为RESTFul接口。
添加依赖
除了Spring Cloud依赖之外,添加配置中心依赖包。
pom.xml
<!--spring config-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
启动类
启动类添加注解 @EnableConfigServer,开启配置服务支持。
KittyConfigApplication.java
package com.louis.kitty.config; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer; @EnableConfigServer
@EnableDiscoveryClient
@SpringBootApplication
public class KittyConfigApplication { public static void main(String[] args) {
SpringApplication.run(KittyConfigApplication.class, args);
}
}
配置文件
修改配置文件,添加如下内容。如果是私有仓库需要填写用户名密码,如果是公开仓库,可以不配置密码。
application.yml
server:
port: 8020
spring:
application:
name: kitty-config
cloud:
consul:
host: localhost
port: 8500
discovery:
serviceName: ${spring.application.name} # 注册到consul的服务名称
config:
label: master # git仓库分支
server:
git:
uri: https://gitee.com/liuge1988/kitty.git # 配置git仓库的地址
search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。
username: username # git仓库的账号
password: password # git仓库的密码
Spring Cloud Config也提供本地存储配置的方式,只需设置属性spring.profiles.active=native
,Config Server会默认从应用的src/main/resource
目录下检索配置文件。另外也可以通过spring.cloud.config.server.native.searchLocations=file:D:/properties/
属性来指定配置文件的位置。虽然Spring Cloud Config提供了这样的功能,但是为了更好的支持内容管理和版本控制,还是比较推荐使用GIT的方式。
测试效果
启动注册中心,配置中心,访问 http://localhost:8020/kitty-consumer/dev,返回结果如下。
{
"name": "kitty-consumer",
"profiles": ["dev"],
"label": null,
"version": "1320259308dfdf438f5963f95cbce9e0a76997b7",
"state": null,
"propertySources": [{
"name": "https://gitee.com/liuge1988/kitty.git/config-repo/kitty-consumer-dev.properties",
"source": {
"consumer.hello": "hello, this is dev configurations. "
}
}]
}
访问 http://localhost:8020/kitty-consumer/pro,返回结果如下。
{
"name": "kitty-consumer",
"profiles": ["pro"],
"label": null,
"version": "1320259308dfdf438f5963f95cbce9e0a76997b7",
"state": null,
"propertySources": [{
"name": "https://gitee.com/liuge1988/kitty.git/config-repo/kitty-consumer-pro.properties",
"source": {
"consumer.hello": "hello, this is pro configurations. "
}
}]
}
上述的返回的信息包含了配置文件的位置、版本、配置文件的名称以及配置文件中的具体内容,说明server端已经成功获取了git仓库的配置信息。
访问:http://localhost:8020/kitty-consumer-dev.properties,返回结果如下。
修改一下dev配置文件内容如下(末尾加了一个 2):
再次访问:http://localhost:8020/kitty-consumer-dev.properties,返回结果如下。
发现读取的是修改后提交的信息,说明服务端会自动读取最新提交的数据。
仓库中的配置文件会被转换成相应的WEB接口,访问可以参照以下的规则:
- /{application}/{profile}[/{label}]
- /{application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
以kitty-consumer-dev.properties为例子,它的application是kitty-consumer,profile是dev。client会根据填写的参数来选择读取对应的配置。
客户端实现
添加依赖
打开kitty-consumer工程,添加相关依赖。
pom.xml
<!-- spring-cloud-config -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
配置文件
添加一个bootstrap.yml配置文件,添加配置中心,并把注册中心的配置移到这里,因为在通过配置中心查找配置时需要通过注册中心的发现服务。
bootstrap.yml
spring:
cloud:
consul:
host: localhost
port: 8500
discovery:
serviceName: ${spring.application.name} # 注册到consul的服务名称
config:
discovery:
enabled: true # 开启服务发现
serviceId: kitty-config # 配置中心服务名称
name: kitty-consumer # 对应{application}部分
profile: dev # 对应{profile}部分
label: master # 对应git的分支,如果配置中心使用的是本地存储,则该参数无用
配置说明:
- spring.cloud.config.uri:配置中心的具体地址
- spring.cloud.config.name:对应{application}部分
- spring.cloud.config.profile:对应{profile}部分
- spring.cloud.config.label:对应git的分支。如果配置中心使用的是本地存储,则该参数无用
- spring.cloud.config.discovery.service-id:指定配置中心的service-id,便于扩展为高可用配置集群。
特别注意:
上面这些与spring cloud相关的属性必须配置在bootstrap.yml中,这样config部分内容才能被正确加载。
因为config的相关配置会先于application.yml,而bootstrap.yml的加载也是先于application.yml文件的。
application.yml
server:
port: 8005
spring:
application:
name: kitty-consumer
boot:
admin:
client:
url: "http://localhost:8000"
zipkin:
base-url: http://localhost:9411/
sleuth:
sampler:
probability: 1 #样本采集量,默认为0.1,为了测试这里修改为1,正式环境一般使用默认值
# 开放健康检查接口
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS
#开启熔断器
feign:
hystrix:
enabled: true
控制器
添加一个 SpringConfigController 控制器, 添加注解 @Value("${comsumer.hello}"),声明hello属性从配置文件读取。
SpringConfigController.java
package com.louis.kitty.consumer.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
class SpringConfigController { @Value("${comsumer.hello}")
private String hello; @RequestMapping("/hello")
public String from() {
return this.hello;
}
}
测试效果
启动注册中心、配置中心,访问: http://localhost:8500,确认相关服务启动并注册。
访问 http://localhost:8005/hello,返回结果。
说明客户端已经成功从服务端读取了配置信息。
现在手动修改一下仓库配置文件的内容,移除末尾数字 2,修改完成并提交。
再次访问 http://localhost:80052/hello,效果如下。
我们发现返回结果并没有读取最新提交的内容,这是因为Spring Boot项目只有在启动的时候才会获取配置文件的内容,虽然GIT配置信息被修改了,但是客户端并没有重新去获取,所以导致读取的信息仍然是旧配置。那么该如何去解决这个问题呢?这就是我们下一章要讲的 Spring Cloud Bus。
Refresh机制
我们在上面讲到,Spring Boot程序只在启动的时候加载配置文件信息,这样在GIT仓库配置修改之后,虽然配置中心服务器能够读取最新的提交信息,但是配置中心客户端却不会重新读取,以至于不能及时的读取更新后的配置信息。这个时候就需要一种通知刷新机制来支持了。
refresh机制是Spring Cloud Config提供的一种刷新机制,它允许客户端通过POST方法触发各自的/refresh,只要依赖spring-boot-starter-actuator包就拥有了/refresh的功能,下面我们为我们的客户端加上刷新功能,以支持更新配置的读取。
添加依赖
我们的 kitty-consumer 在之前已经添加过actuator依赖,所以这里就不用添加了,如果之前没有添加需要加上。actuator是健康检查依赖包,依赖包里携带了 /refresh 的功能。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在使用配置属性的类型加上 @RefreshScope 注解,这样在客户端执行 /refresh 的时候就会刷新此类下面的配置属性了。
package com.louis.kitty.consumer.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RefreshScope
@RestController
class SpringConfigController { @Value("${consumer.hello}")
private String hello; @RequestMapping("/hello")
public String from() {
return this.hello;
}
}
修改配置
健康检查接口开放需要在配置文件添加以下内容,开放refresh的相关接口,因为这个我们在之前也配置过了,所以也不需添加了。
management:
endpoints:
web:
exposure:
include: "*"
通过上面的接口开放配置,以后以post请求的方式访问 http://localhost:8005/actuator/refresh 时,就会更新修改后的配置文件了。
特别注意:
这里存在着版本大坑,1.x跟2.x的配置不太一样,我们用的是2.0+版本,务必注意。
1.安全配置变更
新版本
management.endpoints.web.exposure.include="*"
老版本
management.security.enabled=false
2.访问地址变更
新版本
http://localhost:8005/actuator/refresh
老版本
http://localhost:8005/refresh
这里还是解释一下上面这个配置起到了什么具体作用,其实actuator是一个健康检查包,它提供了一些健康检查数据接口,refresh功能也是其中的一个接口,但是为了安全起见,它默认只开放了health和info接口(启动信息会包含如下图所示信息),而上面的配置就是设置要开放哪些接口, 我们设置成 “*”,是开放所有接口。你也可以指定开发几个,比如: health,info,refresh,而这里因为我们需要用的refresh功能,所以需要把refresh接口开放出来。
设置成 “*” 后,启动信息会包含以下信息,而这个叫refresh的post方法,就是我们需要的,上面说的接口地址变更从这里也可以看得出来。
测试效果
重新启动服务,访问 http://localhost:8005/hello,返回结果如下。
修改仓库配置内容,末尾加个数字 5,如下图所示。
再次访问 http://localhost:8005/hello,如我们所料,结果并没有更新,因为我们还没有调refresh方法。
通过工具或自写代码发送post请求 http://localhost:8005/actuator/refresh,刷新配置。
这里通过在线测试网站发送,地址:https://getman.cn/Mo2FX 。
注意:先让你的Chrome支持跨域。设置方法:在快捷方式的target后加上 --disable-web-security --user-data-dir,重启即可。
刷新之后,再次访问 http://localhost:8005/hello,返回结果如下。
查看返回结果,刷新之后已经可以获取最新提交的配置内容,但是每次都需要手动刷新客户端还是很麻烦,如果客户端数量一多就简直难以忍受了,有没有什么比较好的办法来解决这个问题呢,那是当然的,答案就是:Spring Cloud Bus。
Spring Cloud Bus
Spring Cloud Bus,被大家称为消息总线,它通过轻量级的消息代理来连接各个分布的节点,可以利用像消息队列的广播机制在分布式系统中进行消息传播,通过消息总线可以实现很多业务功能,其中对于配置中心客户端刷新,就是一个非常典型的使用场景。
下面这张图可以很好的解释消息总线的作用流程(图片描述来源:纯洁的微笑:配置中心博文)。
Spring Cloud Bus 进行配置更新步骤如下:
1、提交代码触发post请求给/actuator/bus-refresh
2、server端接收到请求并发送给Spring Cloud Bus
3、Spring Cloud bus接到消息并通知给其它客户端
4、其它客户端接收到通知,请求Server端获取最新配置
5、全部客户端均获取到最新的配置
安装RabbitMQ
因为我们需要用到消息队列,我们这里选择RabbitMQ,使用Docker进行安装。
拉取镜像
执行以下命令,拉取镜像。
docker pull rabbitmq:management
完成之后执行以下命令查看下载镜像。
docker images
创建容器
执行以下命令,创建docker容器。
docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management
启动成功之后,可以执行以下命令查看启动容器。
docker ps
登录界面
容器启动之后就可以访问web管理界面了,访问 http://宿主机IP:15672。
系统提供了默认账号。 用户名:guest 密码: guest
管理界面
客户端实现
添加依赖
打开客户端 kitty-consumer,添加消息总线相关依赖。
pom.xml
<!-- bus-amqp -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
修改配置
修改配置,添加RebbitMq的相关配置,这样客户端代码就改造完成了。
bootstrap.yml
spring:
cloud:
consul:
host: localhost
port: 8500
discovery:
serviceName: ${spring.application.name} # 注册到consul的服务名称
config:
discovery:
enabled: true # 开启服务发现
serviceId: kitty-config # 配置中心服务名称
name: kitty-consumer # 对应{application}部分
profile: dev # 对应{profile}部分
label: master # 对应git的分支,如果配置中心使用的是本地存储,则该参数无用
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
服务端实现
添加依赖
修改 kitty-conifg,添加相关依赖。
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
修改配置,添加RabbitMq的和接口开放相关配置,这样服务端代码也改造完成了。
application.yml
server:
port: 8020
spring:
application:
name: kitty-config
cloud:
consul:
host: localhost
port: 8500
discovery:
serviceName: ${spring.application.name} # 注册到consul的服务名称
config:
label: master # git仓库分支
server:
git:
uri: https://gitee.com/liuge1988/kitty.git # 配置git仓库的地址
search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。
username: username # git仓库的账号
password: password # git仓库的密码
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
management:
endpoints:
web:
exposure:
include: "*"
测试效果
1.启动服务端,成功集成消息总线后,启动信息中可以看到如下图中的信息。
2.启动客户端,发现居然报错了,网上也找不到相关资料,也没见其他人提过相关问题。猜测是网上教程多是使用Euraka,而这里用的时Consul,瞎鼓捣了好久,反正是不想换回Euraka,2.0停止开发消息出来以后,将来还不定什么情况,只能硬着头皮解决了。
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'configServerRetryInterceptor' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:685) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1210) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.getDelegate(AnnotationAwareRetryOperationsInterceptor.java:180) ~[spring-retry-1.2.2.RELEASE.jar:na]
at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:151) ~[spring-retry-1.2.2.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.cloud.config.client.ConfigServerInstanceProvider$$EnhancerBySpringCGLIB$$dd44720b.getConfigServerInstances(<generated>) ~[spring-cloud-config-client-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration.refresh(DiscoveryClientConfigServiceBootstrapConfiguration.java:84) [spring-cloud-config-client-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration.startup(DiscoveryClientConfigServiceBootstrapConfiguration.java:69) [spring-cloud-config-client-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
然后就跟踪代码,发现是在下图中的位置找不到相应的Bean,那么答案就比较明显了,要么是程序有BUG,不过可能性不大,那应该是就是缺包了,在缺失的包里有这个Bean。但是这个Bean是在哪个包?排查了半天也没找到,网上也没有想过资料,对比了一下网上消息总线的配置,依赖也没有少加什么。
没有办法,最后只能自己上手了,不就是在刷新的时候缺少一个拦截器吗,自己给他弄一个试试呗。
使用就加了一个配置类,并在resources下新建了META-INF目录和一个spring。factories文件。
RetryConfiguration.java
package com.louis.kitty.consumer; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.retry.interceptor.RetryInterceptorBuilder;
import org.springframework.retry.interceptor.RetryOperationsInterceptor; public class RetryConfiguration {
@Bean
@ConditionalOnMissingBean(name = "configServerRetryInterceptor")
public RetryOperationsInterceptor configServerRetryInterceptor() {
return RetryInterceptorBuilder.stateless().backOffOptions(1000, 1.2, 5000).maxAttempts(10).build();
}
}
spring.factories
org.springframework.cloud.bootstrap.BootstrapConfiguration=com.louis.kitty.consumer.RetryConfiguration
在这里指定新建的拦截器,这样系统初始化时会加载这个Bean。
然后重启启动,果然没有报错了,还是先别高兴,看看能不能用先。
4.先访问一下 http://localhost:8005/hello,效果如下图所示。
5.修改仓库配置文件,把数字5改成15,修改完成提交。
再次访问发现还是旧信息。
6.再用工具发送post请求 http://localhost:8020/actuator/bus-refresh 。
注意这次是向注册中心服务端发送请求,发送成功之后服务端会通过消息总线通知所有的客户端进行刷新。
另外开启消息总线后的请求地址是 /actuator/bus-refresh,不再是refresh了。
7.给服务端发送刷新请求之后,再次访问 http://localhost:8005/hello,结果如下(需要一点刷新时间)。
我们愉快的发现客户端已经能够通过消息总线获取最新配置了。
Spring Boot + Spring Cloud 实现权限管理系统 配置中心(Config、Bus)的更多相关文章
- Spring Boot实战系列(7)集成Consul配置中心
本篇主要介绍了 Spring Boot 如何与 Consul 进行集成,Consul 只是服务注册的一种实现,还有其它的例如 Zookeeper.Etcd 等,服务注册发现在微服务架构中扮演这一个重要 ...
- 携程Apollo(阿波罗)配置中心Spring Boot迁移日志组件,使用配置中心进行管理的思路
说明: 1.Spring Boot项目默认使用logback进行日志管理 2.logback在启动时默认会自动检查是否有logback.xml文件,如果有时会有限加载这个文件. 3.那么如果是用配置中 ...
- 新书上线:《Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统》,欢迎大家买回去垫椅子垫桌脚
新书上线 大家好,笔者的新书<Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统>已上线,此书内容充实.材质优良,乃家中必备垫桌脚 ...
- [权限管理系统(四)]-spring boot +spring security短信认证+redis整合
[权限管理系统]spring boot +spring security短信认证+redis整合 现在主流的登录方式主要有 3 种:账号密码登录.短信验证码登录和第三方授权登录,前面一节Sprin ...
- 基于Spring Boot和Shiro的后台管理系统FEBS
FEBS是一个简单高效的后台权限管理系统.项目基础框架采用全新的Java Web开发框架 —— Spring Boot 2.0.3,消除了繁杂的XML配置,使得二次开发更为简单:数据访问层采用Myba ...
- spring Boot+spring Cloud实现微服务详细教程第二篇
上一篇文章已经说明了一下,关于spring boot创建maven项目的简单步骤,相信很多熟悉Maven+Eclipse作为开发常用工具的朋友们都一目了然,这篇文章主要讲解一下,构建spring bo ...
- spring Boot+spring Cloud实现微服务详细教程第一篇
前些天项目组的大佬跟我聊,说项目组想从之前的架构上剥离出来公用的模块做微服务的开发,恰好去年的5/6月份在上家公司学习了国内开源的dubbo+zookeeper实现的微服务的架构.自己平时对微服务的设 ...
- Spring Cloud 入门教程 - 搭建配置中心服务
简介 Spring Cloud 提供了一个部署微服务的平台,包括了微服务中常见的组件:配置中心服务, API网关,断路器,服务注册与发现,分布式追溯,OAuth2,消费者驱动合约等.我们不必先知道每个 ...
- Spring Boot 源码分析 数据源 + Mybatis 配置
公司今年开始使用 Spring Boot 开发,当然使用 Spring Boot 也是大势所趋,尤其是现在微服务的趋向,当然是选择基于Spring Boot 的 Spring Cloud.(所谓的 S ...
随机推荐
- Python记录9:函数4:名称空间作用域+闭包函数+装饰器
''' 一: 名称空间namespaces 名称空间就是存放名字与值绑定关系的内存空间 二: 名称空间分为三种 内置名称空间: 1. 特点: 存放是python解释器自 ...
- 极致21点开发DAY2
今天完成的主要内容时MainScene场景中设置面板的开发.游戏逻辑:点击设置按钮,弹出音乐设置弹框,弹框内容包括音乐的打开与关闭,退出游戏. 点击退出游戏,弹出退出游戏确认面板.效果图: 1.首先在 ...
- idea 更换svn地址
右键项目-->Subversion-->Relocate 上面是旧的SVN地址,下面填入你要更换的目标SVN地址
- Linux hostname设置,静态ip设置,hostname与静态ip相互映射
1,hostname设置 永久设置: centos 7 下,切换到root 用户 vi /etc/hostname 输入要修改的hostname centos 6或者其他linux 系统,可能在/et ...
- pssh批量管理
因为公司金融项目正式上线,有等保的要求,所有的线上服务器对操作过历史命令都要记录下来,需要修改一部分的配制文件.总共有300多台Linux服务器,总不能一台一台去改吧.首先想到是ansble,salt ...
- 配置jmeter环境变量及运行命令解释
Linux下Jmeter的安装及环境变量的配置 1.将Jmeter的安装包上传至Linux的/opt目录下,解压,如下图: 2.编辑/etc/profile文件配置Jmeter的环境变量 3.将编辑好 ...
- *(volatile int *)解读
#def ine reg_gpio_ctrl *(volatile int *)(ToVirtual(GPIO_REG_CTRL)) #define A (*(volatile unsigned lo ...
- Linux下系统时间函数、DST等相关问题总结(转)
Linux下系统时间函数.DST等相关问题总结 下面这个结构体存储了跟时区相关的位移量(offset)以及是否存在DST等信息,根据所在的时区信息,很容易找到系统时间与UTC时间之间的时区偏移,另外根 ...
- K8S学习笔记之k8s日志收集实战
0x00 简介 本文主要介绍在k8s中收集应用的日志方案,应用运行中日志,一般情况下都需要收集存储到一个集中的日志管理系统中,可以方便对日志进行分析统计,监控,甚至用于机器学习,智能分析应用系统问题, ...
- Python爬虫(二)——对开封市58同城出租房数据进行分析
出租房面积(area) 出租房价格(price) 对比信息 代码 import matplotlib as mpl import matplotlib.pyplot as plt import pan ...