首页 文章 精选 留言 我的

精选列表

搜索[整合],共10000篇文章
优秀的个人博客,低调大师

SpringBoot整合jwt和mybatis-plus的脚手架项目

近期给公司弄了个脚手架项目,打算以后后台开发就用这个了,希望留下您宝贵的建议,同时求star~ 点击跳转到github地址 概述Volcano是一个基于springboot后台开发简易脚手架。能实现当作一个单独项目或者作为微服务的一个节点进行快速开发和部署。 概述 Volcano是一个基于springboot后台开发简易脚手架。能实现当作一个单独项目或者作为微服务的一个节点进行快速开发和部署。 项目结构 javasea-volcano 父项目, 用于springcloud和springboot的版本管控, maven插件, 仓库统一管控等. |--javasea-volcano-base: 测试类和相关业务配置, 依赖common项目中的组件,开发的时候在该项目的基础上添 加添加业务代码即可。 |--javasea-volcano-common:常用组件和工具类 项目环境 中间件 版本 备注 JDK 1.8+ JDK1.8及以上 MySQL 5.6+ 5.6及以上 Redis 3.2+ 基本功能 参考包com.zhirui.lmwy.wms.demo下的配置 参数校验 JSR校验 spring-boot-starter-web已经默认集成了JSR303校验,只需要直接使用注解校验即可。 参考测试类: com.zhirui.lmwy.wms.demo.web.controller.TestCheckParamController 手动校验 Assert类定义了不满足条件后快速断言的方式,可以在校验参数中使用。 还可以采用Spring的Assert进行校验 //第一个参数为false则抛出IllegalArgumentException异常 Assert.isTrue(concurrentConsumers > 0, "'concurrentConsumers' value must be at least 1 (one)"); Assert.isTrue(!this.exclusive || concurrentConsumers == 1,"When the consumer is exclusive, the concurrency must be 1"); 也可以采用Optional进行校验 ZOrder order = this.getOrderByOrderNum(orderNum); Optional.ofNullable(order).filter(o -> { return null != o && (0 == o.getStatus() || 3 == o.getStatus() || 9 == o.getStatus()); }).orElseThrow(() -> new ParamException("获取数据异常,订单号有误或者订单状态异常!")); 还可以通过guava的Preconditions类来进行参数检查。有需要请自行百度。 参数转换 参考测试类:TestDateConverterAndJson URL方式传值到后端转换 URL传参到后端包括 如下三种方式传参: @GetMapping("testDateConverter") public Student testDateConverter(@RequestParam Student student){...} @GetMapping("testDateConverter") public Student testDateConverter(Student student){...} @GetMapping("testDateConverter/{id}") public Student testDateConverter(@PathVariable Integer id){...} com.zhirui.lmwy.common.converter包下定义了很多转换类: StringToDateConverter 如果实体属性是Date,通过该转换器将String转Date类型。效果和日期属性上的注解@DatetimeFormat相同。但是启用该转换器的时候,实体属性上的注解@DatetimeFormat不再生效。 StringToDoubleConverter 如果实体属性是Double,通过该转换器将String转Double类型。 StringToIntegerConverter 如果实体属性是Integer,通过该转换器将String转Integer类型。 请求体方式传值到后端转换 请求体方式传参到后端 包括如下方式: @PostMapping("testDateConverter2") public Student testDateConverter2(@RequestBody Student student){...} com.zhirui.lmwy.common.json.jackson包下定义了JSON的序列化和反序列化方式: deserializer 包下的序列化类 在@RequestBody接参时候,会调用该包中的序列化类将JSON转换成实体接参。 serializer 包下的序列化类 用于后端传值给前端。 后端传值给前端 com.zhirui.lmwy.common.json.jackson.serializer包下的定义了序列化类 。 在Controller类上添加注解@ResponseBody或者@RestController,那么后端传值给前端是JSON方式。 如果是返回值是对象或者集合,会用序列化类进行参数类型转换。 如下列子中,Student中的两个属性日期会转换成serializer 定义的格式“yyyy-MM-dd HH:mm:ss”。 序列化类`JacksonDateSerializer的作用相当于在属性上添加了@JsonFormat(pattern="yyyy-MM-dd"),实现将Date类型转换成String,但是添加JacksonDateSerializer后 @JsonFormat(pattern="yyyy-MM-dd")`失效。 @PostMapping("testDateConverter2") public Student testDateConverter2(@RequestBody Student student){ System.out.println(student); Student s = new Student(); s.setBirth(new Date()); s.setCreateTime(LocalDateTime.now()); return s; } 异常处理 在com.zhirui.lmwy.common.exception.impl中定义了三大类异常: BusinessException: 通用业务异常 ParamException:参数校验异常 AuthenticationException:认证失败异常 Exceptions类定义了快捷抛出异常的一些通用方法,在需要抛出异常时请不要去throw new XXXException(),而是用Exceptions类的方法进行操作。 已经定义了全局异常处理器GlobalExceptionHandler对各种异常可以进行处理,请不要在controller和service中try..catch。 返回值处理 controller的返回前端使用ResultModel类进行封装,里面有code,msg,data等字段。 各种场景下的ResultModel返回: //插入后返回方式, msg:插入成功;插入失败 public ResultModel resultInsert(){ boolean flag = false; return ResultModel.resultInsert(flag); } //更新后返回方式, msg:更新成功;更新失败 public ResultModel resultUpdate(){ boolean flag = false; return ResultModel.resultUpdate(flag); } //删除后返回方式, msg:删除成功;删除失败 public ResultModel resultDelete(){ boolean flag = false; return ResultModel.resultDelete(flag); } //删除后返回方式, msg:操作成功;操作失败 public ResultModel result(){ boolean flag = false; return ResultModel.result(flag); } //认证失败返回方式, msg:认证信息异常 public ResultModel errorTokenMsg(){ boolean flag = false; //如果msg参数为null,那么是默认的msg:认证信息异常 return ResultModel.errorTokenMsg(null); } 推荐优先上面的放回方式,如果不能满足,还可以使用如下通用的失败、成功的返回方式: ResultModel.error(); //失败返回方式 ResultModel.ok(); //成功返回方式 AOP 日志输出 添加坐标 <!-- AOP --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- console彩色日志 --> <dependency> <groupId>org.fusesource.jansi</groupId> <artifactId>jansi</artifactId> <version>1.18</version> </dependency> 定义AOP类实现彩色日志输出 参考:com.zhirui.lmwy.common.aop.LogAop 集成 丝袜哥 在common项目中集成了swagger pom中添加注解 <!-- swagger start --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <!-- swagger end --> 添加配置 两个配置类: com.zhirui.lmwy.common.swagger.SwaggerConfiguration com.zhirui.lmwy.common.swagger.SwaggerProperties 设置自定义显示参数 在base项目的application.yaml中添加如下配置进行设置: swagger: # open: true #是否开启swagger,在生产环境下需要关闭 protocol: http #协议,http或https base-package: com.zhirui.lmwy.wms #一定要写对,会在这个路径下扫描controller定义 title: volcano-base项目 version: 1.0 description: volcano-base项目丝袜哥测试 在controller和model中使用swagger controller类中使用参考:com.zhirui.lmwy.wms.demo.web.controller.TestCurdController model类中使用参考:com.zhirui.lmwy.wms.demo.web.entity.User 通过swagger进行http请求 http://localhost:8080/swagger-ui.html#/ 丝袜哥默认的访问方式 http://localhost:8080/docs 通过controller 重定向后的访问方式 集成 mybatis-plus(下文中称为MP) ORM框架使用mybatis-plus,简便了CURD操作 添加pom <!-- mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version> </dependency> <!-- 代码生成器 start --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.2.0</version> </dependency> <!-- 代码生成器需要的引擎模板 --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.0</version> </dependency> <!-- 代码生成器 end --> 在yml中进行配置 mybatis-plus: mapper-locations: classpath:mapper/**/*.xml global-config: db-config: id-type: AUTO #主键自增长 添加配置类配置 下列代码中使用了租户模式,如果只是单纯需要添加分页插件,只需要如下方式即可: @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); return paginationInterceptor; } /** 分页插件: 实现物理分页 */ @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); ArrayList<ISqlParser> iSqlParsers = new ArrayList<>(); TenantSqlParser tenantSqlParser = new TenantSqlParser(); tenantSqlParser.setTenantHandler(new TenantHandler() { @Override public Expression getTenantId(boolean where) { return new StringValue(tenantId); } @Override public String getTenantIdColumn() { //指定表中的租户列 return "tenant_id"; } @Override public boolean doTableFilter(String tableName) { return false; } }); iSqlParsers.add(tenantSqlParser); paginationInterceptor.setSqlParserList(iSqlParsers); return paginationInterceptor; } 通过MP进行CRUD操作 参考测试类和官网:com.zhirui.lmwy.wms.demo.web.controller.TestCurdController 代码生成器 采用了MP的代码生成器,实现了两个代码生成器。 在javasea-volcano-base项目的src/test/java目录的 com.zhirui.lmwy.wms包下,按照注释修改为自己需要的配置运行即可。看个人习惯,推荐用PrimaryCodeGenerator 。 SencondCodeGenerator 通过数据库表生成基本的entity,mapper,controller和service类等基本类。 PrimaryCodeGenerator 在 SencondCodeGenerator 功能的基础上,controller,entity中生成swagger的注解。controller、service生成常用的crud方法。 集成redis 添加pom坐标 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> yml中进行配置 spring: redis: host: 127.0.0.1 port: 6379 password: zhirui888 timeout: 2000 database: 6 lettuce: pool: max-active: 8 max-wait: 1 max-idle: 8 min-idle: 0 配置类中进行配置 定义redis的RedisTemplate 详见:com.zhirui.lmwy.common.redis.RedisTemplateConfig 开启springcache 详见:com.zhirui.lmwy.common.redis.RedisCacheConfig redis工具类 详见:com.zhirui.lmwy.common.redis.RedisUtils 集成 JWT 添加jwt坐标 <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> <scope>compile</scope> </dependency> yml中配置jwt custom: jwt: header: token secret: 666666 #密码,用于生成签名 issuer: volcano #签发人 subject: volcano-jwt #主题 audience: web #签发的目标 expire-minutes: 20 #过期时间 interceptor: jwt: exclude: path: /swagger-resources/**,/api-docs/**,/v2/api-docs/**,/login,/verificationCode,/doc/**,/error/**,/docs,/test/** permission: exclude: path: /swagger-resources/**,/api-docs/**,/v2/api-docs/**,/adminLogin,/sysLogin,/login.html,/verificationCode,/doc/**,/error/**,/docs token-timeout: exclude: path: /swagger-resources/**,/api-docs/**,/v2/api-docs/**,/docs 添加登陆接口 登陆后会将token信息保存到redis 详见登陆controller:com.zhirui.lmwy.wms.security.controller.LoginController 添加jwt拦截器 拦截到请求后会进行校验,校验方式如下: // 验证token是否有效 Jws<Claims> jws = JwtUtil.verify(token); token未过期且合法的才能校验通过,否则抛出401异常。 详见jwt拦截器:com.zhirui.lmwy.wms.security.interceptor.JwtInterceptor 在swagger进行token测试 执行登陆操作,token见返回值中 返回值为: { "code": 200, "msg": "操作成功", "data": { "loginSysUser": { "id": "1", "userName": "admin" }, "token": "eyJjdHkiOiJjdHkiLCJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ2b2xjYW5vLWp3dCIsImF1ZCI6IndlYiIsImlzcyI6InZvbGNhbm8iLCJleHAiOjE1Njg3MTI0MDcsImlhdCI6MTU2ODcxMDYwNywianRpIjoiNDM4NjMwMDAwOTlmNDFkNDk1Y2FlOWVkNWUzNDZhOTgifQ.wVZOx-J2knqUxdnRjRXZqr2nf1S-Qwmap2-0nGXJDXM" }, "time": "2019-09-17 16:56:51" } 点击 Authorize按钮 将token设置到value中,后面丝袜哥所有的请求都会带一个叫做“Authorization”的请求头。 在jwt拦截器JwtInterceptor中会获取该请求头进行校验。 项目启动和部署 单独作为项目使用 javasea-volcano默认是一个独立的springboot项目,可以直接启动javasea-volcano-base项目,在base项目的基础上,直接编写业务代码即可。 集成到现有的springcloud中 默认是独立项目,但是也可以轻松的集成到现有的springcloud环境中,只需要放开部分注释即可。 启用bootstrap.yml的配置(放开注释) #spring: # cloud: # config: # uri: ${WMS_CONFIG_SERVER_URL} # name: zhirui-lmwy2-wms${WMS_DEVELOPER_NAME:} # profile: ${config.profile:dev} # base项目的pom中开启对应eureka client的坐标,启动类WmsApplication通过注解@EnableDiscoveryClient启用eureka client。 <!-- springboot 1.X --> <!--<dependency>--> <!--<groupId>org.springframework.cloud</groupId>--> <!--<artifactId>spring-cloud-starter-eureka</artifactId>--> <!--</dependency>--> <!-- springboot 2.X --> <!--<dependency>--> <!--<groupId>org.springframework.cloud</groupId>--> <!--<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>--> <!--</dependency>--> IDE中配置vm参数,使用已经已经存在的eureka注册中心和配置中心 在IDE中配置VM参数只能是作为测试,如果需要部署后生效,那么需要配置到服务器的环境变量或者在启动的java -jar后添加配置。之后我会添加关于服务部署的文章。 -Dmultipart-location=D:/wms/temp -DDiskLocation=D:/wms/ -DWMS_CONFIG_SERVER_URL=http://192.168.1.230:8861 -DWMS_EUREKA_SERVER_URL=http://192.168.1.230:8090/eureka/ -DWMS_DEVELOPER_NAME=-longxiaonan -Dconfig.profile=dev Maven方式打包 mvn clean package -Dmaven.test.skip=true

优秀的个人博客,低调大师

SpringBoot集成FastDFS+Nginx整合基于Token的防盗链

为什么要用SpringBoot? SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。 创建独立的Spring应用程序 嵌入的Tomcat,无需部署WAR文件 简化Maven配置 自动配置Spring 提供生产就绪型功能,如指标,健康检查和外部配置 绝对没有代码生成并且对XML也没有配置要求 为什么要用Nginx? 概述 Nginx(engine x)是一个开源的,支持高并发的www服务和代理服务软件。Nginx是俄罗斯人Igor Sysoev开发的,最初被应用到俄罗斯的大型网站(www.rambler.ru)上。后来作者将源代码以类BSD许可证的形式开源出来供全球使用。在功能应用方面,Nginx不仅是一个优秀的Web服务软件,还具有反向代理负载均衡和缓存的功能。在反向代理负载均衡方面类似于LVS负载均衡及HAProxy等你专业代理软件。Nginx部署起来更加方便简单,在缓存服务功能方面,有类似于Squid等专业的缓存服务软件。Nginx可以运行在UNIX、Linux、MS Windows Server、Mac OS X Server、Solaris等操作系统中。 Nginx的重要特性 可以针对静态资源高速节点并发访问及缓存。 可以使用反向代理加速,并且可以进行数据缓存。 具有简单负载均衡,节点健康检查和容错功能。 支持远程Fast CGI服务的缓存加速。 支持Fast CGI、Uwsgi、SCGI、Memcached Server的加速和缓存。 支持SSL、TLS、SNI。 具有模块化的架构。 过滤器包括gzip压缩、ranges支持、chunked响应、XSLT、SSL和图像缩放等功能。 在SSL过滤器中,包含多个SSL页面,如果经由Fast CGI或反向代理处理,可以并行处理。 Nginx所具备的WWW服务特性 支持基于域名、端口和IP的虚拟主机配置。 支持KeepAlived和piplined连接。 可进行简单、方便、灵活的配置和管理。 支持修改Nginx配置,并且在代码上线时,可平滑重启,不中断业务访问。 可自定义访问日志格式,临时缓冲写日志操作,快速日志轮询及通过rsyslog处理日志。 可利用信号控制Nginx进程。 支持3xx-5xxHTTP状态码重定向。 支持rewrite模块,支持URI重写及正则表达式匹配。 支持基于客户端IP地址和HTTP基本认证的访问控制。 支持PUT、DELETE、MKCOL、COPY、MOVE等特殊的HTTP请求方法。 支持FLV流和MP4流技术产品应用。 支持HTTP响应速率限制。 支持同一IP地址的并发连接或请求限制。 支持邮件服务代理。 支持高并发,可以支持几百万并发连接。 资源消耗少,在3万并发连接下,可以开启10个nginx的线程消耗的内存不到200MB。 可以做HTTP反向代理及加速缓存,及负载均衡功能,内置对RS节点服务器健康检查功能,折现但能够与专业的HAProxy或LVS的功能。 具备Squid等专业缓存软件等的缓存功能。 支持异步网络I/O事件模型epoll(Linux2.6+)。 Nginx软件主要企业应用 作为Web服务软件。 使用Nginx运行HTML、JS、CSS、小图片等静态数据(类似于Lighttpd)。 结合Fast CGI运行PHP等动态程序(例如使用fastcgi_pass方式)。 Nginx结合Tomcat/Resin等支持Java动态程序(常用proxy_pass)。 反向代理或负载均衡服务(Nginx从1.9.0开始就开始支持TCP的代理了)。 前端业务数据缓存服务。 Web服务应用产品性能对比 静态数据的访问上:处理小文件(小于1MB)时,Nginx和Lighttpd比Apache更有优势,Nginx处理小文件的优势明显,Lighttpd综合最强。 动态数据的访问上:三者差距不大,Apache更有优势,因为处理动态数据的能力在于PHP(Java)和后端数据库的服务能力,也就是说瓶颈不在Web服务器上。 一般情况下普通PHP引擎支持的并发连接参考值300~1000。Java引擎和数据库的并发连接参考值300~1500。 为什么Nginx比Apache的性能高? Nginx使用最新版的eepoll(Linux 2.6内核)和kqueue(FreeBSD)异步网络I/O模型,而Apache使用的是传统的select模型。 目前Linux下能够承受高并发访问的Squid、Memcached软件采用都是epoll模型。 处理大量的连接的读写时,Apache所采用的select网络I/O模型比较低。 如何正确采用Web服务器? 静态业务:如果是高并发场景,尽量采用Nginx或Lighttpd,二者首选Nginx。 动态业务:理论上采用Nginx和Apache均可,建议使用Nginx,为了避免相同业务服务的软件多样化,增加维护成本,动态业务可以使用Nginx兼做前端代理,再根据页面的元素或目录转发到其他的服务器进行处理。 既有动态业务又有静态业务,就用Nginx。 关于部署,就不在重复了,如果需要请移步《Java高级架构之FastDFS分布式文件集群》:https://blog.51cto.com/xvjunjie/2377669 使用IDEA场景启动器创建工程 创建Maven工程,修改POM.xml文件添加如下依赖: <dependencies> <!-- SpringBoot的自动配置相关依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>1.5.20.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <version>1.5.20.RELEASE</version> </dependency> <!-- 日志相关的依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> <version>1.5.20.RELEASE</version> </dependency> <!-- 对象池相关的依赖 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.6.0</version> </dependency> </dependencies> 创建必要的包 annotation:存放相关的注解 autoconfiguation: 存储自动配置类 factory: 存放工厂类 properties: 存放配置参数类 service: 存放服务类 一般情况下,SpringBoot都会提供相应的@EnableXxx注解标注在应用的主启动类上开启某个功能: // EnableFastdfsClient.java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(FastdfsAutoConfiguration.class) @Documented public @interface EnableFastdfsClient { } 下面是相关的自动配置类: // FastdfsAutoConfiguration.java @Configuration @EnableConfigurationProperties(FastdfsProperties.class) public class FastdfsAutoConfiguration { @Autowired private FastdfsProperties fastdfsProperties; @Bean @ConditionalOnMissingBean(FastdfsClientService.class) public FastdfsClientService fastdfsClientService() throws Exception { return new FastdfsClientService(fastdfsProperties); } } 创建相关的工厂类: // StorageClientFactory.java // 用于创建连接对象的工厂类 public class StorageClientFactory implements PooledObjectFactory<StorageClient> { @Override public PooledObject<StorageClient> makeObject() throws Exception { TrackerClient client = new TrackerClient(); TrackerServer server = client.getConnection(); return new DefaultPooledObject<>(new StorageClient(server, null)); } @Override public void destroyObject(PooledObject<StorageClient> p) throws Exception { p.getObject().getTrackerServer().close(); } @Override public boolean validateObject(PooledObject<StorageClient> p) { return false; } @Override public void activateObject(PooledObject<StorageClient> p) throws Exception { } @Override public void passivateObject(PooledObject<StorageClient> p) throws Exception { } } Properties类用来映射application.properties或者application.yml配置文件: // FastdfsProperties.java @ConfigurationProperties(prefix = "fastdfs") public class FastdfsProperties { // 连接超时时间 // 网络超时时间 // 字符集编码 // 是否使用Token // Token加密密钥 // 跟踪器IP地址,多个使用分号隔开 // 连接池的连接对象最大个数 // 连接池的最大空闲对象个数 // 连接池的最小空闲对象个数 // Nginx服务器IP,多个使用分号分割 // 获取连接对象时可忍受的等待时长(毫秒) private String connectTimeout = "5"; private String networkTimeout = "30"; private String charset = "UTF-8"; private String httpAntiStealToken = "false"; private String httpSecretKey = ""; private String httpTrackerHttpPort = ""; private String trackerServers = ""; private String connectionPoolMaxTotal = "18"; private String connectionPoolMaxIdle = "18"; private String connectionPoolMinIdle = "2"; private String nginxServers = ""; // 需要创建相关的Setter和Getter方法 } 在Service类中封装方法, 下面仅展示3个常用的方法: // FastdfsClientSerivce.java public class FastdfsClientService { // SpringBoot加载的配置文件 // 连接池配置项 // 转换后的配置条目 // 连接池 // Nginx服务器地址 private FastdfsProperties fdfsProp; private GenericObjectPoolConfig config; private Properties prop; private GenericObjectPool<StorageClient> pool; private String[] nginxServers; private Logger logger; public FastdfsClientService(FastdfsProperties fdfsProp) throws Exception { this.fdfsProp = fdfsProp; this.logger = LoggerFactory.getLogger(getClass()); init(); create(); info(); } /** * 初始化全局客户端 */ private void init() throws Exception { this.prop = new Properties(); this.logger.info("FastDFS: reading config file..."); this.logger.info("FastDFS: fastdfs.connect_timeout_in_seconds=" + this.fdfsProp.getConnectTimeout()); this.logger.info("FastDFS: fastdfs.network_timeout_in_seconds=" + this.fdfsProp.getNetworkTimeout()); this.logger.info("FastDFS: fastdfs.charset=" + this.fdfsProp.getCharset()); this.logger.info("FastDFS: fastdfs.http_anti_steal_token=" + this.fdfsProp.getHttpAntiStealToken()); this.logger.info("FastDFS: fastdfs.http_secret_key=" + this.fdfsProp.getHttpSecretKey()); this.logger.info("FastDFS: fastdfs.http_tracker_http_port=" + this.fdfsProp.getHttpTrackerHttpPort()); this.logger.info("FastDFS: fastdfs.tracker_servers=" + this.fdfsProp.getTrackerServers()); this.logger.info("FastDFS: fastdfs.connection_pool_max_total=" + this.fdfsProp.getConnectionPoolMaxTotal()); this.logger.info("FastDFS: fastdfs.connection_pool_max_idle=" + this.fdfsProp.getConnectionPoolMaxIdle()); this.logger.info("FastDFS: fastdfs.connection_pool_min_idle=" + this.fdfsProp.getConnectionPoolMinIdle()); this.logger.info("FastDFS: fastdfs.nginx_servers=" + this.fdfsProp.getNginxServers()); this.prop.put("fastdfs.connect_timeout_in_seconds", this.fdfsProp.getConnectTimeout()); this.prop.put("fastdfs.network_timeout_in_seconds", this.fdfsProp.getNetworkTimeout()); this.prop.put("fastdfs.charset", this.fdfsProp.getCharset()); this.prop.put("fastdfs.http_anti_steal_token", this.fdfsProp.getHttpAntiStealToken()); this.prop.put("fastdfs.http_secret_key", this.fdfsProp.getHttpSecretKey()); this.prop.put("fastdfs.http_tracker_http_port", this.fdfsProp.getHttpTrackerHttpPort()); this.prop.put("fastdfs.tracker_servers", this.fdfsProp.getTrackerServers()); ClientGlobal.initByProperties(this.prop); } /** * 显示初始化信息 */ private void info() { this.logger.info("FastDFS parameter: ConnectionPoolMaxTotal ==> " + this.pool.getMaxTotal()); this.logger.info("FastDFS parameter: ConnectionPoolMaxIdle ==> " + this.pool.getMaxIdle()); this.logger.info("FastDFS parameter: ConnectionPoolMinIdle ==> " + this.pool.getMinIdle()); this.logger.info("FastDFS parameter: NginxServer ==> " + Arrays.toString(this.nginxServers)); this.logger.info(ClientGlobal.configInfo()); } /** * 创建连接池 */ private void create() { this.config = new GenericObjectPoolConfig(); this.logger.info("FastDFS Client: Creating connection pool..."); this.config.setMaxTotal(Integer.parseInt(this.fdfsProp.getConnectionPoolMaxTotal())); this.config.setMaxIdle(Integer.parseInt(this.fdfsProp.getConnectionPoolMaxIdle())); this.config.setMinIdle(Integer.parseInt(this.fdfsProp.getConnectionPoolMinIdle())); StorageClientFactory factory = new StorageClientFactory(); this.pool = new GenericObjectPool<StorageClient>(factory, this.config); this.nginxServers = this.fdfsProp.getNginxServers().split(","); } /** * Nginx服务器负载均衡算法 * * @param servers 服务器地址 * @param address 客户端IP地址 * @return 可用的服务器地址 */ private String getNginxServer(String[] servers, String address) { int size = servers.length; int i = address.hashCode(); int index = abs(i % size); return servers[index]; } /** * 带有防盗链的下载 * * @param fileGroup 文件组名 * @param remoteFileName 远程文件名称 * @param clientIpAddress 客户端IP地址 * @return 完整的URL地址 */ public String autoDownloadWithToken(String fileGroup, String remoteFileName, String clientIpAddress) throws Exception { int ts = (int) (System.currentTimeMillis() / 1000); String token = ProtoCommon.getToken(remoteFileName, ts, ClientGlobal.getG_secret_key()); String nginx = this.getNginxServer(this.nginxServers, clientIpAddress); return "http://" + nginx + "/" + fileGroup + "/" + remoteFileName + "?token=" + token + "&ts=" + ts; } /** * 上传文件,适合上传图片 * * @param buffer 字节数组 * @param ext 扩展名 * @return 文件组名和ID */ public String[] autoUpload(byte[] buffer, String ext) throws Exception { String[] upload = this.upload(buffer, ext, null); return upload; } /** * 不带防盗链的下载,如果开启防盗链会导致该方法抛出异常 * * @param fileGroup 文件组名 * @param remoteFileName 远程文件ID * @param clientIpAddress 客户端IP地址,根据客户端IP来分配Nginx服务器 * @return 完整的URL地址 */ public String autoDownloadWithoutToken(String fileGroup, String remoteFileName, String clientIpAddress) throws Exception { if (ClientGlobal.getG_anti_steal_token()) { this.logger.error("FastDFS Client: You've turned on Token authentication."); throw new Exception("You've turned on Token authentication."); } String nginx = this.getNginxServer(this.nginxServers, clientIpAddress); return "http://" + nginx + fileGroup + "/" + remoteFileName; } // 后面还有好多方法,就不一一展示了 } 为了在IDEA中使用便捷的配置提示功能,我们需要创建元数据文件(resources/spring-configuration-metadata.json): { "groups": [ { "name": "fastdfs", "type": "com.bluemiaomiao.properties.FastdfsProperties", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties" } ], "properties": [ { "name": "connectTimeout", "type": "java.lang.String", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties", "defaultValue": "5" }, { "name": "networkTimeout", "type": "java.lang.String", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties", "defaultValue": "30" }, { "name": "charset", "type": "java.lang.String", "defaultValue": "UTF-8" }, { "name": "httpAntiStealToken", "type": "java.lang.String", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties", "defaultValue": "false" }, { "name": "httpSecretKey", "type": "java.lang.String", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties" }, { "name": "httpTrackerHttpPort", "type": "java.lang.Integer", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties" }, { "name": "trackerServers", "type": "java.lang.String", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties" }, { "name": "connectionPoolMaxTotal", "type": "java.lang.Integer", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties", "defaultValue": "18" }, { "name": "connectionPoolMaxIdle", "type": "java.lang.Integer", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties", "defaultValue": "18" }, { "name": "connectionPoolMinIdle", "type": "java.lang.Integer", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties", "defaultValue": "2" }, { "name": "nginxServers", "type": "java.lang.String", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties" } ], "hints": [ { "name": "http_anti_steal_token", "values": [ { "value": "false" }, { "value": "true" } ] } ] } 将自定义starter添加到项目 创建SpringBoot项目,勾选Web选项,版本选择1.5.20 进入场景启动器的项目目录执行mvn clean install 将其安装到本地 在POM.xml文件中添加依赖: <dependency> <groupId>com.bluemiaomiao</groupId> <artifactId>fastdfs-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency> 记得开启IDEA的自动导入功能 创建配置文件application.properties fastdfs.nginx-servers=192.168.80.2:8000,192.168.80.3:8000,192.168.80.4:8000 fastdfs.tracker-servers=192.168.80.2:22122,192.168.80.3:22122,192.168.80.4:22122 fastdfs.http-secret-key=2scPwMPctXhbLVOYB0jyuyQzytOofmFCBIYe65n56PPYVWrntxzLIDbPdvDDLJM8QHhKxSGWTcr+9VdG3yptkw fastdfs.http-anti-steal-token=true fastdfs.http-tracker-http-port=8080 fastdfs.network-timeout=30 fastdfs.connect-timeout=5 fastdfs.connection-pool-max-idle=18 fastdfs.connection-pool-min-idle=2 fastdfs.connection-pool-max-total=18 fastdfs.charset=UTF-8 或者使用application.yml fastdfs: charset: UTF-8 connect-timeout: 5 http-secret-key: 2scPwMPctXhbLVOYB0jyuyQzytOofmFCBIYe65n56PPYVWrntxzLIDbPdvDDLJM8QHhKxSGWTcr+9VdG3yptkw network-timeout: 30 http-anti-steal-token: true http-tracker-http-port: 8080 connection-pool-max-idle: 20 connection-pool-max-total: 20 connection-pool-min-idle: 2 nginx-servers: 192.168.80.2:8000,192.168.80.3:8000,192.168.80.4:8000 tracker-servers: 192.168.80.2:22122,192.168.80.3:22122,192.168.80.4:22122 创建控制器类测试方法 // controllers.DownloadController.java @Controller @RequestMapping(value = "/download") public class DownloadController { @Autowired private FastdfsClientService service; @ResponseBody @RequestMapping(value = "/image") public String image() throws Exception { // 之前上传过的数据,实际应用场景应该使用SQL数据库来存储 return service.autoDownloadWithToken("group1", "M00/00/00/wKhQA1ysjSGAPjXbAAVFOL7FJU4.tar.gz", "192.168.80.1"); } } 项目主页:https://github.com/bluemiaomiao/fastdfs-spring-boot-starter 国内项目主页:https://gitee.com/bluemiaomiao/fastdfs-spring-boot-starter

优秀的个人博客,低调大师

Docker Hub 迎来大幅改版整合,全新特性先睹为快!

出品丨Docker公司(ID:docker-cn)编译丨小东每周一、三、五,与您不见不散! Docker 近日宣布将 Docker Store 和 Docker Cloud 集成到 Docker Hub 中,为用户提供一种集查找、存储以及共享容器镜像的一站式体验。这意味着: Docker 官方镜像和经验证的发布者镜像现在可以直接在 Docker Hub 上进行查找和下载了; Docker Hub 拥有了全新的用户体验; 数百万的个人用户以及数十万的组织都在使用 Docker Hub、Store 和 Cloud 来满足其容器内容的需要。本次 Docker Hub 的更新就是要将这些产品中用户最熟悉、最喜欢的功能集中在一起,同时围绕用户、镜像仓库和团队管理的易用性等方面来处理已知的 Docker Hub 请求。下面将对新特性进行简单介绍。 镜像仓库 在镜像仓库页面可以查看最近推送的标签和自动构建的内容; 添加了镜像仓库标签分页功能; 改进了在 Docker Hub 主页登录时的镜像仓库过滤机制; 组织和团队 作为一名组织的管理者,可以在一瞥间快速查看镜像仓库中的团队权限; 通过电子邮件将 Docker Hub 用户添加到团队(如果您不记得他们的 Docker ID); 全新的自动化构建 使用构建缓存加快构建速度; 在构建中可以添加环境变量和进行运行测试; 对现有的镜像仓库添加自动化构建; 注意:对于组织,需要将 GitHub 和 BitBucket 帐户重新链接到您的组织中,这样才可以使用全新的自动化构建。现有的自动化构建将在未来几个月内迁移到这个新系统。 浏览 https://docs.docker.com/docker-hub/builds/classic/ 了解更多详细。 改进容器镜像过滤机制 按官方、发布者和已认证镜像进行过滤,确保每一个搜索出来的 Docker 镜像的质量水平; 根据类别进行过滤,快速挖掘您正在寻找的镜像类型; 现有的 URL 将继续有效,您可以在适当的时候进行自动重定向。无需更新任何自定义标签; 经验证的发布者镜像和插件 经验证的发布者镜像现已在 Docker Hub 上提供。 与官方镜像类似,这些镜像已经通过了 Docker 官方的审查。Docker 维护官方镜像库时,经验证的发布者镜像将由我们的第三方软件供应商提供。 下一步 随着时间的推移,我们将向用户推出全新的 Docker Hub,浏览 https://hub.docker.com 了解更多相关信息。

优秀的个人博客,低调大师

在spring boot项目中,如何整合freemarker与shiro标签使用

一、导入依赖包 <!--freemarker+shiro(标签)--> <dependency> <groupId>net.mingsoft</groupId> <artifactId>shiro-freemarker-tags</artifactId> <version>0.1</version> </dependency> 二、在项目中添加配置 packagecom.web.common.config; importorg.springframework.beans.factory.InitializingBean; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.stereotype.Component; importcom.jagregory.shiro.freemarker.ShiroTags; importfreemarker.template.Configuration; @Component publicclassPlatformFreeMarkerConfigurerimplementsInitializingBean{ @Autowired privateConfigurationconfiguration; @Override publicvoidafterPropertiesSet()throwsException{ //加上这句后,可以在页面上使用shiro标签 configuration.setSharedVariable("shiro",newShiroTags()); } } 三、 <@shiro.hasPermissionname="admin:add"> <ahref="${base}/admin/user/role/add">添加角色</a> </@shiro.hasPermission> <@shiro.hasPermissionname="admin:update"> <ahref="${base}/admin/user/role/edit?id=${bean.id}">修改</a> <ahref="${base}/admin/user/role/delete?id=${bean.id}">删除</a> </@shiro.hasPermission>

优秀的个人博客,低调大师

Spring Cloud 2.x系列之springcloud整合lettuce使用redis

Redis是一种nosql数据库,以键值对<key,value>的形式存储数据,其速度相比于MySQL之类的数据库,相当于内存读写与硬盘读写的差别,所以常常用作缓存,用于少写多读的场景下,直接从缓存拿数据比从数据库(数据库要I/O操作)拿要快得多。Redis目前几乎无处不在,大公司小公司都在用。 Spring cloud 2.x版本后默认Redis客户端连接池类型使用的是lettuce,而Sping cloud 1.5.x使用的jedis。Lettuce和Jedis的都是连接Redis Server的客户端程序。Jedis在实现上是直连redis server,多线程环境下非线程安全,除非使用连接池,为每个Jedis实例增加物理连接;Lettuce基于Netty的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,且线程安全,满足多线程环境下的并发访问,同时它是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。 1、新建项目sc-redis,对应的pom.xml文件如下 <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.0http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>spring-cloud</groupId> <artifactId>sc-redis</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>sc-redis</name> <url>http://maven.apache.org</url> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project> 2、新建spring boot启动类 package sc.redis; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RedisApplication { public static void main(String[] args) { SpringApplication.run(RedisApplication.class,args); } } 3、新建配置文件application.yml server: port: 9004 spring: application: name: sc-redis redis: host: 127.0.0.1 password: port: 6379 timeout: 10000 # 连接超时时间(毫秒) database: 0 # Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0 lettuce: pool: max-active: 8 # 连接池最大连接数(使用负值表示没有限制)默认 8 max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)默认 -1 max-idle: 8 # 连接池中的最大空闲连接默认 8 min-idle: 0 # 连接池中的最小空闲连接默认 0 备注:reids对用的所有配置项可以在以下类查看 org.springframework.boot.autoconfigure.data.redis.RedisProperties 4、自定义Reids的Template 默认情况下的模板只能支持RedisTemplate<String, String>,也就是只能存入字符串,这在开发中是不友好的,所以自定义模板是很有必要的,当自定义了模板又想使用String存储这时候就可以使用StringRedisTemplate的方式,它们之间并不冲突。 package sc.redis.config; import java.io.Serializable; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @AutoConfigureAfter(RedisAutoConfiguration.class) public class RedisCacheAutoConfiguration { @Bean public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactoryredisConnectionFactory) { RedisTemplate<String, Serializable> template = newRedisTemplate<>(); //键的序列化方式 template.setKeySerializer(newStringRedisSerializer()); //值的序列化方式 template.setValueSerializer(newGenericJackson2JsonRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); returntemplate; } } 5、新建一个模拟的Controller类 packagesc.redis.config; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import sc.redis.model.User; @RestController publicclassUserCacheController { //@Autowired //private StringRedisTemplate stringRedisTemplate; @Autowired privateRedisTemplate<String, Serializable> redisCacheTemplate; @GetMapping(value = "/cache/user/cacheUser") @ResponseBody public Map<String, Object> cacheUser() { Map<String,Object> result = new HashMap<String, Object>(); result.put("code", "000000"); result.put("msg", "success"); User u = new User(); u.setId(1L); u.setAge(23); u.setUserName("huangjinjin"); u.setPosition("cto"); result.put("body", u); redisCacheTemplate.opsForValue().set(String.valueOf(u.getId()), u); returnresult; } /** * 获取缓存信息 * @param id * @return */ @GetMapping(value = "/cache/user/getCacheUser/{id}") @ResponseBody public Map<String, Object> getCacheUser(@PathVariable Long id) { Map<String,Object> result = new HashMap<String, Object>(); result.put("code", "000000"); result.put("msg", "success"); User u = (User) redisCacheTemplate.opsForValue().get(String.valueOf(1)); System.out.println(u.getUserName()); result.put("body", u); returnresult; } } 6、启动sc-redis项目,并验证是否启动成功 7、使用postman访问接口 (1)存储值到redis http://127.0.0.1:9004/cache/user/cacheUser (2)从redis获取存贮的值 http://127.0.0.1:9004/cache/user/getCacheUser/1 使用redis客户端redis-cli查看是否把相关数据存贮到redis 下列的就是Redis其它类型所对应的操作方式: opsForValue:对应 String(字符串) opsForZSet:对应 ZSet(有序集合) opsForHash:对应 Hash(哈希) opsForList:对应 List(列表) opsForSet:对应 Set(集合) opsForGeo:对应 GEO(地理位置) 源码: https://gitee.com/hjj520/spring-cloud-2.x 原文发布时间:2018-9-24 本文作者:java乐园 本文来自云栖社区合作伙伴“java乐园”,了解相关信息可以关注“java乐园”

优秀的个人博客,低调大师

(十六) 整合spring cloud云架构 -使用spring cloud Bus刷新配置

我们使用spring cloud分布式微服务云架构做了b2b2c的电子商务系统,除了架构本身自带的系统服务外,我们将b2b2c的业务服务进行了细粒度拆分,做成了不同的业务微服务。 当我们的业务系统越来越庞大复杂的时候,各种配置也会随之增多。配置文件只要一修改,会对commonservice-config配置中心先停止服务,然后再重新启动,最后使配置生效。 如果服务少,我们可以手动方式来启动,但是对业务和系统的稳定性肯定有一定的影响。 如果是成百上千的服务都靠手动操作,我估计运维人员或技术人员会疯掉的。 针对以上问题,commonservice-config服务端和业务微服务分别做了相关的配置,服务端负责将git(svn或本地文件系统)中存储的配置文件进行配置化(我们使用的是本地配置方案,方便直接将配置文件更新到linux上), 业务微服务通过配置从服务端配置中心获取相关配置,如果配置文件变动了,通过刷新业务微服务的方式,将最新的配置信息获取。 spring cloud Bus通过一个轻量级消息代理连接分布式系统的节点。这可以用于广播状态更改(如配置更改)或其他管理指令。 接下来,我们就来实施通过spring cloud Bus方案,动态刷新服务端配置,具体步骤如下: 业务微服务配置(以honghu-member-servcie会员服务为例): pom文件配置: <span style="font-size: 16px;"> <dependency> <groupId>org.springframework.boot</groupId> <artifactId><span style="font-size: 16px;">spring-boot-starter-actuator</span></artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId><span style="font-size: 16px;">spring-cloud-starter-bus-amqp</span></artifactId> </dependency></span> yml文件配置: <span style="font-size: 16px;">server: port: 5012 spring: application: name: honghu-member-client profiles: active: dev,discoveryClient cloud: config: discovery: enabled: true service-id: commonservice-config-server <span style="color: #ff0000;"><strong>name: honghu-member profile: dev bus: trace: enabled: true #开启消息跟踪 </strong> <strong>rabbitmq: host: 192.168.1.254 port: 5672 username: honghu password: honghu</strong> </span> eureka: client: serviceUrl: defaultZone: http://honghu:123456@localhost:8761/eureka/ instance: prefer-ip-address: true logging: level: root: INFO org.springframework.security: INFO management: security: enabled: false security: basic: enabled: false</span> 查看注册中心,commonservice-config、honghu-member-service服务是否已经注册成功 访问一下profile,获取profile对应的配置信息(原配置): 访问http://localhost:7071/profile ==》 访问结果:123456 修改config配置中心的配置文件,将profile=123456修改为honghu123456 再次访问http://localhost:7071/profile ==》 访问结果:123456 使用spring cloud bus 刷新方案(使用post man测试工具进行测试) http://localhost:7071/bus/refresh 再次访问http://localhost:7071/profile ==》 访问结果:honghu123456 到此,整个commonservice-config配置中心动态刷新方案整理完毕! 欢迎大家和我一起学习spring cloud构建微服务云架构,我这边会将近期研发的spring cloud微服务云架构的搭建过程和精髓记录下来,帮助更多有兴趣研发spring cloud框架的朋友,大家来一起探讨spring cloud架构的搭建过程及如何运用于企业项目。 完整项目的源码来源 技术支持1791743380

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册