kafka分布式_带有Kafka和ZeroMQ的分布式类星体演员

kafka分布式

因此,您已经有了使用actor的精美设计,选择了JVM和Quasar在该主题上的强大而忠实的观点。 所有明智的决定,但是在集群上进行分配时您有什么选择呢?

星系

Galaxy是一个非常酷的选择:快速的内存中数据网格,针对数据局部性进行了优化,具有复制,可选的持久性,分布式参与者注册表,甚至参与者之间的参与者迁移! 唯一需要注意的是:要发布正式版的生产质量的银河版,还需要几个月的时间。 不建议将当前版本的Galaxy用于生产。

如果我们需要在那之前上线怎么办?

幸运的是,Quasar Actors的阻塞编程模型非常简单,以至于将其与大多数消息传递解决方案集成起来都是轻而易举的,并且为了证明让我们用两种快速,流行且截然不同的模型来做到这一点: Apache Kafka和ØMQ 。

代码和计划

可以在GitHub上找到以下所有示例,只需快速阅读简短的README ,您就可以立即运行它们。

Kafka和ØMQ分别有两个示例:

  • 快速而肮脏的人直接进行发布/投票或发送/接收演员的呼叫。
  • 详细介绍了代理角色,这些代理角色将您的代码与消息传递API隔离开。 为了证明我没有在撒谎,该程序对两种技术使用了相同的生产者和消费者参与者类 ,并且几乎使用了相同的引导程序。

卡夫卡

Apache Kafka的采用率急剧上升,这是由于其基于持久性提交日志和用于并行消息使用的使用者组的独特设计:这种结合形成了快速,可靠,灵活和可扩展的代理。

该API包括两种类型的生产者:sync和async;一种消费者(仅sync); Comsat包括社区贡献的,对光纤友好的Kafka生产商集成 。

Kafka生产者句柄是线程安全的,在共享时表现最佳,可以像这样在actor主体(或其他任何地方)中轻松获得和使用:

final Properties producerConfig = new Properties();
producerConfig.put("bootstrap.servers", "localhost:9092");
producerConfig.put("client.id", "DemoProducer");
producerConfig.put("key.serializer", "org.apache.kafka.common.serialization.IntegerSerializer");
producerConfig.put("value.serializer", "org.apache.kafka.common.serialization.ByteArraySerializer");try (final FiberKafkaProducer<Integer, byte[]> producer = new FiberKafkaProducer<>(new KafkaProducer<>(producerConfig))) {final byte[] myBytes = getMyBytes(); // ...final Future<RecordMetaData> res = producer.send(new ProducerRecord<>("MyTopic", i, myBytes));res.get(); // Optional, blocks the fiber until the record is persisted; thre's also `producer.flush()`
}

我们用Comsat的FiberKafkaProducer包装KafkaProducer对象,以便找回光纤阻塞的未来。

但是,使用者句柄不是线程安全的1,而仅是线程阻塞的:

final Properties producerConfig = new Properties();
consumerConfig = new Properties();
consumerConfig.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP);
consumerConfig.put(ConsumerConfig.GROUP_ID_CONFIG, "DemoConsumer");
consumerConfig.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
consumerConfig.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
consumerConfig.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000");
consumerConfig.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.IntegerDeserializer");
consumerConfig.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.ByteArrayDeserializer");try (final Consumer<Integer, byte[]> consumer = new KafkaConsumer<>(consumerConfig)) {consumer.subscribe(Collections.singletonList(TOPIC));final ConsumerRecords<Integer, byte[]> records = consumer.poll(1000L);for (final ConsumerRecord<Integer, byte[]> record : records) {final byte[] v = record.value();useMyBytes(v); // ...}
}

由于我们不想阻塞光纤的基础线程池(除了那些Kafka屏蔽的线程池之外-我们无法对它们做太多事情),因此在我们的actor的doRun我们将使用FiberAsync.runBlocking代替,以喂入固定的具有异步任务的size执行程序,该任务将阻塞光纤直到poll (将在给定的池中执行)返回之前:

final ExecutorService e = Executors.newFixedThreadPool(2);try (final Consumer<Integer, byte[]> consumer = new KafkaConsumer<>(consumerConfig)) {consumer.subscribe(Collections.singletonList(TOPIC));final ConsumerRecords<Integer, byte[]> records = call(e, () -> consumer.poll(1000L));for (final ConsumerRecord<Integer, byte[]> record : records) {final byte[] v = record.value();useMyBytes(v); // ...}
}

其中call是一个定义如下的实用程序方法(如果不是此Java编译器bug,则没有必要):

@Suspendable
public static <V> V call(ExecutorService es, Callable<V> c) throws InterruptedException, SuspendExecution {try {return runBlocking(es, (CheckedCallable<V, Exception>) c::call);} catch (final InterruptedException | SuspendExecution e) {throw e;} catch (final Exception e) {throw new RuntimeException(e);}
}

在第一个完整的示例中,我们将从生产者角色向消费者发送一千个序列化消息。

ØMQ

ØMQ(或ZeroMQ)不是集中的代理解决方案,而更多地是针对各种通信模式(请求/答复,发布/订阅等)的套接字的一般化。 在我们的示例中,我们将使用最简单的请求-答复模式。 这是我们的新生产者代码:

try (final ZMQ.Context zmq = ZMQ.context(1 /* IO threads */);final ZMQ.Socket trgt = zmq.socket(ZMQ.REQ)) {trgt.connect("tcp://localhost:8000");final byte[] myBytes = getMyBytes(); // ...trgt.send(baos.toByteArray(), 0 /* flags */)trgt.recv(); // Reply, e.g. ACK
}

如您所见,上下文充当套接字工厂,并传递了要使用的I / O线程数:这是因为ØMQ套接字不是连接绑定的OS句柄,而是用于处理的机器的简单前端重试连接,多个连接,高效的并发I / O甚至为您排队。 这就是为什么send调用几乎从不阻塞,而recv调用不是连接上的I / O调用,而是线程与专门的I / O任务之间的同步的原因,该任务将处理来自一个或多个连接的传入字节。

但是,我们将在角色中阻塞光纤,而不是线程,因此让我们在read调用上使用FiberAsync.runBlocking ,以防万一它阻塞甚至在send时阻塞:

final ExecutorService ep = Executors.newFixedThreadPool(2);try (final ZMQ.Context zmq = ZMQ.context(1 /* IO threads */);final ZMQ.Socket trgt = zmq.socket(ZMQ.REQ)) {exec(e, () -> trgt.connect("tcp://localhost:8000"));final byte[] myBytes = getMyBytes(); // ...call(e, trgt.send(myBytes, 0 /* flags */));call(e, trgt::recv); // Reply, e.g. ACK
}

这是消费者:

try (final ZMQ.Context zmq = ZMQ.context(1 /* IO threads */);final ZMQ.Socket src = zmq.socket(ZMQ.REP)) {exec(e, () -> src.bind("tcp://*:8000"));final byte[] v = call(e, src::recv);exec(e, () -> src.send("ACK"));useMyBytes(v); // ...
}

exec是另一个实用程序函数,类似于call

@Suspendable
public static void exec(ExecutorService es, Runnable r) throws InterruptedException, SuspendExecution {try {runBlocking(es, (CheckedCallable<Void, Exception>) () -> { r.run(); return null; });} catch (final InterruptedException | SuspendExecution e) {throw e;} catch (final Exception e) {throw new RuntimeException(e);}
}

这是完整的第一个示例 。

在不改变逻辑的情况下进行分发:与救援人员的松散耦合

很简单,不是吗? 但是,有一个令人烦恼的事情:我们在网络另一端与演员打交道的方式与本地角色截然不同。 无论他们位于何处或如何连接,这些都是我们愿意写的演员:

public final class ProducerActor extends BasicActor<Void, Void> {private final ActorRef<Msg> target;public ProducerActor(ActorRef<Msg> target) {this.target = target;}@Overrideprotected final Void doRun() throws InterruptedException, SuspendExecution {for (int i = 0; i < MSGS; i++) {final Msg m = new Msg(i);System.err.println("USER PRODUCER: " + m);target.send(m);}System.err.println("USER PRODUCER: " + EXIT);target.send(EXIT);return null;}
}
public final class ConsumerActor extends BasicActor<Msg, Void> {@Overrideprotected final Void doRun() throws InterruptedException, SuspendExecution {for (;;) {final Msg m = receive();System.err.println("USER CONSUMER: " + m);if (EXIT.equals(m))return null;}}
}

幸运的是,每个演员,无论做什么,都具有相同的非常基本的接口:称为信箱的传入消息队列。 这意味着我们可以在两个通信参与者之间插入任意数量的中间参与者或代理 ,尤其是我们希望有一个发送代理,它将通过中间件将消息获取到目的地主机,并在其中接收接收代理,以捕获传入的消息。并将它们放入目标目的地的邮箱中。

因此,在我们的主程序中,我们将简单地为我们的ProducerActor提供合适的发送代理,并让我们的ConsumerActor从合适的接收代理接收:

final ProducerActor pa = Actor.newActor(ProducerActor.class, getSendingProxy()); // ...
final ConsumerActor ca = Actor.newActor(ConsumerActor.class);
pa.spawn();
System.err.println("USER PRODUCER started");
subscribeToReceivingProxy(ca.spawn()); // ...
System.err.println("USER CONSUMER started");
pa.join();
System.err.println("USER PRODUCER finished");
ca.join();
System.err.println("USER CONSUMER finished");

让我们看看如何首先使用Kafka然后使用ØMQ来实现这些代理。

卡夫卡男演员代理

代理参与者的工厂将与特定的Kafka主题相关联:这是因为可以对主题进行分区 ,以使多个使用者可以同时读取该主题。 我们希望能够最佳地利用每个主题的最大级别或并发性:

/* ... */ KafkaProxies implements AutoCloseable {/* ... */ KafkaProxies(String bootstrap, String topic) { /* ... */ }// ...
}

当然,我们希望对多个参与者使用一个主题,因此发送代理将指定接收者参与者ID,接收代理将仅将消息转发给绑定到该ID的用户参与者:

/* ... */ <M> ActorRef<M> create(String actorID) { /* ... */ }
/* ... */ void drop(ActorRef ref) throws ExecutionException, InterruptedException { /* ... */ }
/* ... */ <M> void subscribe(ActorRef<? super M> consumer, String actorID) { /* ... */ }
/* ... */ void unsubscribe(ActorRef<?> consumer, String actorID) { /* ... */ }

关闭AutoClosable工厂将通知所有代理终止,并清理簿记参考:

/* ... */ void close() throws Exception { /* ... */ }

生产者实现是非常简单且无趣的,同时给消费者带来了更多的乐趣,因为它将使用Quasar Actors的选择性接收将传入消息保留在其邮箱中,直到至少有一个订阅的用户actor可以使用它们为止:

@Override
protected Void doRun() throws InterruptedException, SuspendExecution {//noinspection InfiniteLoopStatementfor (;;) {// Try extracting from queuefinal Object msg = tryReceive((Object m) -> {if (EXIT.equals(m))return EXIT;if (m != null) {//noinspection uncheckedfinal ProxiedMsg rmsg = (ProxiedMsg) m;final List<ActorRef> l = subscribers.get(rmsg.actorID);if (l != null) {boolean sent = false;for (final ActorRef r : l) {//noinspection uncheckedr.send(rmsg.payload);sent = true;}if (sent) // Someone was listening, remove from queuereturn m;}}return null; // No subscribers (leave in queue) or no messages});// Something from queueif (msg != null) {if (EXIT.equals(msg)) {return null;}continue; // Go to next cycle -> precedence to queue}// Try receiving//noinspection Convert2Lambdafinal ConsumerRecords<Void, byte[]> records = call(e, () -> consumer.get().poll(100L));for (final ConsumerRecord<Void, byte[]> record : records) {final byte[] v = record.value();try (final ByteArrayInputStream bis = new ByteArrayInputStream(v);final ObjectInputStream ois = new ObjectInputStream(bis)) {//noinspection uncheckedfinal ProxiedMsg rmsg = (ProxiedMsg) ois.readObject();final List<ActorRef> l = subscribers.get(rmsg.actorID);if (l != null && l.size() > 0) {for (final ActorRef r : l) {//noinspection uncheckedr.send(rmsg.payload);}} else {ref().send(rmsg); // Enqueue}} catch (final IOException | ClassNotFoundException e) {e.printStackTrace();throw new RuntimeException(e);}}
}

由于我们还需要处理邮箱,因此我们以足够小的超时来轮询Kafka。 还要注意,许多参与者可以订阅相同的ID,传入消息将广播给所有参与者。 每个主题创建的接收actor代理(即光纤)的数量,以及池线程和Kafka使用者句柄( consumer是本地线程,因为Kafka使用者不是线程安全的)的数量将等于每个主题划分分区:这使接收吞吐量达到最大。

目前,此实现使用Java序列化在字节之间来回转换消息,但是当然可以使用其他框架,例如Kryo 。

ØMQ演员代理

ØMQ模型是完全去中心化的:既没有经纪人,也没有话题,因此我们可以简单地将ØMQ地址/端点与一组参与者等同,而无需使用额外的参与者ID:

/* ... */ ZeroMQProxies implements AutoCloseable {/* ... */ ZeroMQProxies(int ioThreads) { /* ... */ }/* ... */ <M> ActorRef<M> to(String trgtZMQAddress) { /* ... */ }/* ... */ void drop(String trgtZMQAddress)/* ... */ void subscribe(ActorRef<? super M> consumer, String srcZMQEndpoint) { /* ... */ }/* ... */ void unsubscribe(ActorRef<?> consumer, String srcZMQEndpoint) { /* ... */ }/* ... */ void close() throws Exception { /* ... */ }
}

同样,在这种情况下,并且由于与以前相同的原因,使用方有点有趣,但幸运的是,线程安全性方面的任何问题都因为ØMQ套接字在多个线程中可以正常工作:

@Override
protected Void doRun() throws InterruptedException, SuspendExecution {try(final ZMQ.Socket src = zmq.socket(ZMQ.REP)) {System.err.printf("PROXY CONSUMER: binding %s\n", srcZMQEndpoint);Util.exec(e, () -> src.bind(srcZMQEndpoint));src.setReceiveTimeOut(100);//noinspection InfiniteLoopStatementfor (;;) {// Try extracting from queuefinal Object m = tryReceive((Object o) -> {if (EXIT.equals(o))return EXIT;if (o != null) {//noinspection uncheckedfinal List<ActorRef> l = subscribers.get(srcZMQEndpoint);if (l != null) {boolean sent = false;for (final ActorRef r : l) {//noinspection uncheckedr.send(o);sent = true;}if (sent) // Someone was listening, remove from queuereturn o;}}return null; // No subscribers (leave in queue) or no messages});// Something processable is thereif (m != null) {if (EXIT.equals(m)) {return null;}continue; // Go to next cycle -> precedence to queue}System.err.println("PROXY CONSUMER: receiving");final byte[] msg = Util.call(e, src::recv);if (msg != null) {System.err.println("PROXY CONSUMER: ACKing");Util.exec(e, () -> src.send(ACK));final Object o;try (final ByteArrayInputStream bis = new ByteArrayInputStream(msg);final ObjectInputStream ois = new ObjectInputStream(bis)) {o = ois.readObject();} catch (final IOException | ClassNotFoundException e) {e.printStackTrace();throw new RuntimeException(e);}System.err.printf("PROXY CONSUMER: distributing '%s' to %d subscribers\n", o, subscribers.size());//noinspection uncheckedfor (final ActorRef s : subscribers.getOrDefault(srcZMQEndpoint, (List<ActorRef>) Collections.EMPTY_LIST))//noinspection uncheckeds.send(o);} else {System.err.println("PROXY CONSUMER: receive timeout");}}}
}

更多功能

这篇简短的文章有望使人们一眼就可以看出,由于Quasar的Actor具有顺畅的顺序流程的特性,因此可以无缝地将Quasar的Actor与消息传递解决方案进行交互。 当然,可以更进一步,例如:

  • 演员查找和发现 :我们如何提供全球演员命名/发现服务? 例如,Kafka使用ZooKeeper,因此可能值得利用,但是ØMQ大量押注去中心化,并且故意不提供预先打包的基础。
  • Actor故障管理 :我们如何支持在不同节点中运行的actor之间的故障管理链接和监视?
  • 消息路由 :如何在不更改参与者内部逻辑的情况下动态调整节点与参与者之间的消息流?
  • 角色移动性 :我们如何将角色移动到其他节点,例如,使其更靠近消息源,以提高性能或移动到具有不同安全性的位置?
  • 可伸缩性和容错性 :如何管理参与者节点的添加,删除,死亡和分区? 像Galaxy这样的分布式IMDG和像Kafka这样的基于代理的解决方案通常已经做到了,但是像ØMQ这样的结构级解决方案通常不这样做。
  • 安全性 :我们如何支持相关的信息安全性属性?
  • 测试,记录,监视 :我们如何方便地整体测试,跟踪和监视分布式参与者集合?

这些主题尤其是分布式系统设计(尤其是分布式参与者)的“硬核”,因此要有效地解决它们,可能需要大量的精力。 Galaxy解决了所有这些问题,但是Quasar参与者提供了一个SPI ,涵盖了上述一些主题,并允许与发行技术更紧密地集成。 您可能也对Akka和Quasar + Galaxy之间的比较感兴趣,该比较涵盖了许多此类方面。

就是这样,请与您分布的Quasar演员一起玩乐,并在Quasar-Pulsar用户组中留下有关您的旅程的注释!

  1. 实际上,它也禁止除第一个线程外的任何线程使用。

翻译自: https://www.javacodegeeks.com/2016/05/distributed-quasar-actors-kafka-zeromq.html

kafka分布式

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

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

相关文章

vs 不能自动 析构函数_深入理解C++虚函数的override、overload与hide以及虚析构函数...

今天主要讲的是虚函数的override与overload的区别。首先我们来看一段代码&#xff1a;示例代码#include <stdio.h>#include <string>#include <iostream>#include <complex>using namespace std;class Father{public: Father(); ~Father(); virtual vo…

【开源项目】C++BASE64图像编解码算法

ZBase64.h #pragma once #include <string> using namespace std; class ZBase64 { public:ZBase64(void);~ZBase64(void);/*编码DataByte[in]输入的数据长度,以字节为单位*/string Encode(const unsigned char* Data, int DataByte);/*解码DataByte[in]输入的数据长度,…

log4j2 xsd_Log4j 2.x XSD的描述不完整

log4j2 xsd在博客文章JAXB和Log4j XML配置文件中 &#xff0c;我讨论了“与使用JAXB通过Java类处理[Log4j 1.x和Log4j 2.x] XML配置文件相关的细微差别。” 在本文中&#xff0c;我将探讨与通过Log4j 2.x XML Schema文件Log4j-config.xsd生成的JAXB对象生成Log4j 2.x配置XML相关…

tampermonkey怎么不能用了_普洱茶发霉了怎么办?能不能用太阳晒

大家都知道普洱茶要储藏在通风干燥的地方&#xff0c;如果储存不恰当就可能变质发霉。而且普洱茶很容易吸味&#xff0c;需要存放到干净的环境并避免日晒。当发现茶叶发霉的现象比较严重时&#xff0c;需要直接扔掉。如果不严重&#xff0c;可以采用以下方法&#xff1a;千万不…

java开发常见异常_最常见的Java异常及其对Java开发人员的评价

java开发常见异常我知道我说过在完成视频之前我不会发帖&#xff0c;但这一直困扰着我。 至于视频的更新&#xff0c;我有些懒散&#xff0c;有些忙碌&#xff0c;但是我已经准备好记录第一集&#xff0c;这是我第一次有机会&#xff0c;而且我不需要太多的编辑。 无论如何&…

vue 动态变量名_【告别复制粘贴】动态模板生成小技巧

? 这是第 75篇不掺水的原创&#xff0c;想要了解更多&#xff0c;请戳上方蓝色字体&#xff1a;政采云前端团队关注我们吧&#xff5e;本文首发于政采云前端团队博客&#xff1a;告别复制粘贴&#xff1a;动态模板生成小技巧https://www.zoo.team/article/dynamic-template-ge…

【安卓开发 】Android初级开发(六)Activity生命周期

Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d("Activity生命周期","onCreate创建页面");}Overrideprotected void onStart() {super.onStart();Log.d(…

jboss项目导入idea_如何导入任何JBoss BRMS示例项目

jboss项目导入idea在过去几周内&#xff0c;JBoss BRMS演示的用户反复询问我以下内容时&#xff0c;会给您这些提示和技巧&#xff1a; “如何将与各种JBoss BRMS演示项目相关的项目导入到我自己的现有安装中&#xff1f;” 这意味着用户希望在个人安装产品时有一个示例项目&…

amos看拟合度在哪里看_哪里可以看亲爱的热爱的电视剧全集

由杨紫、李现、胡一天、张格出演的暖心甜宠剧《亲爱的&#xff0c;热爱的》正在热播中。该剧根据墨宝非宝小说《蜜汁炖鱿鱼》改编&#xff0c;讲述了一场高智商与高情商的甜萌梦幻爱情故事。剧中李现饰演的高冷G神和杨紫饰演的萌妹学霸&#xff0c;从开头的一见钟情到后面的相处…

【开源项目】Android下自定义HASH【支持一个key对应多个value--根据key排序】

package com.peace.love.carpo_test.tool;import java.util.List; import java.util.Map; import java.util.Set;/*** <p>Can save multiple the value of the map.</p>* Created in 2021/2/20.** author 一笑奈何;*/ public interface HongDou_MultiValueMap<K…

jaxb xml配置_JAXB和Log4j XML配置文件

jaxb xml配置Log4j 1.x和Log4j 2.x均支持使用XML文件来指定日志记录配置 。 这篇文章探讨了与使用JAXB通过Java类处理这些XML配置文件相关的一些细微差别。 本文中的示例基于Apache Log4j 1.2.17 &#xff0c; Apache Log4j 2.6.2和Java 1.8.0_73&#xff08;带有JAXB xjc 2.2.…

multiprocessing.queue取数据要加锁么_干货 | 小程序多页面接口数据缓存

小程序里面多个页面&#xff0c;有时候会需要用到同一个接口的数据。而这些数据全局来说只需要拉取一遍&#xff0c;如果要存到缓存&#xff0c;要怎么保证其他页面取缓存的时候&#xff0c;数据已经拉取回来了呢&#xff1f;答案是多页面接口数据缓存实现。思路设计其实这种场…

【安卓开发 】Android初级开发(七)MD5加密

//MD5加密public String encrypt(String raw){String md5Str raw;try {MessageDigest md MessageDigest.getInstance("MD5");md.update(raw.getBytes());byte[]encryContext md.digest();int i ;StringBuffer buf new StringBuffer("");for (int offse…

apache.camel_Apache Camel 2.18 –即将推出的功能的亮点

apache.camel骆驼骑士正在忙于即将发布的Apache Camel 2.18版本。 当我们动态更新发行说明时 &#xff0c;这是一种快速查看即将发生的情况的方法。 我只是想在夏天在这里进行快速更新&#xff0c;到目前为止&#xff0c;我们已经完成了简短的重点介绍。 随着发行版的临近和文档…

【安卓开发 】Android初级开发(八)WebView网页

1.网页的基本组成 2.WebView的常用方法 WebView webView findViewById(R.id.webvv);//加载线上网页webView.loadUrl("https://www.baidu.com");//加载SD卡上的html文件,Environment.getDownloadCacheDirectory().getPath()为获取SD卡根目录路径webView.loadUrl(&quo…

python整数加法计算器_Python应用实例赏析2.1简单计算

在日常应用中&#xff0c;我们会经常使用计算器进行计算&#xff0c;有些时候普通计算器的功能不能满足需要&#xff0c;例如使用计算机系统自带的计算器&#xff0c;计算不能超过32位数&#xff0c;手机自带计算器不能超过15位等(我的电脑和手机)&#xff0c;也可能计算需要的…

functor_纯Java中的Functor和Monad示例

functor本文最初是我们使用RxJava进行React式编程的附录。 但是&#xff0c;尽管与React式编程非常相关&#xff0c;但对monad的介绍却不太适合。 因此&#xff0c;我决定将其取出并作为博客文章单独发布。 我知道&#xff0c;“ 我对单子的自己的&#xff0c;一半正确和一半的…

【开源项目】Android开发内置App自动升级

1.准备工作&#xff0c;首先需要在AndroidManifest.xml中的application中加入以下内容 <providerandroid:name"android.support.v4.content.FileProvider"android:authorities"需要更新app的包名.fileprovider"android:grantUriPermissions"true&q…

智能雷达物位计说明书_?浅谈人工检尺法和雷达液位计在油罐液位测量中的应用...

近年来&#xff0c;油罐液位的测量工作已成为工业测量中十分重要的部分。过去&#xff0c;在油罐测量方面&#xff0c;人工检尺法是进行液位测量的主要方法之一&#xff0c;但随着科技的发展和进步&#xff0c;雷达液位计等智能化液位测量设备使油罐的监控和测量方式趋向于多元…

【安卓开发 】Android初级开发(九)Android中封装View提供接口供点击事件回调的方法及使用

自古一楼先上图 package com.example.mydialog;import android.app.Dialog; import android.content.Context; import android.os.Bundle; import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; import android.view.Window; i…