小规模流处理kata。 第1部分:线程池

我再次为我的公司在GeeCON 2016上举办了编程竞赛。 这次分配需要设计并根据以下要求选择实施系统:

一个系统每秒传送约一千个事件。 每个Event至少具有两个属性:

  • clientId –我们期望一个客户端每秒最多可以处理几个事件
  • UUID –全球唯一

消耗一个事件大约需要10毫秒。 设计此类流的使用者:

  1. 允许实时处理事件
  2. 与一个客户端有关的事件应按顺序进行处理,即,您不能并行处理同一clientId事件
  3. 如果10秒钟内出现重复的UUID ,请将其删除。 假设10秒钟后不会出现重复

这些要求中没有几个重要的细节:

  1. 1000个事件/秒和10毫秒消耗一个事件。 显然,我们至少需要10个并发使用者才能实时消费。
  2. 事件具有自然的聚合ID( clientId )。 在一秒钟内,我们可以为给定的客户预料到一些事件,并且不允许我们同时或无序处理它们。
  3. 我们必须以某种方式忽略重复的消息,最有可能的是通过记住最近10秒钟内的所有唯一ID。 这使大约一万个UUID得以临时保留。

在本文中,我将指导您完成几个正确的解决方案,并进行一些尝试。 您还将学习如何使用少量精确定位的指标来解决问题。

天真的顺序处理

让我们通过迭代解决这个问题。 首先,我们必须对API进行一些假设。 想象一下:

interface EventStream {void consume(EventConsumer consumer);}@FunctionalInterface
interface EventConsumer {Event consume(Event event);
}@Value
class Event {private final Instant created = Instant.now();private final int clientId;private final UUID uuid;}

典型的基于推送的API,类似于JMS。 一个重要的注意事项是EventConsumer正在阻止,这意味着直到EventConsumer消耗了前一个Event ,它才交付新的Event 。 这只是我所做的一个假设,并没有彻底改变需求。 这也是JMS中消息侦听器的工作方式。 天真的实现只附加了一个侦听器,该侦听器需要大约10毫秒才能完成:

class ClientProjection implements EventConsumer {@Overridepublic Event consume(Event event) {Sleeper.randSleep(10, 1);return event;}}

当然,在现实生活中,该消费者会将一些东西存储在数据库中,进行远程调用等。我在睡眠时间分配中添加了一些随机性,以使手动测试更加实际:

class Sleeper {private static final Random RANDOM = new Random();static void randSleep(double mean, double stdDev) {final double micros = 1_000 * (mean + RANDOM.nextGaussian() * stdDev);try {TimeUnit.MICROSECONDS.sleep((long) micros);} catch (InterruptedException e) {throw new RuntimeException(e);}}}//...EventStream es = new EventStream();  //some real implementation here
es.consume(new ClientProjection());

它可以编译并运行,但是为了确定未满足要求,我们必须插入少量指标。 最重要的度量标准是消息消耗的延迟,以消息创建到开始处理之间的时间来衡量。 我们将为此使用Dropwizard指标 :

class ClientProjection implements EventConsumer {private final ProjectionMetrics metrics;ClientProjection(ProjectionMetrics metrics) {this.metrics = metrics;}@Overridepublic Event consume(Event event) {metrics.latency(Duration.between(event.getCreated(), Instant.now()));Sleeper.randSleep(10, 1);return event;}}

提取ProjectionMetrics类以分离职责:

import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Slf4jReporter;
import lombok.extern.slf4j.Slf4j;import java.time.Duration;
import java.util.concurrent.TimeUnit;@Slf4j
class ProjectionMetrics {private final Histogram latencyHist;ProjectionMetrics(MetricRegistry metricRegistry) {final Slf4jReporter reporter = Slf4jReporter.forRegistry(metricRegistry).outputTo(log).convertRatesTo(TimeUnit.SECONDS).convertDurationsTo(TimeUnit.MILLISECONDS).build();reporter.start(1, TimeUnit.SECONDS);latencyHist = metricRegistry.histogram(MetricRegistry.name(ProjectionMetrics.class, "latency"));}void latency(Duration duration) {latencyHist.update(duration.toMillis());}
}

现在,当您运行幼稚的解决方案时,您会很快发现中值延迟以及99.9%的百分数无限增长:

type=HISTOGRAM, [...] count=84,   min=0,  max=795,   mean=404.88540608274104, [...]median=414.0,   p75=602.0,   p95=753.0,   p98=783.0,   p99=795.0,   p999=795.0
type=HISTOGRAM, [...] count=182,  min=0,  max=1688,  mean=861.1706371990878,  [...]median=869.0,   p75=1285.0,  p95=1614.0,  p98=1659.0,  p99=1678.0,  p999=1688.0[...30 seconds later...]type=HISTOGRAM, [...] count=2947, min=14, max=26945, mean=15308.138585757424, [...]median=16150.0, p75=21915.0, p95=25978.0, p98=26556.0, p99=26670.0, p999=26945.0

30秒后,我们的应用程序平均会延迟15秒处理事件。 并非完全实时 。 显然,缺少并发是任何原因。 我们的ClientProjection事件使用者大约需要10毫秒才能完成,因此它每秒可以处理多达100个事件,而我们还需要一个数量级。 我们必须以某种方式扩展ClientProjection 。 而且我们甚至都没有触及其他要求!

天真线程池

最明显的解决方案是从多个线程调用EventConsumer 。 最简单的方法是利用ExecutorService

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;class NaivePool implements EventConsumer, Closeable {private final EventConsumer downstream;private final ExecutorService executorService;NaivePool(int size, EventConsumer downstream) {this.executorService = Executors.newFixedThreadPool(size);this.downstream = downstream;}@Overridepublic Event consume(Event event) {executorService.submit(() -> downstream.consume(event));return event;}@Overridepublic void close() throws IOException {executorService.shutdown();}
}

我们在这里使用装饰器模式 。 实现EventConsumer的原始ClientProjection是正确的。 但是,我们使用EventConsumer另一个实现来包装它,该实现增加了并发性。 这将使我们能够编写复杂的行为而无需更改ClientProjection本身。 这样的设计促进:

  • 松散耦合:各种EventConsumer彼此都不了解,可以自由组合
  • 单一职责:每个人都做一份工作,然后委派给下一个组成部分
  • 开放/封闭原则 :我们可以在不修改现有实现的情况下更改系统的行为。

打开/关闭原理通常通过注入策略和模板方法模式来实现。 在这里,它甚至更简单。 整体接线如下:

MetricRegistry metricRegistry =new MetricRegistry();
ProjectionMetrics metrics =new ProjectionMetrics(metricRegistry);
ClientProjection clientProjection =new ClientProjection(metrics);
NaivePool naivePool =new NaivePool(10, clientProjection);
EventStream es = new EventStream();
es.consume(naivePool);

我们精心设计的指标表明情况确实好得多:

type=HISToOGRAM, count=838, min=1, max=422, mean=38.80768197277468, [...]median=37.0, p75=45.0, p95=51.0, p98=52.0, p99=52.0, p999=422.0
type=HISTOGRAM, count=1814, min=1, max=281, mean=47.82642776789085, [...]median=51.0, p75=57.0, p95=61.0, p98=62.0, p99=63.0, p999=65.0[...30 seconds later...]type=HISTOGRAM, count=30564, min=5, max=3838, mean=364.2904915942238, [...]median=352.0, p75=496.0, p95=568.0, p98=574.0, p99=1251.0, p999=3531.0

但是,我们仍然看到延迟的规模越来越小,在30秒后,延迟达到了364毫秒。 它一直在增长,所以问题是系统的。 我们……需要……更多……指标。 请注意, NaivePool (您很快就会知道为什么它是naive )有正好有10个线程NaivePool 。 这应该足以处理数千个事件,每个事件需要10毫秒来处理。 实际上,我们需要一点额外的处理能力,以避免垃圾收集后或负载高峰时出现问题。 为了证明线程池实际上是我们的瓶颈,最好监视其内部队列。 这需要一些工作:

class NaivePool implements EventConsumer, Closeable {private final EventConsumer downstream;private final ExecutorService executorService;NaivePool(int size, EventConsumer downstream, MetricRegistry metricRegistry) {LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();String name = MetricRegistry.name(ProjectionMetrics.class, "queue");Gauge<Integer> gauge = queue::size;metricRegistry.register(name, gauge);this.executorService = new ThreadPoolExecutor(size, size, 0L, TimeUnit.MILLISECONDS, queue);this.downstream = downstream;}@Overridepublic Event consume(Event event) {executorService.submit(() -> downstream.consume(event));return event;}@Overridepublic void close() throws IOException {executorService.shutdown();}
}

这里的想法是手动创建ThreadPoolExecutor ,以提供自定义的LinkedBlockingQueue实例。 我们稍后可以使用该队列来监视其长度(请参阅: ExecutorService – 10个技巧 )。 Gauge将定期调用queue::size并将其报告给您需要的地方。 度量标准确认线程池大小确实是一个问题:

type=GAUGE, name=[...].queue, value=35
type=GAUGE, name=[...].queue, value=52[...30 seconds later...]type=GAUGE, name=[...].queue, value=601

容纳待处理任务的队列的大小不断增加,这会损害延迟。 将线程池大小从10增加到20最终会报告出不错的结果,并且没有停顿。 但是,我们仍然没有解决重复项,也没有针对同一clientId防止事件的同时修改。

模糊锁定

让我们从避免对同一clientId的事件进行并发处理开始。 如果两个事件接连发生,并且都与同一个clientId相关,那么NaivePool将同时选择它们并开始同时处理它们。 首先,我们至少通过为每个clientId设置一个Lock来发现这种情况:

@Slf4j
class FailOnConcurrentModification implements EventConsumer {private final ConcurrentMap<Integer, Lock> clientLocks = new ConcurrentHashMap<>();private final EventConsumer downstream;FailOnConcurrentModification(EventConsumer downstream) {this.downstream = downstream;}@Overridepublic Event consume(Event event) {Lock lock = findClientLock(event);if (lock.tryLock()) {try {downstream.consume(event);} finally {lock.unlock();}} else {log.error("Client {} already being modified by another thread", event.getClientId());}return event;}private Lock findClientLock(Event event) {return clientLocks.computeIfAbsent(event.getClientId(),clientId -> new ReentrantLock());}}

这肯定是朝错误的方向前进。 复杂程度不计其数,但运行此代码至少表明存在问题。 事件处理管道如下所示,一个装饰器包装了另一个装饰器:

ClientProjection clientProjection =new ClientProjection(new ProjectionMetrics(metricRegistry));
FailOnConcurrentModification failOnConcurrentModification =new FailOnConcurrentModification(clientProjection);
NaivePool naivePool =new NaivePool(10, failOnConcurrentModification, metricRegistry);
EventStream es = new EventStream();es.consume(naivePool);

有时会弹出错误消息,告诉我们其他一些线程已经在处理同一clientId事件。 对于每个clientId我们关联一个我们检查的Lock ,以便确定当前是否有另一个线程不在处理该客户端。 尽管丑陋,但实际上我们已经接近残酷的解决方案。 而不是因为另一个线程已经在处理某个事件而无法获得Lock时失败,让我们稍等一下,希望Lock可以被释放:

@Slf4j
class WaitOnConcurrentModification implements EventConsumer {private final ConcurrentMap<Integer, Lock> clientLocks = new ConcurrentHashMap<>();private final EventConsumer downstream;private final Timer lockWait;WaitOnConcurrentModification(EventConsumer downstream, MetricRegistry metricRegistry) {this.downstream = downstream;lockWait = metricRegistry.timer(MetricRegistry.name(WaitOnConcurrentModification.class, "lockWait"));}@Overridepublic Event consume(Event event) {try {final Lock lock = findClientLock(event);final Timer.Context time = lockWait.time();try {final boolean locked = lock.tryLock(1, TimeUnit.SECONDS);time.stop();if(locked) {downstream.consume(event);}} finally {lock.unlock();}} catch (InterruptedException e) {log.warn("Interrupted", e);}return event;}private Lock findClientLock(Event event) {return clientLocks.computeIfAbsent(event.getClientId(),clientId -> new ReentrantLock());}}

这个想法非常相似。 但是, tryLock()失败,它最多等待1秒,以希望释放给定客户端的Lock 。 如果两个事件很快相继发生,则一个事件将获得一个Lock并继续执行,而另一个事件将阻止等待unlock()发生。

不仅这些代码确实令人费解,而且还可能以许多微妙的方式被破坏。 例如,如果同一个clientId两个事件几乎完全同时发生,但显然是第一个事件,该怎么办? 这两个事件将同时请求Lock ,并且我们无法保证哪个事件会首先获得不公平的Lock ,从而可能会乱序使用事件。 肯定有更好的办法…

专用线程

让我们退后一步,深吸一口气。 您如何确保事情不会同时发生? 好吧,只需使用一个线程! 事实上,这是我们一开始所做的,但是吞吐量并不令人满意。 但是我们不关心不同的clientId的并发性,我们只需要确保具有相同clientId事件始终由同一线程处理即可!

也许您会想到创建从clientIdThread的映射? 好吧,这将过于简单化。 我们将创建成千上万个线程,大部分时间根据需求空闲(对于给定的clientId每秒只有很少的事件)。 一个不错的折衷方案是固定大小的线程池,每个线程负责clientId的众所周知的子集。 这样,两个不同的clientId可以结束在同一线程上,但是同一clientId将始终由同一线程处理。 如果出现同一clientId两个事件,则它们都将被路由到同一线程,从而避免了并发处理。 实现非常简单:

class SmartPool implements EventConsumer, Closeable {private final List<ExecutorService> threadPools;private final EventConsumer downstream;SmartPool(int size, EventConsumer downstream, MetricRegistry metricRegistry) {this.downstream = downstream;List<ExecutorService> list = IntStream.range(0, size).mapToObj(i -> Executors.newSingleThreadExecutor()).collect(Collectors.toList());this.threadPools = new CopyOnWriteArrayList<>(list);}@Overridepublic void close() throws IOException {threadPools.forEach(ExecutorService::shutdown);}@Overridepublic Event consume(Event event) {final int threadIdx = event.getClientId() % threadPools.size();final ExecutorService executor = threadPools.get(threadIdx);executor.submit(() -> downstream.consume(event));return event;}
}

关键部分就在最后:

int threadIdx = event.getClientId() % threadPools.size();
ExecutorService executor = threadPools.get(threadIdx);

这个简单的算法将始终对相同的clientId使用相同的单线程ExecutorService 。 不同的ID可在同一池中结束,例如,当池大小是20 ,客户机72747等,将使用相同的线程。 但这可以,只要一个clientId始终使用同一线程即可。 此时,不需要锁定,并且可以保证顺序调用,因为同一客户端的事件始终由同一线程执行。 旁注:每个clientId一个线程无法扩展,但是每个clientId一个角色(例如,在Akka中)是一个很好的主意,它可以简化很多工作。

为了更加安全,我在每个线程池中插入了平均队列大小的指标,从而使实现更长:

class SmartPool implements EventConsumer, Closeable {private final List<LinkedBlockingQueue<Runnable>> queues;private final List<ExecutorService> threadPools;private final EventConsumer downstream;SmartPool(int size, EventConsumer downstream, MetricRegistry metricRegistry) {this.downstream = downstream;this.queues = IntStream.range(0, size).mapToObj(i -> new LinkedBlockingQueue<Runnable>()).collect(Collectors.toList());List<ThreadPoolExecutor> list = queues.stream().map(q -> new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, q)).collect(Collectors.toList());this.threadPools = new CopyOnWriteArrayList<>(list);metricRegistry.register(MetricRegistry.name(ProjectionMetrics.class, "queue"), (Gauge<Double>) this::averageQueueLength);}private double averageQueueLength() {double totalLength =queues.stream().mapToDouble(LinkedBlockingQueue::size).sum();return totalLength / queues.size();}//...}

如果您偏执狂,甚至可以为每个队列创建一个指标。

重复数据删除和幂等

在分布式环境中,当生产者至少有一次保证时,接收重复事件是很常见的。 这种行为背后的原因不在本文讨论范围之内,但我们必须学习如何解决该问题。 一种方法是将全局唯一标识符( UUID )附加到每封邮件,并在使用方确保具有相同标识符的邮件不会被处理两次。 每个Event都有这样的UUID 。 根据我们的要求,最直接的解决方案是简单地存储所有可见的UUID并在到达时验证接收到的UUID从未见过。 按原样使用ConcurrentHashMap<UUID, UUID> (JDK中没有ConcurrentHashSet )会导致内存泄漏,因为随着时间的推移,我们将不断积累越来越多的ID。 这就是为什么我们仅在最近10秒内查找重复项。 从技术上讲,您可以拥有ConcurrentHashMap<UUID, Instant> ,在遇到该问题时可以将其从UUID映射到时间戳。 通过使用后台线程,我们可以删除10秒钟以上的元素。 但是,如果您是快乐的Guava用户,则具有声明驱逐策略的Cache<UUID, UUID>可以解决此问题:

import com.codahale.metrics.Gauge;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;import java.util.UUID;
import java.util.concurrent.TimeUnit;class IgnoreDuplicates implements EventConsumer {private final EventConsumer downstream;private Cache<UUID, UUID> seenUuids = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).build();IgnoreDuplicates(EventConsumer downstream) {this.downstream = downstream;}@Overridepublic Event consume(Event event) {final UUID uuid = event.getUuid();if (seenUuids.asMap().putIfAbsent(uuid, uuid) == null) {return downstream.consume(event);} else {return event;}}
}

为了保证生产安全,我至少认为有两个指标可能会有用:缓存大小和发现的重复项数量。 让我们也插入以下指标:

class IgnoreDuplicates implements EventConsumer {private final EventConsumer downstream;private final Meter duplicates;private Cache<UUID, UUID> seenUuids = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).build();IgnoreDuplicates(EventConsumer downstream, MetricRegistry metricRegistry) {this.downstream = downstream;duplicates = metricRegistry.meter(MetricRegistry.name(IgnoreDuplicates.class, "duplicates"));metricRegistry.register(MetricRegistry.name(IgnoreDuplicates.class, "cacheSize"), (Gauge<Long>) seenUuids::size);}@Overridepublic Event consume(Event event) {final UUID uuid = event.getUuid();if (seenUuids.asMap().putIfAbsent(uuid, uuid) == null) {return downstream.consume(event);} else {duplicates.mark();return event;}}
}

最终,我们拥有了构建解决方案的所有要素。 这个想法是由相互封装的EventConsumer实例组成管道:

  1. 首先,我们应用IgnoreDuplicates拒绝重复项
  2. 然后,我们调用SmartPool ,它将始终将给定的clientId到同一线程,并在该线程中执行下一阶段
  3. 最后,调用ClientProjection ,它执行真实的业务逻辑。

您可以选择在SmartPoolClientProjection之间放置FailOnConcurrentModification步骤,以提高安全性(设计时不应进行并发修改):

ClientProjection clientProjection =new ClientProjection(new ProjectionMetrics(metricRegistry));
FailOnConcurrentModification concurrentModification =new FailOnConcurrentModification(clientProjection);
SmartPool smartPool =new SmartPool(12, concurrentModification, metricRegistry);
IgnoreDuplicates withoutDuplicates =new IgnoreDuplicates(smartPool, metricRegistry);
EventStream es = new EventStream();
es.consume(withoutDuplicates);

我们花了很多工作才能提出相对简单且结构合理的解决方案(我希望您同意)。 最后,解决并发问题的最佳方法是……避免并发并在一个线程中运行受竞争条件约束的代码。 这也是Akka actor(每个actor处理单个消息)和RxJava( Subscriber处理的一条消息)背后的思想。 在下一部分中,我们将在RxJava中看到声明式解决方案。

翻译自: https://www.javacodegeeks.com/2016/10/small-scale-stream-processing-kata-part-1-thread-pools.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/351422.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VC++ MSDN中的 _beginthreadex与_endthreadex 的使用例子

1._beginthread, _beginthreadex .用于创建线程[cpp] view plaincopy uintptr_t _beginthread( void( *start_address )( void * ), unsigned stack_size, void *arglist ); uintptr_t _beginthreadex( //推荐使用 void *security, //安全属性&#…

「澳洋主数据项目」主数据促企业变革

part 1 企业简介&#xff1a; 澳洋集团是一家跨地区、多元化的民营企业集团&#xff0c;总部位于全国百强县市前三甲的江苏省张家港市。集团成立于1998年7月&#xff0c;2007年经国家工商总局核准&#xff0c;升格为免冠行政区划的大型集团企业。集团现有37家下属子&#xff08…

linux bin目录误删,Linux下误删 /user/bin目录后的补救

当危险的动作发生&#xff0c; 误删 /user/bin目录后的补救以下是昨天晚上真实的误操作现场&#xff0c;模拟记录一下(这是测试环境&#xff0c;所以操作得很随意&#xff0c;有些执行动作很不规范)在上面编译一个软件Dboop&#xff0c;完事以后想把它做个软链到 /usr/binsudo …

使用JFlex生成词法分析器 1:安装配置

环境&#xff1a;Windows 10 STEP 1&#xff1a; 下载 JFlex 文件&#xff0c;我选择的是 jflex-1.7.0.zip。下载完成后解压到想安装的位置。 文件结构如下&#xff08;假设解压目录为 C:\&#xff09;&#xff1a; C:\jflex-1.7.0\ --bin\ (start scri…

问题: 将N个元素使用push_back插入到vector中, 求push_back操作的复杂度。

简单分析如下&#xff1a; 考虑vector每次内存扩充两倍的情况。 如果我们插入N个元素&#xff0c; 则会引发lgN次的内存扩充&#xff0c;而每次扩充引起的元素拷贝次数为 2^0, 2^1, 2^2, ..., 2^lgN. 把所有的拷贝次数相加得到 2^0 2^1 2^2 ... 2^lgN 2 * 2^lgN - 1 约为…

linux superblock 时间,Linux命令(八)

1、文件系统:windows的FAT&#xff0c;win2000以后的NTFS文件系统&#xff0c;Linux的正规文件系统为EXT2(Linux second extended file system&#xff0c;Ext2fs)传统方式中&#xff0c;一个分区只能格式化为一个分区。由于新技术的利用&#xff0c;一个分区可以格式化为多个文…

在Amazon Elastic Beanstalk上部署Spring Boot应用程序

在此博客中&#xff0c;我们将看到如何在Amazon ElasticBeanstalk上部署Spring Boot应用程序。 Amazon ElasticBeanstalk具有一个预配置的Java环境&#xff0c;可用于部署内部装有servlet容器的Spring Boot JAR。 对于我们的示例&#xff0c;此处将使用maven作为构建工具。 …

Linux上静态库和动态库的编译和使用

linux上静态库和动态库的编译和使用&#xff08;附外部符号错误浅谈&#xff09; 这就是静态库和动态库的显著区别&#xff0c;静态库是编译期间由链接器通过include目录找到并链接到到可执行文件中&#xff0c;而动态库则是运行期间动态调用&#xff0c;只有运行时找不到对应动…

Linux Socket API Connect 函数详解

在讲解套接字编程函数之前&#xff0c;有必要对socket编程的两个不可或缺的结构体进行说明。 第一个结构体式struct sockaddr.。这个结构为许多类型的套接字储存套接字地址信息&#xff1a; Sockaddr结构体介绍 #include<sys/socket.h> struct sockaddr { …

java 适用参数_Java功能的适用性

java 适用参数Java语言和标准库功能强大&#xff0c;但功能强大&#xff0c; 责任重大 。 一方面看到很多用户代码滥用或滥用稀有的Java功能&#xff0c;另一方面却完全忘记了大多数基本功能之后&#xff0c;我决定撰写此摘要。 这不是每个Java开发人员都应该探索&#xff0c;了…

linux查看tar进程进度,Linux:wget后台下载/查看后台任务进度

今天在自己的服务器上使用wget下载一个大文件时&#xff0c;不小心把ssh断开连接了&#xff0c;重新登上去后想查看这个文件的下载进度&#xff0c;现记录一些wget的知识点。1&#xff1a;后台下载使用wget -b url[root8f9fbda9bb48 ~]# wget -b http://cn.wordpress.org/word…

【redis】在windos下的redis服务器的搭建

1.下载Redis-x64-3.2.100&#xff08;楼主用的版本&#xff0c;需要安装包的可以找我要&#xff09; 下载官方版本 2.解压后在cmd下运行 redis-server redis.windos.conf 此时redis服务已经在该windows下6379端口运行 3.把该服务设置成windos服务 redis-server --service-insta…

《Linux网络接口》---------struct ifreq struct ifconf

网络接口--------------struct ifconf&#xff0c;struct ifreq 网络相关的ioctl请求的request参数及arg地址必须指向的数据类型如下表所示&#xff1a; 接口 SIOCGIFCONF SIOCSIFADDR SIOCGIFADDR SIOCSIFBRDADDR SIOCGIFBRDADDR SIOCSIFNETMASK SIOCGIFNETMASK 获取所有接口…

会议季Mic Drop:您不应该错过的13场Java演讲

您的老板没有派您参加真正的会议吗&#xff1f; 我们为您准备了最好的讲座 9月主要发生在一些重大事件上&#xff1a;秋季的第一天&#xff0c;甚至全国熏肉日。 这也是召开会议最忙的月份之一&#xff0c;一些大型Java事件涵盖了平台的新的重要更新。 在下面的帖子中&#x…

linux7设备的挂载,centos7磁盘分区与挂载解析

Linux系统在磁盘、U盘以及光盘等设备分区和挂载操做才能使用。centos1、磁盘分区原理与规则ui磁盘分区类型&#xff1a;主分区&#xff0c;扩展分区&#xff0c;逻辑分区this分区规则&#xff1a;centos7一、主分区扩展分区的数量不能超过4个&#xff0c;且扩展分区只能有1个。…

【公众号系列】SAP的新零售

公众号&#xff1a;SAP Technical本文作者&#xff1a;matinal原文出处&#xff1a;http://www.cnblogs.com/SAPmatinal/ 原文链接&#xff1a;【公众号系列】SAP的新零售写在前面 还是以前的一篇文章&#xff08;一八年三月&#xff09;&#xff0c;拿出来重新了解一下。 随着…

linux下汇编语言开发总结

汇编语言是直接对应系统指令集的低级语言&#xff0c;在语言越来越抽象的今天&#xff0c;汇编语言并不像高级语言那样使用广泛&#xff0c;仅仅在驱动程序&#xff0c;嵌入式系统等对性能要求苛刻的领域才能见到它们的身影。但是这并不表示汇编语言就已经没有用武之地了&#…

使用openocd调试Linux内核,openocd安装与调试

环境&#xff1a;硬件&#xff1a;PC机ARM仿真器v8.00已下载好bit流的Xinlinx SoC开发板(其上有arm cortex-a9核)软件&#xff1a;Redhat Linux6(或虚拟机) openocd使用openocd下载程序&#xff0c;调试arm cortex-a9核。一、openocd安装下载libusb库安装或直接yum install li…

execl中设置的格式无法实现

在一次项目中&#xff0c;需要导出execl表&#xff0c;并且要给表中的表格设置格式&#xff0c;因为每列的格式都不一样&#xff0c;需要单独设置设置这些格式&#xff0c;在后期使用中因为导入的数据过多&#xff0c;是的后面的单元格中设置的格式无法实现。 每次打开execl表格…

loadrunner监控linux性能指标,使用LoadRunner监控Linux系统性能.doc

使用LoadRunner监控Linux系统性能性能监控案例■秘密 □机密 □绝密PAGELinux系统性能监控案例(仅供内部使用)版 本 号&#xff1a;V0.1保 密 等 级&#xff1a;■秘密 □机密 □绝密编 制&#xff1a;XXX审 核&#xff1a;修订记录日期版本号描述作者2011-06-130.1初稿完成目录…