首页 文章 精选 留言 我的

精选列表

搜索[数据处理],共9357篇文章
优秀的个人博客,低调大师

PB 级数据处理挑战,Kubernetes如何助力基因分析?

作者李鹏,原文首发于InfoQ,《容器混合云,Kubernetes 助力基因分析》 引言 James Watson 和 Francis Crick 于 1953 年发现了 DNA 的双螺旋结构,从此揭开了物种进化和遗传的神秘面纱,开启了人类对数字化遗传的认知,但是人类基因奥秘却是一点点被读懂的。 1956 年,一则癌症和染色体相关性的发现令整个癌症研究界震动:慢性骨髓性白血病(CML)患者的第 22 号染色体,比一般然明显短很多。二十余年后,学者们发现,9 号染色体的 Abl 基因,与 22 号染色体的 BCR 基因连到了一块,交错易位产生了一条 BCR-Abl 融合基因。BCR-Abl 蛋白一直处于活跃状态且不受控制,引发不受控的细胞分裂,从而导致癌症。 也就是说,只要细胞表达 BCR-Abl 蛋白,就有血癌风险。美国着手深入研究,并成

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

java8学习:并行数据处理与性能

内容来自《 java8实战 》,本篇文章内容均为非盈利,旨为方便自己查询、总结备份、开源分享。如有侵权请告知,马上删除。书籍购买地址:java8实战 在java7之前实现并行处理数据集合非常麻烦 得明确的把包含数据的数据结构分成若干子部分 要给每个子部分分配一个独立的线程 在恰当的时候对他们进行同步来避免不希望出现的竞争条件,等待所有线程完成,最后把结果汇总在一起 在java7引入了fork/join框架来实现并行,在这篇文章中,将介绍利用Stream来实现并行和所需要注意的事项,并且介绍fork/join框架 之前我们提到过stream()是顺序执行,而parallelStream()是并行执行,并行流就是一个把内容分成多个数据块,并用不同的线程分别处理每个数据块的流,这样可以把压力分担给不同的内核 下面是一个例子:就是求和操作,从1加到给出的n,我们用顺序流实现一下 @Test public void test() throws Exception { long l = System.currentTimeMillis(); System.out.println(parallelSum(10_000_000)); //一千万相加 System.out.println(System.currentTimeMillis() - l); } public long sequentialSum(long n){ //237+ return Stream.iterate(0L, i -> i + 1) .limit(n) .reduce(0L,Long::sum); } 下面是并行流 public long parallelSum(long n){ //1533+ return Stream.iterate(0L, i -> i + 1) .limit(n) .parallel() //切换并行流 .reduce(0L,Long::sum); } 上面并行流将Stream内部数据分为几块,对不同的块进行归约操作后在汇总成最终结果 parallel是将顺序流转为并行流,而sequential是将并行流转为顺序流,你可能会想:利用这两个转换方法来使得更精确的控制流的,但是需要注意的是 Stream.iterate(0, i -> i + 1) .parallel() //如果贴到IDEA中,这会变灰的,也证明了它是无效的操作 .limit(n) .sequential() .reduce(0,Integer::sum); 上面只是一个例子,我们先转换为并行流然后经过操作在转换为顺序流,本以为可以精确控制流的,但最终stream是以顺序流的方式执行的,也就是说,最后调用的转换方法将影响整个stream 提到的并行流,那么他肯定需要多个线程,那么线程是从哪里来的?他有几个? 并行流内部使用了ForkJoinPool,它默认的线程数量就是自己使用的机器的处理器数量,可以通过Runtime.getRuntime().availableProcessors();得到,当然自己想改这个值的话,需要设置系统属性通过System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","12");设置,一般是不需要修改的 测试一下性能 顺序流:上面已经测试了,时间在90ms+ fori long l = System.currentTimeMillis(); long sum = 0; for (int i = 0; i <= 10000000; i++) { sum += i; } System.out.println(sum); System.out.println(System.currentTimeMillis() - l); //7+ 并行流:上面已经测试了,时间在130ms+ 结果并不是我们期待的并行流更加快速,对于fori的结果的解释是:他的操作更加偏底层所以也更快,并且它是使用的基本类型避免了拆箱装箱的操作。提到装箱拆箱,这我们自然的就会想到变更上面顺序流和并行流中使用的Stream为基本类型Stream,测试如下 public long sequentialSum(long n){ //85+ return LongStream.iterate(0L, i -> i + 1) .limit(n) .reduce(0L,Long::sum); } public long parallelSum(long n){ //313+ return LongStream.iterate(0L, i -> i + 1) .limit(n) .parallel() .reduce(0L,Long::sum); } 结果已经提升了不少时间了,但是结果还是比较让人失望,因为相差fori还是有很大距离,那么我们下面就来详细的看一下为什么会发生这样的事情 上面Stream都是用到了iterate方法来生成数字,但是这存在问题 iterate生成的是装箱的对象,必须拆箱之后才能进行数字求和 很难把iterate分成多个独立的块来并行执行(必须意识到哪些流更容易让并行流来切分为子任务,iterate很难切分为子任务的原因是:每次应用iterate都会依赖上次生成的结果,因此如果切分成独立小块,那么他就不知道下一个会生成的是什么了(自己的理解。)) 我们之前学过一个生成数字范围的方法来我们来尝试一下,为什么要尝试这个?因为他直接生成一个数字范围并不依赖于之前的任何东西就可以生成,测试如下 public long sequentialSum(long n){ //62+ return LongStream.rangeClosed(0L,n) .reduce(0L,Long::sum); } public long parallelSum(long n){ //73 + return LongStream.rangeClosed(0L,n) .parallel() .reduce(0L,Long::sum); } 测试一亿数字相加结果:fori:43,stream:119,parallel:99.这时候并行流才会超过顺序流 好了到这基本就完事了,如果你购买了这本书,那么对于书上的并行流计算1千万,它达到的结果是1毫秒。但是对比机器我的是八核他仅仅是四核。所以对自己的测试产生了疑问,如果你现在正好看到这里,恳请您可以用自己的电脑测试一下,评论到下面:机器配置+各个的测试结果,对于您的评论将感激不尽 对于上面我们看到了,因为更改了一些api和一些基本类的stream而达到的提升是很惊人的,并行流从一秒多提升到73毫秒。所以下面将要说的是如何争取的使用并行流 对于并行中共享变量的演示 class A { public long total = 0; public void add(long value){ total += value; } } public long sum(long n){ A a = new A(); LongStream.rangeClosed(0,n).parallel().forEach(a::add); return a.total; } @Test public void test() throws Exception { System.out.println(sum(10_000_000)); } 如上的结果各式各样,原因就是total += value并不是原子性的,所以为了保证正确需要加同步,但是这又违反了并行原则。所以到这的结果就是在并行计算中避免使用共享变量 高效的使用流 下面的是书上给出的一些建议,对于流写完评价性能,请重复测试,以保证流是比较高效的 对于拆箱装箱的问题要意识到,如果有那么就有意识的使用api或者类给避免掉 对于较小的数据量不推荐并行流,因为并行流有拆分和合并的过程 考虑数据结构是否容易分解,比如上面的iterate就不容易分解,下面列出了常用的类和对应的可分解性 考虑终端操作中合并步骤的代价的大小,如果合并代价很大,那么组合每个子流产生的部分结果所付出的代价就可能会超过通过并行流得到的性能提升 注意一些api在流中的使用,比如limit和findFirst,因为他们依赖元素的顺序(并行流把数据拆分为几块,如果findFirst那么他就必须去寻找正在的数据的头元素,而不是每个子流中的任一的头元素),他们在并行流上使用代价很大,当然findAny就比findFirst快,因为随便一个子流返回一个就可以了,流中有一个操作unordered方法会将有序流转换为无序流,那么现在如果你需要流中的n个元素而不是专门要前n个元素的话,对无序并行流调用limit可能会比单个有序流更高效 考虑操作流水线的总计算成本:设N是要处理元素的数量,Q是元素通过流水线的大致处理成本,则N*Q就是这个对成本的一个估计,Q值较高就代表使用并行流的性能会更好(自己的理解:综合考虑元素合数和对每个元素处理的复杂度,复杂度越高就越推荐使用并行流,这样可以分摊到各个内核并行处理元素) | 源 | 可分解性 | | -------- |------- | | ArrayList | 极佳 | | LinkedList | 差 | | IntStream.range | 极佳 | | Stream.iterate | 差 | | TreeSet | 好 | | HashSet | 好 | 前面提到了并行流的实现就是使用了forkjoin框架,那么现在来了解一下forkjoin框架:移步到ForkJoin 了解完forkjoin,我们知道了在此框架中是如何的切分数据的,那么我们的并行流是怎么实现切分数据的呢? 能够帮助并行流切分数据的就是Spliterator机制:可分迭代器 Spliterator是java8新接口,可以用于遍历数据源中的元素,但它是为了并行执行而设计的,一般在开发中不需自己开发 接口定义 public interface Spliterator<T> { boolean tryAdvance(Consumer<? super T> action); Spliterator<T> trySplit(); long estimateSize(); int characteristics(); ... } 泛型是需要遍历的元素类型 tryAdvance:会按顺序一个个使用Spliterator中的元素,并且如果还有其他元素要遍历就返回true trySplit:专门为此接口设计的,它可以把一些元素划出去分给第二个Spliterator,由该方法返回,让两个Spliterator并行处理 estimateSize:用来估计还剩下多少元素要遍历,因为即使不那么确切,能快速算出来是一个值也有助于让拆分均匀一点 拆分过程 将Stream拆分的过程是一个递归的过程,先将最开始的Spliterator尝试trySplit拆分,生成第二个Spliterator2,然后再次对这两个Spliterator进行拆分...框架一直拆分Spliterator,直到Spliterator调用trySplit返回null,标示处理的数据结构已经不能分割,这时候此Spliterator上的拆分就终止了 拆分过程也受Spliterator本身的特性影响,而特性就是由上面定义的方法characteristics声明的 Spliterator特性 characteristics返回一个int,代表Spliterator本身特性集的编码。使用Spliterator可以通过特性来更好的控制和优化它的使用 | 特性 | 含义 | | ------------- | ------------- | | ORDERED | 元素有既定的顺序:List,因此Spliterator在遍历和划分的时候也会遵循这一规律 | | DISTINCT | 对于任意一对遍历过的元素x和y,x.equals(y)=false | | SORTED | 遍历的元素按照一个预定义的顺序排序 | | SIZED | 该Spliterator由一个一只大小的源建立:Set,因此 estimateSize返回的是准确值 | | NONNULL | 保证遍历的元素不会为null | | IMMUTABLE | Spliterator的数据源不能修改,就意味着在遍历时不能增删改任何元素 | | CONCURRENT | Spliterator的数据源可以被其他线程同时修改而无需同步 | | SUBSIZED | Spliterator和所有从它拆分出来的Spliterator都是SIZED | 实现自己的Spliterator 实现String字符串中有多少个单词 String str = "hello hello hellohello hello hello hellohello hello hello"; IntStream.range(0,str.length()).mapToObj(str::charAt).forEach(System.out::println); 如上代码结果就是str字符串被一个个输出,现在我们得到了这个流,那么我们现在就可以根据流来判断有多少个单词了,因为只要是" "那么就是遇到空格那么肯定就是一个单词出现了 现在我们需要两个值来保存状态:counter用来计算到目前为止数过的字数,还有一个Boolean记录上一个是否遇到的是空格,如下代码 public class WordCount { private final int counter;//单词累计 private final boolean lastSpace;//字符标志 public WordCount(int counter, boolean lastSpace) { this.counter = counter; this.lastSpace = lastSpace; } //接收流中的一个个char并判断 public WordCount accumulate(Character c) { if (Character.isWhitespace(c)){ //如果是true的话,那么就返回当前对象, return lastSpace ? this : new WordCount(counter,true); }else { //如果是true,那么就代表上一个字符是空格,那么就为下一个对象的累加计数器加一,并为字符标志器赋值为false代表遇到的不是空格 return lastSpace ? new WordCount(counter + 1 , false) : this; } } //合并方法:合并两个wordCounter public WordCount combine(WordCount wordCount){ return new WordCount(counter + wordCount.counter,wordCount.lastSpace); } public int getCounter() { return counter; } } accumulate定义了用哪个状态来建立新的WordCOunter,因为类中的变量是不可变的,每次遍历Stream中的一个新的Character时,就会调用此方法 combine会对作用与Character流的两个不同子部分的两个WordCounter的部分结果进行汇总,也就是将WordCounter内部计数器加起来 测试如上方法 //共用测试方法 private int countWords(Stream<Character> stream){ return stream.reduce(new WordCount(0,true),WordCount::accumulate,WordCount::combine).getCounter(); } String s = " hello hello hellohello hello hello hellohello hello hello "; Stream<Character> stream = IntStream.range(0, s.length()) .mapToObj(s::charAt); int i = countWords(stream); System.out.println("i = " + i); //顺序流并不会调用combine,因为它的累加在创建新的WordCounter类时已经累加了 我们看上面的测试方法,可以传入一个stream,如果让他调用转换为并行流的方法应该也行得通,比如 String s = " hello hello hellohello hello hello hellohello hello hello "; Stream<Character> stream = IntStream.range(0, s.length()) .mapToObj(s::charAt); int i = countWords(stream.parallel()); //注意变化 System.out.println("i = " + i); 但是失望的是结果是不对的,有个好消息就是我们知道了并行流的情况下,才会调用combine方法 结果为什么不对?因为你的字符串转换为并行流处理后,并行流并不清楚如何切分你的hello,可能把他且分为两个流中去了,比如A流有Hell,而o却跑到了B流中,以至于一个单词被算成了两个单词,我们要想办法避免并行流随机切分数据块,那么这时候就会用到Spliterator了 不让并行流随意切分的思路是:我们在切分数据的时候依旧是从数据的中间定位开始,就是index=length/2,但是我们要判断此处是否是空格,如果是的话那么直接切,不是的话,就要往前挪或者往后挪index,直到出现空格为止,所以实现代码如下 public class WordCountSpliterator implements Spliterator<Character> { private final String string; private int currentChar = 0; public WordCountSpliterator(String string) { this.string = string; } //会按顺序一个个使用Spliterator中的元素,并且如果还有其他元素要遍历就返回true @Override public boolean tryAdvance(Consumer<? super Character> action) { action.accept(string.charAt(currentChar++)); return currentChar < string.length(); } //可以把一些元素划出去分给第二个Spliterator,由该方法返回,让两个Spliterator并行处理 @Override public Spliterator<Character> trySplit() { int currentSize = string.length() - currentChar; if (currentSize < 10){ return null; } for (int splitPos = currentSize / 2 + currentChar; splitPos < string.length(); splitPos++) { if (Character.isWhitespace(string.charAt(splitPos))){ Spliterator<Character> spliterator = new WordCountSpliterator(string.substring(currentChar,splitPos)); currentChar = splitPos; return spliterator; } } return null; } //用来估计还剩下多少元素要遍历,因为即使不那么确切,能快速算出来是一个值也有助于让拆分均匀一点 @Override public long estimateSize() { return string.length() - currentChar; } //返回一个int,代表Spliterator本身特性集的编码。 @Override public int characteristics() { return ORDERED + SIZED + SUBSIZED + NONNULL + IMMUTABLE; } } 代码逻辑并不难,不要被Spliterator和并行流吓住,拿本子画一画就很清楚明白了,比如切分是这样的 测试代码 String s = "hello hello hellohello hello hello hellohello hello hello"; WordCountSpliterator spliterator = new WordCountSpliterator(s); Stream<Character> stream = StreamSupport.stream(spliterator, true); int i = countWords(stream); System.out.println("i = " + i); Spliterator代码解释 tryAdvance把String中当前位置的Character传给了Consumer,并让位置加一。这里只有一个归约函数即WordCounter类的accumulate方法。如果新的指针位置小于String的总长,且还有要遍历的Character,则tryAdvance返回true trySplit定义了拆分要遍历的数据结构的逻辑。在方法中首先定下了什么时候不在拆分,不拆分的时候返回null,需要拆分的时候,就把试探的拆分为止设在要解析的String字符串的中间,如果不是空格就往后找,避免把String错切分成两个单词。一旦找到空格那么就创建一个新的Spliterator来遍历从当前位置到拆分位置的子串,把当前位置this设置为拆分位置,因为之前的部分将由新Spliterator处理最后返回 还需要遍历的元素的estimatedSize就是这个Spliterator解析的字符串的总长度和当前遍历的位置的差 characteristics告诉框架这个SPliterator是 ORDERED:顺序就是String各个char的次序 SIZED:estimateSize方法的返回值是精确的 SUBSIZED:trySplit方法创建的Spliterator也有确切大小 NONULL:字符串中不能有null IMMUTABLE:在解析字符串时不能再添加数据,因为字符串不可变 Spliterator最后需要注意的一个功能是:就是可以在第一次遍历,第一次拆分或第一次查询估计大小时绑定元素的数据源,而不是在创建时就绑定

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

网络数据处理缓冲区和缓冲池实现

在编写网络应用的时候数据缓冲区是应该比较常用的方式,主要用构建一个内存区用于存储发送的数据和接收的数据;为了更好的利用已有数据缓冲区所以构造一个缓冲池来存放相关数据方便不同连接更好地利用缓冲区,节省不停的构造新的缓冲区所带的损耗问题。 缓冲区 其实构造一个缓冲区非常简单,根据需分本相关大小的byte数组即可;既然是用于存放数据那就自然要实现读和写方法,看一下具体实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 public class DataBuffer : IDisposable { public byte [] Data; private int mLength; private int mPostion = 0; internal int mCount = 0; public DataBuffer( byte [] data) { Data = data; mLength = data.Length; mPostion = 0; mCount = data.Length; } public DataBuffer( int length) { mLength = length; Data = new byte [length]; } public void From(Array source, int index, int count) { Array.Copy(source, index, Data, 0, count); mPostion = 0; mCount = count; } public int Write( byte [] data) { return Write(data, 0); } public int Write( byte [] data, int index) { int count = 0; if (mPostion + (data.Length-index) > mLength) { count = mLength - mPostion; } else { count = data.Length - index; } if (count > 0) { Array.Copy(data, index, Data, mPostion, count); mPostion += count; mCount += count; } return count; } public ArraySegment< byte > Read( int count) { int end = count; if (mPostion + count > mCount) end = mCount - mPostion; ArraySegment< byte > result= new ArraySegment< byte >(Data, mPostion, end); mPostion += end; return result; } public void Seek() { Seek(0); } public void Seek( int postion) { mPostion = 0; } public ArraySegment< byte > GetSegment() { return new ArraySegment< byte >(Data, 0, mCount); } internal BufferPool Pool { get ; set ; } public void Dispose() { if (Pool != null ) { mPostion = 0; mCount = 0; Pool.Push( this ); } } } 为了方便使用,Buffer实现了IDisposable接口,其作为就是当释放的时候把Buffer放回到Pool里. Buffer提供了两个方法分别是Write和Read用于写和读数据,由于缓冲区有大小限制,所以在写的时候会返回一个成功写入的数量;而read则返回一个ArraySegment<byte>用于描述其位置。为什么要这样做呢,其实有些情况一个数据成员会被写入到不同的缓冲区,当读出来的时候就会存要在多个缓冲区中获取。 缓冲池 缓冲池用于发放和回收级冲区,实现一个重用的目的。池的实现并不复杂,封装一个简单的队列操作即可。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 public class BufferPool : IDisposable { private static List<BufferPool> mPools = new List<BufferPool>(); private static int mIndex = 0; public static void Setup( int pools, int buffers) { Setup(pools, buffers, 2048); } public static void Setup( int pools, int buffers, int bufferlength) { lock (mPools) { for ( int i = 0; i < pools; i++) { mPools.Add( new BufferPool(buffers, bufferlength)); } } } public static void Clean() { lock (mPools) { foreach (BufferPool item in mPools) { item.Dispose(); } mPools.Clear(); } } public static BufferPool GetPool() { lock (mPools) { if (mIndex == mPools.Count) { mIndex = 0; } return mPools[mIndex]; } } Queue<DataBuffer> mBuffers; private int mBufferLength; public BufferPool( int count, int bufferlength) { mBufferLength = bufferlength; mBuffers = new Queue<DataBuffer>(count); for ( int i = 0; i < count; i++) { mBuffers.Enqueue(createBuffer(bufferlength)); } } private DataBuffer createBuffer( int length) { DataBuffer item = new DataBuffer(length); item.Pool = this ; return item; } public DataBuffer Pop() { lock (mBuffers) { return mBuffers.Count > 0 ? mBuffers.Dequeue() : createBuffer(mBufferLength); } } public void Push(DataBuffer buffer) { lock (mBuffers) { mBuffers.Enqueue(buffer); } } private bool mDisposed = false ; private void OnDispose() { lock (mBuffers) { while (mBuffers.Count > 0) { mBuffers.Dequeue().Pool = null ; } } } public void Dispose() { lock ( this ) { if (!mDisposed) { OnDispose(); mDisposed = true ; } } } } BufferPool实现了几个静态方法 Setup 主要目的是用于构造多个缓冲池,缓冲区数量和缓冲区大小。为什么会考虑多个池呢,主要原因是在高并发的来分配处理减低池的负载。 Clean 用于清除释放缓冲池 GetPool 平均地分发缓冲池给使用者 一个简单的数据缓冲区和数据缓冲池已经实现了,在后面的文章里会讲述如何构造BufferWriter和BufferReader,根据对象的需要把信息分别写入多个缓冲区和在多个缓冲区中读取信息还原对象。 c#组件设计交流群:47164588 c# socket :136485198 微博http://weibo.com/ikende

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

海量的物联网数据处理应该怎么部署

目前,许多企业正在采用物联网来使用数据,从而更好地了解其运营情况,做出更明智的决策,重新定位客户参与度,并重新思考如何创造价值。随着低成本传感器,弹性计算和数据科学的快速发展,许多行业观察家期望企业迅速部署物联网设备。物联网专家预计,在这一时期内,所有这些开发项目将在全球范围内产生大约44万亿兆字节的额外的物联网数据。这使人们想到了一个核心问题:采用哪种最佳技术架构来解决这一爆炸性增长的数据趋势? 本地部署的物联网架构 本地部署的物联网架构采用边缘计算,其中在网络边缘处理数据,这个位置最接近数据源头。而根据调研机构IDC的调查,到2019年,45%的物联网设备数据将被存储,处理,并靠近边缘计算。该模式可以提供更小的性能足迹,可以帮助企业对数据进行更多的实时响应。例如,在石油钻井平台上,采用传感器可以检测故障的阀门是否产生火灾隐患。在这种情况下,企业不能承受任何延误。如果数据需要发送到卫星,在数据中心返回到通知关闭阀门之前,其响应时间可能太晚了。但是,随着更快的边缘部署,数据不必远离其数据来源。这可以减少时间延迟,并允许做出关键的决定。 此外,本地部署的架构不依赖互联网连接,如云环境。并且本地部署的架构也受到面临严重数据安全问题的企业的青睐。利用边缘计算的本地体系结构有很多意义。 云端物联网架构 云端物联网架构有利于组织管理大量的连接设备,通过内部和外部数据的组合驱动价值。例如,供应链应用程序可以从理解一个部分相对于整体聚合视图的具体视图而受益。而完整视图之外只有一组数据失去意义。例如,通过单独使用本地部署的架构,就不可能试图为资产构建的每个组件协调供应链。 此外,云计算架构在与其他物联网设备和云系统集成和交互方面提供更大的互操作性。该模型提供了更多的架构灵活性和外部数据源的利用率。云应用程序在生态系统中看到了更多的创新,其部分原因是软件开发人员重点关注大型市场。利用云计算架构的物联网部署可以更有效,因为很多具有技术创新和竞争性的产品已经可用。从本质上说,云计算架构可以使组织能够面对未来的投资回报。 混合的物联网架构 通常最好的方法是高效地结合边缘计算的大型核心数据集的处理,然后在核心处理一组简化的聚合衍生数据。例如,智能城市部署的停车传感器可以处理靠近车位的所有传感器的数据,只提供有关不同车库开放的地点和数量的汇总数据,从而为进入市区的司机提供智能寻找车位的建议。毕竟,每隔几秒就传输所有这些数据的成本可能是很昂贵的,而接近目的地的驾驶人员不一定知道停车场的那些位置是开放的。在这种情况下,采用混合架构是理想的选择。 资产优化的另一个例子是风力涡轮机的应用,风力涡轮机使用传感器在本地部署收集和分析每个涡轮机上的数据,并总体优化其总体性能。在这里,通过许多数据点可以深入了解涡轮机组件的运行状况。每个组件的健康状况汇总在一起提供了风力发电机的状况视图。最后,汇总来自所有风力发电机的数据,为风电场提供可操作的信息。在这样的情况下,应该在网络边缘处理多少数据,而将哪些数据在数据中心处理,这是一个重要考虑的架构。本地部署架构的实时响应以及云计算的全系统访问和可扩展性的组合将会得到最好的发挥。

资源下载

更多资源
Mario

Mario

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

腾讯云软件源

腾讯云软件源

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

Rocky Linux

Rocky Linux

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

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册