
暂无个人介绍
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/84256882 又一种JDK选择——Amazon Corretto 2018.11.19 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 一、简介 Oracle公司提供了两个JDK: Oracle OpenJDK:https://jdk.java.net/11/ Oracle JDK:https://www.oracle.com/technetwork/java/javase/downloads/jdk11-downloads-5066655.html 除了以上两种JDK,业界还有不少第三方提供的JDK,它们大都基于OpenJDK。比如Amazon Corretto。 Amazon Corretto官网:https://aws.amazon.com/cn/corretto/ Amazon Corretto是Amazon提供的一个免费的、基于OpenJDK的、多平台的、生产就绪的JDK发行版。Amazon Corretto提供了长期支持,其中包括性能增强和安全修复。Amazon在内部数千种生产服务上运行着Corretto,并且Corretto已被证明能够兼容Java SE标准。借助Corretto,用户可以在常用操作系统(包括Amazon Linux 2、Windows和macOS)上开发和运行Java应用程序。 Amazon Corretto可以免费下载和使用。没有任何其他付费功能或限制。Amazon公司计划每季度发布一次更新,其中包括对企业应用程序开发至关重要的性能增强和错误修复。 目前Amazon发布了Corretto的Corretto 8 Preview预览版本,它基于OpenJDK 8源码。 下载地址:https://docs.aws.amazon.com/zh_cn/corretto/latest/corretto-8-ug/downloads-list.html 二、安装 1、在Amazon Linux 2环境中安装Amazon Corretto 8 启用corretto8的YUM仓库 $ amazon-linux-extras enable corretto8 可以将Amazon Corretto 8安装为运行时环境(JRE)或完整开发环境(JDK)。后者包含了运行时环境。 将Amazon Corretto 8安装为JRE: $ sudo yum install java-1.8.0-amazon-corretto 将Amazon Corretto 8安装为JDK: $ sudo yum install java-1.8.0-amazon-corretto-devel 安装位置是/usr/lib/jvm/java-1.8.0-amazon-corretto.x86_64。 验证安装 在终端中,运行以下命令: $ java -version openjdk version "1.8.0_192" OpenJDK Runtime Environment (build 1.8.0_192-amazon-corretto-preview-b12) OpenJDK 64-Bit Server VM (build 25.192-b12, mixed mode) 卸载Amazon Corretto 8 可以使用以下命令卸载Amazon Corretto 8。 卸载JRE: $ sudo yum remove java-1.8.0-amazon-corretto 卸载JDK: $ sudo yum remove java-1.8.0-amazon-corretto-devel 2、Amazon Corretto 8的Docker镜像 建立Amazon Corretto 8的Docker镜像 $ docker build -t amazon-corretto-8 github.com/corretto/corretto-8-docker 命令完成后,将拥有一个名为amazon-corretto-8的镜像。 要在本地运行此镜像,请运行以下命令: $ docker run -it amazon-corretto-8 还可以将此镜像推送到Amazon ECR。 创建一个新的Docker镜像 可以使用Amazon Corretto 8 Docker镜像作为父镜像来创建新的Docker镜像。 创建Dockerfile,内容如下: FROM amazon-corretto-8 RUN echo $' \ public class Hello { \ public static void main(String[] args) { \ System.out.println("Welcome to Amazon Corretto!"); \ } \ }' > Hello.java RUN javac Hello.java CMD ["java", "Hello"] 构建新镜像: $ docker build -t hello-app . 运行新镜像: $ docker run hello-app 将获得以下输出。 Welcome to Amazon Corretto!
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/81509169 Java编程中资源对象管理的进化 2018.8.8 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 使用Java开发业务时,常常需要处理资源,这是很常见的需求。 Java 7以前 在Java 7以前,处理(关闭)资源是这样的: public static int getAccountStatusCodeFromDataStore_traditional(String accountId) throws SQLException { String accountStatusCodeQuery = getAccountStatusCodeQuery(accountId); Statement statement = null; ResultSet resultSet = null; try { statement = createStatementFromConnection(); resultSet = statement.executeQuery(accountStatusCodeQuery); return getAccountStatusCodeFromResultSet(resultSet); } finally { if (resultSet != null) resultSet.close(); if (statement != null) statement.close(); } } 开发人员必须关闭创建的所有资源,否则会导致资源泄漏。 Java 7/8 而在Java 7中,引入了try-with-resources的新方法,可以在try-catch块中使用的正确顺序自动处理资源的关闭,比如: public static int getAccountStatusCodeFromDataStore_java7(String accountId) throws SQLException { String accountStatusCodeQuery = getAccountStatusCodeQuery(accountId); try (Statement statement = createStatementFromConnection(); ResultSet resultSet = statement.executeQuery(accountStatusCodeQuery)) { return getAccountStatusCodeFromResultSet(resultSet); } } 在此示例中,可以看到代码更简洁了,整体可读性提高了,它实现了资源的自动管理。我们可以在try-with-resources语句中拥有多个资源,且多个资源的声明之间应该用分号分隔。当这些资源在自动关闭时,也会保持声明的反向逻辑顺序依次关闭(最后声明的资源将首先关闭)。 如果这里要抛出异常,try块的异常会会压制try-with-resources块的异常。如果确实有需要,可以通过从try块抛出的异常中调用Throwable.getSuppressed方法来检索被try块抑制的异常。 另外,try-with-resources语句也可以有catch和finally块。在声明的资源被关闭后会运行任何catch或finally块。 Java 9 而到了Java 9时代,对于try-with-resources的资源处理,Java 9中引入了更简洁的版本。如果开发者已经将资源声明为final或effective final类型,那么可以在try-with-resources中直接使用它们而无需创建任何新变量。这使得我们可以进一步利用自动资源管理。上面的代码现在可使用更简洁的try-with-resources实现,如下: public static int getAccountStatusCodeFromDataStore_java9(String accountId) throws SQLException { String accountStatusCodeQuery = getAccountStatusCodeQuery(accountId); // 明确声明final final Statement statement = createStatementFromConnection(); // effective final ResultSet resultSet = statement.executeQuery(accountStatusCodeQuery); try (statement; resultSet) { return getAccountStatusCodeFromResultSet(resultSet); } } 可见,代码的易读性提高了。 其实大多数的资源类在背后实现了AutoCloseable或Closeable接口,因此与try-with-resources语句协同工作才实现了自动资源管理。如果我们处理的资源没有实现AutoCloseable或Closeable接口,那么就必须遵循传统的方法来关闭资源。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/81042018 实现Java集合迭代的高性能 2018.7.14 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 一、介绍 Java开发者经常会遇到处理集合(比如ArrayList、HashSet)的情况,Java 8也提供了Lambda表达式和Streaming API来简化集合相关的工作。在大多数应用场景下,无需考虑集合迭代的性能消耗。但是,在一些极端情况下,比如集合包含了上百万条记录的情况,这个时候集合迭代就需要选择正确的姿势,否则性能会较差。 使用JMH检查下面每段代码片段的运行时间。 二、forEach vs. C Style vs. Stream API 迭代是一个非常基本的功能,所有的编程语言都有简单的迭代语法,允许程序员在集合上运行迭代。Stream API可以通过Collections用非常直接的方式进行迭代。 public List<Integer> streamSingleThread(BenchMarkState state) { List<Integer> result = new ArrayList<>(state.testData.size()); state.testData.stream().forEach(item -> { result.add(item); }); return result; } public List<Integer> streamMultiThread(BenchMarkState state) { List<Integer> result = new ArrayList<>(state.testData.size()); state.testData.stream().parallel().forEach(item -> { result.add(item); }); return result; } 使用forEach循环也非常简单: public List<Integer> forEach(BenchMarkState state) { List<Integer> result = new ArrayList<>(state.testData.size()); for(Integer item : state.testData) { result.add(item); } return result; } C style方式的迭代其代码要冗长一些,但仍然非常紧凑: public List<Integer> forCStyle(BenchMarkState state) { int size = state.testData.size(); List<Integer> result = new ArrayList<>(size); for(int j = 0; j < size; j ++){ result.add(state.testData.get(j)); } return result; } 以上代码的性能评分如下: Benchmark Mode Cnt Score Error Units TestLoopPerformance.forCStyle avgt 200 18.068 ± 0.074 ms/op TestLoopPerformance.forEach avgt 200 30.566 ± 0.165 ms/op TestLoopPerformance.streamMultiThread avgt 200 79.433 ± 0.747 ms/op TestLoopPerformance.streamSingleThread avgt 200 37.779 ± 0.485 ms/op 对于C style方式的迭代,JVM只是简单地增加了一个整型变量,它直接从内存读值。这使它非常快。但forEach迭代则不同,根据Oracle官方文档,JVM必须把forEach转换为迭代器并为每个数据项调用hasNext()。这就是为什么forEach比C style
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/80658582 数据分析师、数据科学家、大数据专家三个职位的区别 2018.6.11 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 随着数据科学和大数据作为主流职业选择的出现,不少人对相关职位名称的内涵存在一定的混乱,有些人认为大数据等同于数据科学,另外有些人则认为大数据是数据科学的子集。数据科学已经存在了很长一段时间,而大数据则是相当新的,它源于数据科学。 下面是数据分析师,数据科学家和大数据专家之间的一点比较。 数据分析师 1. 定义 使用自动化工具,他们可以获取分离的数据和见解。他们定义数据集并进行广泛的人口统计分析以确定与业务和产品相关的策略。 2. 所需技能 编程,统计学和数学,机器学习,数据可视化和通信技术,数据处理和数据集定义 3. 适用领域 医疗保健,保险,旅游,行政,游戏,分布式系统 数据科学家 1. 定义 获取数据,构建和维护数据库,根据各种需求清理和分离数据,并从事数据可视化和分析工作。 2. 所需技能 SAS/R/类似工具,Python,Hadoop,SQL,重构数据,数据库构建和管理 3. 适用领域 搜索引擎,广告,自适应算法,AI系统 大数据专家 1. 定义 处理连续大量的数据,定义用于分析的参数和数据集,并编制分析系统,为企业提供战略见解。 2. 所需技能 数学和统计学,程序设计和计算机科学,分析技能,商业战略 3. 适用领域 零售,电子商务,金融服务,通信 正如所看到的,数据分析是这些选项中最基本的。数据分析师的工作有更广泛的应用,因此在不同的行业应用更加多样化。即使数据分析师的教育和学术要求也较低。 接下来是大数据工作,这些工作相当复杂,需要高级技能。有时候,大数据认证是获得大数据分析师工作的强制性要求。由于数字技术在各行业的普及,大数据工作的范围日益扩大。 最重要的是数据科学工作。数据科学认证是获得工作的必备条件。数据科学家的范围比大数据要低,这是由于数据科学不同的概况所致。 有了以上信息,数据分析师、数据科学家和大数据专家之间的差异应该清楚。这个信息可以用于在数据分析和商业战略领域制定更好的职业规划。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/80628826 阿里云RDS PostgreSQL时序数据的优化 2008年6月8日 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 PostgreSQL是最流行的开源数据库之一。PostgreSQL的一个优势是它可以通过多种方式进行优化,例如数据合并和数据清理。而数据合并和数据清理在一些应用场景下是必需的。 例如: 当数据表的具体内容被更新(插入、更新、删除)时,我们需要先合并,然后迅速得到每个主键的最新值。 当我们有大量连续不断地报告数据的传感器时,我们需要及时收集每个传感器的最新读数。 我们可以使用窗口查询进行这种操作,但我们需要快速检索批量数据。 通常有四种优化时序数据的方法: 当只有很少的唯一值和一个未知的范围时,我们可以使用递归(recursion)。 当只有很少的唯一值并且它们的范围已经确定,我们可以使用子查询(subquery)。 当有许多唯一值时,窗口查询(Window query)比上述方法更合适。 流计算(Stream computing)是所有场景中最好的。 本文只会比较前三种方法。流计算不需要进行比较,因为它是所有场景中最强大的方法。 递归 vs. 子查询 vs. 窗口查询 在比较中,将使用一个包含500万条唯一值的数据库作为数据源,并在以下情况下比较这些方法。 递归 情景1、有大量有效的唯一值(100万个唯一值) 第1步:创建一个表 \timing drop table test; create unlogged table test(id int , info text, crt_time timestamp); 第2步:构建数据 insert into test select ceil(random()*1000000), md5(random()::text), clock_timestamp() from generate_series(1,5000000); 第3步:创建一个索引 create index idx_test_1 on test (id, crt_time desc); 第4步:递归查询效率 explain (analyze,verbose,timing,costs,buffers) with recursive skip as ( ( select test as v from test where id in (select id from test where id is not null order by id,crt_time desc limit 1) limit 1 ) union all ( select ( select t as v from test t where t.id>(s.v).id and t.id is not null order by id,crt_time desc limit 1 ) from skip s where (s.v).id is not null ) -- The "where (s.v).id is not null" must be included. Else you will be stuck in an infinite loop. ) select (t.v).id, (t.v).info, (t.v).crt_time from skip t where t.* is not null; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- CTE Scan on skip t (cost=54.35..56.37 rows=100 width=44) (actual time=0.042..6626.084 rows=993288 loops=1) Output: (t.v).id, (t.v).info, (t.v).crt_time Filter: (t.* IS NOT NULL) Rows Removed by Filter: 1 Buffers: shared hit=3976934 CTE skip -> Recursive Union (cost=0.91..54.35 rows=101 width=69) (actual time=0.034..6006.615 rows=993289 loops=1) Buffers: shared hit=3976934 -> Limit (cost=0.91..0.93 rows=1 width=69) (actual time=0.033..0.033 rows=1 loops=1) Output: test.* Buffers: shared hit=8 -> Nested Loop (cost=0.91..10.19 rows=500 width=69) (actual time=0.032..0.032 rows=1 loops=1) Output: test.* Buffers: shared hit=8 -> HashAggregate (cost=0.48..0.49 rows=1 width=4) (actual time=0.021..0.021 rows=1 loops=1) Output: test_1.id Group Key: test_1.id Buffers: shared hit=4 -> Limit (cost=0.43..0.47 rows=1 width=12) (actual time=0.016..0.016 rows=1 loops=1) Output: test_1.id, test_1.crt_time Buffers: shared hit=4 -> Index Only Scan using idx_test_1 on public.test test_1 (cost=0.43..173279.36 rows=5000002 width=12) (actual time=0.015..0.015 rows=1 loops=1) Output: test_1.id, test_1.crt_time Index Cond: (test_1.id IS NOT NULL) Heap Fetches: 1 Buffers: shared hit=4 -> Index Scan using idx_test_1 on public.test (cost=0.43..9.64 rows=6 width=73) (actual time=0.009..0.009 rows=1 loops=1) Output: test.*, test.id Index Cond: (test.id = test_1.id) Buffers: shared hit=4 -> WorkTable Scan on skip s (cost=0.00..5.14 rows=10 width=32) (actual time=0.006..0.006 rows=1 loops=993289) Output: (SubPlan 1) Filter: ((s.v).id IS NOT NULL) Rows Removed by Filter: 0 Buffers: shared hit=3976926 SubPlan 1 -> Limit (cost=0.43..0.49 rows=1 width=81) (actual time=0.005..0.005 rows=1 loops=993288) Output: t_1.*, t_1.id, t_1.crt_time Buffers: shared hit=3976926 -> Index Scan using idx_test_1 on public.test t_1 (cost=0.43..102425.17 rows=1666667 width=81) (actual time=0.005..0.005 rows=1 loops=993288) Output: t_1.*, t_1.id, t_1.crt_time Index Cond: ((t_1.id > (s.v).id) AND (t_1.id IS NOT NULL)) Buffers: shared hit=3976926 Planning time: 0.354 ms Execution time: 6706.105 ms (45 rows) 情景二、只有很少有效的唯一值(1,000个唯一值) 第1步:创建一个表 \timing drop table test; create unlogged table test(id int , info text, crt_time timestamp); 第2步:构建数据 insert into test select ceil(random()*1000), md5(random()::text), clock_timestamp() from generate_series(1,5000000); 第3步:创建一个索引 create index idx_test_1 on test (id, crt_time desc); 第4步:递归查询效率 Query statement stays unchanged QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- CTE Scan on skip t (cost=55.09..57.11 rows=100 width=44) (actual time=0.046..8.859 rows=1000 loops=1) Output: (t.v).id, (t.v).info, (t.v).crt_time Filter: (t.* IS NOT NULL) Rows Removed by Filter: 1 Buffers: shared hit=4007 CTE skip -> Recursive Union (cost=0.91..55.09 rows=101 width=69) (actual time=0.039..8.203 rows=1001 loops=1) Buffers: shared hit=4007 -> Limit (cost=0.91..1.67 rows=1 width=69) (actual time=0.038..0.038 rows=1 loops=1) Output: test.* Buffers: shared hit=8 -> Nested Loop (cost=0.91..6335.47 rows=8333 width=69) (actual time=0.038..0.038 rows=1 loops=1) Output: test.* Buffers: shared hit=8 -> HashAggregate (cost=0.48..0.49 rows=1 width=4) (actual time=0.021..0.021 rows=1 loops=1) Output: test_1.id Group Key: test_1.id Buffers: shared hit=4 -> Limit (cost=0.43..0.47 rows=1 width=12) (actual time=0.016..0.017 rows=1 loops=1) Output: test_1.id, test_1.crt_time Buffers: shared hit=4 -> Index Only Scan using idx_test_1 on public.test test_1 (cost=0.43..173279.55 rows=5000002 width=12) (actual time=0.015..0.015 rows=1 loops=1) Output: test_1.id, test_1.crt_time Index Cond: (test_1.id IS NOT NULL) Heap Fetches: 1 Buffers: shared hit=4 -> Index Scan using idx_test_1 on public.test (cost=0.43..6284.98 rows=5000 width=73) (actual time=0.015..0.015 rows=1 loops=1) Output: test.*, test.id Index Cond: (test.id = test_1.id) Buffers: shared hit=4 -> WorkTable Scan on skip s (cost=0.00..5.14 rows=10 width=32) (actual time=0.008..0.008 rows=1 loops=1001) Output: (SubPlan 1) Filter: ((s.v).id IS NOT NULL) Rows Removed by Filter: 0 Buffers: shared hit=3999 SubPlan 1 -> Limit (cost=0.43..0.49 rows=1 width=81) (actual time=0.007..0.007 rows=1 loops=1000) Output: t_1.*, t_1.id, t_1.crt_time Buffers: shared hit=3999 -> Index Scan using idx_test_1 on public.test t_1 (cost=0.43..102425.80 rows=1666667 width=81) (actual time=0.007..0.007 rows=1 loops=1000) Output: t_1.*, t_1.id, t_1.crt_time Index Cond: ((t_1.id > (s.v).id) AND (t_1.id IS NOT NULL)) Buffers: shared hit=3999 Planning time: 0.353 ms Execution time: 8.980 ms (45 rows) 子查询 情景1、有大量有效的唯一值(100万个唯一值) 第1步:子查询查询效率 如果ID的值范围过宽,子查询效率会很低下。 需要维护一个唯一的ID表。这里我们使用generate_series作为测试的替代。 explain (analyze,verbose,timing,costs,buffers) select (select test from test where id=t.id order by crt_time desc limit 1) from generate_series(1,1000000) t(id); QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------- Function Scan on pg_catalog.generate_series t (cost=0.00..1976.65 rows=1000 width=32) (actual time=70.682..2835.109 rows=1000000 loops=1) Output: (SubPlan 1) Function Call: generate_series(1, 1000000) Buffers: shared hit=3997082 SubPlan 1 -> Limit (cost=0.43..1.97 rows=1 width=77) (actual time=0.002..0.002 rows=1 loops=1000000) Output: test.*, test.crt_time Buffers: shared hit=3997082 -> Index Scan using idx_test_1 on public.test (cost=0.43..9.64 rows=6 width=77) (actual time=0.002..0.002 rows=1 loops=1000000) Output: test.*, test.crt_time Index Cond: (test.id = t.id) Buffers: shared hit=3997082 Planning time: 0.119 ms Execution time: 2892.712 ms (14 rows) 情景二、只有很少有效的唯一值(1,000个唯一值) 第1步:子查询查询效率 查询语句更改为 explain (analyze,verbose,timing,costs,buffers) select (select test from test where id=t.id order by crt_time desc limit 1) from generate_series(1,1000) t(id); QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------ Function Scan on pg_catalog.generate_series t (cost=0.00..1699.41 rows=1000 width=32) (actual time=0.107..7.041 rows=1000 loops=1) Output: (SubPlan 1) Function Call: generate_series(1, 1000) Buffers: shared hit=4000 SubPlan 1 -> Limit (cost=0.43..1.69 rows=1 width=77) (actual time=0.006..0.007 rows=1 loops=1000) Output: test.*, test.crt_time Buffers: shared hit=4000 -> Index Scan using idx_test_1 on public.test (cost=0.43..6284.98 rows=5000 width=77) (actual time=0.006..0.006 rows=1 loops=1000) Output: test.*, test.crt_time Index Cond: (test.id = t.id) Buffers: shared hit=4000 Planning time: 0.131 ms Execution time: 7.126 ms (14 rows) 窗口查询 情景1、有大量有效的唯一值(100万个唯一值) 第1步:窗口查询效率 explain (analyze,verbose,timing,costs,buffers) select id,info,crt_time from (select row_number() over (partition by id order by crt_time desc) as rn, * from test) t where rn=1; postgres=# explain (analyze,verbose,timing,costs,buffers) select id,info,crt_time from (select row_number() over (partition by id order by crt_time desc) as rn, * from test) t where rn=1; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------- Subquery Scan on t (cost=0.43..310779.41 rows=25000 width=45) (actual time=0.027..6398.308 rows=993288 loops=1) Output: t.id, t.info, t.crt_time Filter: (t.rn = 1) Rows Removed by Filter: 4006712 Buffers: shared hit=5018864 -> WindowAgg (cost=0.43..248279.39 rows=5000002 width=53) (actual time=0.026..5973.497 rows=5000000 loops=1) Output: row_number() OVER (?), test.id, test.info, test.crt_time Buffers: shared hit=5018864 -> Index Scan using idx_test_1 on public.test (cost=0.43..160779.35 rows=5000002 width=45) (actual time=0.019..4058.476 rows=5000000 loops=1) Output: test.id, test.info, test.crt_time Buffers: shared hit=5018864 Planning time: 0.121 ms Execution time: 6446.901 ms (13 rows) 情景二、只有很少有效的唯一值(1,000个唯一值) 第1步:窗口查询效率 查询语句保持不变 QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------- Subquery Scan on t (cost=0.43..310779.61 rows=25000 width=45) (actual time=0.027..6176.801 rows=1000 loops=1) Output: t.id, t.info, t.crt_time Filter: (t.rn = 1) Rows Removed by Filter: 4999000 Buffers: shared hit=4744850 read=18157 -> WindowAgg (cost=0.43..248279.58 rows=5000002 width=53) (actual time=0.026..5822.576 rows=5000000 loops=1) Output: row_number() OVER (?), test.id, test.info, test.crt_time Buffers: shared hit=4744850 read=18157 -> Index Scan using idx_test_1 on public.test (cost=0.43..160779.55 rows=5000002 width=45) (actual time=0.020..4175.082 rows=5000000 loops=1) Output: test.id, test.info, test.crt_time Buffers: shared hit=4744850 read=18157 Planning time: 0.108 ms Execution time: 6176.924 ms (13 rows) 横向效率比较图 结论 随着物联网的兴起,越来越多的业务会使用时序数据,在必须根据这些数据提供服务的情况下,计算数据中的最新值和滑动窗口中的值是至关重要的。 PostgreSQL是开源数据库的最佳选择,因为它为相同的问题提供了几种解决方案。参考数据优化方法,我们可以得出结论: 递归适用于只有少量唯一值但范围未知的情况。 子查询适用于唯一值很少且范围已确定的情况。 当有大量唯一值时,窗口查询比子查询更合适。 流计算是所有场景的最佳选择。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/80502641 MariaDB TX 3.0企业开源数据库 2018.5.29 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 MariaDB TX是一款开源的、基于MariaDB的企业数据库解决方案,它具有轻量级连接器、高级数据库代理和防火墙、可插拔的通用数据库、专用存储引擎、多主集群,并为数据库管理员和开发人员提供了通知服务。 MariaDB TX刚发布了最新的3.0版本。 MariaDB TX 3.0新特性 1. 引入了对Oracle数据库的兼容性 支持包含Oracle数据类型、控制语句,静态和动态SQL(包括带参数的游标)、触发器、序列和存储过程/函数。 MariaDB TX 3.0是第一个具有Oracle数据库兼容性的企业开源数据库,包括对使用PL/SQL编写的存储过程的支持。到目前为止,如果需要Oracle数据库兼容性,就需要专用数据库(如IBM DB2或EnterpriseDB),现在又多了一个选择。 2. 临时表和查询 引入了内建的系统版本化表(SVT,System-Versioned Table)来自动、透明地存储数据行的当前和历史版本,以及基于特定时间点,在两个特定时间点或所有时间之间查询它们的SQL语法。 到目前为止,如果需要Oracle闪回(Flashback)查询功能或Microsoft SQL Server临时表功能,都需要专有数据库。而现在可以选择MariaDB TX。 3. 专用存储 MariaDB TX 3.0引入了MyRocks引擎的一般可用性,MyRocks是Facebook开发的针对空间优化和写入优化的存储引擎,推荐用于写密集型工作负载的场景;还引入了Spider引擎,Spider引擎是官方推荐的适合存储、写入的可伸缩性和并发性的分布式存储引擎。 MariaDB TX 3.0是第一款支持多种工作负载的企业开源数据库,它们都具有相同级别的性能,它利用了多种专用存储引擎: 用于混合或读取为主要工作负载的场景:InnoDB引擎,也是默认的存储引擎 用于写入密集型工作负载、针对SSD优化的场景:MyRocks引擎 用于需要极高可伸缩性和并发性的场景:Spider引擎 4. 高级安全性 MariaDB TX 3.0引入了部分数据屏蔽和全面数据模糊处理,以保护敏感安全和个人身份信息免受未经授权的访问,这是遵循法规所必需的,包括欧盟通用数据保护条例GDPR。 MariaDB TX 3.0的企业可靠性 高可用性:通过集群、复制和自动故障转移功能确保关键任务应用的正常运行时间。 性能:通过多核处理器和多线程架构满足用户对性能的期望。 灾难恢复:通过备份和恢复或时间点回滚功能搞定非预期的故障恢复。 可扩展性:通过分布式、多主存储功能实现按需扩展来适应业务的持续增长。 安全:使用角色、加密、数据保护、数据混淆/屏蔽和查询阻止来保护客户的数据。 架构的灵活性:使用内建的JSON函数创建混合数据模型并能即时添加列。 MariaDB TX 3.0的高可用性 自动故障转移 透明的查询路由 多主集群 无损半同步复制 多源复制
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/80354356 Protostuff使用示例 2018.5.17 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 1、引入Maven依赖的JAR包 <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-core</artifactId> <version>1.5.9</version> </dependency> <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-runtime</artifactId> <version>1.5.9</version> </dependency> <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-api</artifactId> <version>1.5.9</version> </dependency> 2、编写需要被序列化的实体类 这里是User.java(成员属性包含了其它类) package demo.protostuff; import java.util.List; public class User { private String firstName; private String lastName; private String email; private List<User> friends; private List<Car> cars; public User() { } public User(String email) { this.email = email; } // getters and setters public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public List<User> getFriends() { return friends; } public void setFriends(List<User> friends) { this.friends = friends; } public List<Car> getCars() { return cars; } public void setCars(List<Car> cars) { this.cars = cars; } @Override public String toString() { return "User{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", email='" + email + '\'' + ", friends=" + friends + ", cars=" + cars + '}'; } } Car.java package demo.protostuff; public class Car { private String color; private String car_name; private Integer price; public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getCar_name() { return car_name; } public void setCar_name(String car_name) { this.car_name = car_name; } public Integer getPrice() { return price; } public void setPrice(Integer price) { this.price = price; } public Car(String car_name) { super(); this.car_name = car_name; } public Car() { super(); } @Override public String toString() { return "Car [color=" + color + ", car_name=" + car_name + ", price=" + price + "]"; } } 3、主程序类 这里是App.java package demo.protostuff; import java.util.ArrayList; import java.util.List; import io.protostuff.LinkedBuffer; import io.protostuff.ProtostuffIOUtil; import io.protostuff.runtime.RuntimeSchema; public class App { private static RuntimeSchema<User> schema = RuntimeSchema.createFrom(User.class); public static void main(String[] args) { User user1 = new User(); user1.setEmail("10000@qq.com"); user1.setFirstName("zhang"); user1.setLastName("sanfeng"); List<User> users = new ArrayList<>(); users.add(new User("20000@qq.com")); user1.setFriends(users); Car car1 = new Car("宾利"); Car car2 = new Car("法拉利"); List<Car> cars = new ArrayList<>(); cars.add(car1); cars.add(car2); user1.setCars(cars); byte[] bytes = ProtostuffIOUtil.toByteArray(user1, schema, LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE)); User user2 = schema.newMessage(); ProtostuffIOUtil.mergeFrom(bytes, user2, schema); System.out.println(user2); System.out.println(); // 使用自定义的工具类 byte[] bytes1 = ProtostuffUtil.serializer(user1); User newUser = ProtostuffUtil.deserializer(bytes1, User.class); System.out.println(newUser); } } 程序说明: RuntimeSchema类用于在运行时从Java实体对象中生成所需的模式Schema ProtostuffIOUtil是一个工具类,用于对消息或对象进行序列化/反序列化 LinkedBuffer是一个缓冲区类,它封装了字节数组并具有对下一个缓冲区的引用以便能动态增加容量。 执行程序,输出如下: User{firstName=’zhang’, lastName=’sanfeng’, email=’10000@qq.com’, friends=[User{firstName=’null’, lastName=’null’, email=’20000@qq .com’, friends=null, cars=null}], cars=[Car [color=null, car_name=宾利, price=null], Car [color=null, car_name=法拉利, price=null]]} User{firstName=’zhang’, lastName=’sanfeng’, email=’10000@qq.com’, friends=[User{firstName=’null’, lastName=’null’, email=’20000@qq .com’, friends=null, cars=null}], cars=[Car [color=null, car_name=宾利, price=null], Car [color=null, car_name=法拉利, price=null]]} 4、根据Protostuff库自行封装的工具类 这里是ProtostuffUtil.java package demo.protostuff; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import io.protostuff.LinkedBuffer; import io.protostuff.ProtostuffIOUtil; import io.protostuff.Schema; import io.protostuff.runtime.RuntimeSchema; public class ProtostuffUtil { private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>(); private static <T> Schema<T> getSchema(Class<T> clazz) { @SuppressWarnings("unchecked") Schema<T> schema = (Schema<T>) cachedSchema.get(clazz); if (schema == null) { schema = RuntimeSchema.getSchema(clazz); if (schema != null) { cachedSchema.put(clazz, schema); } } return schema; } /** * 将对象序列化 * @param obj 对象 * @return */ public static <T> byte[] serializer(T obj) { @SuppressWarnings("unchecked") Class<T> clazz = (Class<T>) obj.getClass(); LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); try { Schema<T> schema = getSchema(clazz); return ProtostuffIOUtil.toByteArray(obj, schema, buffer); } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } finally { buffer.clear(); } } /** * 将字节数组数据反序列化 * @param data 字节数组 * @param clazz 对象 * @return */ public static <T> T deserializer(byte[] data, Class<T> clazz) { try { T obj = clazz.newInstance(); Schema<T> schema = getSchema(clazz); ProtostuffIOUtil.mergeFrom(data, obj, schema); return obj; } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } } }
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/79980756 Nginx Unit详解系列(一) 2018.4.17 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 一、什么是Nginx Unit Nginx Unit是一个开源的、以Nginx为基础的、支持多语言的动态Web应用服务器,它支持Python、PHP、Perl、Ruby和Go等多语言应用程序,可以在不中断服务的情况下完成部署配置更改,以多种语言运行代码。 Nginx Unit是一个新的开源项目,由Igor Sysoev发起,他说:“我想着手开发一款应用服务器,它能够远程动态配置,并且能够从一种语言的应用程序版本动态切换到另一种语言的应用程序。”Igor认为动态配置和交换无疑是主要问题,人们希望在不中断客户端处理的情况下重新配置服务器。 Nginx Unit使用REST API进行动态配置,它没有静态配置文件。所有配置更改直接在内存中发生,配置更改无需重新加载或服务中断即可生效。 Nginx Unit刚刚发布了1.0版,它可以在同一台服务器上支持Go、Perl、PHP、Python和Ruby,而且还支持多语言版本,比如用户可以在同一台服务器上同时运行PHP 5和PHP 7编写的应用程序。未来的Nginx Unit版本计划支持包括Java在内的其他语言。 NGINX Unit可以根据需要启动和扩展应用程序的进程,并在自己的安全沙箱中执行每一个应用程序实例。 Nginx Unit通过一个单独的“路由器”进程管理和路由所有传入网络通信到应用程序,因此它可以在不中断服务的情况下快速实施配置的更改。 Nginx Unit的配置采用了JSON格式,因此用户可以手动编辑,而且非常适合脚本编写。 Nginx Unit运行多种语言运行时的能力是基于它内部的路由器进程之间的隔离,路由器进程可终止传入的HTTP请求,以及应用程序进程的分组,它实现了应用程序运行时并执行应用程序代码。 路由器进程是持久的,它从不重新启动,意味着配置更新可以无缝地实现,而不会中断服务。每一个应用程序进程都部署在自己的沙箱中(在开发中支持Linux控制组 [cgroups]),以便Nginx Unit为用户代码提供安全的隔离。 二、Nginx Unit的下一步 Nginx Unit工程团队在发布1.0之后的下一个里程碑的内容主要是HTTP成熟度、静态内容服务和其他语言的支持。 “我们计划在单元中添加SSL和HTTP/2功能,”Igor说,“另外,我们计划在配置中支持路由。目前,我们有一个监听端口直接映射到一个应用程序,我们计划使用URI和主机名等添加路由。另外,我们希望为Unit增加更多的语言支持,我们正在完成Ruby实现,接下来我们将考虑Node.js和Java,Java将以Tomcat兼容的方式添加。” Nginx Unit的最终目标是为分布式多语言应用程序创建一个开源平台,该应用程序可以安全、可靠地运行应用程序代码并以最佳的性能运行。该平台将自行管理,具有自动调节功能以满足资源约束条件下的SLA,以及服务发现和内部负载平衡,以便轻松创建服务网格。 三、Nginx Unit和Nginx应用平台 Nginx Unit平台通常会提供Nginx开源的前端层或Nginx Plus反向代理,以提供入口控制,边缘负载均衡和安全性。然后可以使用Nginx控制器对联合平台(Nginx Unit和Nginx或Nginx Plus)进行全面管理,以监控、配置和控制整个平台。 这三个组件:Nginx Plus,Nginx Unit和Nginx Controller组成了Nginx应用平台。Nginx应用平台是一个产品套件,提供负载均衡、缓存、API管理、WAF和应用服务,并具有丰富的管理和控制面板,可简化单片应用、微服务和过渡应用的操作任务。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/79871929 Java 10改进了对Docker容器的支持 2018.4.9 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 许多运行在Java虚拟机中的应用程序(包括Apache Spark和Kafka等数据服务以及传统的企业应用程序)都可以在Docker容器中运行。但是在Docker容器中运行Java应用程序一直存在一个问题,那就是在容器中运行JVM程序在设置内存大小和CPU使用率后,会导致应用程序的性能下降。这是因为Java应用程序没有意识到它正在容器中运行。随着Java 10的发布,这个问题总算得以解决,JVM现在可以识别由容器控制组(cgroups)设置的约束。可以在容器中使用内存和CPU约束来直接管理Java应用程序,其中包括: 遵守容器中设置的内存限制 在容器中设置可用的CPU 在容器中设置CPU约束 Java 10的这个改进在Docker for Mac、Docker for Windows以及Docker Enterprise Edition等环境均有效。 容器的内存限制 在Java 9之前,JVM无法识别容器使用标志设置的内存限制和CPU限制。而在Java 10中,内存限制会自动被识别并强制执行。 Java将服务器类机定义为具有2个CPU和2GB内存,以及默认堆大小为物理内存的1/4。例如,Docker企业版安装设置为2GB内存和4个CPU的环境,我们可以比较在这个Docker容器上运行Java 8和Java 10的区别。 首先,对于Java 8: docker container run -it -m512 --entrypoint bash openjdk:latest $ docker-java-home/bin/java -XX:+PrintFlagsFinal -version | grep MaxHeapSize uintx MaxHeapSize := 524288000 {product} openjdk version "1.8.0_162" 最大堆大小为512M或Docker EE安装设置的2GB的1/4,而不是容器上设置的512M限制。 相比之下,在Java 10上运行相同的命令表明,容器中设置的内存限制与预期的128M非常接近: docker container run -it -m512M --entrypoint bash openjdk:10-jdk $ docker-java-home/bin/java -XX:+PrintFlagsFinal -version | grep MaxHeapSize size_t MaxHeapSize = 134217728 {product} {ergonomic} openjdk version "10" 2018-03-20 设置可用的CPU 默认情况下,每个容器对主机CPU周期的访问是无限的。可以设置各种约束来限制给定容器对主机CPU周期的访问。Java 10可以识别这些限制: docker container run -it --cpus 2 openjdk:10-jdk jshell> Runtime.getRuntime().availableProcessors() $1 ==> 2 分配给Docker EE的所有CPU会获得相同比例的CPU周期。这个比例可以通过修改容器的CPU share权重来调整,而CPU share权重与其它所有运行在容器中的权重相关。此比例仅适用于正在运行的CPU密集型的进程。当某个容器中的任务空闲时,其他容器可以使用余下的CPU时间。实际的CPU时间的数量取决于系统上运行的容器的数量。这些可以在Java 10中设置: docker container run -it --cpu-shares 2048 openjdk:10-jdk jshell> Runtime.getRuntime().availableProcessors() $1 ==> 2 cpuset约束设置了哪些CPU允许在Java 10中执行。 docker run -it --cpuset-cpus="1,2,3" openjdk:10-jdk jshell> Runtime.getRuntime().availableProcessors() $1 ==> 3 分配内存和CPU 使用Java 10,可以使用容器设置来估算部署应用程序所需的内存和CPU的分配。我们假设已经确定了容器中运行的每个进程的内存堆和CPU需求,并设置了JAVA_OPTS配置。例如,如果有一个跨10个节点分布的应用程序,其中五个节点每个需要512Mb的内存和1024个CPU-shares,另外五个节点每个需要256Mb和512个CPU-shares。 请注意,1个CPU share比例由1024表示。 对于内存,应用程序至少需要分配5Gb。 512Mb × 5 = 2.56Gb 256Mb × 5 = 1.28Gb 该应用程序需要8个CPU才能高效运行。 1024 x 5 = 5个CPU 512 x 5 = 3个CPU 最佳实践是建议分析应用程序以确定运行在JVM中的每个进程实际需要多少内存和分配多少CPU。但是,Java 10消除了这种猜测,可以通过调整容器大小以防止Java应用程序出现内存不足的错误以及分配足够的CPU来处理工作负载。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/79802993 Java EE 8的五大新特性详解 2018.4.3 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 Java EE 8带来了很多新特性,其中最好的新特性有下面五个。 备受期待的Java企业版第8版(Java EE 8)发布了两个令人兴奋的新API(JSON-Binding 1.0和Java EE Security 1.0),并改进了已有的API(JAX-RS 2.1,Bean Validation 2.0,JSF 2.3,CDI 2.0,JSON-P 1.1,JPA 2.2和Servlet 4.0)。这是Oracle近四年来发布的Java企业平台,它包含数百个新特性、更新功能和错误修复。 新特性TOP 5 1、Java EE Security 1.0 API 提供了注释驱动的认证机制。这个全新的安全API包含三项出色的新功能:身份存储抽象、新的安全上下文以及新注释驱动的身份验证机制(使用web.xml配置文件进行声明的方式过时了)。 2、JAX-RS 2.1 API 新的响应式客户端。JAX-RS 2.1规范中定义了新型响应式客户端,它包含响应式编程风格并允许端点结果的组合。 3、JSON-Binding 1.0 API 新的JSON绑定API,为JSON序列化和反序列化提供了本地Java EE解决方案。 4、CDI 2.0规范 在Java SE中使用。在CDI 2.0中有趣的新功能是允许在Java SE应用程序中引导CDI。 5、Servlet 4.0规范 Servlet 4.0支持服务器推技术。这种推技术使得Servlet规范与HTTP/2协议保持了一致。 新的安全API 或许Java EE 8最重要的新特性就是新的安全API。发布这个新API的主要动机是简化,标准化和现代化跨容器和实现处理安全问题的方式。 Web身份认证的配置:已经实现了现代化,这要归功于三个使web.xml文件声明成为冗余的新注释。 新的安全上下文:API标准化了Servlet和EJB容器执行认证的方式。 新的I身份存储抽象简化了身份存储的使用。 注释驱动的认证机制 发布了3个关于配置网络安全的新注释。之前传统的方式是通过在web.xml配置文件中进行声明。 HttpAuthenticationMechanism接口,它代表了HTTP身份验证,并带有三个内置的启用CDI的实现,每个实现代表Web安全性可配置的三种方式之一。 3个新注释如下: @BasicAuthenticationMechanismDefinition(基本身份认证机制定义) @FormAuthenticationMechanismDefinition(表单身份认证机制定义) @CustomFormAuthenticationMechanismDefinition(自定义表单身份认证机制定义) 例如,要启用基本认证,所有必要的是将BasicAuthenticationMechanismDefinition 注释添加Servlet,如下: @BasicAuthenticationMechanismDefinition(realmName="${'user-realm'}") @WebServlet("/user") @DeclareRoles({ "admin", "user", "demo" }) @ServletSecurity(@HttpConstraint(rolesAllowed = "user")) public class UserServlet extends HttpServlet { ... } 开发者可以丢弃XML配置,并使用其中一个新注释来驱动网络安全。 JAX-RS 2.1中的新响应式客户端 响应式方法的核心是数据流的概念,其中执行模型通过流传播变化。一个典型的例子是JAX-RS的方法调用。当调用返回时,将对方法调用的结果(可能是继续、完成或错误)执行下一个操作。 您可以将其视为异步流程的数据,下一个进程将处理前一个进程的结果,然后将进程的结果传递给链中的下一个进程。可组合的流程使得开发者可以将许多流程组合并转换为一个结果。 通过调用rx()Invocation.Builder实例的方法来构造客户端实例,启用响应式特性。它的返回类型是带有参数化Response类型的CompletionStage。此CompletionStage接口在Java 8中引入,并提出了一些有趣的可能性。 例如,在这个代码片段中,两个调用是对不同的端点进行的,然后将结果合并: CompletionStage<Response> cs1 = ClientBuilder.newClient() .target(".../books/history") .request() .rx() .get(); CompletionStage<Response> cs2 = ClientBuilder.newClient() .target(".../books/geology") .request() .rx() .get(); cs1.thenCombine(cs2, (r1, r2) -> r1.readEntity(String.class) + r2.readEntity(String.class)) .thenAccept(System.out::println); 新的JSON绑定API 新的JSON绑定API为JSON序列化和反序列化提供了本地Java EE解决方案。 以前,如果想要对JSON进行序列化和反序列化,就必须依赖Jackson、GSON、FastJson等第三方API。现在可以使用这个新的JSON绑定API,直接使用本地可用的所有功能。 从Java对象生成JSON文档现在非常简单了,只需调用toJson()方法并将它传递给想要序列化的实例即可。比如: String bookJson = JsonbBuilder.create().toJson(book); 将JSON文档反序列化为Java对象也非常简单,只需将JSON文档和目标类传递给fromJson()方法,然后返回Java对象。比如: Book book = JsonbBuilder.create().fromJson(bookJson, Book.class); 其中的设计哲学来自于Gson库。但这并非全部。 行为自定义 可以通过注释字段、JavaBean方法和类来自定义默认的序列化和反序列化行为。 例如,可以使用@JsonbNillable自定义null处理,在类级别使用@JsonbPropertyOrder注释来自定义属性的顺序,还可以使用@JsonbNumberFormat()注释指定数字的格式,使用@JsonbProperty()注释来更改字段的名称。 @JsonbNillable @JsonbPropertyOrder(PropertyOrderStrategy.REVERSE) public class Booklet { @JsonbProperty("cost") @JsonbNumberFormat("#0.00") private Float price; } 或者,可以选择使用句柄自定义JsonbConfig以便在运行时配置构建器,比如: JsonbConfig jsonbConfig = new JsonbConfig() .withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES) .withNullValues(true) .withFormatting(true); Jsonb jsonb = JsonbBuilder.create(jsonbConfig); 无论使用哪种方式,JSON绑定API都为Java对象的序列化和反序列化提供了广泛的功能。 CDI 2.0规范 在CDI 2.0 API版中拥有许多新功能,其中一个有趣的功能是在Java SE应用程序中引导CDI的功能。 要在Java SE中使用CDI,就必须明确引导CDI容器。这需要通过在SeContainerInitializer抽象类上调用静态方法newInstance()来实现。它返回一个SeContainer实例作为CDI运行时的句柄,通过该实例可以执行CDI解析,如下面的代码片段所示,它可以访问BeanManager,这是CDI的核心入口点: SeContainer seContainer = SeContainerInitializer.newInstance().initialize(); Greeting greeting = seContainer.select(Greeting.class).get(); greeting.printMessage("Hello World"); seContainer.close(); 通过在select()方法中传递CDI Bean的类名来取回CDI Bean,以便检索和使用。 配置选项 更多的配置可以使用SeContext通过添加拦截器、扩展、替代、属性或装饰器来进一步配置。 .enableInterceptors() .addExtensions() .selectAlternatives() .setProperties() .enableDecorators() 由于SeContainer继承了AutoCloseable接口,故当使用try-with-resources结构时,可通过在SeContainer调用close()方法手动地或自动地关闭容器。 Servlet 4.0规范 Servlet 4.0规范中定义了服务器推送功能,以便于HTTP/2协议保持一致。要理解这个特性,你首先需要知道服务器推送是什么。 什么是服务器推送 服务器推送是HTTP/2协议中的新特性之一,旨在通过将服务器端的资源推送到浏览器的缓存中来预测客户端的资源需求,以便当客户端发送网页请求并接收来自服务器的响应时,它需要的资源已经在缓存中。这是一项提高网页加载速度的性能增强的功能。 如何在Servlet 4.0公开服务器推送 在Servlet 4.0中,服务器推送功能是通过PushBuilder实例公开的,此实例是从HttpServletRequest实例中获取的。 看下面这个代码片段,可以看到header.png的路径是通过path()方法设置在PushBuilder实例上的,并通过调用push()方法被推送到客户端。当方法返回时,路径和条件报头将被清除,以便构建器重用。然后推送menu.css文件,接着是推送ajax.js这个JavaScript文件。 protected void doGet(HttpServletRequest request, HttpServletResponse response) { PushBuilder pushBuilder = request.newPushBuilder(); pushBuilder.path("images/header.png").push(); pushBuilder.path("css/menu.css").push(); pushBuilder.path("js/ajax.js").push(); // Return JSP that requires these resources } 当Servlet的doGet()方法执行完毕后,资源将会到达浏览器。从JSP生成的HTML需要这些资源,但不需要从服务器请求它们,因为它们已经在浏览器的缓存中。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/79639919 在SPA应用中利用JWT进行身份验证 2018.3.21 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 SPA SPA即Single Page Application,单页应用程序,是一种Web应用程序或网站,通过动态地重写当前的页面,而不是从服务器端加载一个新网页与用户交互。此方法避免了连续页面之间的用户体验的中断,使应用程序更像是桌面应用程序。在SPA中,所有必需的代码(HTML,JavaScript和CSS)都是通过单页加载来检索的,或者适当的资源是动态加载的并根据需要添加到页面中,通常是响应用户操作。尽管可以使用位置散列或HTML5 History API来提供应用程序中单独的逻辑页面的感知和导航性,但页面在该过程的任何时间点都不会重新加载,也不会将控件转移到另一个页面。与单页面应用程序的交互通常涉及在后台与Web服务器的动态通信。像Angular.js、Ember.js、Meteor.js、ExtJS、React等JS框架都采用了SPA原则。 JWT JWT即JSON Web Token,是一个基于JSON的开放标准(RFC-7519),它用于创建访问令牌以判断权利要求。例如,服务器可能会生成一个令牌,其中声明“以管理员身份登录”并将其提供给客户端。然后客户端可以使用该令牌来证明它以管理员身份登录。令牌由服务器密钥签名,因此客户端和服务器都能够验证令牌是否合法。JWT令牌被设计成紧凑的、URL-safe的、尤其适合在Web浏览器单点登录SSO的上下文。JWT声明通常可用于传递身份提供者与服务提供者之间的身份验证用户身份,或业务流程所要求的任何其他类型的声明。JWT令牌还可以被认证和加密。JWT标准还依赖于其它基于JSON的标准,比如RFC-7515的JWS(JSON Web Signature)、RFC-7516的JWE(JSON Web Encryption)。 一个成功的令牌认证系统要求用户了解安全细节和其他认证凭证。SPA应用通常与Restful API紧密联系在一起,将UI与所需数据进行绑定,并为UI带来更好的外观和感觉。但问题是,怎样以安全的方式实现API访问? 一个这样的SPA框架是Angular。我们可以借助基于JSON Web Token的身份验证来验证我们的Angular应用程序。因此,当创建Angular应用程序时,一个大问题是,您如何知道您的用户是谁以及他们可以访问哪些内容? 针对所有这些问题的答案以及针对API的认证和授权问题的解决方案是JWT认证系统,它正在取代传统的基于cookie的认证。这给你一个很好的方式来声明一个用户及其在应用程序中的访问权限。它为您提供了一个加密报头作为令牌,以保护经身份验证的用户对应用程序的访问,并为您的前端和后端构建访问控制逻辑。 让我们通过在您的Angular应用程序(版本2+)中查看几个验证代码来深入了解它。 前端工作 当用户发出登录请求时,我们通过我们的身份验证服务调用后端,如下所示: login(username: string, password: string): Observable < boolean > { return this.http.post('/api/authenticate', JSON.stringify({ username: username, password: password })) .map((response: Response) => { // login successful if there's a jwt token in the response let token = response.json() && response.json().token; if (token) { // set token property this.token = token; // store username and jwt token in local storage to keep user logged in between page refreshes localStorage.setItem('currentUser', JSON.stringify({ username: username, token: token })); // return true to indicate successful login return true; } else { // return false to indicate failed login return false; } }); } 服务器端工作 当用户尝试使用用户名和密码登录应用程序时,服务器端将: 验证用户 生成令牌 将令牌发送给用户 后端可以是任何像Node.js这样的服务器端框架,Node.js使用npm的jsonwebtoken模块来签署和验证JSON Web令牌。以下是一些示例代码: 模块 var jwt = require("jsonwebtoken"); var bcrypt = require("bcryptjs"); 登录功能 login: function (req, res) { // this is param checking if they are provided if (!_.has(req.body, 'email') || !_.has(req.body, 'password')) { return res.serverError("No field should be empty."); } // check if the username matches any email or phoneNumber User.findOne({ email: req.body.email }).exec(function callback(err, user) { if (err) return res.serverError(err); if (!user) return res.serverError("User not found, please sign up."); //check password bcrypt.compare(req.body.password, user.password, function (error, matched) { if (error) return res.serverError(error); if (!matched) return res.serverError("Invalid password."); //save the date the token was generated for already inside toJSON() var token = jwt.sign(user.toJSON(), "this is my secret key", { expiresIn: '10m' }); //return the token here res.ok(token); }); }); } 令牌返回如何工作 用户使用其凭据成功登录后,会返回一个Web令牌,它以本地存储方式进行保存。现在,无论何时用户想要访问受保护的路由,都应该在请求报头中携带授权报头项发送JWT。报头的内容如下所示: Authorization: Bearer 因此,JWT将被用于认证和授权API,这些API将授予访问其受保护的路由和资源的权限。 使用Angular处理Web令牌 由于JWT现在保存在本地存储中,我们可以使用它来防止未经身份验证的用户访问受限制的路由。它用于路由模块到任何受保护的路由。为此,我们使用Angular 2中的AuthGuard。AuthGuard使用了来自@angular/router的CanActivate方法。 import { Injectable } from '@angular/core'; import { Router, CanActivate } from '@angular/router'; @Injectable() export class AuthGuard implements CanActivate { constructor(private router: Router) { } canActivate() { if (localStorage.getItem('currentUser')) { // logged in so return true return true; } // not logged in so redirect to login page this.router.navigate(['/login']); return false; } } 因此,可以像这样在路由模块中保护路由: { path: '', component: SomeComponent, canActivate: [AuthGuard] } 接下来,我们使用此令牌来获取SPA应用所需的数据。它可以访问用户的API端点。此端点将以SPA为用户需要的数据作出响应。我们可以使用用户服务,如下所示。 getUsers(): Observable<User[]> { // add authorization header with jwt token let headers = new Headers({ 'Authorization': 'Bearer ' + this.authenticationService.token }); let options = new RequestOptions({ headers: headers }); // get users from api return this.http.get('/api/users', options) .map((response: Response) => response.json()); } 注意我们通过使用RequestOptions实现了在报头Header中携带了上面讨论的Authorization Bearer项。为此,可以使用npm中的另一个模块,即’angular2-jwt’。它是帮助构建Angular 2应用程序的辅助工具库。当从应用程序发出HTTP请求时,它会自动将JWT作为授权报头附加。它使用AuthHttp类发送一个JWT请求,从而有条件地允许基于JWT状态的路由导航。它可以按照如下所示进行安装。 npm install angular2-jwt AuthHTTP可以在调用用户API时使用,如下所示: // get users from api return this.authHttp.get('/api/users') .map((response: Response) => response.json()); 最后 本文的主旨是提供一个关于JWT的概述,以及如何使用JWT来保护单页应用程序。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/79546866 OpenCSV正确处理反斜线 2018.3.13 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 OpenCSV是一个开源的、处理CSV数据的Java库。但它在处理反斜杠时存在一个小问题,本文讲述这个问题以及如何解决它。 OpenCSV的Maven依赖如下: <dependency> <groupId>com.opencsv</groupId> <artifactId>opencsv</artifactId> <version>4.1</version> </dependency> 问题 下面是使用OpenCSV编写的读取CSV数据的一个代码片段: import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import com.opencsv.CSVReader; import com.opencsv.CSVWriter; ...... String dataValue = "test"; // writing StringWriter writer = new StringWriter(); try (CSVWriter csvwriter = new CSVWriter(writer)) { String[] originalData = new String[2]; originalData[0] = dataValue; originalData[1] = dataValue; System.out.println("Original data: " + originalData[0] + "," + originalData[1]); csvwriter.writeNext(originalData); } catch (IOException e) { throw new RuntimeException(e); } System.out.println("Written data: " + writer.toString()); // reading try (CSVReader csvReader = new CSVReader(new StringReader(writer.toString()))) { String[] readData = csvReader.readNext(); System.out.println("Read data: " + readData[0] + "," + readData[1]); } catch (IOException e) { throw new RuntimeException(e); } 上面的代码片段输出如下: Original data: test,test Written data: "test","test" Read data: test,test 这是预期的结果。但是,如果在CSV数据中遇到反斜线字符(’\’),OpenCSV就会遇到问题。 假定dataValue带有反斜线字符: String dataValue = "t\\est"; 输出如下: Original data: t\est,t\est Written data: "t\est","t\est" Read data: test,test 请注意,读取CSV数据中的反斜线字符消失了。 原因 默认情况下,CSVReader使用双反斜线(’\’)作为其转义字符。同时,CSVWriter使用双引号(’“’)作为转义字符。 因此,反斜线字符会导致不正确的转义。在读数据时,CSVParser将忽略单个反斜线字符,因为它是转义字符。 解决方案 默认情况下,CSVReader使用CSVParser解析CSV数据。OpenCSV还提供了一个严格遵循RFC4180标准的解析器:RFC4180Parser。 使用RFC4180Parser解析器,CSVReader会以双引号(’“’)作为转义字符,这样就可以与CSVWriter的转义方式保持一致。 故上面的代码片段可以修改如下: // reading RFC4180Parser rfc4180Parser = new RFC4180ParserBuilder().build(); CSVReaderBuilder csvReaderBuilder = new CSVReaderBuilder(new StringReader(writer.toString())).withCSVParser(rfc4180Parser); try (CSVReader csvReader = csvReaderBuilder.build()) { String[] readData = csvReader.readNext(); System.out.println("Read data: " + readData[0] + "," + readData[1]); } catch (IOException e) { throw new RuntimeException(e); } 执行代码,输出: Original data: t\est,t\est Written data: "t\est","t\est" Read data: t\est,t\est 补充一句,也可以选择Apache Commons CSV开源库,它也是很好的选择。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/79431533 MySQL 8复制性能的增强 2018.3.3 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 MySQL从5.7版发布后没多久,其8.0版本的开发就提上了日程。MySQL在编号方面跳跃了几个版本,6.0被直接丢弃,7.0被保留用于MySQL的集群版本,因为Oracle官方认为,MySQL 5.6版就相当于6.0版,5.7版就相当于7.0版,因此作为5.7版的下一个版本,直接命名为8.0版也就可以理解了。8.0版这个新版本有许多改变(和bug修复),其中最令人兴奋的是复制性能的增强。本文将概述复制性能增强的特性,包括新增的复制时间戳、性能模式表报告的其他信息,以及通过更新复制线程之间的关系以降低复制延迟的效率,从而降低复制延迟。 新的复制时间戳 管理复制过程中最常见的任务是确保实际上正在发生的复制的顺利进行,并且从设备与主设备之间没有错误。使用的主要语句是“SHOW SLAVE STATUS”,它提供了有关从线程的基本参数的状态信息。因此,必须在每个从设备上执行它。以下是一个输出示例: mysql> SHOW SLAVE STATUS\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: localhost Master_User: root Master_Port: 13000 Connect_Retry: 60 Master_Log_File: master-bin.000002 Read_Master_Log_Pos: 1307 Relay_Log_File: slave-relay-bin.000003 Relay_Log_Pos: 1508 Relay_Master_Log_File: master-bin.000002 Slave_IO_Running: Yes Slave_SQL_Running: Yes / * / * / * / Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: 0 Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: ETC... 其中一个输出度量是Seconds_Behind_Master。虽然这完全适用于简单的主从设置,但此度量标准不适合更复杂的复制场景。Seconds_Behind_Master度量有四个主要缺点: 它只报告从设备和最高层主设备之间的延迟。例如,在链式复制设置中,Seconds_Behind_Master只报告相对于原始主设备的延迟,而不提供任何关于从设备与其最近的中间层注设备之间的延迟的信息。 它与原始主设备的时区相关。因此,跨时区的服务器的复制会导致测量的延迟被两台服务器之间的时区差异所抵消。 根据语句的执行开始时间,以每个事件为基础测量延迟。从事务处理实际发生在主事务处理的时间起,更具有洞察力的措施将是每笔事务处理。 用于度量复制延迟的时间戳只能提供精确到最近的秒数。 MySQL 8引入了两个新的时间戳,这些时间戳Seconds_Behind_Master在规避上述问题时补充了度量标准。这些时间戳与每个事务的全局事务标识符(GTID)相关联(与每个事件相对),写入二进制日志。GTID是创建的唯一标识符,并与源(主)服务器上提交的每个事务相关联。此标识符不仅是其源自的服务器,而且是给定复制设置中的所有服务器的唯一标识符。与事务处理相关联,所有事务处理和所有GTID之间存在1对1的映射关系。 这两个新的时间戳是: 原始提交时间戳(OCT,Original Commit Timestamp):将事务写入原始主服务器的二进制日志时的微秒数(起始时间为1970-01-01T00:00:00Z) 即时提交时间戳(ICT,Immediate Commit Timestamp):事务处理写入即时主机的二进制日志花销的微秒数 mysqlbinlog以两种格式显示新的时间戳: 微秒数 在用户时区的TIMESTAMP格式(为了更好的可读性) 从设备的二进制日志中的这段代码显示了两个时间戳: #170404 10:48:05 server id 1 end_log_pos 233 CRC32 0x016ce647 GTID last_committed=0 sequence_number=1 original_committed_timestamp=1491299285661130 immediate_commit_timestamp=1491299285843771 # original_commit_timestamp=1491299285661130 (2018-01-04 10:48:05.661130 WEST) # immediate_commit_timestamp=1491299285843771 (2018-01-04 10:48:05.843771 WEST) /*!80001 SET @@session.original_commit_timestamp=1491299285661130*//*!*/; SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1'/*!*/; # at 288 性能模式表报告的新信息 MySQL 8.0对性能模式进行了一些更改,从而获得更好的性能信息和更多的指标信息: 可以检测服务器错误 现在支持索引 可以向现有的性能模式复制状态表添加新的字段 下面详细地探讨这些内容。 检测服务器错误 MySQL 8引入了五个新的汇总表来帮助处理服务器错误,包括: events_errors_summary_by_account_by_error events_errors_summary_by_host_by_error events_errors_summary_by_thread_by_error events_errors_summary_by_user_by_error events_errors_summary_global_by_error 在所有上述表中,错误统计数据都会被错误汇总。此外,除了events_errors_summary_global_by_error之外,每个表都存储与特定用户、主机、帐户或线程相关的错误;events_errors_summary_global_by_error包含整个服务器的错误。 表结构 每个表都包含以下字段: +-------------------+---------------------+------+-----+---------------------+ | Field | Type | Null | Key | Default | +-------------------+---------------------+------+-----+---------------------+ | ERROR_NUMBER | int(11) | YES | | NULL | | ERROR_NAME | varchar(64) | YES | | NULL | | SQL_STATE | varchar(5) | YES | | NULL | | SUM_ERROR_RAISED | bigint(20) unsigned | NO | | NULL | | SUM_ERROR_HANDLED | bigint(20) unsigned | NO | | NULL | | FIRST_SEEN | timestamp | YES | | 0000-00-00 00:00:00 | | LAST_SEEN | timestamp | YES | | 0000-00-00 00:00:00 | +-------------------+---------------------+------+-----+---------------------+ 注意: FIRST_SEEN/LAST_SEEN列字段表示第一次和最后一次看到的特定错误。 SUM_ERROR_RAISED列字段列出了引发特定错误的次数。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/79349439 Spring Boot 2.0详述 2018.2.22 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 Spring Boot 2.0即将发布,目前已经发布了v2.0.0 RC2版,据传说下周可能就会正式发布。Spring Boot 2.0有一系列重大的改变,下面将一一详述。 一、Spring Boot 2的基线变化 Java 8基线 Spring Boot 2.0需要Java 8以上版本为基础,它不再支持过时的Java 6和Java 7,这可以推动业内开发人员在开发基于Spring Framework的应用时能够充分利用Java 8/9提供的新特性。 支持Java 9 Spring Boot 2.0完全支持Java 9,并且提供了一个专门的网页来汇集Spring Boot with Java 9的相关知识和技巧,具体见:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-with-Java-9。 Spring Framework 5.0 Spring Boot 2.0建立在Spring Framework 5.0之上。尽管Spring框架v5.0自2017年9月发布,但到目前为止,大多数基于Spring框架的项目都没有使用它。估计在Spring Boot 2.0GA版发布后,能快速推动Spring框架v5.0的使用吧。Spring框架v5.0引入了一些很好的改进,其中最值得注意的新特性之一就是它广泛支持构建响应式应用程序。 二、内嵌的Servlet容器 Spring Boot 2.0可以让应用程序内嵌Servlet容器,目前主要提供了三种Servlet容器,包括:Jetty、Tomcat、Undertow。其中,Jetty容器是9.4以上版本;Tomcat容器是8.5以上版本。 TLS配置 可以使用server.ssl.*配置属性为WebFlux应用程序配置SSL,并且这种配置方式对Tomcat、Jetty、Undertow和Reactor Netty等应用服务器均有效。 支持HTTP/2 Spring Boot 2.0应用程序还可以通过server.http2.enabled设置让MVC或WebFlux应用程序能够使用HTTP/2通信协议,并且这种配置方式对Tomcat、Jetty、Undertow应用服务器均有效。具体则依赖于选择的Web服务器和应用程序环境,因为HTTP/2协议不受JDK 8开箱即用的支持。 三、构建Build Maven Maven项目默认使用-parameters编译器标志进行编译。 Gradle Spring Boot 2.0需要Gradle 4.x以上版本。Spring Boot的Gradle插件重写了大量内容,实现了许多重大改进。具体可以阅读:https://docs.spring.io/spring-boot/docs/2.0.0.BUILD-SNAPSHOT/gradle-plugin/reference 配置Gradle bootRun BootRun任务提供了用于配置应用程序参数(args)和JVM参数(jvmArgs)的属性,以及通过execSpec提供了更高级的配置。详细情况可以阅读:https://docs.spring.io/spring-boot/docs/2.0.0.M5/gradle-plugin/reference/html/#running-your-application。根据用户的反馈,这个BootRun任务再次成为Gradle JavaExec任务的一个子类,它可以像任何其他JavaExec任务一样进行配置。 四、JDBC/ORM的变化 默认连接池 Spring Boot 2.0默认使用的连接池已从Tomcat的连接池更改为HikariCP。如果在提供的环境中使用Hikari,tomcat-jdbc包是provided,可以通过spring.datasource.type重写来移除它。同样,如果想要保留Tomcat连接池,只需将以下内容添加到应用的配置中即可: spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource 数据库迁移 Liquibase和Flyway配置键被转移到了spring命名空间(即spring.liquibase和spring.flyway)。 Flyway/Liquibase的灵活配置 如果只提供了自定义url或user,auto-configuration会重用标准数据源属性而不是忽略它们。这使得可以针对迁移数据库的目的创建自定义的数据源DataSource。 数据源DataSource初始化的检测 如果你正在使用Flyway或Liquibase管理你的数据源的模式Schema,以及使用的是嵌入式数据库,Spring Boot 2.0将自动关闭Hibernate的自动DDL功能。 数据库初始化器 只有在使用嵌入式数据库时,应用程序的组件中出现了Spring Batch,Spring Integration,Spring Session或Quartz时,数据库初始化才会默认发生。其原先的enabled属性已被具有更多内容的枚举类型所取代。例如,如果想执行Spring Batch初始化,可以设置: spring.batch.initialize-schema=always 数据源初始化 数据源DataSource的初始化仅针对嵌入式数据源启用的情况,并在应用程序使用生产数据库时立即关闭。此外,新的配置项spring.datasource.initialization-mode(替换原spring.datasource.initialize)提供了更多的控制。 数据源测量 Instrumentation负责监控所有可用的数据源并发布每个指标的度量标准(最小值,最大值和使用率)。 可配置的JPA映射资源 如果将Spring Boot的JPA配置扩展到注册映射资源,则有一个spring.jpa.mapping-resources属性。 JdbcTemplate Spring Boot 2.0的auto-configuration可以通过spring.jdbc.template命名空间自定义JdbcTemplate。此外,NamedParameterJdbcTemplate自动配置的内容会重用JdbcTemplate。 jOOQ Spring Boot 2.0可以根据数据源DataSource自动检测jOOQ方言(与JPA方言所做的相似)。此外,@JooqTest注释还引入了一个简化测试,但只供jOOQ使用。 Hibernate Spring Boot 2.0支持的Hibernate的最低版本是5.2版。可以阅读Hibernate 5.2迁移指南以了解如何升级:https://github.com/hibernate/hibernate-orm/wiki/Migration-Guide—5.2 支持自定义Hibernate命名策略 对于高级场景,可以在上下文中定义ImplicitNamingStrategy或PhysicalNamingStrategy用作常规bean。 Hibernate属性自定义 可以通过暴露HibernatePropertiesCustomizer bean来提供更细粒度的方式自定义Hibernate的属性。 五、NoSQL Cassandra 通过spring.data.cassandra暴露池选项。 支持响应式Couchbase 通过Spring Data响应式库可用于Couchbase,官方提供了一个spring-boot-starter-data-couchbase-reactive轻松上手指南。 InfluxDB 如果设置了InfluxDB的Java客户端和spring.influx.url,那么InfluxDB客户端会自动配置,也支持凭证。health终端可以监控InfluxDB服务器。 Redis缓存配置 可以暴露一个RedisCacheConfiguration来控制RedisCacheManager,同时引入了一个新的注释@DataRedisTest。 Elasticsearch Spring Boot 2.0需要Elasticsearch 5.4以上版本。与Elastic宣布的嵌入式Elasticsearch不再受支持保持一致,原型的自动配置NodeClient已被删除。TransportClient 可以通过使用spring.data.elasticsearch.cluster-nodes提供要连接的一个或多个节点的地址来实现自动配置。 Mongo客户端自定义 通过定义一个MongoClientSettingsBuilderCustomizer类型的bean,Spring Boot的auto-configures可以将高级自定义应用于MongoDB客户端。 六、测试 Mockito 1.x Spring Boot 2.0不再支持Mockito 1.x版的@MockBean和@SpyBean注释。如果不使用spring-boot-starter-test管理你的依赖,就应该升级到Mockito 2.x版。 TestRestTemplate的Kotlin扩展 Spring Boot 2.0提供了Kotlin的RestTemplate扩展,使开发者的体验保持一致。 测试改进 可以通过@WebMvcTest和@WebFluxTest注释自动扫描Converter Bean和GenericConverter Bean。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/79349090 数据湖 2018.2.21 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 我们生活在数据时代,根据Gartner的报告,全球信息数据量每年以59%的速度递增。管理海量数据是一个重大挑战,数据的品种和增长速度使管理变得更加困难。而且非常明显的是,越来越巨量的数据随着时间在继续生成,特别是在手持设备和互联网连接设备数量的指数增长的背景下。 对于参与其中的组织来说,这是事实——但对于其他传统组织来说,数据量的增长并不是那么高。不同组织的数据量不同。尽管存在这种差异,但它们之间的一个共同因素是,对于不同的利益相关方来说,进行有意义且有用的分析的重要性。随着越来越多的组织使用不同功能的工具,为不同利益相关者生成有意义且有用的报告的任务变得越来越具有挑战性。 什么是数据湖? Gartner研究总监Nick Heudecker解释了数据湖: “从广义上讲,数据湖作为企业范围的数据管理平台进行销售,以分析原生格式的不同数据源。这个想法很简单:不是将数据放入专门构建的数据存储区,而是将其移入原始格式的数据湖。这消除了数据摄入的前期成本,如转换。数据放入湖中后,可供组织中的每个人分析。” 因此,数据湖通过打破数据孤岛,帮助企业洞察数据。“数据湖”一词在2010年首次使用,其定义/特征仍在不断演变。一般来说,“数据湖”指的是一个中央存储库,能够存储从各种内部和外部源以接近原始数据的格式获取的Zettabytes数据。 数据湖的挑战 数据湖通常被认为是收集和整理来自遗留系统和来源,数据仓库和分析系统,第三方数据,社交媒体数据,点击流数据以及可能被视为有用信息的所有企业数据企业。虽然这个定义很有趣,但它对每个组织来说都是可行的还是必需的? 不同的组织具有不同的分布式数据挑战和模式,并且随着场景的多样化,每个组织都有自己的数据湖需求。虽然数据的需求,模式,来源和体系结构不同,但在构建中央存储或数据中心方面面临的挑战是相同的: 将来自不同来源的数据导入共同的中央池 处理少量但高度多样化的数据 与数据仓库或大数据相比,将数据存储在低成本基础架构中 与中央数据存储接近实时同步数据 中央数据的可追溯性和治理 数据湖的实施注意事项 在大多数情况下,数据湖与数据即服务模型的实质部署在一起,被视为集中记录系统,为企业级别的其他系统提供服务。本地化数据湖不仅扩展到支持多个团队,而且还生成多个数据湖实例以支持更大的需求。这些集中的数据可以被所有不同的团队用于分析需求。 有了这些理解,就可以在集成和治理方面讨论数据湖泊的各种需求。 数据湖整合的挑战 为了在企业级部署数据湖,它需要具备某些功能,以便将其整合到组织的整体数据管理策略,IT应用程序和数据流环境中。 为了使数据湖的数据在以后的时间点有用,确保湖泊在正确的时间获取正确的数据非常重要。例如,数据湖可能会从企业财务软件中提取月度销售数据。如果数据湖太早接收数据,它可能只会获得部分数据集或根本没有数据。这可能会导致报告不准确,导致公司朝错误的方向发展。因此,将数据总体背景中的数据集成平台运行到数据湖应该能够根据业务情况实时和按需地从各种工具推送数据。 虽然数据库的主要目的是存储数据,但有时(基于不同的业务案例,为了方便其他部门将来使用这些数据),一些数据需要在插入数据之前进行提取或处理湖。因此,集成平台不仅应该支持这一点,还要确保数据处理的准确性和正确的顺序。 只有当存储的数据可以被所有不同部门提取以供自己使用时,集中式数据存储才是有用的。应该有能力将数据湖与其他应用程序或下游报告/分析系统集成。数据湖应该也支持REST API,不同的应用程序可以通过它们交互来获取或推送他们自己的数据。 数据湖治理的挑战 数据湖不仅仅是集中存储数据并在需要时将其提供给不同的部门。随着越来越多的用户开始直接使用数据湖或通过下游应用程序或分析工具,数据湖治理的重要性也随之增加。数据湖通过将来自不同储存库的多样化数据集引入单一储存库,创造了新的挑战和机遇。 主要挑战是确保数据治理政策和程序的存在并在数据湖中实施。每个数据集的拥有者在进入湖泊时都应该有明确的定义。应该有一个关于每个数据所需的可访问性,完整性,一致性和更新的非常详细的政策或指南。 为了解决上述问题,数据湖中应该有内置的机制来跟踪和记录数据湖中存在的任何数据资产的操纵。 数据湖对每个人都是一样的吗? 对于所有组织而言,数据湖的实施情况并不相同,因为数据量和数据收集要求因组织而异。总的来说,数据湖带来的观念是数据量应该在PB级或甚至更多,并且需要使用NoSQL数据库来实现。实际上,这些数据量和NoSQL DB的实现可能并不是所有组织都可能需要或可能无法实现的。拥有适合组织所有分析需求的中央数据存储的最终目标可以从SQL DB开始,并具有相当大的数据量。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/79332630 CentOS设置精准时间 2018.2.17 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 本文主要讲述如何在CentOS发行版中快速获取准确的服务器时间。通常情况下,如果您用户是将CentOS安装在桌面环境中,那么可以通过GUI的“启用网络时间协议”功能将计算机配置为通过远程服务器同步其时钟,这种方法最简单。 但是,有时上述功能无法按预期工作。那么我们可以通过命令行设置精确的服务器时间。 下面均假设为root用户的操作,如果不是root权限的用户,那么虚加上sudo命令获取root权限。 可以使用ntp和ntpdate命令行实用程序来执行此操作,该实用程序通过NTP设置系统日期和时间。如果您的系统中未安装此软件包,请运行以下命令进行安装: # yum install ntp ntpdate 安装软件包后,启动并启用ntpd服务,并按如下所示查看其状态。 # systemctl start ntpd # systemctl enable ntpd # systemctl status ntpd 然后运行下面的ntpdate命令来添加指定的CentOS NTP服务器。这里,-u选项告诉ntpdate使用非特权端口输出数据包,并-s选项启用从标准输出(默认)将输出记录到系统syslog工具。 # ntpdate -u -s 0.centos.pool.ntp.org 1.centos.pool.ntp.org 2.centos.pool.ntp.org 接下来,重新启动ntpd守护进程以将CentOS NTP服务器日期和时间与当地日期和时间同步。 # systemctl restart ntpd 现在使用timedatectl命令检查是否启用了NTP同步并且它是否实际同步。 # timedatectl 最后,使用hwclock实用程序,使用以下-w选项将硬件时钟设置为当前系统时间。 # hwclock -w 更详细的文档可以参阅ntpdate和hwclock的man pages。 # man ntpdate # man hwclock 如果担心NTP服务出现异常,那么可以指定专门的日志输出(编辑/etc/ntp.conf配置文件): logfile /var/log/ntp.log NTP是网络时间协议(Network Time Protocol),它用于同步网络设备(如计算机、手机等设备)的时间的协议。 国内常用的NTP服务器有: cn.pool.ntp.org Windows系统自带:time.windows.com和time.nist.gov MacOS X系统自带:time.apple.com和time.asia.apple.com cn.ntp.org.cn 阿里云NTP服务器:ntp1.aliyun.com、ntp2.aliyun.com、ntp3.aliyun.com、ntp4.aliyun.com、ntp5.aliyun.com、ntp6.aliyun.com、ntp7.aliyun.com 腾讯云NTP服务器:ntpupdate.tencentyun.com 国家授时中心服务器:210.72.145.44 清华大学NTP服务器:s1b.time.edu.cn、s1e.time.edu.cn、s2a.time.edu.cn、s2b.time.edu.cn
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/79332551 PowerShell在Ubuntu系统的使用 2018.2.17 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 本文主要介绍如何在Ubuntu 16.04 LTS上安装和使用PowerShell。要知道,PowerShell Core是微软公司推出的一个跨平台(Windows,Linux和macOS)自动化和配置工具/框架,可与现有工具很好地配合使用,并对结构化数据(如JSON, CSV,XML等),REST API和对象模型的处理做了优化。PowerShell包括一个命令行shell,一个相关的脚本语言和一个处理cmdlet的框架。 下面先介绍在Ubuntu 16.04(Xenial Xerus)服务器上逐步安装Microsoft PowerShell的过程。 在Ubuntu 16.04 LTS上安装PowerShell 步骤1:首先在终端中运行以下apt-get命令,确保所有系统软件包都是最新的。 # sudo apt-get update # sudo apt-get upgrade 步骤2:在Ubuntu 16.04上安装PowerShell。有两种方法。 方法1:使用Debian软件包安装 首先,将Debian软件包下载到你的Ubuntu服务器上: # wget https://github.com/PowerShell/PowerShell/releases/download/v6.0.1/powershell_6.0.1-1.ubuntu.16.04_amd64.deb # dpkg -i powershell_6.0.1-1.ubuntu.16.04_amd64.deb 使用以下命令安装PowerShell: # apt-get install -f 方法2:基于微软提供的软件仓库安装 使用官方的Ubuntu安装PowerShell Microsoft Repository: # curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list | sudo tee /etc/apt/sources.list.d/microsoft.list 然后在终端中执行以下操作: # apt-get update # apt-get install -y powershell 安装后,就可以运行powershell,只需在提示符下输入以下命令“pwsh”即可: # pwsh 此时已成功安装PowerShell。 PowerShell介绍 Windows PowerShell是专门为系统管理员设计的Windows命令行Shell。Windows PowerShell包含了可以单独或组合使用的交互提示和脚本编写环境。 与大多数Shell(它们接受和返回文本)不同,Windows PowerShell是在dotNET Framework公共语言运行时(CLR)和dotNET Framework的基础上生成的,它接受和返回dotNET Framework对象。环境中的这一基本更改为Windows的管理和配置带来了全新的工具和方法。 Windows PowerShell引入了cmdlet(读作“command-let”)的概念,它是内置于Shell的简单的单一函数命令行工具。可以分别使用每个cmdlet,但只有组合使用这些简单的工具来执行复杂的任务时,你才会意识到它们的强大功能。Windows PowerShell包含了一百多个基本核心cmdlet,你可以编写自己的cmdlet并与其他用户共享。Windows PowerShell旨在通过消除长期存在的问题和添加新功能改进命令行和脚本环境。 PowerShell v6.0.1版的变化主要如下: 使用的dotNet Core Runtime和包升级到2.0.5版 修复了数个安全问题 PowerShell的基本操作: 更改计算机状态 收集有关计算机的信息 兼容性别名 创建自定义PowerShell快捷方式 创建dotNET和COM对象(New-Object) 创建自定义输入框 创建图形日期选取器 获取WMI对象(Get WmiObject) 管理当前位置 使用Process Cmdlet管理进程 管理服务 管理Windows PowerShell驱动器 直接操作项 多选列表框 其他有用的脚本对象 执行网络任务 使用Out Cmdlet重定向数据 从管道中删除对象(Where对象) 为多个对象重复执行任务(ForEach 对象) 从列表框中选择项 选择对象部件(Select对象) 对对象进行排序 使用格式命令更改输出视图 使用静态类和方法 查看对象结构(Get Member) 使用文件和文件夹 使用文件、文件夹和注册表项 使用对象 使用打印机 使用注册表条目 使用注册表项 使用软件安装
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/79233855 Java 9新增的有趣方法InputStream.transferTo() 2018.2.1 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 JDK 9新增了一个有趣的方法,InputStream.transferTo(OutputStream)方法,此方法允许从对象调用方法表示的输入流中轻松传输(复制)字节到提供给该方法的输出流。正如方法的Javadoc注释所述,从该输入流中读取所有字节,并按照读取的顺序将字节写入给定的输出流。此方法可以使Java的有些例行任务变得更容易。 在使用此方法时要注意: 此方法不关闭任何流 强烈建议,如果发生I/O错误,那么输入输出两个流都应该立即关闭。 通常是在try-with-resources语句中实例化源InputStream和目标OutputStream对象。如下面的代码所示: import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class StreamsTransfer { public static void main(String[] args) { if(args.length < 1) { System.out.println("USAGE StreamsTransfer <fileName>"); System.exit(-1); } final String fileName = args[0]; try(final InputStream is = new FileInputStream(fileName); final OutputStream os = new FileOutputStream(fileName + ".copy")) { is.transferTo(os); } catch(IOException e) { System.out.println("Exception encountered: " + e); } } } 通过try-with-resources语句可以确保两个资源的正确回收。上面的代码很简单,实际的应用场景就非常多了,比如可以把InputStream替换成URL.openStream()等等。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/79059116 JSON-B和Yasson详解 2018.1.14 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 JSON-B是Java EE 8规范中的新API之一。它用于解决标准化Java对象如何在JSON中进行序列化。 JSON-B,即Java API for JSON Binding,用于JSON绑定的Java API,是一套最新的API,也是Java EE 8规范的一部分。JSON-B规范试图标准化Java对象在JSON中序列化/反序列化的方式,并在这方面定义了一套标准的API。 本文将展示JSON-B API提供的核心功能。 一、建立项目 创建一个新的Maven项目,添加如下依赖关系: <dependencies> <dependency> <groupId>javax.json.bind</groupId> <artifactId>javax.json.bind-api</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.eclipse</groupId> <artifactId>yasson</artifactId> <version>1.0.1</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.json</artifactId> <version>1.1.2</version> </dependency> </dependencies> 注意:由于Yasson(JSON-B参考实现)在内部依赖于JSON-P API,所以对类路径添加了对JSON-P参考实现的依赖。如果将来使用另一个JSON-B实现,或者如果要在运行时已经提供此依赖关系的应用程序服务器上部署应用程序,则可能不需要这样做。 二、Eclipse Yasson Eclipse Yasson是JSON-B规范的参考实现,与XML领域的JAXB相似。Yasson是JSR-367规范(JSON Binding)官方的参考实现。 Yasson定义了一个在现有Java类和JSON文档之间的默认的映射算法,适合大多数情况: Jsonb jsonb= JsonbBuilder.create(); String result = jsonb.toJson(someObject); 如果还不够,可以使用它提供的一组丰富的注释和API来实现定制的功能: // 创建自定义配置 JsonbConfig config = new JsonbConfig() .withNullValues(true) .withFormating(true); // 使用自定义配置来创建Jsonb Jsonb jsonb = JsonbBuilder.create(config); // 使用它 String result = jsonb.toJson(someObject); 三、JSON-B API JSON-B API在javax.json.bind包内提供。最重要的接口是Jsonb,它可以通过名为JsonbBuilder的构建器类来实例化。当有这个类的引用时,你可以调用toJson或fromJson方法之一来对JSON字符串进行序列化和反序列化。 Shape shape = new Shape(); shape.setArea(12); shape.setType("RECTANGLE"); Jsonb jsonb = JsonbBuilder.create(); // 将对象shape序列化为JSON String jsonString = jsonb.toJson(shape); // 输出:{"area":12, "type":"RECTANGLE"} // 将一个JSON字符串反序列化为一个对象 Shape s = jsonb.fromJson("{\"area\":12, \"type\":\"RECTANGLE\"}"); 请注意,对于反序列化过程,类应该有一个默认的构造函数,否则会抛出一个异常。 JSON-B的API非常精炼,几乎不涉及复杂性。在javax.jsonb包下提供的所有其他API和注解(数量很少)仅用于定制序列化和反序列化的过程。 四、基本的Java类型映射 Java基本类型及其相应的包装类被序列化为JSON的方式遵循其toString()方法文档定义的转换过程。同样,对于反序列化过程,使用其parse()方法(parseInt,parseLong等)定义的转换过程。然而,参考实现没有必要调用这些方法,只要遵守它们的转换过程即可。 为了演示如何将这些类型映射到JSON,请考虑一个名为Apartment的类,其结构如下: public class Apartment { private boolean rented; private byte rooms; private int price; private long id; private float loan; private double area; private char district; private String ownerName; public boolean isRented() { return rented; } public void setRented(boolean rented) { this.rented = rented; } public byte getRooms() { return rooms; } public void setRooms(byte rooms) { this.rooms = rooms; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } public long getId() { return id; } public void setId(long id) { this.id = id; } public float getLoan() { return loan; } public void setLoan(float loan) { this.loan = loan; } public double getArea() { return area; } public void setArea(double area) { this.area = area; } public char getDistrict() { return district; } public void setDistrict(char district) { this.district = district; } public String getOwnerName() { return ownerName; } public void setOwnerName(String ownerName) { this.ownerName = ownerName; } } 下面的代码片段试图序列化这个类的一个实例: import javax.json.bind.Jsonb; import javax.json.bind.JsonbBuilder; public class Application { public static void main(String[] args) { Apartment apartment = new Apartment(); apartment.setRented(true); apartment.setRooms((byte) 4); apartment.setPrice(7800000); apartment.setId(234L); apartment.setLoan(3580000.4f); apartment.setArea(432.45); apartment.setDistrict('S'); apartment.setOwnerName("Nerssi"); Jsonb jsonb = JsonbBuilder.create(); String jsonString = jsonb.toJson(apartment); System.out.println(jsonString); } } 执行程序,JSON输出显示如下(添加注释使其更具可读性): { "area":432.45, // double "district":"S", // char "id":234, // long "loan":3580000.5, // float "ownerName":"Nerssi", // String "price":7800000, // int "rented":true, // boolean "rooms":4 // byte } 可以看出,每个字段使用的值与在相应的包装类上调用toString方法完全相同。此外,字符串String类和字符Character类都转换为UTF-8字符串。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/78987753 在Windows 10上搭建TensorFlow环境 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 了解如何为受支持的GPU测试Windows系统,安装和配置所需的驱动程序,获取最新的TensorFlow每日构建版并确保其正常工作。 在前面的文章中,我展示了如何测试你的Linux系统,看看你是否能够按照带GPU支持的TensorFlow。在本文中,我将介绍如何在Windows 10上搭建TensorFlow环境。 同样,仍然需要Python环境和pip工具。 1、获取Python和pip 在Windows环境安装Python 3.x版本,通常安装包中集成了pip工具,安装会非常简单方便。具体见:https://www.python.org/downloads/windows/ 一旦下载并执行,需要确保选择安装的自定义选项。可以看到如下界面: 完成安装后,可以打开命令提示符并键入python,以查看您正在使用的版本。这里可以看到,我下载了3.6.4版: 然后可以退出Python解释器环境: exit() 然后测试pip工具安装的情况: pip -V 可以看到这样: 接下来,在安装TensorFlow之前,需要先检查主机的GPU是否支持,在命令提示符下,执行命令: control /name Microsoft.DeviceManager 然后查看“显示适配器”设置,将其打开,然后阅读显示适配器的名称,如下: 正如你所看到的,主机系统有一个GTX 980 Ti。然后去NVIDIA官网查看信息,具体见:https://developer.nvidia.com/cuda-gpus,就可以知道是否可以使用带GPU支持的TensorFlow。这里已经确定是支持的。但是在安装和运行TensorFlow之前,需要为你的机器安装CUDA驱动。 2、安装CUDA驱动程序 要说明一点,目前TensorFlow的每日构建版提供了对CUDA 9.0的支持,而Release版则只能支持CUDA 8.0版。如果访问CUDA的下载网站,见:https://developer.nvidia.com/cuda-toolkit,可以看到CUDA目前的最新版本是9.1版,因此请确保通过选择下面的“Legacy Releases”链接来下载正确版本的驱动程序。 在运行TensorFlow之前,还需要一个与主机的CUDA版本相匹配的CuDNN版本。 3、安装TensorFlow 安装TensorFlow的Nightly Build版。从命令提示符下安装它,只需输入: pip install tf-nightly-gpu 一旦安装完成,在命令提示符窗口中输入: python 打开Python编辑器,在其中输入: import tensorflow as tf 如果CUDA驱动程序有错误,就可能会显示 cudart64_XX.dll 失败,其中XX是版本号。 如果CUDA驱动程序正确,但CuDNN驱动程序有错误,就可能会显示说 cudnn64_X.dll 缺少什么东西,其中X是一个版本号。 4、安装CuDNN库 CuDNN库是CUDA针对深度神经网络的更新包,TensorFlow会使用它用于加速NVidia GPU上的深度学习。可以从这里下载,见:https://developer.nvidia.com/cudnn。 但必须首先要注册一个NVidia开发者帐号,它是免费的。登录后,您会看到各种CuDNN下载。然后选择匹配的版本。由于之前使用了CUDA 9.0,所以确定为CUDA 9.0选择了cuDNN v7.0.5。 下载下来的是一个包含了几个文件夹的ZIP文件,每个文件夹包含CuDNN文件(一个DLL,一个头文件和一个库文件)。找到你的CUDA安装目录,这里应该是这样的: C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0 可以看到从ZIP文件的目录也在这个目录,即有一个bin、一个include,一个lib等。将文件从ZIP复制到相关的目录。 比如把cudnn64_7.dll文件拖拽到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin目录,其它相似。 完成后,重新打开命令提示符窗口并再次测试TensorFlow! import tensorflow as tf 然后,可以输入以下内容来检查TensorFlow版本: print(tf.__version__) 可以看到TensorFlow的版本得以正确显示:
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/78987532 在Ubuntu搭建TensorFlow环境 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 我一直想要最大化TensorFlow的计算能力,于是我决定在家用电脑上安装和优化它。这里,我将分享如何选择安装TensorFlow的某个版本——它并不像看起来那么容易,因为你需要了解系统功能,而且涉及到GPU、以及你正在使用的Python版本。在选择TensorFlow的版本时,可以选择“Release”版本,也可以选择每日构建版。本文讲述如何轻松获得每日构建版并安装到自己的机器上运行。(系统环境是Ubuntu Linux) 在安装TensorFlow前,有四个主要选项: Python 2.x Python 2.x + GPU Python 3.x Python 3.x + GPU 带GPU支持的版本可以充分利用GPU的计算能力,但首先要检查您的系统是否支持CUDA GPU。 打开终端,执行命令: # lspci | grep -i nvidia 看看返回了什么,比如我的: 查看GPU型号名称:GeForce GTX 860M,然后可以在NVIDIA官网检查是否兼容CUDA的信息,具体见:https://developer.nvidia.com/cuda-gpus 可以看到支持我的GPU,所以可以安装带GPU的TensorFlow版本。查看TensorFlow网站,具体见:https://www.tensorflow.org/install/install_linux 可以看到有一个我需要安装的依赖库libcupti。安装它: # sudo apt-get install libcupti-dev 关于如何安装TensorFlow本身,可以阅读:https://www.tensorflow.org/install/install_linux#InstallingNativePip 使用Nightly Builds版安装TensorFlow 接下来需要确定本机的Python版本以及其相关的pip工具。如果还没有Python环境,应该先安装它们。可以参阅系统说明:https://www.python.org/ Ubuntu Linux默认自带了Python 2.7,就将就这个版本吧,先安装pip工具: # sudo apt-get install python-pip python-dev 请注意,如果您使用Python 3.x,就应该使用软件包python3-pip和python3-dev。 还要注意,pip工具的版本至少应为8.1以上的版本。(用pip -V检查版本) 一旦完成了这一切,就可以简单地安装Nightly Build版的TensorFlow: # sudo pip install tf-nightly-gpu 或者,如果您的系统不支持CUDA(请参阅上面的部分),请使用: # sudo pip install tf-nightly 现在可以使用以下步骤检查TensorFlow是否工作。 首先:打开一个Python解释器: # python 然后,导入TensorFlow库: import tensorflow as tf 请注意,如果没有正确安装TensorFlow,或者将带GPU的版本安装在不受支持的系统上,就会在此处出现错误。CUDA错误在这一步上非常普遍。如果有效,就可以试试打印出TensorFlow的版本: print(tf.__version__) (注意version前后有2个下划线) 完成后,您应该看到打印出的TensorFlow版本 - 如下所示: 可以看到,由于使用了最新的每日构建版,所以TensorFlow当前的最新版本是1.4。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/78975731 自然语言处理NLP概述 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 自然语言处理(Natural language processing,NLP)是计算机和人类语言之间的关系纽带。更具体地说,自然语言处理是计算机对自然语言的理解、分析、操纵和/或生成。计算机程序能否将一段英文文本转换成程序员友好的数据结构来描述自然语言文本的含义?不幸的是,这种数据结构的形式是否存在并没有形成共识。在解决这些基本的人工智能问题之前,计算机科学家必须解决提取描述文本信息有限方面的简单表示的简化目标。 概述自然语言处理 自然语言处理(NLP)可以被定义为人类语言的自动(或半自动)处理。“NLP”这个术语有时被用于比这更窄的范围,通常不包括信息检索,有时甚至不包括机器翻译。NLP有时还与“计算语言学”相对立,NLP被认为更适用。如今,往往首选使用替代术语,如“语言技术(Language Technology)”或“语言工程(Language Engineering)”。语言(Language)经常与演讲(Speech)(比如演讲技术和语言技术)相对照。但是我将简单地提到NLP并广义地使用这个术语。NLP本质上是多学科的:它与语言学密切相关(尽管NLP公然借鉴语言理论的程度差异很大)。 什么是自然语言处理? NLP是计算机以一种聪明而有用的方式分析,理解和从人类语言中获取意义的一种方式。通过利用NLP,开发者可以组织和构建知识来执行自动摘要,翻译,命名实体识别,关系提取,情感分析,语音识别和话题分割等任务。NLP用于分析文本,使机器了解人的说话方式。这种人机交互使现实世界的应用,如自动文摘,情感分析,主题提取,命名实体识别,零部件,词性标注,关系提取,词干,等等。NLP通常用于文本挖掘,机器翻译和自动问答。 图1:NLP技术 NLP的重要性 早期的NLP方法涉及更基于规则的方法,在这种方法中,简单的机器学习算法被告知要在文本中查找哪些单词和短语,并在这些短语出现时给出特定的响应。但深度学习是一个更灵活,直观的方法,在这个方法中,算法学会从许多例子中识别说话者的意图,就像孩子如何学习人类语言一样。 在考虑以下两个陈述时,可以看到自然语言处理的优势:“云计算保险应该成为每个服务级别协议的一部分”和“良好的SLA确保夜间睡眠更加容易 - 即使在云端”。如果您使用国家语言处理的搜索,程序将认识到云计算是一个实体,云是云计算的缩写形式,并且SLA是服务级别协议的行业首字母缩略词。 自然语言处理的术语 这些分区与语言学的一些标准分支松散地相对应: 形态学(Morphology):词的结构。例如,不寻常的可以被认为是由一个前缀un-,一个词干和一个词缀-ly组成。构成是构成加上屈折词缀:拼写规则意味着我们结束而不是组成。 语法(Syntax):单词用于形成短语的方式。例如,它是英语语法的一部分,诸如意志之类的确定者会在名词前面出现,而且确定者对于某些单数名词是强制性的。 语义(Semantics):构成语义是基于语法的意义的建构(通常表示为逻辑)。这与词汇语义(即单词的含义)形成对照。 自然语言处理的应用 以下是目前使用NLP的几种常用方法: Microsoft Word中的拼写检查功能是最基本和最知名的应用程序。 文本分析也称为情感分析,是NLP的一个关键用途。企业可以使用它来了解他们的客户感受到的情绪,并使用这些数据来改善他们的服务。 通过使用电子邮件过滤器分析流经其服务器的电子邮件,电子邮件提供商可以使用朴素贝叶斯垃圾邮件过滤来计算电子邮件基于其内容的可能性。 呼叫中心代表经常听到来自客户的相同的具体投诉,问题和问题。挖掘这些数据的情绪可以产生令人难以置信的可操作的情报,可以应用于产品布局,消息传递,设计或其他一系列的用途。 Google,Bing和其他搜索系统使用NLP从文本中提取条件来填充其索引并解析搜索查询。 Google Translate将机器翻译技术应用于翻译单词,而且还用于理解句子的含义以改善翻译。 金融市场使用NLP,通过明文公告和提取相关信息的格式进行算法交易决策。例如,公司之间合并的消息可能会对交易决策产生重大影响,并且合并细节(例如,参与者,价格,谁获得谁)的速度可以被纳入到交易算法中,在数百万美元。 自然语言处理的例子 使用Summarizer自动总结一个文本块,严格的主题句子,并忽略其余的。 生成关键字话题标签文档使用LDA(隐含狄利克雷分布),它从一个确定最相关的词文件。该算法是自动标记和自动标记URL微服务的核心 基于Stanford NLP的情感分析可以用来辨别一个陈述的感觉,观点或信念,从非常消极,中立到非常积极。 参考 [1] Ann Copestake, “Natural Language Processing”, 2004, 8 Lectures, available online at: https://www.cl.cam.ac.uk/teaching/2002/NatLangProc/revised.pdf [2] Ronan Collobert and Jason Weston, “Natural Language Processing (Almost) from Scratch”, Journal of Machine Learning Research 12 (2011) pp. 2493-2537 [3] “Top 5 Semantic Technology Trends to look for in 2017”, available online at: https://ontotext.com/top-5-semantic-technology-trends-2017/
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/78975666 PyCharm:适用于专业开发人员的Python IDE 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 如今,Python早已成为流行的高级编程语言。它很容易学习,有清晰的语法和缩进结构,可以让具有其他编程语言背景的程序员能够快速地掌握Python,并且初学者发现使用和掌握它非常简单。 集成开发环境IDE可以带来良好的和不好的编程体验,一个优秀的、适合Python语言的IDE是Pycharm。 Pycharm是一个功能强大且跨平台的Python IDE,集成了所有所需的开发工具。它功能丰富,并提供了社区版(免费和开源)和专业版。 Pycharm特性 高度可定制的和可插拔的 提供了智能代码完成 提供了代码检查功能 有突出的错误提示和快速修复 带有自动化代码重构和丰富的导航功能 内置了很多开发工具,如集成调试器和测试运行器、Python分析器、一个内置的终端|与主要的VCS和内置的数据库工具集成等 提供了多种Web开发工具和框架,特定的模板语言,如JavaScript、TypeScript、CoffeeScript、Node.js、HTML/CSS等 还提供了Matplotlib和NumPy等科学工具库 本文将向您展示如何在Linux系统中安装PyCharm IDE社区版。 如何在Linux中安装PyCharm IDE 首先进入PyCharm下载部分,获取PyCharm社区版的最新版本,或者使用下面的wget命令将其直接下载到终端。 $ wget https://download.jetbrains.com/python/pycharm-community-2017.3.2.tar.gz $ tar -xvf pycharm-community-2017.3.2.tar.gz $ cd pycharm-community-2017.3.2/ 要像任何其他命令一样运行pycharm,需要创建一个软链接到pycharm可执行文件并按如下所示运行pycharm。 $ sudo ln -s ./pycharm-community-2017.3.2/bin/pycharm.sh /usr/bin/pycharm $ pycharm 注意:Pycharm目前有快照包可直接安装使用。Ubuntu 16.04或更高版本的用户可以从命令行来安装它: $ sudo snap install [pycharm-professional|pycharm-community] --classic 接下来,要求用户通过点击“accept”接受Pycharm的隐私政策协议,如下面的屏幕截图所示。 之后,将看到Pycharm的欢迎页面。 现在创建一个项目测试一下环境是否能正常工作。创建一个新项目,输入一个名称,然后点击“Create”。 要查阅Pycharm的文档,见:https://www.jetbrains.com/pycharm/documentation/
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/78631023 使用Speedment实现事务处理 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 一、Speedment介绍 Speedment是一个开源的、基于Java的、流式ORM工具包和运行时工具,它把对现有数据库和表的各种操作封装成Java 8的Stream操作。Speedment的新版本还提供了支持数据库事务处理的便捷操作方式。 Speedment在GitHub的地址:https://github.com/speedment/speedment 一行代码 搜索时长大于120分钟的电影。 使用SQL查询: SELECT `film_id`, `title`, `description`, `release_year`, `language_id`, `original_language_id`, `rental_duration`, `rental_rate`, `length`, `replacement_cost`, `rating`, `special_features`, `last_update` FROM `sakila`.`film` WHERE (`length` > 120) 使用Speedment: Optional<Film> longFilm = films.stream() .filter(Film.LENGTH.greaterThan(120)) .findAny(); 可见,使用Speedment无需手动编写SQL语句,而且代码更简洁。 二、Speedment实现事务处理 本文讲述怎样学习使用开源的Speedment ORM工具和Java 8/9实现数据库的事务处理。 有时我们要确保我们的数据库操作是原子执行的,并且与其他数据库操作是相互隔离的。这就是事务处理发挥作用的地方。事务处理是一组操作建议,可以被数据库管理系统视为原子操作。因此,事物处理中的所有操作要么都被接受,要么都不接受。事务处理的另一个优点是事务处理开始时,数据库的状态将在本地“冻结”,所以在事务处理中我们不会看到其他线程的更新。 更新 想象一下,假设我们正在填写一个银行账户操作的申请表,我们想从一个账户(1)转移100美元到另一个账户(2)。在这种情况下,最重要的是我们的钱不能丢失(也即从账户1中扣除了100美元,但却没存入账户2中),或者甚至更糟(即账户2存入了100美元但不是从账户1中扣除的金额,银行会找用户的麻烦)。遇到这种情况,我们可以使用如Speedment这样的数据库事务处理来保证安全: txHandler.createAndAccept(tx -> Account sender = accounts.stream() .filter(Account.ID.equal(1)) .findAny() .get(); Account receiver = accounts.stream() .filter(Account.ID.equal(2)) .findAny() .get(); accounts.update(sender.setBalance(sender.getBalance() - 100)); accounts.update(receiver.setBalance(receiver.getBalance() + 100)); tx.commit(); } 当调用tx.commit()方法时,两个更新将以原子操作的方式提交给数据库,并对其他所有线程可见。如果我们不显式调用tx.commit()方法,那么事务将自动回滚(即更新不会有任何影响,相关操作都被丢弃)。 准备工作 在使用事务处理之前,我们需要获取一个TransactionHandler对象,比如这样: BankApplication app = .... TransactionComponent transactionComponent = app.getOrThrow(TransactionComponent.class); TransactionHandler txHandler = transactionComponent.createTransactionHandler(); 然后AccountManager可以从应用程序中检索,比如: AccountManager accounts = app.getOrThrow(AccountManager.class);
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/78623986 MariaDB AX开源分析解决方案 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 MariaDB公司推出了新的增强产品——MariaDB AX,它为数据仓库提供了一种现代化的方法,使得客户能够更经济高效地执行快速可扩展的分析。MariaDB AX扩展了MariaDB服务器,提供了一个解决方案,支持使用分布式存储和并行处理的高性能分析,并可以在线或在任何云平台上的现有硬件环境上进行扩展。 MariaDB AX是一款功能强大的开源解决方案,可用于执行定制和复杂的分析。为了充分发挥大数据的力量,让MariaDB的客户需要能够近乎实时地收集数据,而不管数据来自何处。利用MariaDB AX,从不同来源获取和分析流数据比以往更容易,同时通过新的高可用性和备份功能确保最高水平的可靠性。 传统的数据仓库价格昂贵并且操作复杂,因此需要更有意义、能够更及时的分析以满足硬件和成本压力。MariaDB AX专为性能和可扩展性而构建,使用分布式和列式开源存储引擎,并行查询处理,允许客户存储更多数据并更快速地进行分析。MariaDB AX支持各行各业的高级分析用例,例如识别健康趋势以告知医疗保健计划和政策,行为分析以通知客户服务和销售策略,并分析财务异常情况以通知欺诈计划。 MariaDB AX包含一些新功能,可实现轻松,高性能的数据分析,例如: 简化和简化数据提取 使数据可以快速轻松地进行分析。MariaDB AX通过引入数据适配器和对底层存储引擎的直接访问,消除了导入数据所需的复杂,耗时的批处理过程。通过缩短数据可用于分析的时间,企业可以实现更快速的洞察力及其带来的竞争优势。新的数据适配器包括: 批量数据适配器 开发应用程序和服务以持续收集和写入MariaDB AX的大量数据进行分析,或者将机器学习结果发布到MariaDB AX,供数据科学家和其他人通过SQL查看或进一步分析。 MariaDB MaxScale的流式数据适配器 在创建的几秒钟内,几乎实时地分析操作数据。将MariaDB TX部署用于运行工作负载时,数据适配器使用MariaDB MaxScale和change-data-capture流自动连续地将数据从操作环境复制到MariaDB AX。 针对Apache Kafka的流式数据适配器 通过汇总来自Web,移动和物联网应用程序的数据,合并和分析来自多个来源的数据。目前处于测试阶段的数据适配器使用发布到Apache Kafka主题的消息,并将数据写入MariaDB AX,使数据科学家能够分析来自多个来源的相关数据。 更强大的自定义分析 通过为关系数据和非关系数据(如JSON)创建自定义分析函数,扩展MariaDB AX的分析功能。引入用于创建用户定义的聚合和窗口函数的API,并结合对文本和二进制数据的支持,使数据科学家能够使用自定义分析函数分析结构化,半结构化和非结构化数据。 改进的高可用性和灾难恢复 确保数据始终可用于分析,并简化备份和恢复数据的过程。MariaDB AX增加了对GlusterFS的支持,以提供数据高可用性,而不需要昂贵的SAN,而新工具自动使用多个并发连接来从一个位置备份和恢复多个服务器上的分布式数据。 MariaDB AX现在可以下载,见:https://mariadb.com/downloads/mariadb-ax
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/78591647 支持JavaEE 8和Java 9的IDE和服务器探讨 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 作为Java领域最重量级的升级,JavaEE 8和Java 9发布了。使用这些新技术单独看起来简单,但是要把它们组合起来就很复杂了,实际上很难让支持者两种新技术的IDE和服务器组合在一起工作。 这个问题的第一步是寻找支持Java 9的IDE,目前所有主要的IDE都提供了对Java 9的支持: Eclipse Oxygen IntelliJ IDEA 2017.3 for Java 9 Netbeans 8.2 第二步是寻找适合JavaEE 8的应用程序服务器 这一步比较复杂,因为目前兼容并支持JavaEE 8的应用程序服务器只有: Glassfish 5.0 Payara 5 但是比较复杂的地方是Eclipse还不支持Glassfish 5。不确定IntelliJ是否支持,这就只剩NetBeans 8.2了。 如上所述,必须同时安装Java 8和Java 9。这不算问题。 设置Netbeans Eclipse支持Glassfish 5的帖子见:https://github.com/javaee/glassfish/issues/22279 Netbeans 8.2设置支持Glassfish 5.0的帖子见:https://github.com/javaee/j1-hol#initial-setup 其焦点是Java 8,要注意Glassfish 5.0还不支持Java 9,因此必须使用Java 8。Glassfish 5.0.1即将发布,说是会支持Java 9。 JavaEE 8 很多项目并不需要完整的Java EE 8支持,因为只需特定的组件即可。通常会使用的组件有: Servlet 4.0 JAX-RS 2.1 JSF2.3 CDI 2.0 JPA 2.1 应用服务器WildFly 11将会支持Java 9。 从以上列出的信息来看,目前想在项目中同时实践JavaEE 8和Java 9还非常困难,但时间会解决这一切!
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/78589659 Java 9模块化特性及工具探讨 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 Java 9于2017年九月下旬发布。随着Java 9的发布,它变得模块化了,并通过模块化提高其可伸缩性和性能。模块化是一个普遍的概念。在软件中,它适用于编写和实现一个程序或作为独立模块的计算系统,而不是一个单一的、单体式的设计。在模块化的帮助下,程序员可以在特定的sprint周期或项目中独立地进行功能测试,并行参与开发工作。这在整个软件开发生命周期中都会提高效率。 但并不是所有的Java工具都支持Java 9的模块化功能。本文概述了支持Java模块化的特性,并提供了支持Java 9新模块化功能的工具列表。 支持模块化的特性: Java 9是独特的,因为它在整个JDK中引入了模块化组件和片段。支持模块化的主要特点包括: 模块化源代码 JRE和JDK将重新安排到Java 9中的可互操作模块中,这支持创建可在小型设备上执行的可扩展运行时。 构建时执行 在Java 9中,构建系统将通过JEP 201进行编译和实施模块边界。 部署工具 在Jigsaw项目中提供了工具,在部署时支持模块化边界、约束和依赖关系。 分段的代码缓存 它不是一个严格的模块化设施,但是Java 9新的分段代码缓存将遵循模块化的思想,并享有一些相同的好处。 适用于Java 9模块化特性的工具: Apache Ant Apache Ant是一个Java库和命令行工具,用于构建Java应用程序。Ant项目管理委员会早些时候确保Ant将在2017年2月的Ant 1.10.1版本上运行Java 9。Java 9和模块化速度正在加快。它支持javac、java和JUnit任务的模块路径和相关选项。 Apache Maven Apache Maven 3.7版本开始支持Java 9和模块化。 Eclipse IDE Eclipse IDE是一个开源工具,从2017年6月的Oxygen版本开始支持Java 9。但是,要使用Java 9模块,Java开发人员需要将以下vmargs(JVM的命令行参数)添加到eclipse.ini:-add-modules = ALL-SYSTEM。项目可能无法运行,因为类型正在用在java.base或java.se.ee中,例如javafx.Base中的类型。在这种情况下,根据Eclipse文档,你必须弄清楚需要使用-add-modules添加哪些模块。 JetBrains IntelliJ IDEA 从2017.2版开始,流行的IntelliJ IDEA开发环境开始全面支持Java 9的模块化。开发人员可以构建Java 9的模块化项目,就像使用模块路径而不是类路径一样。还支持module-info.java文件的开发,以及这些文件的代码完成和快速修复。 尚未准备好支持Java 9模块化特性的工具: Gradle Gradle软件构建工具尚未完全支持Java 9,尽管其开发人员预计在2018年可以这样做。目前,Gradle使用的开发人员可以通过变通方法尝试模块化。 Jenkins 用于软件开发项目的持续集成和部署的Jenkins开源自动化服务器尚不支持Java 9模块化仍在进行中。Cloud Bees首席技术官Kohsuke Kawaguchi表示,Cloud Bees在Jenkins社区领导着与Jenkins Java 9兼容的工作。但他没有给Jenkins提供Java 9兼容性的大致日期。 总结 在本文中,我们讨论了Java 9的模块化特性,还介绍了适用于这些新特性的工具。最后,列出了一些尚未准备好支持Java 9模块化特性的工具。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/78342573 Java应用在Ubuntu平台以服务的方式运行 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 本文讲述在Ubuntu平台上通过使用服务封装器的方式,把JAR文件以服务的方式运行。读者可以了解器做法,包括自动启动的方式和日志技巧。 假设你有一个可执行的JAR文件,你需要以服务的方式运行这个JAR文件。而且,当系统重启时,希望这个服务能够实现开机自启动。 Ubuntu平台内建了一个创建自定义服务的机制,允许用户把应用程序以服务的方式启动或停止,同时还支持开机自启动。下面,我将分享怎样实现服务封装的方法,通过它把JAR文件封装为服务,并以服务的方式运行JAR文件。 1、创建一个服务 # sudo vim /etc/systemd/system/my-webapp.service 把以下内容复制粘贴到上面的文件中。 [Unit] Description=My Webapp Java REST Service [Service] User=ubuntu # The configuration file application.properties should be here: #change this to your workspace WorkingDirectory=/home/ubuntu/workspace #path to executable. #executable is a bash script which calls jar file ExecStart=/home/ubuntu/workspace/my-webapp SuccessExitStatus=143 TimeoutStopSec=10 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target 2、创建一个调用服务的Bash脚本 下面是调用我的JAR文件(my-webapp)的Bash脚本: #!/bin/sh sudo /usr/bin/java -jar my-webapp-1.0-SNAPSHOT.jar server config.yml 不要忘记为你脚本分配执行权限: # sudo chmod u+x my-webapp 3、启动服务 # sudo systemctl daemon-reload # sudo systemctl enable my-webapp.service # sudo systemctl start my-webapp # sudo systemctl status my-webapp 4、设置日志 首先,运行 sudo journalctl –unit=my-webapp 如果要查看实时日志,那么使用-f选项。 如果想查看部分,可以使用-n选项查看日志的指定行数。 # sudo journalctl -f -n 1000 -u my-webapp 使用-f选项查看最新的实时日志的输出: # sudo journalctl -f -u my-webapp 要停止服务,执行: # sudo systemctl stop my-webapp
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/78197012 2017年Java日志框架及工具综述 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 应用程序中的日志信息对于了解所有应用程序运行时的行为至关重要,特别是在遇到意外的场景、错误或仅用于跟踪某些活动的情况下。 随着越来越多的企业逐渐转向云端,日志分析和日志管理的工具和服务变得越来越重要。一些工具,比如Loggly,Logstash,Graylog等,可帮助开发者分析和监控日志。 Loggly:https://www.loggly.com/ Logstash:https://www.elastic.co/products/logstash Graylog:https://www.graylog.org/ 下面来看Java领域主要的开源日志记录框架和工具。 1. Apache Log4j 2 官网:https://logging.apache.org/log4j/2.x/ Apache Log4j 2是Log4j日志框架的升级版,提供了很多同Logback的改进特性,同时修复了Logback架构中的一些固有问题,而且提供了异步日志功能,这显著提高了性能,尤其是在多线程应用程序中。 Apache Log4j 2的Maven依赖如下: <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.9.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.9.1</version> </dependency> 2. Logback 官网:https://github.com/qos-ch/logback Logback是Log4j的改进版,它在概念上像Log4j,因为两者都是同一个开发者开发的。如果熟悉Log4j的使用方法,那么使用Logback可以快速上手。 Logback的Maven依赖如下: <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> 3. tinylog 官网:http://www.tinylog.org/ tinylog是一个面向Java和Android平台的日志框架,它的日志器是静态的,故在使用时不需要创建日志器的实例。 tinylog的Maven依赖如下: <dependency> <groupId>org.tinylog</groupId> <artifactId>tinylog</artifactId> <version>1.2</version> </dependency> 4. Logbook 官网:https://github.com/zalando/logbook Logbook是一个Java库,可以为各种客户端和服务器端技术启用完整的请求和响应的日志记录。它提供了对Servlet容器、Apache的HTTP客户端、以及其它框架的支持,而且在Spring Boot环境可自动完成配置。 Logbook的Maven依赖如下: <dependency> <groupId>org.zalando</groupId> <artifactId>logbook-core</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.zalando</groupId> <artifactId>logbook-servlet</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.zalando</groupId> <artifactId>logbook-httpclient</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.zalando</groupId> <artifactId>logbook-spring-boot-starter</artifactId> <version>1.4.0</version> </dependency> 结论 在应用程序的开发过程中,日志记录非常重要,因为在应用程序的运行期间,日志记录可以带来非常有用的、可操作的见解。开发者可根据自己项目的实际需求选择适合的日志框架。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/78178158 解决Eclipse启动报缺少javax/annotation/PostConstruct的错误 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 我使用的是Mac开发环境,Eclipse使用4.7的Oxygen版。 当我在本机上安装了Java 9后,我发现Eclipse无法启动了。 打开Eclipse的日志文件(我的在这里Users/XXXXX/Documents/eclipsework/.metadata/.log) 可以看到有如下错误输出: !SESSION 2017-10-08 22:04:45.234 ----------------------------------------------- eclipse.buildId=4.7.0.I20170612-0950 java.version=9 java.vendor=Oracle Corporation BootLoader constants: OS=macosx, ARCH=x86_64, WS=cocoa, NL=zh_CN Framework arguments: -product org.eclipse.epp.package.jee.product -keyring /Users/liqiang/.eclipse_keyring Command-line arguments: -os macosx -ws cocoa -arch x86_64 -product org.eclipse.epp.package.jee.product -keyring /Users/liqiang/.eclipse_keyring !ENTRY org.eclipse.osgi 4 0 2017-10-08 22:04:47.669 !MESSAGE Application error !STACK 1 org.eclipse.e4.core.di.InjectionException: java.lang.NoClassDefFoundError: javax/annotation/PostConstruct at org.eclipse.e4.core.internal.di.InjectorImpl.internalMake(InjectorImpl.java:410) at org.eclipse.e4.core.internal.di.InjectorImpl.make(InjectorImpl.java:318) at org.eclipse.e4.core.contexts.ContextInjectionFactory.make(ContextInjectionFactory.java:162) at org.eclipse.e4.ui.internal.workbench.swt.E4Application.createDefaultHeadlessContext(E4Application.java:491) at org.eclipse.e4.ui.internal.workbench.swt.E4Application.createDefaultContext(E4Application.java:505) at org.eclipse.e4.ui.internal.workbench.swt.E4Application.createE4Workbench(E4Application.java:204) at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:614) at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336) at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:594) at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:148) at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:151) at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134) at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104) at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:388) at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:243) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:564) at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:653) at org.eclipse.equinox.launcher.Main.basicRun(Main.java:590) at org.eclipse.equinox.launcher.Main.run(Main.java:1499) Caused by: java.lang.NoClassDefFoundError: javax/annotation/PostConstruct at org.eclipse.e4.core.internal.di.InjectorImpl.inject(InjectorImpl.java:124) at org.eclipse.e4.core.internal.di.InjectorImpl.internalMake(InjectorImpl.java:399) ... 22 more Caused by: java.lang.ClassNotFoundException: javax.annotation.PostConstruct cannot be found by org.eclipse.e4.core.di_1.6.100.v20170421-1418 at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:433) at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:395) at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:387) at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:150) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496) ... 24 more 这个错误是安装了Java 9环境后导致的,即使卸载了Java 9,回退到Java 8,仍然有这个问题。 要解决这个问题并不难,打开Finder,选择“应用程序”,找到Eclipse,鼠标右键点击图标,选择“显示包内容”,打开“Contents”–>“Eclipse”,找到文件“eclipse.ini” 编辑这个文件,在末尾加上一句: --add-modules=ALL-SYSTEM 保存,并退出Finder,重启Eclipse即可恢复正常。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/77513055 使用云CRM的10个理由 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 CRM即Customer Relationship Management,客户关系管理,是一种以”客户关系一对一理论”为基础,旨在改善企业与客户之间关系的新型管理机制。CRM系统是企业为提高核心竞争力,利用相应的信息技术以及互联网技术来协调企业与顾客在销售、营销和服务上的交互,从而提升管理方式,向客户提供创新式的、个性化的客户交互和服务的过程。CRM的最终目标是吸引新客户、保留老客户以及将现有客户转为忠实客户,以增加市场份额。 传统的CRM目前逐步发展为云CRM系统,云CRM可以有效地集中客户数据库,提供快速访问,审查销售机会和与客户互动,并了解如何提高企业的业务流程,以获得更高的盈利能力。 选择云CRM,有如下理由: 1. 较低的前期成本 云CRM是一个由第三方供应商提供的托管解决方案。这意味着,与传统的CRM系统不同,使用企业不必为任何软件购买许可证或承担安装费用。相反,企业只需购买一个云CRM服务,这可以看作是一个运营费用。 2. 无缝访问 使用云CRM,企业员工可以轻松地随时随地地访问企业的数据库。只需一台有网络连接的笔记本电脑。一些云CRM服务提供商还提供了移动CRM客户端,以便让用户使用智能手机、平板电脑或任何其他手持设备访问云CRM。 3. 增强的安全性 网络攻击事件越来越多,企业要实施高安全的系统通常开销很大。如果采用云CRM,云CRM提供商会提供保护自己的云平台的安全基础设施。对企业而言,无需对数据库进行安全方面的大投资,故选择云CRM会有更好、更便宜的安全措施。 4. 稳定性 企业CRM系统总是存在故障的风险,需要定期更新,并需要支持人员进行长期维护。而使用云CRM,企业可以把这一切都交给服务提供商,只需支付一个较低的固定成本,就能享受极高的正常运行时间。 5. 云CRM有更快的ROI 由于企业不必花几千美元在硬件上安装CRM,还可以从云CRM中获得一个坚实的投资回报率,而且速度也相对较快。 6. 更高的灵活性 云CRM为企业提供了根据需求更改订阅包的能力,企业只需为所使用的服务付费。这非常灵活,对企业非常有益。 7. 敏捷性 云CRM敏捷为企业提供了同时面向多个用户,而不影响系统的效率。这使得大型企业,尤其是带分支机构和部门的企业能够实现更好的内部协同合作。 8. 备份和灾难恢复 云CRM提供商可以不断地将企业的数据备份到一个安全的位置,宝贵的客户数据可以在灾难发生时恢复。 9. 与其他技术集成 使用云CRM,企业可以与其他云技术连接,使企业能够无缝地利用多个系统和帐户,而无需在系统或设备之间进行切换。 10. 环境友好 由于云CRM是按需利用资源,故对环境有积极的影响。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/76376760 利用Nginx加GeoIP MaxMind数据库获取用户的地理位置 版权声明:本文为博主chszs的原创文章,未获得博主授权均不能转载,否则视为侵权。 本文讲述仅通过配置Nginx加上GeoIP MaxMind数据库,就能获得用户IP地址的实际物理位置,而无需编写任何代码。 地理位置数据在业务中有重要作用,这些数据可以用于向某些人群推广品牌、产品或服务,还有助于增强用户体验。 本文讲述仅通过配置Nginx加上GeoIP MaxMind数据库,就能获得用户IP地址的实际物理位置,而无需编写任何代码。 Nginx是一个开源的HTTP和IMAP/POP3代理服务器,主要用作Web服务器或反向代理服务器。Nginx的GeoIP模块(即ngx_http_geoip_module)使用了预编译的MaxMind数据库来设置变量,比如变量geoipcountryname、变量geoip_country_code、变量$geoip_city等等,而这些值则取决于用户客户端的访问地址。 先决条件:Ubuntu 16.04 用例:在Ubuntu系统安装Nginx,配置Nginx使用GeoIP MaxMind数据库,再根据用户的IP地址查找具体的地理位置。 概要: 1)安装和配置 2)使用客户端IP数据取回具体的地理位置 一、安装和配置 下面看在Ubuntu上安装和配置NGINX,安装GeoIP模块,下载GeoIP MaxMind GeoCity和GeoCountry数据库,并配置让Nginx使用GeoIP MaxMind数据库。 要安装和配置Ubuntu,执行以下操作。 要安装Nginx发布包,先获得官方的签名Key: $ curl -s https://nginx.org/keys/nginx_signing.key | sudo apt-key add – 再使用以下命令把Nginx仓库添加到apt源: $ sudo echo -e "deb https://nginx.org/packages/mainline/ubuntu/ `lsb_release -cs` nginx\n deb-src https://nginx.org/packages/mainline/ubuntu/ `lsb_release -cs` nginx" > /etc/apt/sources.list.d/nginx.list 使用以下命令重新同步apt源,并安装Nginx软件包: $ sudo apt-get update $ sudo apt-get install nginx 二、安装GeoIP模块 GeoIP模块用于查看连接到服务器的客户机的IP地址。 要安装GeoIP模块,执行以下的步骤。 使用以下命令下载并载入GeoIP模块到/usr/lib/Nginx/modules目录: $ sudo add-apt-repository ppa:nginx/stable $ sudo apt-get update $ sudo apt-get install nginx-module-geoip 编辑Nginx的配置文件nginx.conf: $ sudo nano /etc/nginx/nginx.conf 添加以下内容: load_module "modules/ngx_http_geoip_module.so"; 注意:如果–with-http-geoip-module已经存在于你安装的Nginx,那么跳过此步骤。 要检查GeoIP模块的存在,使用以下命令: $ nginx -V 三、下载GeoIP MaxMind GeoCity和GeoCountry数据库 要在Ubuntu系统下载并提取MaxMind GeoCity和GeoCouontry数据库,使用以下命令: mkdir - p / etc / nginx / geoip cd / etc / nginx / geoip wget http: //geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoI.dat.gz gunzip GeoIP.dat.gz wget http: //geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz gunzip GeoLiteCity.dat.gz 四、配置Nginx使用GeoIP MaxMind数据库 要配置Nginx使用GeoIP MaxMind GeoCity和GeoCountry数据库,需访问MaxMind geo-变量。执行以下命令: load_module "modules/ngx_http_geoip_module.so"; worker_processes 1; events { worker_connections 1024; } http { geoip_country / etc / nginx / geoip / GeoIP.dat;# the country IP database geoip_city / etc / nginx / geoip / GeoLiteCity.dat;# the city IP database log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log / var / log / nginx / access.log main;# Set Geo variables in proxy headers proxy_set_header X_COUNTRY_CODE $geoip_country_code; proxy_set_header X_CITY_COUNTRY_CODE $geoip_city_country_code; proxy_set_header X_REGION $geoip_region; proxy_set_header X_CITY $geoip_city; proxy_set_header X_POSTAL_CODE $geoip_postal_code; server { listen 80;# All other traffic gets proxied right on through. location / { proxy_pass http: //127.0.0.1:3000; } } } 五、使用客户端IP地址检索地理位置数据 下面创建一个简单的Web应用(Node.js程序),它可以返回JSON格式的请求报头参数。把自定义geo-字段添加到请求报头,并且可以从应用程序访问它。此Web应用通过Nginx进行反向代理。 要获得用户的地理位置数据,代码如下: // This sample web application returns request headers in response const express = require('express') const app = express() var count = 1; // Location "/show_my_identity" hosts the incoming request headers in response in JSON format app.get('/show_my_identity', function (req, res) { res.send(JSON.stringify(req.headers)); console.log('Request',count++,'received from country : ',req.headers.x_country_code); }) // Default "/" message app.get('/', function (req, res) { res.send("Welcome to Treselle lab"); }) // Application listening on port 3000 app.listen(3000, function () { console.log('Treselle Lab - App listening on port 3000!') }) 具有地理定位数据的应用程序的输出看起来与此类似: 注意:要运行此Node.js应用程序,相关的依赖模块要安装。 具有地理位置数据的应用程序日志与以下内容类似: 就是这些!
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/73664071 优化Git本地仓库 版权声明:本文为博主chszs的原创文章,未获得博主授权均不能转载,否则视为侵权。 在搭建Git服务器并运行一段时间后,随着托管的项目的源码和文档的数量不断增加,以及使用用户数量的不断增加,Git服务器本身可能会出现性能问题。遇到这种问题,最简单的解决方法是把传统的硬盘换用超高速的SSD存储。 当Git本地仓库中存有成千上万的对象时,特别是如果经常做重新生成这样的工作流程操作,那么本地仓库往往存在大量不必要的对象,这些对象就可以通过安全操作来清理掉。可以在后台运行一个特殊的命令: # git gc 这是一个垃圾收集命令,可以清理Git本地仓库。此命令对远程仓库不会产生任何影响。除此之外,还可以进行深度的清理: # git gc --aggressive 最佳的做法是每当执行了数百次提交(比如300次)后,就该执行一次清理工作。这样的话,可以保持Git本地仓库始终保持快速高效。更准确地说是,每当本地仓库存在了7000个以上的松散对象或50个以上的包文件后,就该考虑执行清理操作了。 对本地仓库执行了清理操作后,本地仓库的结构会变得更加紧凑,操作速度会提升不少。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/72979648 基于Spring Boot和Kotlin的联合开发 版权声明:本文为博主chszs的原创文章,未获得博主授权均不能转载,否则视为侵权。 一、概述 Spring官方最近宣布,将在Spring Framework 5.0版本中正式支持Kotlin语言。这意味着Spring Boot 2.x版本将为Kotlin提供一流的支持。 这并不会令人意外,因为Pivotal团队以广泛接纳JVM语言(如Scala和Groovy)而闻名。下面我们用Spring Boot 2.x和Kotlin应用程序。 二、搭建环境 1、环境 IntelliJ和Eclipse都对Kotlin提供了支持,可以根据自己的喜好搭建Kotlin开发环境。 2、构建应用 首先创建一个Spring Boot 2项目,然后修改POM配置,让项目保护指定的Java版本和Kotlin版本。依赖关系如下: <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib-jre8</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-reflect</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-kotlin</artifactId> <version>1.1.2</version> </dependency> 注意,我们正在为Kotlin源码文件和测试文件指定文件位置: <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory> <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory> 要编译Kotlin模块和源码,需要使用kotlin-maven-plugin插件: <plugin> <artifactId>kotlin-maven-plugin</artifactId> <groupId>org.jetbrains.kotlin</groupId> <version>1.1.2</version> <configuration> <compilerPlugins> <plugin>spring</plugin> </compilerPlugins> <jvmTarget>1.8</jvmTarget> </configuration> <executions> <execution> <id>compile</id> <phase>compile</phase> <goals> <goal>compile</goal> </goals> </execution> <execution> <id>test-compile</id> <phase>test-compile</phase> <goals> <goal>test-compile</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-allopen</artifactId> <version>1.1.2</version> </dependency> </dependencies> </plugin> 到此为止,构建Kotlin应用程序所需的一切就搭建好了。注意,可以去Maven中央仓库寻找以下组件的最新版本:spring-boot-starter-web、kotlin-stdlib-jre8、kotlin-reflect、jackson-module-kotlin、spring-boot-starter-test。 下面设置应用程序的上下文。 3、应用程序上下文 下面进入Kotlin的代码,编写熟悉的Spring Boot应用程序上下文: @SpringBootApplication class KotlinDemoApplication fun main(args: Array<String>) { SpringApplication.run(KotlinDemoApplication::class.java, *args) } 可以看到熟悉的@SpringBootApplication注解。 我们有一个类定义了KotlinDemoApplication类。在Kotlin中,类的默认范围是public,所以可以省略。另外,如果一个类没有变量、没有函数,它可以被声明为没有大括号。所以,从本质上讲,我们只是定义了一个类。 另外,方法或函数默认是公开的,所以不必在这里声明。另外,不返回任何内容的函数不需要指定一个void返回类型。 最后,在一个类的外部定义的任何函数都是自动静态的。这使得这些函数可以在启动时得到执行。 现在让我们从根目录运行我们的应用程序,使用mvn spring-boot: run。应用程序得以启动,应该可以看到应用程序在端口8080上运行。 接下来,构建一个控制器。 4、控制器 现在添加一个控制器到服务中: @RestController class HelloController { @GetMapping("/hello") fun helloKotlin(): String { return "hello world" } } 与标准的Spring控制器没有太大的不同,但是肯定代码量更精简。我们为此控制器添加一个测试类和案例来验证我们的工作: @RunWith(SpringRunner::class) @SpringBootTest(classes = arrayOf(KotlinDemoApplication::class), webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class KotlinDemoApplicationTests { @Autowired lateinit var testRestTemplate: TestRestTemplate @Test fun whenCalled_shouldReturnHello() { val result = testRestTemplate // ... .getForEntity("/hello", String::class.java) assertNotNull(result) assertEquals(result?.statusCode, HttpStatus.OK) assertEquals(result?.body, "hello world") } } 这个测试显示了Kotlin强大的功能之一——null安全!可以为null的Kotlin变量必须使用“?”声明。然后,编译器知道在访问该属性之前需要进行防御性编码。 在我们的测试中,TestRestTemplate被定义为可空类型,每次访问它时,我们使用null合并运算符“?”来实现——如果被调用对象为空,则返回null。 这声明了在程序中使用null,并强制开发人员在使用它们时编写安全的代码。 接下来,我们添加一个服务并将其集成到我们的控制器中。 5、服务 服务很容易添加到我们的项目中。这样做: @Service class HelloService { fun getHello(): String { return "hello service" } } 这里的简单服务与单个函数返回一个String。接下来,让我们将服务连接到控制器中并使用它来返回值: @RestController class HelloController(val helloService: HelloService) { // ... @GetMapping("/hello-service") fun helloKotlinService(): String { return helloService.getHello() } } 啊,看起来不错!在Kotlin中,主构造函数可以与类声明一起定义。我们从构造函数中省略了@Autowired注释,因为它不是一段时间的强制性的。 这些参数将自动转换为类中的字段。Kotlin称它们为属性。无需定义getter或setter方法,因为它们是自动创建的。当然,如果需要,您可以覆盖这些默认值。 在Kotlin中,函数中的类和变量的属性可以使用var或val来定义。var表示可变属性,val表示final属性。这允许编译器检查非法访问。由于HelloService是一个单例,所以我们把它连接成一个val来防止突变。 接下来,我们为此控制器方法添加一个测试: @Test fun whenCalled_shouldReturnHelloService() { var result = testRestTemplate // ... .getForEntity("/hello-service", String::class.java) assertNotNull(result) assertEquals(result?.statusCode, HttpStatus.OK) assertEquals(result?.body, "hello service") } 最后,我们来看看一个POJO在Kotlin中的样子。 6、Kotlin的数据类 在Java中,我们使用POJO来表示数据对象。在Kotlin中,可以更简洁地表达这种类型的对象——一个数据类。 我们写一个数据对象返回到控制器中: data class HelloDto(val greeting: String) 这里没有什么窍门,自动省略。使用data修饰符,可以获得很多好处。此关键字会自动创建一个equals()方法和hashcode()方法,以及toString()方法和copy()方法。所有这些方法一个修饰符就搞定了。 现在我们来添加一个返回新数据类的方法: // ... @GetMapping("/hello-dto") fun helloDto(): HelloDto { return HelloDto("Hello from the dto") } 数据修饰符不添加默认构造函数,这对于像Jackson这样的库很重要。为了支持这种类型的类,我们将jackson-module-kotlin添加到我们的POM文件中以支持编组。 最后,我们添加一个这个控制器功能的测试: @Test fun whenCalled_shoudlReturnJSON() { val result = testRestTemplate // ... .getForEntity("/hello-dto", HelloDto::class.java) assertNotNull(result) assertEquals(result?.statusCode, HttpStatus.OK) assertEquals(result?.body, HelloDto("Hello from the dto")) } 三、结论 在本文中,结合Spring Boot 2.x和Kotlin语言,我们完成了一个Demo应用。从示例中可以看到,Kotlin可以通过强制来精简代码,保证更安全的代码来简化和增强我们的应用程序。 Kotlin还支持一些惊人的功能,如数据类、类扩展,并与现有的Java代码完全兼容。这意味着开发者可以编写Kotlin代码,并从Java类中调用它,反之亦然。此外,Kotlin是从一开始就建立起来的,在IDE中能得到非常好的支持。 Google和Spring都开始支持Kotlin语言,或许使用Kotlin的时候到了。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/72935228 JAX-RS 2.0如何验证查询参数 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 在JAX-RS 2.0规范中,要验证查询参数并不难,可以通过过滤器ContainerRequestFilter来实现。它还提供了一些可选项,例如使用CDI或EJB的拦截器,或通过@Context注解的HttpServletRequest进行注入。 使用场景:验证由调用者传递的查询参数 步骤: 实现过滤器 从ContainerRequestContext上下文中提取查询参数 执行验证——以适合的响应状态(和错误消息)中止请求 @Provider @QueryParamValidator public class JAXRSReqFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext requestContext) throws IOException { MultivaluedMap < String, String > queryParameters = requestContext.getUriInfo().getQueryParameters(); String queryParam = queryParameters.keySet().stream().findFirst().get(); System.out.println(“Query param - ” + queryParam); if (!queryParam.equals(“p”)) { requestContext.abortWith(Response .status(Response.Status.BAD_REQUEST) .entity(“Invalid Query Param ” + queryParam) .build()); } } } 强制过滤器 使用@NameBinding注解来修饰自定义的注解 在JAX-RS的方法上使用自定义的注解 @NameBinding @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface QueryParamValidator { // do something } @Path(“test”) public class TestResource { @GET @QueryParamValidator public String test(@QueryParam(“p”) String p){ return “Param “+ p + ” on ” + new Date(); } }
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/72887387 Java性能优化的5个技巧 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 要优化Java代码需要正确的分析它的工作机制,影响性能优化有几个因素,比如垃圾收集、操作系统的设置、虚拟机的设置等。 1. 从最小堆分配开始 推荐从最小堆分配开始。然后根据应用程序的实际需要来逐渐增加最小堆,可以通过下面的指令来指示JVM在发生OutOfMemoryError异常时倾倒堆数据: -XX:+HeapDumpOnOutOfMemoryError 对可用内存的适当利用是优化应用速度的好办法。 一开始时,堆尺寸保持在1GB~7GB之间就足以了。然后取决于老一代和新生代对象的比例进行调整。 2. 使用Java性能工具 业界有几个Java性能工具,比如VisualVM、YourKit、Java Mission Control等,可用于跟踪应用程序的性能。 NetBeans开发工具的Profiler也是一个很好的选择。 3. 使用StringBuilder代替“+”操作符 对于字符串的操作,其中如果用到了“+”操作符,那么应该改用StringBuilder代替。比如上面的代码。 这样使得字符串值修改更容易,而且不会对垃圾收集器GC造成额外的压力。 StringBuilder x = new StringBuilder("a"); x.append(args.length); x.append("b"); if (args.length == 1); x.append(args[0]); 4. 避免使用迭代器 如果代码像这样: for (String value: strings) { // Do something useful here } 那么在每次运行这个代码时,都会创建一个新的迭代器实例,而这会消耗大量的内存。 故建议使用下面的代码: int size = strings.size(); for (int i = 0; i < size; i++) { String value: strings.get(i); // Do something useful here } 如果实际的列表对象不需要真正发生改变,那么还可以在其阵列版本上操作,比如: for (String value: stringArray) { // Do something useful here } 编写基于索引的迭代非常有用。 5. 具有更好的并行控制 并行是把多个应用程序以彼此并行的方式运行。为了解决多请求的Web流,推荐使用带乐观锁的分离实体或扩展的持久上下文。 此外,了解关系数据库管理系统RDBMS和数据访问框架的内部机制对提高数据访问层的性能至关重要。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/72872289 Amazon Aurora详解 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 一、Aurora介绍 对于部署在Amazon云平台上的应用,如何在不改变现有应用程序架构的情况下,来提升应用程序的性能?答案是采用AWS Aurora。 在今天的时代,几乎所有的业务应用的首要任务都是发展用户,业务发展的越好,用户群则越大;用户数越多就意味着需要为更多用户提供服务,也意味着需要更进一步地优化应用程序。应用优化有一定的局限性,在一定程度上,在不增加/改变应用架构的情况下很难做到(除非原先的代码写的太烂,否则很难提升性能)。大多数现有应用程序使用了关系数据库,通常采用了单片架构。这种单片架构和SQL查询会使程序变慢。无论SQL查询做了多少优化,它有一定的处理阈值,达到或超过阀值以后程序会死亡。 所以,在不改变现有应用程序架构的情况下,我们如何提高应用程序的性能?答案是采用AWS Aurora。 Amazon对Aurora的定义是: “AWS Aurora是为云平台构建的兼容MySQL的企业级关系数据库引擎,关于数据库迁移,把重点从“升级和转移”的方式转变为迁移(也即,按原样迁移并运行到云虚拟服务器上)到完全可管理的、云本地数据库服务。Amazon Aurora的主要目标之一是以成本效益高的方式来克服传统数据库的性能、可扩展性和可用性的限制,类似于开源数据库。Amazon Aurora在提供类似的性能和高可用性的同时,以商业数据库的十分之一的价格提供了比MySQL高五倍的性能。” AWS目前是世界上最受信任和广泛采用的云平台。它为计算、分析、存储、企业应用程序、移动和数据库提供服务。AWS Aurora被用于作为AWS的数据库部分。Aurora本身基于亚马逊的关系数据库服务(Amazon RDS),是一种在云中建立、运行和扩展的关系数据库的服务。Amazon RDS支持MySQL、MariaDB、PostgreSQL、Oracle和Microsoft SQL Server DB引擎。Aurora提供了每秒50万次以上的SELECT操作和每秒10万次以上的UPDATE操作的性能。 二、架构 当我们创建一个Aurora实例时,我们先创建一个数据库集群。数据库集群由一个主实例和一个集群卷组成。此外,我们还可以创建一个Aurora副本集。它可以进行连续备份到AWS S3(简单存储服务),并对数据以保持99.999999999%的耐久性。 Aurora从分配给实例80GB的块开始,并分配10GB的块作为自动缩放的一部分。 主实例 支持读/写工作负载 对集群卷执行所有数据修改 集群卷 SSD虚拟数据库存储卷 支持多个可用区域(AZ) 每个AZ都有两个集群数据副本 由主实例和Aurora副本共享 Aurora副本集 支持只读操作 最大副本数可以是15 多个Aurora副本,以支持读取工作负载的分发 多个Aurora副本意味着增加数据库可用性 如果主实例失败,其中一个Aurora副本将被提升为主实例 下面我们来看一下Aurora架构图: Aurora是一个基于SOA的实现,它分为几层:存储层、日志层、缓存层,这些都是作为单独的层,而SQL和事务已保存在单个层中。这种架构实现了更多的可扩展性、高可用性和性能。 三、创建Aurora实例 登录到AWS管理控制台并导航到Amazon RDS部分,就可以创建Aurora集群。 首先选择数据库、主实例的大小、数据库凭证、数据库名称、端口号等。 然后,选择“Launch DB Instance”以启动Aurora实例。在“Instances”选项卡下,可以看到新创建的实例,其中有可用于从应用程序连接的端点和端口号。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/72657563 Java简史 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 Java是一个非常易于使用和存在久远的编程语言,今年是其诞生的22周年。Java是一种广泛使用的计算机编程语言,拥有跨平台、面向对象、泛型编程的特性,广泛应用于企业级Web应用开发和移动应用开发。 1995年5月23日,Java语言诞生 1996年1月,第一个JDK————JDK1.0诞生 1996年4月,10个最主要的操作系统供应商申明将在其产品中嵌入JAVA技术 1996年9月,约8.3万个网页应用了JAVA技术来制作 1997年2月18日,JDK 1.1发布 1997年4月2日,JavaOne会议召开,参与者逾一万人,创当时全球同类会议规模之纪录 1997年9月,JavaDeveloperConnection社区成员超过十万 1998年2月,JDK 1.1被下载超过2,000,000次 1998年12月8日,JAVA2企业平台J2EE发布 1999年6月,SUN公司发布Java的三个版本:标准版(J2SE)、企业版(J2EE)和微型版(J2ME)的1.2版 2000年5月8日,J2SE 1.3发布 2001年6月5日,NOKIA宣布,到2003年将出售1亿部支持Java的手机 2001年9月24日,J2EE 1.3发布 2002年2月26日,J2SE 1.4发布,自此Java的计算能力有了大幅提升 2004年9月30日18:00PM,J2SE1.5发布,成为Java语言发展史上的又一里程碑。为了表示该版本的重要性,J2SE 1.5更名为Java SE 5.0 2005年6月,JavaOne大会召开,SUN公司公开Java SE 6。此时,Java的各种版本已经更名,以取消其中的数字“2”:J2EE更名为Java EE,J2SE更名为Java SE,J2ME更名为Java ME 2006年12月,SUN公司发布Java SE 6.0 2009年12月,SUN公司发布Java EE 6 2010年1月27日,Oracle收购SUN公司 2010年11月,由于Oracle公司对于Java社区的不友善,因此Apache扬言将退出JCP 2011年7月28日,Oracle公司发布Java SE 7 2014年3月18日,Oracle公司发表Java SE 8 2017年7月,Oracle公司将发布JDK 9
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/71940729 2017年十大移动应用开发的测试工具 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 自动化测试工具介绍 自动化测试工具基本上是移动应用(Android和iOS)程序开发测试的必备工具,正确开展自动测试可以减少测试过程所需的时间以及测试执行过程中人为错误的几率。市面上有很多可用的自动化测试工具,其中一些免费,另一些收费。其中一些自动化测试工具早已有之; 而另一些工具才刚进入市场。每一种自动化测试工具都是独一无二的,都具有自己独有的特性。因此选择适合的自动化测试工具并不容易,下面推荐十款优秀的自动化测试工具。 1、Appium(iOS / Android) Appium官网:http://appium.io/ Appium是一个开源的、支持混合应用和原生应用的跨平台自动化测试工具,它支持Android 2.3以上版本。Appium像服务器一样运行在后台,就像Selenium服务器那样。 Appium支持多种编程语言,如Java,Ruby,C#和其他WebDriver库中的编程语言。Appium利用WebDriver接口进行测试。 Appium使用UIAutomator库实现自动化测试Android,而UIAutomator库是Google提供的Android SDK中的内容。在移动设备上,它可以控制Safari和Chrome。它可以与测试框架TestNG同步。在这种情况下,UIAutomator可以生成翔实、详细的报告,类似于Ranorex生成的报告。 Appium的优点 由于在所有平台上使用了标准的自动化API,故开发者无需以任何方式修改或重新编译应用程序 开发者可以使用任何与WebDriver兼容的语言(比如Java,Objective-C,JavaScript)来编写测试用例 开发者可以使用任何测试框架 易于在不同的平台上进行设置 支持各种语言,如Ruby,Java,PHP,Node,Python 在设备上不需要安装任何东西 还可以使用Selenium Webdriver JSON连线协议 不必在不同的平台上重新编译移动应用 借助Java,它可以与其他工具集成 2、Robotium Robotium官网:https://code.google.com/p/robotium/ Robotium是一个免费的Android UI测试工具,可以轻松地为Android应用程序编写强大的自动黑盒测试用例,且无需了解Android应用程序结构或实现类的相关信息。只需知道主类的名称和链接到它的路径。Robotium支持Android 1.6以上版本。Robotium中的测试使用Java编写。事实上,Robotium是一个单元测试库。 但是通过Robotium准备测试需要花费大量的精力和时间,因为必须使用程序源代码来自动化测试。该工具不太适合与系统软件交互,它无法锁定和解锁智能手机或平板电脑。Robotium中没有播放或记录功能,且不提供截图。 Robotium的优点 对项目基本无需了解就可以创建强大的测试用例 可以自动处理多个Android活动 创建稳固的测试用例需要的时间非常少 与Ant或Maven轻松同步,作为持续集成的一部分运行测试 可以在预安装的应用程序上运行测试用例 可以获得Robotium测试的代码覆盖 3、Selendroid Selendroid官网:http://selendroid.io/ Selendroid是一个测试自动化框架,支持Android本地应用、混合应用、移动Web应用的UI自动化测试。它使用Selenium 2客户端API测试编写。 Selendroid的优点 完全兼容JSON线协议 需要对被测试应用进行更改才能自动化 自动化本地应用或混合应用使用了相同的概念 通过不同的定位器类型,可以找到UI元素 可以同时与多个Android设备进行交互 Selendroid支持硬件设备的热插拔 通过不同的定位器类型,可以找到UI元素 Selendroid自带了一个有用的工具,即Selenium Inspector。它允许您检查应用程序UI的当前状态。 4、MonkeyRunner MonkeyRunner官网:http://developer.android.com/tools/help/monkeyrunner_concepts.html MonkeyRunner工具提供了一套API,用于编写从Android代码之外控制Android设备或模拟器的程序。这个工具是在Robotium之后就性能而言的。测试用Python编写,用于创建可以使用录音工具的测试。 MonkeyRunner的不同之处在于,需要为每个设备编写脚本,并且每次当测试程序的用户界面改变时,测试都需要调整。 MonkeyRunner的优点 可以控制多个设备 为了自动化测试,开发者不必处理源代码 可以用于功能的自动化测试 也可用于回归测试 Jython允许MonkeyRunner API与Android应用程序交互 5、Calabash Calabash官网:https://github.com/calabash/calabash-ios Calabash由多个库组成,这些库支持对代码以可编程的方式进行测试,且支持本地应用和混合应用。 Calabash的优点 提供了专门针对在触摸屏设备上运行的本地应用程序的API 包含了支持本地应用和混合应用的对代码以可编程的方式进行测试的库 支持Cucumber框架,这使得业务专家和非技术质量保证人员更容易理解 6、Frank Frank官网:https://github.com/moredip/Frank Frank是一个轻量级的基于UI的自动化测试工具,是一个使用Cucumber和JSON命令组合的工具。Frank可作为用于iOS版本的iOS应用程序的Selenium。 Frank的特征包括 有清晰的、类似CSS选择器的语法,允许相当宽容的测试 支持预定义的步骤,可以立即使用它们进行测试 由Cucumber驱动 包含symbiote一个活的内省工具 支持与CI集成 可以在模拟器和设备上运行测试 记录测试运行的视频,以显示应用程序的行动 7、KIF KIF官网:https://github.com/kif-framework/KIF KIF是一个基于Objective-C的框架,仅用于iOS应用的自动化测试。KIF是一个直接与XCTests集成的自动化框架。只要不涉及到修改其测试规范时,就可以使用它。 KIF的特征 有积极的社区和良好的支持 与XCTests无缝集成,KIF配有“KIFtestCase”,可以用来代替“XCTTestCase” KIF通过其辅助功能标签访问UI元素 一切都在一种语言Objective-C中,因此,纯iOS开发人员可以轻松获取 有令人印象深刻的命令行和CI 对手势有相当合理的支持 8、MonkeyTalk MonkeyTalk官网:https://github.com/sonarme/monkeytalk 一切都是从数据驱动的测试套件到简单的“烟雾测试”,MoneyTalk可自动实现iOS和Android应用程序的真实功能的交互式测试。 MonkeyTalk的特征 脚本简单易懂 MonkeyTalk IDE可以记录/播放测试脚本 不需任何强大的编程或脚本知识 支持网络设备和仿真器 对于Android和iPhone,可以使用相同的脚本 支持循环概念 可以使用此工具生成XML和HTML报告,它还会在发生故障时捕获截图 为了持续集成,MonkeyTalk支持Jenkins和Hudson。它还支持JUnit报告 9、Testdroid Testdroid官网:http://testdroid.com/ Testdroid是一个基于云计算的移动应用程序,可帮助开发人员节省应用程序开发费用,加快产品的上市时间,降低运营成本和不可预测的成本。Testdroid是测试应用程序针对各种真正的Android和iOS设备与不同的硬件平台,屏幕分辨率和操作系统版本的最快的方式。价格范围根据需求从499−4999/月不等。它是Android和iOS游戏的稳固的手机游戏测试平台之一。在启动应用程序之前,它可以远程手动访问超过300个运行Android的真实设备。 Testdroid的优点 节省了应用程序的开发成本 最小化实际设备和敏捷测试的风险 降低运营成本和不可预测的成本 提高应用程序评级和日常活动用户 10、SeeTest by ExperiTest SeeTest见:https://experitest.com/ SeeTest是ExperiTest提供的先进的移动测试、性能测试和监控工具。SeeTest提供了基于云的移动测试,您可以远程在真实的移动设备中运行测试。SeeTest云提供各种设备,移动操作系统和网络速度的模拟。 SeeTest可以自动化您的移动测试,并与CI/CD解决方案结合得很好,且可以并行测试。SeeTest还提供了一个可以让测试人员远程执行移动测试的Appium Enterprise。 SeeTest允许通过SeeTest Network Virtualization和Loadrunner附加组件对应用进行性能测试。 如何选择最佳自动化工具 确定需要自动化的测试 研究和分析满足自动化需求的自动化产品 根据您的需求和预算做取舍 根据要求,选择最合适的一个或多个工具 与其他利益相关者讨论所选择的自动化工具,解释选择并获得批准 继续测试自动化
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/71106629 Apache JMeter 3.2版新特性详述 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 一、Apache JMeter介绍 Apache JMeter是一个基于Java的桌面应用程序,是Apache基金会旗下的项目之一,主要用作负载测试工具,分析和测量各种服务的性能,尤其是Web应用程序。 Apache JMeter 3.2版刚发布,下载地址见:http://jmeter.apache.org/download_jmeter.cgi 二、Apache JMeter 3.2版值得注意的点 1、重要的改变 JMeter现在需要Java 8,需确保使用了最新版本 日志输出已迁移到SLF4J和Log4j 2,对于配置和第三方插件有一些影响 55个增强和41个bug修复 PDF文档已经取消,HTML格式的文档才保持更新 从JMeter3.2版开始,“查看结果树”中的结果数量默认限制为500个条目。 ** 如果需要显示更多条目,则必须将属性view.results.tree.max_results设置为更高的值。 ** 如果不想强制执行任何限制,则必须将其设置为0。 ** 可以在bin/user.properties中设置属性。 2、简化HTTP请求 基本的Tab界面就包含了常用的属性: 高级属性: 3、HTTP测试脚本记录器 界面分成了两个Tab选项卡 第一个Tab为最常见的属性,特别是«前缀»,它允许在录制过程中命名每个事务: 第二个Tab允许根据不同的条件,过滤记录的请求: 4、查找/替换功能 这是一个新特性,可以通过变量来替换一些数据: 5、报告/仪表板的改进 JMeter现在提供了一个InfluxDB接口的新BackendListener实现: 该实现使用了异步HTTP调用InfluxDB的HTTP API来发送数据,为用户提供下面的注解图: 6、DNS缓存管理器新增了一个表 这个表列出了允许的静态主机的解析 7、JMS的发布者和订阅者的改进 JMS的发布者和订阅者现在可以通过暂停实现错误重连。 8、报告/仪表板的改进 统计数据进行了重新组合,结果更清晰了。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/70547772 巧用Netstat排除网络故障 作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 当在Linux服务器上遇到网络故障时,ping和traceroute是常用的两个命令,但是很多时候你需要了解更多的网络细节才有助于解决问题。要实现这一点,可以使用netstat命令,它可以提供网络套接字的详细信息以及其它有用的信息。与ping和traceroute命令一样,可以简单地在命令行使用netstat并立即获取结果。 一、什么是Netstat netstat命令是处理网络问题的一个非常有用的工具。netstat是“Network Statistics”即网络统计的缩写,它可以显示传入和传出的网络连接,还可以用于获取网络统计信息、协议统计信息、路由表信息等。 我们可以使用netstat来查找网络问题并测量网络流量,因此可以用它收集网络的中断、降速或网络瓶颈。 二、基本的Netstat 要获取当前所有连接的一个列表,只需使用-a选项。 # netstat -a Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 *:1922 *:* LISTEN tcp 0 216 chdc154:1922 223.99.111.233:11303 ESTABLISHED tcp6 0 0 [::]:9000 [::]:* LISTEN tcp6 0 0 [::]:8009 [::]:* LISTEN tcp6 0 0 [::]:mysql [::]:* LISTEN tcp6 0 0 [::]:1922 [::]:* LISTEN tcp6 0 0 [::]:9090 [::]:* LISTEN tcp6 0 0 localhost:8005 [::]:* LISTEN ...... 它提供了一些对于不同类型的协议(比如TCP和UDP)等的连接的基本信息,以及活跃的Unix域套接字信息。但是,natstat还允许用户获取更具体的信息,以便对调试更有帮助。 三、按连接类型过滤 基于连接类型对结果进行过滤有助于找到所需的信息。比如,你想查看TCP连接,那么可以在上面的-a选项后紧跟一个t选项,具体如下: # netstat -a Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 *:1922 *:* LISTEN tcp 0 216 chdc154:1922 223.99.111.233:11303 ESTABLISHED tcp6 0 0 [::]:9000 [::]:* LISTEN tcp6 0 0 [::]:8009 [::]:* LISTEN ...... 相似的,如果在-a选项后紧跟u选项,则值列出UDP连接。 四、按监听连接进行过滤 如果想要查看正在监听的连接,那么可以使用-l选项(移除-a选项),比如: # netstat -l Active Internet connections (only servers) ...... Active UNIX domain sockets (only servers) Proto RefCnt Flags Type State I-Node Path unix 2 [ ACC ] STREAM LISTENING 47834116 /run/systemd/private unix 2 [ ACC ] STREAM LISTENING 1661287 /run/user/0/systemd/private unix 2 [ ACC ] SEQPACKET LISTENING 15450 /run/udev/control unix 2 [ ACC ] STREAM LISTENING 96528873 /run/snapd-snap.socket unix 2 [ ACC ] STREAM LISTENING 10581 /var/lib/lxd/unix.socket unix 2 [ ACC ] STREAM LISTENING 10578 /run/uuidd/request unix 2 [ ACC ] STREAM LISTENING 10582 /run/acpid.socket ...... 与-a选项相似,-l选项紧跟t选项,即-lt选项,表示查看正在监听的TCP连接;-lu表示查看正在监听的TCP连接。使用这种方式,可以轻松查看指定端口是否打开和监听,并确定网站应用或APP是否按预期方式运行。 五、查看网络统计信息 # netstat -s Ip: 1473970908 total packets received 17795365 with invalid addresses 0 forwarded 0 incoming packets discarded 1453512118 incoming packets delivered 2392531460 requests sent out 40 outgoing packets dropped 3 fragments dropped after timeout 48 reassemblies required 15 packets reassembled ok 3 packet reassembles failed Icmp: 3589646 ICMP messages received 37 input ICMP message failed. ICMP input histogram: destination unreachable: 178 timeout in transit: 18 echo requests: 3589445 echo replies: 5 ...... 如你所见,-s选项提供了一些在调试时可能有用的统计信息,例如总数,传入和传出数据包以及收到,发送和失败的ICMP消息。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/69951839 Spring Boot的新Gradle插件详解 作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 在Spring Boot 2.0 M1版本中,有一个显著的改进,那就是引入了新的Gradle插件。这些改进也同时放入了Spring Boot的最新快照版本中。Spring Boot的Gradle插件提供了Spring Boot对Gradle构建工具的功能支持,可用于打包项目为可执行的jar或war文档,或者是运行Spring Boot应用程序,或是使用spring-boot-dependencies提供的依赖管理。 一、新Gradle插件 这个新Gradle插件需要主机安装Gradle 3.4以上的版本,想使用这个新特性,需下载最新的Spring Boot的快照版本,具体见: https://repo.spring.io/libs-snapshot 而使用这个快照版本最简单的方法是通过start.spring.io创建一个新Gradle项目,并且选择使用Spring Boot 2.0快照版本。 或者,新项目的build.gradle配置应该是这样的: buildscript { ext { springBootVersion = '2.0.0.BUILD-SNAPSHOT' } repositories { mavenCentral() maven { url 'https://repo.spring.io/libs-snapshot' } } dependencies { classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}" } } apply plugin: 'java' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' 应用的插件可以选择如上的java,也可以选择groovy或org.jetbrains.kotlin.jvm插件。 二、构建可执行jar或war文件 在构建可执行jar或war文件文件时,原先的bootRepackage任务被新的bootJar任务和bootWar任务所取代。 三、依赖管理 Spring Boot的Gradle插件不再自动应用其依赖管理插件。取而代之的是,Spring Boot的插件现在通过导入正确的spring-boot-dependencies BOM清单来反作用于依赖管理插件。这使得开发者对依赖管理可以做更多的控制,对于大多数应用程序而言,应用依赖管理插件是足够的: apply plugin: 'io.spring.dependency-management' 要注意,依赖管理插件仍然是一个spring-boot-gradle-plugin传递依赖,所以无需在build.gradle配置中明确列出此依赖。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/60970765 密码哈希函数Bcrypt的最大密码长度限制 作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs Bcrypt是一个很流行的密码哈希算法,是Niels Provos和DavidMazières基于Blowfish加密算法设计的密码哈希算法,于1999年在USENIX协会上提交。Bcrypt在设计上包含了一个盐Salt来防御彩虹表攻击,还提供了一种自适应功能,可以随着时间的推移,通过增加迭代计数以使其执行更慢,使得即便在增加计算能力的情况下,Bcrypt仍然能保持抵抗暴力攻击。 Bcrypt是OpenBSD和SUSE Linux等操作系统默认的密码哈希算法。但是在使用Bcrypt算法的实现时,要注意它有最大密码长度限制,通常为50~72字符,准确的长度限制取决于具体的Bcrypt实现。超过最大长度的密码将被截断。 下面使用Spring Security的BCryptPasswordEncoder为例: BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); // 72 字符 String password1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; // 73 字符 String password2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"; String encodedPassword1 = passwordEncoder.encode(password1); boolean matches = passwordEncoder.matches(password2, encodedPassword1); System.out.println("encodedPassword1: " + encodedPassword1); System.out.println("matches: " + matches); 当运行程序时,会输出这样的结果: encodedPassword1: $2a$10$A5OpVKgjEZzmy6UNsqzkjuG2xGET1wp3b/9ET5dz/tHQ3eRvyXSSO matches: true 这证明了Password字符串超过72字符的部分被截断丢弃了。 要解决Bcrypt密码算法72字符长度限制的问题,可以这样: 先使用SHA-256算法对字符串进行加密,再使用Bcrypt算法加密,用伪码示意如下: hashpw(sha256('password'), salt);
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/60466183 Serverless架构的演进 作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs Serverless架构风格挑战了软件设计和软件部署基础的现状,以实现最佳开发、最优运营和最优的管理开销。虽然它继承了微服务架构MSA的基本概念,但它已被赋予了新的架构模式,尽可能实现最高效的硬件利用。 尽管Serverless架构有显著的进步,但适应这种架构需要一个周全的过程,把企业解决方案精确映射到Serverless架构上。 部署在物理服务器上的软件系统,其初始实现不能最佳地利用底层硬件的计算能力,因为在给定时间内只能有一个操作系统实例运行。随后的改造中,在计算资源中识别时间共享能力之后,多个虚拟计算机能够通过在它们之间切换CPU和I/O操作从而实现在相同硬件上的同时运行。 这种技术演进导致了行业中的许多创新,最重要的是云的诞生。此时,虚拟机是用于部署软件的隔离计算环境中最易于管理的、可扩展的和可编程的单元。Linux容器技术出现在2006年左右,当时Google实现了符合Linux内核特性的控制组。 Linux容器自那时以来一直存在。然而,只有规模大、技术上超越的企业,比如谷歌,才能够规模化的使用它。到2012年,在欧洲,一个软件架构师讨论组引入了微服务架构的概念。在2013年晚些时候,Docker巧妙地填补了容器生态系统中的可访问性、可用性和支持服务的空白,因此,容器开始变得流行起来。 Linux容器打开了一个新的视野,将大型单片系统分解成独立的自包含服务,并以细粒度的资源利用来执行它们。为了加快这些进展,容器集群管理系统(如Kubernetes和Mesosphere)在同一时期开始提供端到端的容器即服务(CaaS)的能力。 到2015年晚些时候,AWS通过引入AWS Lambda实现了另一个飞跃,它可以通过按需运行微服务进一步节省软件部署成本,并在无负载时自动停止。这种概念类似于节能车辆中的停止-启动的特性,其自动关闭内燃机以降低燃料消耗。 它是如何工作的? 尽管术语“Serverless”乍一看是荒谬的,但其实际的意义在于,部署软件无需涉及基础设施的建设。Serverless平台可以根据需要自动构建、部署和启动服务的整个过程。用户只需注册所需的业务功能及其资源需求。 显然,这样的功能可以分为两种主要类型:由客户端请求触发的功能,和需要通过时间触发器或事件触发的后台执行的功能。 通常,这种Serverless系统可以使用具有动态路由器的容器集群管理器(CCM)来实现,该动态路由器可以按需调整容器。然而,还需要考虑路由器的延迟、容器的创建时间、语言支持、协议支持、功能接口、函数初始化时间、配置参数的传递、提供证书文件等。 尽管这种部署方式要求在没有负载时停止容器,但实际上在服务请求之后很快就停止容器,这种开销也将是昂贵的,因为在短时间间隔内可能有更多的请求进入。因此,更通常的做法是,在Serverless计算容器中将保留预先配置的时间段以便能重用于对服务的更多请求。这类似于PaaS平台中的自动缩放行为。一旦服务被扩展了,实例将被保留一段时间以便能及时处理更多的请求,而不会立即终止它们。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/60141447 亚马逊AWS的Serverless架构 作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs Serverless平台允许运行应用程序,包括计算、存储和网络——无需启动和管理单个(虚拟)机器。本文主要介绍AWS上的Serverless架构,包括Lambda、API Gateway、DynamoDB、S3等。Serverless架构模式也可以转移到其他云平台实施。 能够专注于软件开发而不是操作一组服务器是Serverless的主要驱动力。或者正如Amazon.com首席技术官Werner Vogels所说:“没有服务器比不管理服务器更容易管理。”Serverless基础设施的另一个重要方面是细粒度的计费和极高的可扩展性。为了给你一个典型用例的印象,这里列举了一些Serverless应用程序: 在高负载场景使用REST API从Web应用程序收集指标 从传入的包含订单和状态信息的电子邮件中提取和存储数据 创建提供标准后端CRUD操作的REST API 在大数据场景的ETL过程中收集和转换数据 分析来自实时数据流的日志消息 构建一个在后台可以与多个API交互的聊天机器人chatbot 限制 在高可用性和高可扩展性的基础设施上执行源代码的能力是每个Serverless平台的核心。例如,AWS Lambda允许运行源代码以响应事件。AWS提供了计算基础设施和针对JavaScript(Node.js)、Java、Python或.NET Core(C#)等编程语言的运行时环境。典型的Serverless应用程序由多个函数组成。在AWS Lambda上,每个函数执行的超时时间为5分钟。但可以通过高度并行的方式执行相同或不同的函数。 遵循Serverless方法时,需要将应用程序划分为多个小功能。利用微服务架构可以帮助实现这个功能划分的目标。Serverless最适合事件驱动的架构。当编排多个Serverless函数来构建一个系统时,事件是触发Serverless函数的源。 基于以下事件可以触发一个AWS Lambda的函数: 传入HTTPS请求 更改存储在数据库或对象存储中的数据 传入邮件(例如电子邮件) 将任务发布到主题或实时数据流 计划事件(与cronjob相似)
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/58606201 基于Node.js的模板引擎大比拼 作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 本文对基于Node.js的模板引擎做了一个汇总和对比,具体如下。 Mustache 官网见:http://mustache.github.io/ Mustache是一个非常简单易用的模板引擎,号称无逻辑的模板引擎,可以用于HTML、配置文件、源代码等等场景。 之所以称之为无逻辑的模板引擎,是因为它没有if语句、else条件、for循环等结构体。只有标签,使用值来替换标签,值可以是哈希或对象,就这么简单。 Mustache模板有两种定义,Mustache (1)和Mustache (5) Mustache支持主流的编程语言,比如Ruby, JavaScript, Python, Erlang, node.js, PHP, Perl, Perl6, Objective-C, Java,Android, C++, Go, Lua等。 Mustache也可以很好地与编辑器TextMate, Vim, Emacs, Coda, Atom等相结合。 Mustache (1)手册:< http://mustache.github.io/mustache.5.html> Mustache (5) 手册:< http://mustache.github.io/mustache.1.html> Mustache (1)的灵感来自于ctemplate,并于2009年底发布第一版。第一个版本的模板引擎使用Ruby语言实现,运行YAML模板文本。采用的主要原则有:一是强调“无逻辑”,没有显式的控制流程语句,所有控制都由数据驱动;二是强调“逻辑与表示的分离”,不可能将应用程序逻辑嵌入到模板中。 Handlebars 官网:< http://handlebarsjs.com/> Handlebars.js是Chris Wanstrath创建的Mustache模板语言的扩展。Handlebars.js和Mustache都是无逻辑模板语言,保持视图和代码分离。 一般来说,Handlebars.js模板的语法是Mustache模板的超集。 其基本语法可以参考Mustache的帮助页:< http://mustache.github.com/mustache.5.html> Handlebars允许模板被预编译,并包含到JavaScript代码中,使得启动时间更短。 Handlebars不兼容Mustache的几点: * Handlebars默认不执行递归查找,除非在编译时compat标志必须设置为启用此功能。用户应注意,启用此标志会产生性能成本。 * 不支持可选的Mustache风格的Lambdas表达式。 * 不支持备用分隔符 Dust.js 官网:< http://www.dustjs.com/> Dust是一个Javascript模板引擎,它继承了ctemplate语言风格,并设计为在服务器和浏览器上异步运行。 与其它模板引擎相比,Dust不是无逻辑的,只是有较少的逻辑。 *不能在Dust模板中编写任意Javascript。但是,您仍然有基本的逻辑运算符,如比较,小于/大于,存在/不存在。这在模板可读性和数据控制之间达到平衡。 * Dust鼓励将逻辑移动到数据模型。可以在模型中创建函数,然后通过模板调用,可以完全控制模板如何呈现,而不会使逻辑混乱。 *异步模板加载,渲染和流式传输。故不必预加载模板。 *可组合模板,支持部分包含和动态模板块,将模板拼接在一起,实现手动构建布局。 * HTML安全,格式无关。Dust通过安全地转义数据,防止跨站点脚本攻击。 *高性能。在性能和功能之间取得平衡。虽然它没有Mustache那么快,但它的异步性意味着可以更快地渲染大模板。 * Dust工作在JavaScript。 Underscore.js 官网:< http://underscorejs.org/> Underscore是一个JavaScript库,提供了一系列有用的函数式编程帮助程序,而不扩展任何内置对象。 Underscore提供了超过100个函数,支持map、filter、invoke等特性,以及函数绑定、JavaScript模板、创建快速索引、深度等价测试等。 doT 官网:< http://olado.github.io/doT/> doT并非最易于使用的模板引擎,但它满足以下需求: * 如果模板引擎在客户端和服务器端都需要使用 * 如果模板需要庞大的逻辑,而且还想让应用程序跑得很快 * 如果需要预编译的模板 Jade 官网:< http://jade-lang.com/> 使用这个模板引擎,可以让代码写得更少,开发很轻松。但在Node.js环境使用比较费时,因为必须先把文件转换为HTML,再转换成Jade。 EJS 官网:< http://ejs.co/> EJS是CanJS默认的模板语言,它提供了与Observes的实时绑定的使用。EJS非常易于使用,在模板中写入想要的HTML,以及一些表示动态行为的魔法标签即可。JES不支持block功能。 swig Swig没有抽象的HTML语法,但可以用Swig填充Angular.js的语法,支持block功能。 结论 作者喜欢EJS,但选择哪一个取决于你的实际需求。考虑的第一个优先级是性能,其次是编写代码轻松。doT的性能优秀,但它的语法难以掌握。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/58049192 Google Guice 3.0开发 作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs Google Guice框架是一个基于Java 6以上的轻量级依赖注入框架,由Google开发。你可能会问,如果我们已经有Spring用于依赖注入,那么为什么我们需要Guice?好吧,如果你只需在应用程序中实现依赖注入,那么你无需使用Spring容器。Spring不仅仅是一个依赖注入框架,二期大多数Spring应用都使用了XML作为依赖注入的方式。而Guice却是一个相对更轻量级的框架,它的集成更少,有Java实例配置和运行时绑定。通过使用Java绑定,可以获得编译时的类型检查和IDE自动完成功能。 Guice框架的运行速度也很快。默认情况下,Guice为每一个依赖(对应Spring的“prototype”范围)注入一个新的、单独的对象实例,而Spring则默认提供单态实例。Guice框架将依赖注入提升到一个新的水平,充分利用Java类型的所有功能,特别是注释和泛型,使得构建的DI应用程序更加模块化,更容易编写,并且错误更少、易于维护。 一、下载Google Guice Google Guice的Maven依赖如下: <dependency> <groupId>com.google.inject</groupId> <artifactId>guice</artifactId> <version>3.0</version> </dependency> 也可以从Github下载,见:https://github.com/google/guice 要运行基于Guice的应用程序,有三个依赖包是必须的: guice-3.0.jar javax.inject.jar aopalliance.jar 二、实战示例 下面创建一个简单的Java程序,使用Guice实现依赖注入。 1、服务接口类ArithmeticService.java public interface ArithmeticService { void calculate(int a, int b); } 2、服务接口实现类AdditionService.java public class AdditionService implements ArithmeticService { @Override public void calculate(int a, int b) { System.out.println("Addition of " + a + " and " + b + " is: " + (a + b)); } } 3、另一个服务接口实现类SubtractionService.java public class SubtractionService implements ArithmeticService { @Override public void calculate(int a, int b) { System.out.println("Subtraction of " + a + " and " + b + " is: " + (a - b)); } } 4、第三个服务接口实现类MultiplicationService.java public class MultiplicationService implements ArithmeticService { @Override public void calculate(int a, int b) { System.out.println("Multiplication of " + a + " and " + b + " is: " + (a * b)); } } 5、第四个服务接口实现类DivisionService.java public class DivisionService implements ArithmeticService { @Override public void calculate(int a, int b) { System.out.println("Divisiton of " + a + " and " + b + " is: " + (a / b)); } } 6、Consumer Class import javax.inject.Inject; public class MyApplication { private ArithmeticService service; // constructor based injection // @Inject // public MyApplication(ArithmeticService service){ // this.service=service; // } // setter method injection @Inject public void setService(ArithmeticService service) { this.service = service; } public void calculate(int a, int b) { service.calculate(a, b); } } Guice框架既支持基于构造方法的注入方式,也支持基于setter的注入方式。在上面的例子中,使用了基于setter的注入方式,并注释了基于构造方法的注入方式。 7、绑定服务实现 Google Guice框架需要知道要使用哪一个服务的实现类,因此必须配置它——通过继承AbstractModule类,并为configure()方法提供一个实现对象。如下: import com.google.inject.AbstractModule; public class GuiceInjector extends AbstractModule { @Override protected void configure() { bind(ArithmeticService.class).to(AdditionService.class); // bind(ArithmeticService.class).to(MultiplicationService.class); // bind(ArithmeticService.class).to(DivisionService.class); // bind(ArithmeticService.class).to(SubtractionService.class); } } 注意在上面的配置中,我已经将服务绑定到AdditionService实现类上。我们可以将绑定任一实现类到服务接口。如果将来要使用不同的服务实现,如SubtractionService或MultiplicationService,只需更改上述文件中的绑定。而无需修改客户端应用程序。 8、客户端应用程序 下面写一个客户端示例程序。 import com.google.inject.Guice; import com.google.inject.Injector; public class ClientApplication { public static void main(String[] args) { Injector injector = Guice.createInjector(new GuiceInjector()); MyApplication app = injector.getInstance(MyApplication.class); app.calculate(1, 2); } } 这里我们使用Guice类的createInjector()方法创建了一个Injector对象,并传递了我们自定义的注入器实现类的对象。然后使用注入器对象的getInstance()方法来创建一个服务消费类(MyApplication)的实例。在创建MyApplication对象时,Guice框架会自动注入其依赖的服务类实现(这里为AdditionService)。下面是运行代码的输出: Addition of 1 and 2 is: 3 还可以使用Google Guice来创建一个没有配置注入器的服务类(也即绑定服务类实现),如下: import com.google.inject.Guice; import com.google.inject.Injector; public class ClientApplication { public static void main(String[] args) { Injector injector = Guice.createInjector(); AdditionService addService = injector.getInstance(AdditionService.class); SubtractionService subService = injector.getInstance(SubtractionService.class); MultiplicationService mulService = injector.getInstance(MultiplicationService.class); DivisionService divService = injector.getInstance(DivisionService.class); addService.calculate(1, 2); subService.calculate(10, 5); mulService.calculate(5, 2); divService.calculate(4, 2); } } 如上面的例子所示,没有使用像GuiceInjector这样的自定义注入器类。运行上面的代码,将产生以下输出: Addition of 1 and 2 is: 3 Subtraction of 10 and 5 is: 5 Multiplication of 5 and 2 is: 10 Divisiton of 4 and 2 is: 2 这就是Java语言的Google Guice框架。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/57405155 在Ubuntu 16.04 LTS服务器部署Jenkins 作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 本文讲述怎样在Ubuntu 16.04 LTS服务器上部署Jenkins环境。Jenkins是基于Java语言开发的持续集成工具,为软件开发提供了一个持续集成服务。Jenkins是一个基于服务器的系统,运行在Servlet容器(比如Apache Tomcat)中。Jenkins支持SCM工具,包括AccuRev、CVS、Subversion、Git、Mercurial、Perforce、Clearcase和RTC等,可以只需基于Apache Ant或Apache Maven的项目,以及任意Shell脚本或Windows批处理命令。 1、检查系统环境 # cat /etc/os-release NAME="Ubuntu" VERSION="16.04.2 LTS (Xenial Xerus)" 2、确保系统保持最新 apt update apt upgrade 3、安装Java环境 通过PPA软件仓库安装OpenJDK 8。 add-apt-repository ppa:openjdk-r/ppa apt update apt install openjdk-8-jdk 验证Java环境的安装: # java -version openjdk version "1.8.0_121" OpenJDK Runtime Environment (build 1.8.0_121-8u121-b13-0ubuntu1.16.04.2-b13) OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode) 4、安装python-software-properties apt install python-software-properties 5、安装Jenkins 添加Jenkins源: wget -q -O - http://pkg.jenkins-ci.org/debian-stable/jenkins-ci.org.key | sudo apt-key add - OK 为Jenkins创建源列表: sh -c 'echo deb http://pkg.jenkins-ci.org/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list' 再次更新,并开始Jenkins的安装过程: apt update apt install jenkins 6、启动Jenkins服务 systemctl start jenkins Jenkins启动后,默认的日志会输出到/var/log/jenkins/jenkins.log文件中。启动后默认会创建一个管理员账号admin,以及为此账号产生一个密码,我的是:9**a947****f97a971d***4e**f 密码会写入到文件/var/lib/jenkins/secrets/initialAdminPassword中。查看日志文件,发现安装的Jenkins是最新的2.32.2 LTS版。 另外还可以对配置做一些调整。 7、为Jenkins安装并配置Apache HTTP Server Jenkins直接放公网不合适,通常会使用Nginx或Apache2作Jenkins的反向代理。故下面将为Jenkins安装并配置Apache HTTP Server。注意,如果不需要反向代理,可省略此步骤。 apt install apache2 在Apache中创建一个新的虚拟主机指令。比如,在虚拟主机上创建一个新的、名为“jenkins.conf”的Apache配置文件: a2enmod proxy a2enmod proxy_http a2ensite jenkins touch /etc/apache2/sites-available/jenkins.conf ln -s /etc/apache2/sites-available/jenkins.conf /etc/apache2/sites-enabled/jenkins.conf nano /etc/apache2/sites-available/jenkins.conf 添加以下内容: 8、访问Jenkins服务 Jenkins默认使用HTTP协议和主机的8080端口。浏览器访问http://ip:port/,可以看到如下界面。 输入初始密码,进入主界面。 然后就可以开始Jenkins之旅了。 9、修改Jenkins默认的端口 Jenkins默认使用8080端口,要修改此端口,编辑/etc/default/jenkins配置文件,将以下内容: HTTP_PORT=8080 改成想要的端口号,比如9080: HTTP_PORT=9080
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/56289457 Spring Framework 5.0的响应式微服务 作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs Spring团队已经宣布从5.0版本开始支持响应式编程模型。新的Spring 5.0版本可能会在今年3月发布。幸运的是,包含这些特性的里程碑版本和快照版本(非稳定版)现在可以从Spring存储库获得。另外还有一个新的Spring Web Reactive项目,支持响应式的@Controller注解和一个新的WebClient的客户端响应式。下面,可以进一步看看Spring团队提出的解决方案。 遵循这个文档,见: http://docs.spring.io/spring-framework/docs/5.0.0.BUILD-SNAPSHOT/spring-framework-reference/html/web-reactive.html Spring框架在内部使用Reactor实现了对响应式的支持。Reactor是一个Reactive Streams的实现,它进一步扩展了基本的Reactive Streams Publisher以及Flux和Mono composable API,对数据序列0…N和0…1提供了声明式的操作。在服务器端,Spring支持基于注释和函数式编程模型。注释模型使用了@Controller注解和其他同时也支持Spring MVC的注解。对于同步服务,响应式控制器与标准REST控制器是非常相似的,下面说明如何怎样使用注释模型和MongoDB的响应式模块来开发一个简单的响应式微服务。 在例子中,使用了Spring Boot 2.0.0快照版和Spring Web Reactive 0.1.0版。依赖配置pom.xml的主要片段和单个微服务的内容如下。在微服务中,使用Netty来代替了默认的Tomcat服务器。 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.BUILD-SNAPSHOT</version> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot.experimental</groupId> <artifactId>spring-boot-dependencies-web-reactive</artifactId> <version>0.1.0.BUILD-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId> </dependency> <dependency> <groupId>org.springframework.boot.experimental</groupId> <artifactId>spring-boot-starter-web-reactive</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.projectreactor.ipc</groupId> <artifactId>reactor-netty</artifactId> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> </dependency> <dependency> <groupId>pl.piomin.services</groupId> <artifactId>common</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.projectreactor.addons</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> </dependencies> 有两个微服务:帐户服务和客户服务。每个微服务都有自己的MongoDB数据库,且对外暴露简单的响应式API,用于搜索和保存数据。另外,客户服务与帐户服务可以相互通信,以获取所有的客户帐户,并通过客户服务API方法返回。下面是帐户控制器代码: @RestController public class AccountController { @Autowired private AccountRepository repository; @GetMapping(value = "/account/customer/{customer}") public Flux<Account> findByCustomer(@PathVariable("customer") Integer customerId) { return repository.findByCustomerId(customerId) .map(a -> new Account(a.getId(), a.getCustomerId(), a.getNumber(), a.getAmount())); } @GetMapping(value = "/account") public Flux<Account> findAll() { return repository.findAll().map(a -> new Account(a.getId(), a.getCustomerId(), a.getNumber(), a.getAmount())); } @GetMapping(value = "/account/{id}") public Mono<Account> findById(@PathVariable("id") Integer id) { return repository.findById(id) .map(a -> new Account(a.getId(), a.getCustomerId(), a.getNumber(), a.getAmount())); } @PostMapping("/person") public Mono<Account> create(@RequestBody Publisher<Account> accountStream) { return repository .save(Mono.from(accountStream) .map(a -> new pl.piomin.services.account.model.Account(a.getNumber(), a.getCustomerId(), a.getAmount()))) .map(a -> new Account(a.getId(), a.getCustomerId(), a.getNumber(), a.getAmount())); } } 在所有API方法中,还执行common模块从帐户实体(MongoDB @Document注解的)到帐户DTO的映射。下面是帐户存储库类。它使用ReactiveMongoTemplate与Mongo集合进行交互。 @Repository public class AccountRepository { @Autowired private ReactiveMongoTemplate template; public Mono<Account> findById(Integer id) { return template.findById(id, Account.class); } public Flux<Account> findAll() { return template.findAll(Account.class); } public Flux<Account> findByCustomerId(String customerId) { return template.find(query(where("customerId").is(customerId)), Account.class); } public Mono<Account> save(Mono<Account> account) { return template.insert(account); } } 在Spring Boot的main或@Configuration类中,应该为MongoDB声明Spring Bean以及连接设置。 @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } public @Bean MongoClient mongoClient() { return MongoClients.create("mongodb://192.168.99.100"); } public @Bean ReactiveMongoTemplate reactiveMongoTemplate() { return new ReactiveMongoTemplate(mongoClient(), "account"); } } 使用docker MongoDB容器来处理这个示例。 docker run -d --name mongo -p 27017:27017 mongo 在客户服务中,从帐户服务调用端点的/account/customer/{customer}。在主类中声明为@Bean WebClient。 @Autowired private WebClient webClient; @GetMapping(value = "/customer/accounts/{pesel}") public Mono<Customer> findByPeselWithAccounts(@PathVariable("pesel") String pesel) { return repository.findByPesel(pesel).flatMap(customer -> webClient.get().uri("/account/customer/{customer}", customer.getId()).accept(MediaType.APPLICATION_JSON) .exchange().flatMap(response -> response.bodyToFlux(Account.class))).collectList().map(l -> {return new Customer(pesel, l);}); } 可以使用Web浏览器或REST客户端来测试GET调用。而用POST,则没有那么简单。下面有两个简单的测试用例,用于添加新客户和获得客户的帐户信息。要测试getCustomerAccounts,需要先在端口2222上运行帐户服务。 @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class CustomerTest { private static final Logger logger = Logger.getLogger("CustomerTest"); private WebClient webClient; @LocalServerPort private int port; @Before public void setup() { this.webClient = WebClient.create("http://localhost:" + this.port); } @Test public void getCustomerAccounts() { Customer customer = this.webClient.get().uri("/customer/accounts/234543647565") .accept(MediaType.APPLICATION_JSON).exchange().then(response -> response.bodyToMono(Customer.class)) .block(); logger.info("Customer: " + customer); } @Test public void addCustomer() { Customer customer = new Customer(null, "Adam", "Kowalski", "123456787654"); customer = webClient.post().uri("/customer").accept(MediaType.APPLICATION_JSON) .exchange(BodyInserters.fromObject(customer)).then(response -> response.bodyToMono(Customer.class)) .block(); logger.info("Customer: " + customer); } } 结论 Spring框架开始支持响应式编程非常不错,但现在它还处于早期阶段,也没法与Spring Cloud等项目一起使用。希望在不久的将来,类似服务发现和负载平衡的功能也可用于与Spring响应式微服务相集成。Spring还有一个Spring Cloud Stream项目,支持响应式模型。以后再看吧!
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/56022508 12个最应该使用的Linux服务器OS(下) 作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 9. Slackware http://www.slackware.com/ Slackware是一个长期开发的Linux服务器发行版。第一次迭代于1993年首次亮相。根据Slackware Linux网站,该项目的目标是“最类似于UNIX的Linux发行版”。默认情况下,Slackware启动到命令行界面。 完整的Slackware安装包括C和C++,X Window系统,邮件服务器,Web 服务器,FTP服务器和新闻服务器。此外,Slackware非常轻巧,它与奔腾系统兼容。连续的版本确保了稳定性和简单性。 谁应该使用它: Slackware Linux最适合经验丰富的Linux专业人士。它提供了包管理器,pkgtools和slackpkg。但是,由于Slackware默认情况下启动到命令行环境,故更适合作为一个高级Linux服务器操作系统。此外,它相对有一点复杂。用户必须了解Slackware Linux环境生态。 10. Gentoo https://www.gentoo.org/ Gentoo与许多Linux发行版不同。它没有采用传统的发布模式,Gentoo具有模块化设计。因此,用户自行选择要安装的功能。这正是为什么Gentoo作为一个顶级的Linux服务器操作系统的原因之一。 Gentoo的每次安装都是独一无二的。用户可以构建一个内核,提供更多的控制。因此,可以为服务器控制诸如存储器消耗的方面等。由于这种模块化的设计和灵活性,Gentoo满足了Linux专业人士的需要。系统管理员特别欣赏Gentoo提供的定制方法。 谁应该使用它: Gentoo最适用于技术高明的用户和系统管理员。虽然Gentoo有可能被初学者使用,但它的入门级比Ubuntu的衍生版相对要低一些。Gentoo的文档很优秀,Gentoo从其繁荣的社区受益。 11. Fedora https://getfedora.org/ 如果你正在寻找一个新的Linux服务器操作系统,可以尝试Fedora。在Red Hat的支持下,Fedora项目会定期更新。上游社区经常提供帮助。Fedora有几种版本:Workstation工作站版适合于一般用户,并配有一个桌面环境。默认情况下Fedora工作站使用GNOME桌面,但也可使用其它桌面。Fedora Server专注于服务器。 Fedora在许多方面都被视为RPM家族的母亲,就像Debian是Debian家族的母亲一样。这是因为Fedora是从头开始的,并且不是从另一个发行版派生的,而且有很多发行版基于Fedora。Fedora几乎完全由社区控制,由红帽资助。根据个人经验,Fedora可能是最容易加入和参与的发行版之一。Fedora非常专注于提供免费软件。 Fedora是多用途的,可以在任何系统上完美使用,但Fedora更适合测试企业环境。与其他发行版相比,Fedora还具有非常高的前沿性。Fedora的发布周期只持续了13个月。 Fedora Server默认安装缺少GUI。但是,如果需要GUI,也可以安装一个。服务器版本拥有大量的工具。有Cockpit系统管理仪表板。Fedora服务器版本包含了像PostgreSQL这样的数据库服务。 谁应该使用它: 经验丰富的Linux开发人员和系统管理员应选择Fedora Server。它缺乏默认桌面环境和企业级功能意味着Fedora更适合高级服务器。 12. Debian https://www.debian.org/ 好吧,Debian缺少一个专门的服务器版本。不过,Debian是最好的Linux服务器操作系统之一。因为Debian于1993年推出,并于1996年发布了第一个稳定版,这是非常安全的。许多Linux发行版,包括Ubuntu都是基于Debian的。为什么要使用Debian作为另一个操作系统的基础?稳定性。 因此,Debian通常用于服务器,因为它经历了时间的考验。Debian提供了一个包管理器,APT工具和各种前端,如GDebi。所以虽然Debian没有服务器发行版,但可以用它自己动手打造一个完美的Linux服务器操作系统。Debian具有惊人的应用程序兼容性,安全性和稳定性。 谁应该使用它: Debian为双方提供了一个梦幻般的服务器环境。如果你正在寻找一个基本的服务器,如邮件,网络,游戏或媒体服务器,Debian很容易设置。或者,具有特定服务器需求的更高级用户应考虑Debian。然而,这种方法确实需要DIY工作。