关于javaymlif的信息

博主:adminadmin 2022-12-28 19:27:08 862

今天给各位分享javaymlif的知识,其中也会对进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

ElasticSearch存储

使用ElasticSearch快一年了,自认为相关API使用的还比较6,产品提的一些搜索需求实现起来都从心应手;但是前几天同事的一个问题直接将我打回到小白,同事问了句:“ ElasticSearch的索引是怎么存储的?删除文档和更新文档是怎么实现的? ”,当时我就懵逼了,说了句得查一下,好尴尬......

回想起来发现自己从来都没有深入了解过这些细节,于是便觉得非常有必要对ElasticSearch的文档存储做一次深入的了解,知其然不知其所以然对于我们来说是远远不够的,在ElasticSearch中,文档( Document )存储的介质分为内存和硬盘两种:

同时,ElasticSearch进程自身的运行也需要内存空间,必须保证ElasticSearch进程有充足的运行时内存。为了使ElasticSearch引擎达到最佳性能,必须合理分配有限的内存和硬盘资源。

ElasticSearch引擎把文档数据写入到倒排索引( Inverted Index )的数据结构中,倒排索引建立的是分词( Term )和文档( Document )之间的映射关系,在倒排索引中,数据是面向词( Term )而不是面向文档( Document )的。

举个栗子,假设我们有两个文档,每个文档的content域包含如下内容:

1. The quick brown fox jumped over the lazy dog

2. Quick brown foxes leap over lazy dogs in summer

文档和词之间的关系如下图:

字段值被分析之后,存储在倒排索引中,倒排索引存储的是分词( Term )和文档( Doc )之间的关系,简化版的倒排索引如下图:

从图中可以看出,倒排索引有一个词条的列表,每个分词在列表中是唯一的,记录着词条出现的次数,以及包含词条的文档。实际中ElasticSearch引擎创建的倒排索引比这个复杂得多。

段是倒排索引的组成部分,倒排索引是由段( Segment )组成的,段存储在硬盘( Disk )文件中。 索引段不是实时更新的,这意味着,段在写入硬盘之后,就不再被更新。在删除文档时,ElasticSearch引擎把已删除的文档的信息存储在一个单独的文件中,在搜索数据时,ElasticSearch引擎首先从段中执行查询,再从查询结果中过滤被删除的文档 。这意味着,段中存储着被删除的文档,这使得段中含有”正常文档“的密度降低。

多个段可以通过段合并(Segment Merge)操作把“已删除”的文档将从段中物理删除,把未删除的文档合并到一个新段中,新段中没有”已删除文档“,因此,段合并操作能够提高索引的查找速度 ;但是,段合并是IO密集型操作,需要消耗大量的硬盘IO。

在ElasticSearch中,大多数查询都需要从硬盘文件(索引的段数据存储在硬盘文件中)中获取数据;因此, 在全局配置文件elasticsearch.yml 中,把结点的路径(Path)配置为性能较高的硬盘,能够提高查询性能 。默认情况下,ElasticSearch使用基于安装目录的相对路径来配置结点的路径,安装目录由属性path.home显示,在home path下,ElasticSearch自动创建config,data,logs和plugins目录,一般情况下不需要对结点路径单独配置。结点的文件路径配置项:

path.data:设置ElasticSearch结点的索引数据保存的目录,多个数据文件使用逗号隔开。

例如,path.data: /path/to/data1,/path/to/data2 ;

path.work:设置ElasticSearch的临时文件保存的目录;

映射参数index决定ElasticSearch引擎是否对文本字段执行分析操作,也就是说分析操作把文本分割成一个一个的分词,也就是标记流( Token Stream ),把分词编入索引,使分词能够被搜索到:

1) 当index为analyzed时,该字段是分析字段,ElasticSearch引擎对该字段执行分析操作,把文本分割成分词流,存储在倒排索引中,使其支持全文搜索。

2)当index为not_analyzed时,该字段不会被分析,ElasticSearch引擎把原始文本作为单个分词存储在倒排索引中,不支持全文搜索,但是支持词条级别的搜索;也就是说,字段的原始文本不经过分析而存储在倒排索引中,把原始文本编入索引,在搜索的过程中,查询条件必须全部匹配整个原始文本。

3)当index为no时,该字段不会被存储到倒排索引中,不会被搜索到。

字段的原始值是否被存储到倒排索引,是由映射参数store决定的,默认值是false,也就是,原始值不存储到倒排索引中。

映射参数index和store的区别在于:

store用于获取(Retrieve)字段的原始值,不支持查询,可以使用投影参数fields,对stroe属性为true的字段进行过滤,只获取(Retrieve)特定的字段,减少网络负载;

index用于查询(Search)字段,当index为analyzed时,对字段的分词执行全文查询;当index为not_analyzed时,字段的原始值作为一个分词,只能对字段的原始文本执行词条查询;

如果设置字段的index属性为not_analyzed,原始文本将作为单个分词,其最大长度跟UTF8 编码有关,默认的最大长度是32766Bytes(约32KB),如果字段的文本超过该限制,那么ElasticSearch将跳过( Skip )该文档,并在Response中抛出异常消息:

operation[607]: index returned 400 _index: ebrite _type: events _id: 76860 _version: 0 error: Type: illegal_argument_exception Reason: "Document contains at least one immense term in field="event_raw" (whose UTF8 encoding is longer than the max length 32766), all of which were skipped. Please correct the analyzer to not produce such terms. The prefix of the first immense term is: '[112, 114,... 115]...', original message: bytes can be at most 32766 in length; got 35100" CausedBy:Type: max_bytes_length_exceeded_exception Reason: "bytes can be at most 32766 in length; got 35100"

可以在字段中设置ignore_above属性,该属性值指的是字符数量,而不是字节数量;由于一个UTF8字符最多占用3个字节,因此,可以设置

“ignore_above”:10000

这样,超过30000字节之后的字符将会被分析器忽略,单个分词( Term )的最大长度是30000Bytes。

The value for ignore_above is the character count, but Lucene counts bytes. If you use UTF-8 text with many non-ASCII characters, you may want to set the limit to 32766 / 3 = 10922 since UTF-8 characters may occupy at most 3 bytes.

默认情况下,大多数字段被索引之后,能够被搜索到。 倒排索引是由一个有序的词条列表构成的,每一个词条在列表中都是唯一存在的,通过这种数据存储模式,你可以很快查找到包含某一个词条的文档列表 。

但是,排序和聚合操作采用相反的数据访问模式,这两种操作不是查找词条以发现文档,而是查找文档,以发现字段中包含的词条。ElasticSearch使用列式存储实现排序和聚合查询。

文档值( doc_values )是存储在硬盘上的数据结构,在索引时( index time )根据文档的原始值创建,文档值是一个列式存储风格的数据结构,非常适合执行存储和聚合操作,除了字符类型的分析字段之外,其他字段类型都支持文档值存储。默认情况下,字段的文档值存储是启用的,除了字符类型的分析字段之外。如果不需要对字段执行排序或聚合操作,可以禁用字段的文档值,以节省硬盘空间。

字符类型的分析字段,不支持文档值(doc_values),但支持fielddata数据结构。fielddata数据结构存储在JVM的堆内存中。相比文档值(数据存储在硬盘上),fielddata字段(数据存储在内存中)的查询性能更高。默认情况下,ElasticSearch引擎在第一次对字段执行聚合或排序查询时(query-time),创建fielddata数据结构;在后续的查询请求中,ElasticSearch引擎使用fielddata数据结构以提高聚合和排序的查询性能。

在ElasticSearch中,倒排索引的各个段( segment )的数据存储在硬盘文件上,从整个倒排索引的段中读取字段数据之后,ElasticSearch引擎首先反转词条和文档之间的关系,创建文档和词条之间的关系,即创建顺排索引,然后把顺排索引存储在JVM的堆内存中。把倒排索引加载到fielddata结构是一个非常消耗硬盘IO资源的过程。因此,数据一旦被加载到内存,最好保持在内存中,直到索引段( segment )的生命周期结束。

默认情况下,倒排索引的每个段( segment ),都会创建相应的fielddata结构,以存储字符类型的分析字段值,但是,需要注意的是,分配的JVM堆内存是有限的,Fileddata把数据存储在内存中,会占用过多的JVM堆内存,甚至耗尽JVM赖以正常运行的内存空间,反而会降低ElasticSearch引擎的查询性能。

fielddata会消耗大量的JVM内存,因此,尽量为JVM设置大的内存,不要为不必要的字段启用fielddata存储。通过format参数控制是否启用字段的fielddata特性,字符类型的分析字段,fielddata的默认值是paged_bytes,这就意味着,默认情况下,字符类型的分析字段启用fielddata存储。一旦禁用fielddata存储,那么字符类型的分析字段将不再支持排序和聚合查询。

loading属性控制fielddata加载到内存的时机,可能的值是lazy,eager和eager_global_ordinals,默认值是lazy。

lazy:fielddata只在需要时加载到内存,默认情况下,在第一次搜索时,fielddata被加载到内存中;但是,如果查询一个非常大的索引段(Segment),lazy加载方式会产生较大的时间延迟。

eager:在倒排索引的段可用之前,其数据就被加载到内存,eager加载方式能够减少查询的时间延迟,但是,有些数据可能非常冷,以至于没有请求来查询这些数据,但是冷数据依然被加载到内存中,占用紧缺的内存资源。

eager_global_ordinals:按照global ordinals积极把fielddata加载到内存。

ElasticSearch使用 JAVA_OPTS 环境变量( Environment Variable )启动JVM进程,在 JAVA_OPTS 中,最重要的配置是: -Xmx参数控制分配给JVM进程的最大内存,-Xms参数控制分配给JVM进程的最小内存 。(在ElasticSearch启动命令后面可以通过参数方式配置,如: /usr/local/elasticsearch/bin/elasticsearch -d --Xmx=10g --Xms=10g )通常情况下,使用默认的配置就能满足工程需要。

ES_HEAP_SIZE 环境变量控制分配给JVM进程的堆内存( Heap Memory )大小,顺排索引( fielddata )的数据存储在堆内存( Heap Memory )中。

大多数应用程序尝试使用尽可能多的内存,并尽可能把未使用的内存换出,但是,内存换出会影响ElasticSearch引擎的查询性能,推荐启用内存锁定,禁用ElasticSearch内存的换进换出。

在全局配置文档 elasticsearch.yml 中,设置 bootstrap.memory_lock 为ture,这将锁定ElasticSearch进程的内存地址空间,阻止ElasticSearch内存被OS换出( Swap out )。

通过学习,算是对ElasticSearch索引存储及更新有了一个较深的了解,至少能让我从容去面对同事的提问,但与此同时给我敲响了警钟,在使用一门技术的同时,更应该去了解它具体的原理,而不仅仅是停留在使用级别;在学习的路上,我们仍需要更加努力......

java选的jdk11为什么变成了17

Java11升级Java17备忘录

下塘烧饼

白头不厌穷编码,只影孤灯两卷书。

来自专栏一只老程序猿

一、概述

Java17是目前Java最新的LTS版本,SpringBoot从2.5.5开始正式支持Java17,并且计划从3.0版本开始,Java版本要求最低是Java17。

为了顺应Java及其生态的发展,最近对一套JavaWeb开发框架做了版本升级,主要是Java版本和Springboot版本的升级,包括:

Java版本从openJDK11升级到openJDK17

springboot版本从2.1.11升级到2.7.4

本次升级相比从Java8升级到Java11要简单很多,基本没遇到什么问题。

Java8到Java11之间有Java9这个变化很大的拦路虎,包括但不限于:移除了一些以前集成在jdk的lib中的依赖包,引入模块化导致某些内部API不可用,类加载机制变化导致一些第三方依赖包版本不兼容,等等。

而从Java11到Java17,中间并没有Java9那样巨大的变化,只有Java16和Java17中有一些增强Java内部封装的新特性,可能会导致底层类库依赖包的老版本不能兼容Java17。

关于Java8升级Java11的工作,可以参考我以前的文章:

java - Java8升级Java11备忘录_个人文章 - SegmentFault 思否

另外,本篇文章主要讲如何从Java11升级到Java17,以及升级过程中遇到的一些问题。如果想看Java11到Java17有哪些新特性,可以参考我以前的另一片文章:

下塘烧饼:java17相对java11的新特性

二、升级工作内容

升级工作内容大致如下:

2.1 安装openJDK17及其对应的IDEA

这里选择的是eclipse的Adoptium社区版本:

OpenJDK17U-jdk_x64_linux_hotspot_17.0.3_7.tar.gz

下载地址:

更多版本与下载地址请参考文章:

下塘烧饼:java17相对java11的新特性

安装很简单,解压缩到指定目录即可。

只是开发的话,JAVA_HOME与PATH等环境变量不是一定要设置的,比如我这里的环境有多个JDK版本,只要在IDE中添加新的JDK即可。

IDEA的话,使用2021.2.4以上版本即可支持Java17,在其sdk中加入刚刚安装好的JDK目录:

用IDEA任意打开一个java工程,在其Project Structrue - Platform settings - SDKs中添加JDK17目录。

2.2 配置本地Maven

在本地Maven的配置文件中添加新的JDK17的profile,比如我这里的配置文件是/opt/apache-maven-3.5.0/conf/settings.xml,打开并在其中添加:

profiles ... profile idopenJDK17/id activation jdk17/jdk /activation properties JAVA_HOME/usr/java/jdk-17.0.3+7//JAVA_HOME JAVA_VERSION17/JAVA_VERSION maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target maven.compiler.compilerVersion17/maven.compiler.compilerVersion /properties repositories repository idXXX-Repository/id nameXXX Maven Repository/name url;/url snapshots enabledtrue/enabled /snapshots /repository /repositories pluginRepositories pluginRepository idXXX-Repository/id nameXXX Maven Repository/name url;/url snapshots enabledtrue/enabled /snapshots /pluginRepository /pluginRepositories /profile /profiles

JAVA_HOME是本地JDK安装目录。

repository与pluginRepository用来配置maven仓库地址,之后在IDE中启用这里配置的profile。这样maven拉取jar包时,会优先从这里配置的maven仓库拉取,拉取不到时再去中央仓库拉。

配置好settings.xml后,在各个java工程中启用新的profile:

首先在File - Settings - Maven中确定maven及其配置文件目录:

然后在IDEA的maven插件中选择jdk17的profile:

2.3 修改父工程的pom版本控制

升级对象是一套JavaWeb开发框架,有自己的父工程来控制依赖包的版本,在决定升级Java版本与Springboot版本后,父工程的pom文件中的相关依赖包版本需要更新。

首先是父工程自己的版本需要升级,这样仍然依赖老版本父工程的java工程就不会升级相关版本,只有依赖了新版本父工程的java工程才会升级相关版本。

artifactIdparent-xxx/artifactId version2.0.0/version

这里假定老版本是1.x.x,新版本是2.0.0。

实际上父工程的pom是在升级过程中不断修改的,为了不影响使用该父工程的项目开发,你需要与相关开发人员约定好在升级完成后再尝试使用新版本的父工程。

然后修改基本属性与spring相关依赖包的版本,篇幅原因这里只给出版本发生变化的依赖包的修改后版本号,dependency配置略过:

properties !-- 基本属性 -- project.build.sourceEncodingUTF-8/project.build.sourceEncoding project.reporting.outputEncodingUTF-8/project.reporting.outputEncoding java.version17/java.version !-- spring相关版本 -- spring-boot.version2.7.4/spring-boot.version spring-boot-admin.version2.7.4/spring-boot-admin.version spring-cloud.version2021.0.4/spring-cloud.version spring-cloud-alibaba.version2021.0.1.0/spring-cloud-alibaba.version mybatis-spring-boot-starter.version2.2.2/mybatis-spring-boot-starter.version pagehelper-spring-boot-starter.version1.4.5/pagehelper-spring-boot-starter.version !-- 插件版本 -- spring-boot-maven-plugin.version2.7.4/spring-boot-maven-plugin.version mybatis-generator-maven-plugin.version1.4.1/mybatis-generator-maven-plugin.version !-- 其他依赖包版本 -- lombok.version1.18.24/lombok.version /properties

升级后的测试并不充分,这里列出的发生版本变化的依赖包可能并不全面。

如果在编译或运行时发现有某个依赖包报错,说某个jdk的module因为没有导出而无法访问的错误: cannot access class xxx (in module jdk.xxx) because module jdk.xxx does not export xxx to xxx,那么就基本可以确定这个依赖包版本较老不兼容Java17。

解决方法很简单,在当前这个时间点,基本上所有的第三方依赖包都已经有兼容Java17的较新的版本,直接去maven中央仓库找一个新版本下载,就能解决问题。

比如上面的lombok版本升级到了1.18.24,就是为了解决编译时发生的上述不兼容的错误。

另外,如果父工程中还约定了很多自用的通用工程的版本,那么这里需要确保这些通用工程的版本范围在新老版本中的定义没有冲突。

例如老版本的父工程中定义了一些通用工程的版本:

lib-xxx.version[1.0.0-RELEASE,2.0.0-RELEASE)/lib-xxx.version

这里的lib-xxx是这套JavaWeb开发框架中的一个通用库,在老版本的父工程中约定它的版本范围是[1.0.0-RELEASE,2.0.0-RELEASE),即从1.0.0-RELEASE(包含)到2.0.0-RELEASE(不包含)。那么新版本的父工程,对它的版本范围约定就是[2.0.0-RELEASE,3.0.0-RELEASE)。

这样约定的目的是,如果有一些java工程仍然要使用老版本的父工程(假定由于种种原因,只能用Java11与Springboot2.1.x 。。。),那么它就不会依赖2.0.0-RELEASE及其以上版本的lib-xxx;而一旦依赖了新版本的父工程,就只会依赖2.0.0-RELEASE及其以上版本的lib-xxx。

最后要注意,如果在老版本(这里就是Java11和Springboot2.1.11)上还有新的应用或需求变更要开发,那么需要在代码管理库中切出一个新的分支来做升级的工作,并约定好各自的版本范围,比如采用不同的主版本号。

2.4 单个Java工程的版本升级

在前面的2.1到2.3准备工作完成之后,就可以对java应用工程做版本升级了。

首先打开一个java工程,确认maven的目录与配置文件是否正确,并将maven插件的profile选择到jdk17,这一步已在步骤2.2中示意。

然后修改工程依赖的父工程版本为2.3中修改后的父工程版本。

parent groupIdxxx/groupId artifactIdparent-xxx/artifactId version2.0.0/version /parent

然后先使用maven插件刷新pom依赖,顺利的话,可以在maven插件中看到新的依赖包版本:

pom依赖刷新之后,再来修改工程的idea配置中的java版本,打开Project Structrue,依次修改或确认java版本:

然后我们就可以对工程进行编译,检查有没有编译错误或警告。

考虑到devops的需要,你可能需要一个maven编译脚本,要注意java版本,如下所示:

#!/bin/bash export JAVA_HOME=/usr/java/jdk-17.0.3+7 mvn -version mvn clean install package

注意这里指定了JAVA_HOME,maven编译时会用到这个环境变量,因此指定为JDK17的安装目录。本地环境安装有多个JDK版本时要特别注意这一点。

如果是springboot工程,编译成功之后就可以启动服务:

#!/bin/bash JAVA_HOME=/usr/java/jdk-17.0.3+7 JAR_PATH=$(find target -name "*.jar") ${JAVA_HOME}/bin/java -jar "${JAR_PATH}"

2.5 编译与运行时遇到的问题

在编译工程以及启动springboot服务时,可能会遇到以下问题。

2.5.1 JDK模块内API未导出问题

前面说过,Java16和Java17中有一些增强Java内部封装的新特性,该特性加强了对一些以前暴露出来但其实很不安全的关键API的封装,即你不再能从外部访问这些内部API。而java的生态圈中有很多底层的类库比如lombok在以前的版本中会调用到这些内部API。那么在Java17以后将不再能调用它们,所以会有不兼容的问题。

这种问题的典型错误信息:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project api-brood-base: Fatal error compiling: java.lang.IllegalAccessError: class lombok.javac.apt.LombokProcessor (in unnamed module @0x5740ff5e) cannot access class com.sun.tools.javac.processing.JavacProcessingEnvironment (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.processing to unnamed module @0x5740ff5e - [Help 1]

这里的关键信息就是cannot access class xxx (in module jdk.xxx) because module jdk.xxx does not export xxx to xxx,一旦看到这句错误信息,就知道这是由于JDK加强了内部API的封装所导致的不兼容问题。

解决起来很简单,找个新版本就行了。以lombok为例,升级到版本1.18.24即可。

其他类库的包也有可能出现类似的问题,解决方法一样,换用更新的兼容Java17的版本即可。

2.5.2 redisTemplate版本不兼容

如果使用了spring的redisTemplate,那么有可能出现版本不兼容,包括:

redisTemplate.delete方法编译错误,方法参数的泛型发生了变化。

// 版本升级前编译OK,升级后编译错误 redisTemplate.delete(CollectionUtils.arrayToList(key)); // 版本升级后修改如下 redisTemplate.delete(Arrays.asList(key));

GenericObjectPoolConfig的setMaxWaitMillis被废弃不再推荐使用,用setMaxWait代替:

GenericObjectPoolConfig? genericObjectPoolConfig = new GenericObjectPoolConfig(); ... // genericObjectPoolConfig.setMaxWaitMillis(redisProps.getPool().getMaxWait()); genericObjectPoolConfig.setMaxWait(Duration.ofMillis(redisProps.getPool().getMaxWait()));

2.5.3 jackson版本不兼容

spring默认使用的json工具类库jackson,它的ObjectMapper的enableDefaultTyping被废弃不再推荐使用,使用activateDefaultTyping代替

具体代码如下所示:

ObjectMapper om = new ObjectMapper(); // om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);

2.5.4 循环依赖问题

springboot的新版本默认不再支持bean的循环依赖,因此项目中有循环依赖的bean的话,会报错,例如:

The dependencies of some of the beans in the application context form a cycle: xxxxxxxxx ┌─────┐ | xxxService1 (field private com.gcsoft.brood.sentry.service.XxxService2 com.gcsoft.brood.sentry.service.xxxService1.xxxService2) ↑ ↓ | xxxService2 (field private com.gcsoft.brood.sentry.service.XxxService1 com.gcsoft.brood.sentry.service.XxxService2.xxxService1) └─────┘ Action: Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

最好的对应方式是消去bean之间的循环依赖,否则就需要显式声明允许bean的循环依赖,在application.yml中加入属性:

spring: main: allow-circular-references: true

2.5.5 缺少spring.config.import配置

如果在pom工程中依赖了springcloud的相关jar,但并没有使用springcloud相关的config配置,那么在启动springboot服务时可能会失败:

No spring.config.import property has been defined Action: Add a spring.config.import=configserver: property to your configuration. If configuration is not required add spring.config.import=optional:configserver: instead. To disable this check, set spring.cloud.config.enabled=false or spring.cloud.config.import-check.enabled=false.

按照提示,在application.yml中加入属性:

spring: cloud: config: enabled: false

2.5.6 jetty版本冲突

springboot版本升级后,内嵌的jetty版本也升级了。由于某些第三方jar使用了低版本的jetty的某些包,即使springboot没有使用jetty,也依然会在运行时发生jetty的不兼容问题:

java.lang.ClassNotFoundException: org.eclipse.jetty.server.RequestLog$Writer

这里可以选择升级第三方jar。

或者直接去除第三方jar对jetty的依赖:

dependency groupIdorg.apache.hive/groupId artifactIdhive-jdbc/artifactId version${hive.version}-${cdh.version}/version exclusions exclusion artifactIdjetty-all/artifactId groupIdorg.eclipse.jetty.aggregate/groupId /exclusion /exclusions /dependency

2.6 docker镜像

在各个工程完成升级,编译成功,并简单运行OK之后,开始做docker镜像的升级工作。

毕竟现在都在云端跑服务了。。。

这里从Docker Hub上找了与开发使用的openJDK版本一致的docker镜像,也是由eclipse的Adoptium社区提供的。

其实没有必要,其他openJDK17版本的docker镜像也是一样的,单纯的强迫症而已。。。

docker pull eclipse-temurin:17.0.3_7-jdk-alpine

对应docker hub地址:

Docker Hub

在这个openJDK17镜像的基础上,修改了时区与语言等信息,安装了bash与telnet,DockerFile如下:

# 指定基础镜像,在其上进行定制(这里是 eclipse-temurin:17.0.3_7-jdk-alpine 的镜像) FROM eclipse-temurin:17.0.3_7-jdk-alpine # 定制环境变量 ENV TIME_ZONE=Asia/Shanghai \ LANG=en_US.UTF-8 \ LANGUAGE=en_US.UTF-8 \ LC_ALL=en_US.UTF-8 # RUN在build镜像时执行,每RUN一次就会构成一层新的镜像。 # 因此有多个命令要执行时,用""连接写在一起。 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories \ apk add --no-cache tzdata \ echo "${TIME_ZONE}" /etc/timezone \ ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime \ apk add --no-cache bash bash-doc bash-completion busybox-extras

原始的eclipse-temurin:17.0.3_7-jdk-alpine有335M,添加了tzdata,bash,busybox-extras(telnet)之后,大小是340.8M。。。完整的JDK镜像就是这么大。。。如果生产环境不需要JDK,那么可以用JRE作成的镜像,会小不少,但是缺失了很多JDK工具。

用这个DockerFile做成一个新的openJDK17镜像,命名为xxx/base-openjdk17:jdk-17.0.3_001,而各个springboot工程的DockerFile如下所示:

# 指定基础镜像 FROM xxx/base-openjdk17:jdk-17.0.3_001 # JDK11开始支持: -XX:+UseContainerSupport 使JVM能够感知容器资源, -XX:InitialRAMPercentage 初期容器内存占比, -XX:MaxRAMPercentage 最大容器内存占比 ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:InitialRAMPercentage=50 -XX:MaxRAMPercentage=80" # 复制上下文目录下的target/*.jar 到容器里 ADD target/*.jar app.jar # 指定容器启动程序及参数 ENTRYPOINT "CMD" ENTRYPOINT java ${JAVA_OPTS} -jar /app.jar

该DockerFile位于springboot工程根目录下。

打进了springboot fat jar的镜像会变得更大,一般都会有400M以上。。。

三、小结

总的来说,Java11到Java17的升级比较顺利,只有少数依赖包对应版本需要升级。另外就是springboot的升级可能导致需要添加少量配置,比如显式允许bean的循环依赖

配置Elasticsearch

Elasticsearch船只具有良好的默认值,并且只需要很少的配置。可以在运行的集群上使用集群更新设置API更改大多数设置。

配置文件应该包含特定于节点的设置(例如node.name和路径),或者节点为了能够加入集群而需要的设置,例如 cluster.name 和 network.host 。

Elasticsearch有三个配置文件:

这些文件位于config目录中,其默认位置取决于安装是来自存档分发版(tar.gz或zip)还是包分发版(Debian或RPM包)。

对于存档发行版,config目录位置默认为 $ES_HOME/config 。配置目录的位置可以通过 ES_PATH_CONF 环境变量改变,如下所示:

或者,您可以通过命令行或shell配置文件导出ES_PATH_CONF环境变量。

对于包分发,配置目录位置默认为 /etc/elasticsearch 。配置目录的位置也可以通过 ES_PATH_CONF 环境变量更改,但是请注意,仅在shell中设置是不够的。相反,这个变量来源于 /etc/default/elasticsearch (用于Debian包)和 /etc/sysconfig/elasticsearch (用于RPM包)。您需要相应地在其中一个文件中编辑 ES_PATH_CONF=/etc/elasticsearch 条目,以更改配置目录的位置。

配置格式为YAML。下面是更改数据和日志目录路径的示例:

设置也可以扁平化如下:

在YAML中,你可以将非标量值格式化为序列:

虽然不太常见,但你也可以将非标量值格式化为数组:

使用${…}符号将被替换为环境变量的值。例如:

环境变量的值必须是简单字符串。使用逗号分隔的字符串来提供Elasticsearch将解析为列表的值。例如,Elasticsearch将以下字符串分割为 ${HOSTNAME} 环境变量的值列表

集群和节点设置可以根据配置方式进行分类:

您可以使用 集群更新设置API 在运行的集群上配置和更新动态设置。您还可以使用 elasticsearch.yml 在未启动或关闭的节点上本地配置动态设置。

使用集群更新设置API进行的更新可以是持久的(跨集群重启应用),也可以是短暂的(在集群重启后重置)。您还可以通过使用API为临时或持久设置赋值为空来重置它们。

如果您使用多个方法配置相同的设置,Elasticsearch将按照以下优先顺序应用这些设置:

例如,您可以应用瞬变设置来覆盖持久设置或 elasticsearch.yml 设置。然而,对 elasticsearch.yml 的更改,不会覆盖已定义的瞬态或持久设置。

最好使用集群更新设置API设置动态的集群范围设置,并使用 elasticsearch.yml 仅用于本地配置。使用集群更新设置API可以确保所有节点上的设置是相同的。如果您不小心在 elasticsearch.yml 中配置了不同的设置。在不同的节点上,很难注意到差异。

静态设置只能在未启动或关闭的节点上使用 elasticsearch.yml 进行配置。

必须在集群中的每个相关节点上设置静态设置

Elasticsearch开始时只需要很少的配置,但是在生产环境中使用集群之前,有很多方面需要考虑:

Elasticsearch将创建索引的数据写入索引,将数据流写入数据目录。Elasticsearch将自己的应用程序日志(其中包含关于集群运行状况和操作的信息)写入日志目录

对于macOS .tar.gz、Linux .tar.gz和Windows .zip安装,数据和日志默认是 $ES_HOME 的子目录。但是,在升级过程中, $ES_HOME 中的文件有被删除的风险

In production, we strongly recommend you set the path.data and path.logs in elasticsearch.yml to locations outside of $ES_HOME . Docker , Debian , RPM , macOS Homebrew , and Windows .msi installations write data and log to locations outside of $ES_HOME by default.

To avoid errors, only Elasticsearch should open files in the path.data directory. Exclude the path.data directory from other services that may open and lock its files, such as antivirus or backup programs.

Supported path.data and path.logs values vary by platform

只有当一个节点与集群中的所有其他节点共享 cluster.name 时,该节点才能加入集群。默认名称是 elasticsearch ,但是您应该将其更改为描述集群用途的适当名称。

不要在不同的环境中重用相同的集群名称。否则,节点可能会加入错误的集群

Elasticsearch使用 node.name 作为Elasticsearch特定实例的人类可读标识符。这个名称包含在许多api的响应中。当Elasticsearch启动时,节点名默认为机器的主机名,但是可以在 elasticsearch.yml 中显式配置

缺省情况下,Elasticsearch只绑定到 127.0.0.1 和 [::1] 等环回地址。这对于在单个服务器上运行一个或多个节点的集群进行开发和测试已经足够了,但是 弹性生产集群 必须包含其他服务器上的节点。有许多 网络设置 ,但通常你只需要配置 network.host :

当你为 network.host 提供值时。Elasticsearch假定您正在从开发模式转向生产模式,并将一些系统启动检查从警告升级到异常。看看 开发和生产模式 之间的区别。

在投入生产之前,配置两个重要的发现和集群形成设置,以便集群中的节点能够相互发现并选择一个主节点。

Elasticsearch可以开箱即用,无需任何网络配置,它将绑定到可用的环回地址,并扫描本地端口 9300 到 9305 ,以便与运行在同一服务器上的其他节点连接。这种行为提供了一种无需进行任何配置的自动集群体验。

当您希望与其他主机上的节点形成集群时,使用 静态 discovery.seed_hosts 设置. This setting provides a list of other nodes in the cluster that are master-eligible and likely to be live and contactable to seed the discovery process .

此设置接受集群中所有符合主节点的地址的YAML序列或数组。每个地址可以是一个IP地址,也可以是通过DNS解析为一个或多个IP地址的主机名。

当您第一次启动Elasticsearch集群时, 集群引导 步骤将确定在第一次选举中计票的符合主资格的节点集。在 开发模式 下,如果没有配置发现设置,这个步骤将由节点自己自动执行。

因为自动引导 本身就不安全 ,,所以在生产模式下启动新集群时,必须显式列出符合主资格的节点,这些节点的投票应该在第一次选举中计算。您可以使用集群设置此列表。 initial_master_nodes 设置。

在集群第一次成功形成之后,删除每个节点配置中的 Initial_master_nodes 设置。在重新启动集群或向现有集群添加新节点时,不要使用此设置。

通过节点的 node.name 标识初始主节点, 该节点默认为主节点的主机名。请确保 cluster.initial_master_nodes 值 与 node.name 完全匹配如果您使用完全限定的域名(FQDN),例如master-node-a.example.com作为您的节点名,那么您必须在此列表中使用FQDN。相反,如果node.name是没有任何尾随限定符的裸主机名,您也必须省cluster.initial_master_nodes中的尾随限定符如果您使用完全限定的域名(FQDN),例如 master-node-a.example.com 作为您的节点名, 那么您必须在此列表中使用FQDN。相反,如果f node.name 是没有任何尾随限定符的裸主机名,您也必须省略 cluster.initial_master_nodes 中的尾随限定符。

请参见 bootstrapping a cluster 以及 发现和集群形成设置 .

默认情况下,Elasticsearch会根据节点的 角色 和总内存自动设置JVM堆大小。对于大多数生产环境,我们建议使用默认大小。

自动堆大小需要 bundled JDK ,如果使用自定义JRE位置,则需要Java 14或更高版本的JRE。

如果需要,您可以通过手动 设置JVM堆大小 来覆盖默认大小

默认情况下,Elasticsearch将JVM配置为将堆内存溢出异常转储到默认数据目录。在RPM和Debian软件包中,数据目录是/var/lib/elasticsearch。在Linux、MacOS和Windows发行版上,数据目录位于Elasticsearch安装的根目录下。

如果此路径不适合接收堆转储,请修改 -XX:HeapDumpPath=… jvm.options

默认情况下,Elasticsearch启用垃圾收集(GC)日志。这些是在jvm中配置的 jvm.options 并输出到与Elasticsearch日志相同的默认位置。默认配置每64mb轮换一次日志,最多可以消耗2gb的磁盘空间。

您可以使用 JEP 158: Unified JVM Logging 中描述的命令行选项重新配置JVM日志。除非您更改了默认jvm。选项文件,Elasticsearch默认配置将应用于您自己的设置之外。要禁用默认配置,首先通过提供 -Xlog:disable 选项禁用日志记录,然后提供您自己的命令行选项。这将禁用所有JVM日志记录,因此一定要检查可用选项并启用所需的所有内容。

要查看原始JEP中未包含的其他选项,请参见使用 JVM统一日志框架启用日志记录 .

Change the default GC log output location to /opt/my-app/gc.log by creating $ES_HOME/config/jvm.options.d/gc.options with some sample options:

Configure an Elasticsearch Docker container to send GC debug logs to standard error ( stderr ). This lets the container orchestrator handle the output. If using the ES_JAVA_OPTS environment variable, specify:

默认情况下,Elasticsearch使用启动脚本直接在系统临时目录下创建的私有临时目录。

在某些Linux发行版上,如果最近没有访问过/tmp中的文件和目录,系统实用程序将清除它们。如果需要临时目录的特性长时间不使用,那么在Elasticsearch运行时,这种行为会导致私有临时目录被删除。如果随后使用需要此目录的特性,则删除私有临时目录会导致问题。

如果您使用.deb或.rpm包安装Elasticsearch,并在systemd下运行它,那么Elasticsearch使用的私有临时目录将被排除在定期清理之外。

如果您打算在Linux或MacOS上长时间运行.tar.gz发行版,请考虑为Elasticsearch创建一个专用的临时目录,该目录不在将旧文件和目录清除的路径下。这个目录应该设置权限,以便只有作为Elasticsearch运行的用户才能访问它。然后,在启动Elasticsearch之前,设置$ES_TMPDIR环境变量指向这个目录。

默认情况下,Elasticsearch将JVM配置为将致命错误日志写入默认日志目录。对于 RPM 和 Debian 软件包, 这个目录是 /var/log/elasticsearch . On Linux and MacOS and Windows 发行版, logs 目录位于Elasticsearch安装根目录下。

这些日志是JVM遇到致命错误(例如分段错误)时产生的。如果此路径不适合接收日志,请修改 -XX:ErrorFile=... 在 jvm.options 条目。

在灾难中,快照可以防止数据永久丢失。快照生命周期管理是对集群进行定期备份的最简单方法。有关更多信息,请参见备份集群。

在灾难中, 快照 可以防止数据永久丢失. 快照生命周期管理 是对集群进行定期备份的最简单方法. 有关更多信息, 请参见 备份集群 。

备份集群的唯一可靠和受支持的方法是使用快照。您不能通过复制Elasticsearch集群节点的数据目录来备份该集群。不支持从文件系统级备份恢复任何数据的方法。如果试图从这样的备份恢复集群,可能会出现损坏、丢失文件或其他数据不一致的报告,或者看起来已经成功地悄无声息地丢失了一些数据。

有些设置是敏感的,仅依靠文件系统权限来保护它们的值是不够的。对于这个用例,Elasticsearch提供了一个密钥存储库和 elasticsearch -keystore 工具 来管理密钥存储库中的设置。

只有重新启动Elasticsearch后,对keystore的所有修改才会生效。

这些设置就像elasticsearch中的常规设置一样。Yml配置文件,需要在集群中的每个节点上指定。目前,所有安全设置都是特定于节点的设置,在每个节点上必须具有相同的值。

Just like the settings values in elasticsearch.yml , 对密钥存储库内容的更改不会自动应用到运行的Elasticsearch节点。重新读取设置需要重新启动节点。但是,某些安全设置被标记为 可重新加载 。. Such settings can be re-read and applied on a running node .

所有安全设置的值(无论是否可重新加载)必须在所有集群节点上相同。在进行所需的安全设置更改后,使用 bin/elasticsearch-keystore add 命令, call:

keystore-password : 用于加密Elasticsearch密钥库的密码

此API在每个集群节点上解密并重新读取整个密钥存储库,但只应用可重新加载的安全设置。对其他设置的更改直到下次重启才会生效。一旦调用返回,重新加载就完成了,这意味着依赖于这些设置的所有内部数据结构都已更改。所有的设置都应该从一开始就具有新值。

当更改多个可重新加载的安全设置时,在每个集群节点上修改所有安全设置,然后发出 reload_secure_settings 调用,而不是在每次修改后重新加载。

有可重新加载的安全设置:

如何部署Spring Boot应用

在Ubuntu中安装Java8

以下是一个简单的安装方法。

$ sudo add-apt-repository ppa:webupd8team/java

$ sudo apt-get update

$ sudo apt-get install oracle-java8-installer

$ java -version

java version "1.8.0_60"

Java(TM) SE Runtime Environment (build 1.8.0_60-b27)

Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)

2. 本地使用Gradle发布Spring Boot应用

我这里使用Jetty9作为内置的服务器。

// ...

bootRun {

systemProperties = System.properties

}

configurations {

compile.exclude module: "spring-boot-starter-tomcat"

}

dependencies {

// spring boot

compile "org.springframework.boot:spring-boot-starter-web:1.3.0.M5"

compile "org.springframework.boot:spring-boot-starter-jetty"

// ...

}

//...

在本地运行默认使用src/main/resources/application.yml作为配置文件,而在production环境中我们系统它支持提供外部的配置文件application-production.yml。

./gradlew bootRun # 开发环境下默认使用项目里的application.yml

# 在本地测试使用外部配置文件

./gradlew bootRun -Dspring.config.location=/path/to/application-production.yml

# 发布

./gradlew build

# 运行

java -jar build/libs/SpringBlog-0.1.jar  # 默认使用jar包里面的application.yml配置文件

# 使用外部配置文件

java -jar build/libs/SpringBlog-0.1.jar --spring.config.location=/path/to/application-production.yml

3. 在Ubuntu服务器上部署Spring Boot应用

# 上传SpringBlog-0.1.jar到服务器

scp build/libs/SpringBlog-0.1.jar root@your_server_ip:/root/spring-blog/current

# 在服务器上配置生产环境的配置文件

scp application-production.yml root@your_server_ip:/root/spring-blog/current

然后SSH登录服务器,修改配置文件application-production.yml,试运行应用。

ssh root@your_server_ip

cd spring-blog/current

java -jar SpringBlog-0.1.jar --spring.config.location=application-production.yml

# application-production.yml

server:

address: raysmond.com # 使用域名或者IP,启动之后就可以这个域名或IP访问网站了

port: 80

contextPath:

spring:

profiles:

active: production

thymeleaf:

cache: true

jade4j:

caching: true

dataSource:

driverClassName: com.mysql.jdbc.Driver

url: jdbc:mysql://127.0.0.1/spring_blog

username: root

password:

hibernate:

dialect: org.hibernate.dialect.MySQLDialect

hbm2ddl.auto: update

show_sql: false

redis:

host: localhost

port: 6379

4. 如何在Ubuntu中后台运行Spring Boot应用?

推荐使用nohup这个命令。

cd /root/spring-blog/current

nohup java -jar SpringBlog-0.1.jar --spring.config.location=application-production.yml \

../logs/production.log 2 ../logs/production.err

在Ubuntu还可以/etc/init.d目录下新建一个脚本,把SpringBlog作为service来运行,这样不用每次都打这么繁琐的命令了。新建一个/etc/init.d/spring_blog文件,内容如下:

#!/bin/sh

SERVICE_NAME=spring_blog

HOME=/root/spring-blog

PATH_TO_JAR=$HOME/current/SpringBlog-0.1.jar

PID_PATH_NAME=/tmp/spring_blog.pid

LOG=$HOME/logs/production.log

ERROR_LOG=$HOME/logs/production.err

CONFIG=$HOME/application-production.yml

case $1 in

start)

echo "Starting $SERVICE_NAME ..."

if [ ! -f $PID_PATH_NAME ]; then

cd $HOME/current

nohup java -jar $PATH_TO_JAR --spring.config.location=application-production.yml $LOG 2 $ERROR_LOG

echo $! $PID_PATH_NAME

echo "$SERVICE_NAME started ..."

else

echo "$SERVICE_NAME is already running ..."

fi

;;

stop)

if [ -f $PID_PATH_NAME ]; then

PID=$(cat $PID_PATH_NAME);

echo "$SERVICE_NAME stoping ..."

kill $PID;

echo "$SERVICE_NAME stopped ..."

rm $PID_PATH_NAME

else

echo "$SERVICE_NAME is not running ..."

fi

;;

restart)

if [ -f $PID_PATH_NAME ]; then

PID=$(cat $PID_PATH_NAME);

echo "$SERVICE_NAME stopping ...";

kill $PID;

echo "$SERVICE_NAME stopped ...";

rm $PID_PATH_NAME

echo "$SERVICE_NAME starting ..."

cd $HOME/current

nohup java -jar $PATH_TO_JAR --spring.config.location=application-production.yml $LOG 2 $ERROR_LOG

echo $! $PID_PATH_NAME

echo "$SERVICE_NAME started ..."

else

echo "$SERVICE_NAME is not running ..."

fi

;;

esac

现在就可以使用service的方式来运行网站了。

sudo service spring_blog start

sudo service spring_blog stop

sudo service spring_blog restart

5. 在本地自动化部署网站到远程服务器

在本地我用了一个shell脚本和一个python脚本来配合自动化部署。

deploy.sh 使用gradle的命令发布jar包,使用scp命令吧jar包上传到服务器上;

deploy.py 使用SSH远程登录服务器,并在服务器上执行部署命令。

# deploy.sh

#!/bin/bash

SERVER="your_server_ip"

JAR="build/libs/SpringBlog-0.1.jar"

echo "Building $JAR..."

./gradlew build

echo "Upload $JAR to server $SERVER..."

scp $JAR root@$SERVER:/root/spring-blog/

python deploy.py

deploy.py主要使用了一个paramiko库,用于SSH远程登录服务器,并执行命令。这个脚本会把服务器上/root/spring-blog/current/SpringBlog-0.1.jar备份到/root/spring-blog/releases中,并把新发布的jar包放到/root/spring-blog/current中,然后重启spring_blog服务。

全选复制放进笔记#!/usr/bin/python

import paramiko

import threading

import time

ip = 'your_server_ip'

user = 'root'

password = ''

jar = 'SpringBlog-0.1.jar'

home='/root/spring-blog'

current=home+"/current"

releases=home+"/releases"

def execute_cmds(ip, user, passwd, cmd):

try:

ssh = paramiko.SSHClient()

ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

ssh.connect(ip,22,user,passwd,timeout=5)

for m in cmd:

print m

stdin, stdout, stderr = ssh.exec_command(m)

#           stdin.write("Y")

out = stdout.readlines()

for o in out:

print o,

print '%s\tOK\n'%(ip)

ssh.close()

except :

print '%s\tError\n'%(ip)

if __name__=='__main__':

print 'Start deploying %s to server %s'%(jar, ip)

now = time.strftime("%Y%m%d%H%M%S")

cmd = [

'echo Stop spring_blog service... service spring_blog stop',

'echo Flush all redis cache data... redis-cli -r 1 flushall',

'echo Stop redis server... service redis_6379 stop',

'echo Use new jar... ' + \

' mv ' + current + '/' + jar + ' ' + releases + '/' + now + '_' + jar ,

'mv ' + home + '/' + jar + ' ' + current + '/' + jar,

'echo Stop redis... service redis_6379 start',

'echo Start spring_blog service... service spring_blog start ' + \

' echo All done.'

]

a=threading.Thread(target=execute_cmds, args=(ip,user,password,cmd))

a.start()

配置完以后,在本地写完代码就可以运行./deploy.sh一键部署到远程服务器了。

magic-api 快速接口开发

magic-api 是一个基于Java的接口快速开发框架,编写接口将通过magic-api提供的UI界面完成,自动映射为HTTP接口。

1,引入magic-api-spring-boot-starter依赖

org.ssssssss

magic-api-spring-boot-starter

1.7.1

2,application.yml 中配置

magic-api:

#配置web页面入口

web: /magic/web

resource:

# location: /data/magic-api

type: database # 配置接口存储方式,这里选择存在数据库中

table-name: magic_api_file # 数据库中的表名

3,启动服务,访问magic-api web页面

1,RequestParam

GET ;age=49

这样的URL参数magic-api 会自动将name和age映射为同名变量

2,表单参数

POST

name=abcage=49

这样的表单参数magic-api 也会自动将name和age映射为同名变量。

3,Request Header参数获取

magic-api 会对所有RequestHeader统一封装为一个名为header的变量 如要获取 token 可以通过header.token 来获取。

4,POST请求的Request Body参数获取

{

"name": "magic-api"

}

如要获取name属性 则可通过 body.name 来获取

5,Path参数获取

主要是针对URL定义为{id} 的类似接口

如要获取path路径上的id可通过path.id 或 id来获取

6,Cookie,Session参数获取

可以通过cookie.xxx,session.xxx来获取

1,#{} 注入参数,${} 拼接参数

作用和mybatis用法一致

id = #{id};

id=${id};

2,动态SQL参数

通过?{condition,expression}来实现动态拼接SQL,如果条件成立则拼接后部分内容SQL中,与mybatis中的if标签基本一致

return db.select("select * from sys_user ?{id,where id = #{id}}");

相当于mybatis中的

3,Mybatis 语法支持

1.6.0以后的版本支持Mybatis语法

操作入口 db.table('table_name')

1,insert

return db.table('sys_user').insert({ user_name : '李富贵', role : 'admin'})

// insert into sys_user(user_name,role) values('李富贵','admin')

2,update

return db.table('base_dict').primary('code').update({ code: 'insertTerst', name : '测试insert'})

//update base_dict set name = ? where code = ?

3,save

用法和insert相似

return db.table('sys_user').primary('id', uuid()).save({user_name: '李富贵'});

// insert into sys_user(id,user_name) values('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx','李富贵');

4,select,page,where

return db.table('sys_user').select()

return db.table('sys_user').page()

return db.table('sys_user')

.where()

.like('user_name','%李富贵%')

.eq('role','admin')

.select()

yml中配置分页参数

magic-api:

page-config:

size: size # 页大小的请求参数名称

page: page # 页码的请求参数名称

default-page: 1 # 未传页码时的默认首页

default-size: 10 # 未传页大小时的默认页大小

自动分页

使用yml中配置的分页参数

return db.page("""select * from base_dict_detail""")

手动分页

跳过前3条记录,然后取5条

return db.page("""select * from base_dict_detail""",5,3)

自定义分页参数获取

实现 PageProvider接口,复写getPage方法 {

public Page getPage(RuntimeContext runtimeContext) {

long page = 2;

long pageSize = 3;

return new Page(pageSize, (page - 1) * pageSize);

}

此模式会覆盖yml的配置内容

目前内置了三种状态码,分别为 执行成功(1),参数验证失败(0),以及系统异常(-1)

自定义状态码

magic-api:

response-code-config:

success: 200 #执行成功的code值

invalid: 400 #参数验证未通过的code值

exception: 500 #执行出现异常的code值

默认返回格式

{

"code": 1, // 状态码

"message": "success", // 状态说明

"data": ..., // 返回的数据内容

"timestamp": 1629610503506, // 服务器时间

"executeTime": 1 // 执行时间

}

自定义返回格式

magic-api:

response: |- #配置JSON格式,格式为magic-script中的表达式

{

code: code,

message: message,

data,

timestamp,

requestTime,

executeTime,

}

自定义结构配置

实现ResultProvider接口,重写buildResult方法

引入swagger依赖

在yml文件中配置

magic-api:

swagger-config:

version: 1.0.0

description: magic测试文档

title: magic测试

name: 配置化实现

location: /v2/api-docs/magic-api/swagger2.json

Magic-api通过springboot自动配置的方式配置了resource,dataSource,interceptor等内容。

在服务启动时,生成MagicConfiguration注入容器时,通过mappingHandlerMapping.registerAllMapping();来注册所有映射(即在界面上配置的接口请求地址和接口的实际处理类、方法的映射)。映射关系注册到handleMapping中,并在内存中通过ConcurrentHashMap来缓存映射关系

接口调用时,在DispatcherServlet中通过url去寻找handler,找到magic-api的统一处理RequestHandler以及处理方法invoke。

在invoke中根据请求方法和路径获取接口信息封装在ApiInfo中,然后进行参数的验证封装。实际脚本的执行,以及对返回结果的包装

如何修改mssql数据库编码

sql server 2000的unicode编码有特殊性,仅仅在rails中使用utf8编码,和把全部rails项目文件格式改成utf8之外,还是不够的。仅仅这样做,只是部分中文字符能够正确处理,而且存入sql server2000中的中文数据,也完全是乱码。正确的配置方法应该如下。

1. ms sql server2000中数据字段全部要选择成n打头的类型,比如ntext,nvarchar等。

2.安装ADO Driver

安装one -click installer 来安装ruby 的话就已经安装了所有连接SQL Server使用的需求包.但是,并没有安装ADO Driver.

这样来安装它:

在Ruby目录下找到这个目录: \ruby\lib\ruby\site_ruby\1.8\DBD .例如:我的Ruby安装在D:\ruby中,所以是这个目录D:\ruby\lib\ruby\site_ruby\1.8\DBD 在该目录中创建一个ADO文件夹. 下载Ruby-DBI,将lib/dbd_ado/ADO.rb文件拷贝到X:/ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb

3. 配置database.yml:Java代码

development:

adapter: sqlserver

database: database_name

host: server_name

username: user_name

password: your_pw_here

development:

adapter: sqlserver

database: database_name

host: server_name

username: user_name

password: your_pw_here

4.在environment.rb添加下面代码

require 'win32ole'

WIN32OLE.codepage = WIN32OLE::CP_UTF8

在这里稍微解释下第四部分的设置。sql server 2000中使用的unicode 并非是utf8,ado的默认链接编码都是当前系统设置的code pages相关的。

一般的windows设置都是非unicode的,比如简体中文windows系统下一般都是gb2312, 在rails中database.yml设置encoding: utf8,对于sql server没有任何用处。

为了迫使sql server接受utf8数据,必须修改ado链接的code pages值为utf8,才能让ado部分代码在接受rails传入的utf8数据之后,不做任何额外的处理. 否则的话,ado部分代码会根据当前系统的默认code pages值来处理这里字符数据。

于是在中文windows系统上,从utf8的rails项目中传入的数据,会被当作gb2312编码的数据来传递到sql server2000中,于是sql server2000中存入的数据会成为乱码,也有部分数据在处理过程中出错,导致sql 语句执行出错。比如常见的中文字符右边的单引号会不见的情况。

不设置 WIN32OLE.codepage = WIN32OLE::CP_UTF8,你的整个系统编码配置是这样的

rails(utf8)--ado(根据当前系统cp来取得编码,或是gb2312或是其他)--sql server 2000 (unicode)

整个系统编码不一至 。

WIN32OLE.codepage = WIN32OLE::CP_UTF8 这句代码就是为了更改cp值.整个系统编码配置是这样的

rails(utf8)--ado(utf8)--sql server 2000 (unicode)

整个系统编码一至,整个系统中不会再出现任何乱码。

javaymlif的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于、javaymlif的信息别忘了在本站进行查找喔。