Rx2.0后台开发分享

Rxjava2.x

  • 微软的一个函数库,Rx是一个编程模型,模板是提供一致的编程接口,帮助开发者更方便的处理异步数据流,现在Rx已经支持几乎全部的流行编程语言。比较流行的有Rxjava,RxJs,Rx.NET,社区网站:http://reactivex.io/
  • Rx使用观察者模式
    • 使用观察者模式监听:RX可以订阅任何可观察的数据流并且执行操作
    • 组合:RX使用查询式的操作符和变换数据流
    • 创建:Rx可以方便的创建事件流和数据流
  • 简化代码
    • 函数式风格:对可观察数据流使用无副作用的输入输出函数,避免程序中错综复杂的状态
    • 简化代码:Rx的操作符通常可以将复杂难题简单化为很少的代码
    • 异步错误处理: 传统的try/catch没有办法处理异步计算,Rx提供了合适的错误处理机制
    • 轻松使用并发:Rx的Observables和Schedulers让开发者可以摆脱底层线程同步和各种并发问题。
  • jar包maven仓库地址
<dependency><groupId>io.reactivex.rxjava2</groupId><artifactId>rxjava</artifactId><version>2.2.6</version>
</dependency>
  • 一个简单的例子:

     public void myTestObservable(){Observable.fromIterable(Lists.newArrayList(1,2,3,4,5)).filter(integer -> {return integer > 2;}).subscribe(integer -> {System.out.println(Thread.currentThread().getName() + " : "+ integer );});}
    
    • 在Rxjava中,一个实现了Observer接口的对象可以订阅(subscribe)一个Observable类的实例。订阅者(Subscriber)对Observable发射(emit)的任何数据或数据序列作出响应。这种模式简化了并发的操作,因为他不需要阻塞等待Observable发射数据,而是创建一个处于待命状态的观察者哨兵,哨兵在未来某个时刻响应Observable的通知。

[外链图片转存失败(img-aoWj9gqS-1562659418117)(http://reactivex.io/assets/operators/legend.png)]

  • Observable

    • 如上图是Observable发射数据的一个流程图,
    • 时间线 左边 ----右边, 各个不同形状标识Observable上的元素
    • 最后垂直线表示Observable成功执行
    • 向下虚线箭标识数据被发射出去
    • 盒子表示各种操作符来对对应数据进行处理
    • 第二条时间线也是一个Observable,不过是转换之后的
    • 当转换时候出现错误将会并不会终止,他会用一个error事件替代这个位置
  • Subscribe

    • 观察者通过SubScribe操作关联Observable
  • Observer

    • 观察者,决定了事件触发的时候将有怎么样的行为
    void onSubscribe(@NonNull Disposable d);
    void onNext(@NonNull T t);
    void onError(@NonNull Throwable e);
    void onComplete();
    
    • onNext事件,被观察者每发射一个数据都会调onNext事件,相当于异步任务中的回调接口,相当于Feature的get获取,只不过onNext不是阻塞的就是一个哨兵模式,每次发射数据,获取立即获取对应结果,然后执行之后的逻辑
    • onCompleted事件,表示事件队列完成,Rxjava不仅把每一个事件单独处理,还会把他看做一个队列,Rxjava规定,当不会再发射新的元素触发onNext事件时候,需要触发onCompleted事件作为结束标志。
    • onError事件,事件队列处理中出现异常时候,onError会被触发,可以在onError中统一处理异常情况
    • onSubScribe事件,表示元素开始发射,相当于所有元素执行之前的一个预处理位置。
  • Schedulers

    • 默认情况下Observable和Observer执行过程是在同一个线程执行如上面最简单例子,如果想要切换线程在不同线程执行可以用SubscribeOn(),observeOn()。
    • Rxjava提供了几种线调度器
调度器类型效果
Schedulers.newThread();为每一个任务创建一个新线程
Schedulers.computation();用于计算任务,如时间循环和回调处理,不要用于IO操作,默认线程数等于处理器数量
Schedulers.io();用于IO密集型任务,如异步阻塞IO操作,这个调度器的线程池会根据需要增长;对应普通计算任务,一般用上面这个,Schedulers.io默认是一个CachedThreadScheduler,很像一个有线程缓存的新线程调度器。
Schedulers.single();拥有一个线程单例的线程池,所有任务都在这一个线程中执行,当线程中有任务执行时候,他的任务将会按照先进先出的顺序依次执行。
Schedulers.trampoline();Creates and returns a Scheduler that queues work on the current thread to be executed after the current work completes. 在当前线程立即执行任务,如果当前线程有任务在执行,则会将其暂停,等插入进来的任务执行完之后,再将未完成的任务接着执行。
  • 来一个完整的例子来解释一下线程切换:
 Observable.create(new ObservableOnSubscribe<Integer>() {@Overridepublic void subscribe(ObservableEmitter<Integer> emitter) throws Exception {for (Integer integer : intList) {System.out.println(Thread.currentThread().getName() + " : send");emitter.onNext(integer);}emitter.onComplete();}}).subscribeOn(Schedulers.computation()).observeOn(Schedulers.newThread()).flatMap(new Function<Integer, ObservableSource<?>>() {@Overridepublic ObservableSource<?> apply(Integer integer) throws Exception {return Observable.just(integer).subscribeOn(Schedulers.computation()).filter(new Predicate<Integer>() {@Overridepublic boolean test(Integer integer) throws Exception {System.out.println(Thread.currentThread().getName() + ": filter one integer: "+ integer);return integer > 2;}});}}).observeOn(Schedulers.io()).subscribe(new Consumer<Object>() {@Overridepublic void accept(Object o) throws Exception {System.out.println(Thread.currentThread().getName()+ " : onSubscribe");}});

这个代码看起来比较长,也可以这么写:

 Observable.create(emitter -> {intList.forEach(intTemp -> emitter.onNext(intTemp));emitter.onComplete();}).subscribeOn(Schedulers.computation()).observeOn(Schedulers.computation()).flatMap(intStr -> Observable.just(intStr).subscribeOn(Schedulers.computation()).filter(filterInt -> Integer.valueOf(filterInt.toString()) > 2)).observeOn(Schedulers.computation()).subscribe(intTemp -> System.out.println(intTemp));
  • 第一个subscribeOn指定被观察对象发射的线程,使用的computation模型
  • 第一个observeOn指定之后的flatMap操作符切换到另外线程中执行
  • 最后的observeOn指定观察者哨兵消费数据的线程,会有如下结果

1560257895707

  • Observable的这种异步切换线程方式从整体流程来看还是同步的方式,他必须先Observable发射数据-----操作符change-----消费数据并不是每次发射一个数据的同时进行change接着消费的并行实现,因此Rxjava提供了另外一个并行的方式,如下案例
 public static void flowableDemo() throws InterruptedException {Flowable.create(new FlowableOnSubscribe<Integer>() {@Overridepublic void subscribe(FlowableEmitter<Integer> emitter) throws Exception {for (int i = 1; i < 100; i++) {System.out.println(Thread.currentThread().getName() + " 发射数据");emitter.onNext(i);}emitter.onComplete();}}, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()).observeOn(Schedulers.newThread()).filter(new Predicate<Integer>() {@Overridepublic boolean test(Integer integer) throws Exception {System.out.println(Thread.currentThread().getName() + " 过滤发射数据");return integer > 0;}}).observeOn(Schedulers.newThread()).subscribe(new Subscriber<Object>() {public void onSubscribe(Subscription subscription) {System.out.println("取出n条数据");subscription.request(3);}public void onNext(Object o) {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName());System.out.println("消费数据:" + o);}public void onError(Throwable throwable) {System.out.println(throwable.getMessage());}public void onComplete() {System.out.println("onComplete");}});}

1560321426025

  • 用Flowable不仅仅是对每个模块进行了线程的切换,在同时也是并行的执行了整个流程
  • 我觉得在异步编程方面Rxjava的确比原始的Thread ,Runnable这类操作要方便的多,通过几个操作符就可以达到异步的目的,这也是Rxjava的一个优势,而在我们工作中我们一般都是用架构组的异步框架也可以做到很优雅的异步编程,比起Rxjava而言只是会多创建一个异步类而已,那么我们来对比一下两种异步操作,我用之前的批量关注接口做测试
  • 原来的异步方式:
public void batchFollowPush(UserInfo userInfo, List<UserInfo> objectUserInfos, Integer source) {List<Boolean> batchFollowResult = new ArrayList<>();Future<Boolean> pushFuture = null;for (UserInfo objectUserInfo : objectUserInfos) {try {pushFuture = (Future<Boolean>)pushAsync.asyncFollowAndStoreMoment(userInfo, objectUserInfo, source);batchFollowResult.add(pushFuture.get());} catch (Exception e) {if (pushFuture != null) {pushFuture.cancel(true);}throw new RuntimeException(e);}}log.info("batchFollow result:{}", JSON.toJSONString(batchFollowResult));}//异步类中方法public Object asyncFollowAndStoreMoment(UserInfo userInfo, UserInfo objectUserInfo, Integer source) {// 关注交互限制Optional<InteractError> interactErrorOptional = personFacade.interactRestrict(userInfo, objectUserInfo);if (interactErrorOptional.isPresent()) {return Boolean.FALSE;}int result = wooerFacade.addOrUpdatePush2(userInfo.getMemberID(), objectUserInfo.getMemberID(), 1, source, HeadUaUtils.getPlatform());Boolean isTrue = result > 0;if (isTrue == null || !isTrue) {return Boolean.FALSE;}limitedPushFacade.followPush(userInfo, objectUserInfo);MomentListStoreParam momentListStoreParam =new MomentListStoreParam(Arrays.asList(Long.valueOf(objectUserInfo.getMemberID())),userInfo.getMemberID().longValue());log.info("batchFollow afterFilter param:{}", JSON.toJSONString(momentListStoreParam));momentManagerFacade.storeMomentMongoByMomentId(momentListStoreParam);return Boolean.TRUE;}
  • RX方式
public IResponse batchFollowRx(BatchFollowForm batchFollowForm, UserInfo myUserInfo) {log.info("batchFollow param:{}", JSON.toJSONString(batchFollowForm));BusinessAssert.isNotNull(batchFollowForm, CommonError.ARGS_EMPTY);List<Long> objectIDs = batchFollowForm.getObjectIDs();List<UserInfo> objectUserInfo = coreUserService.getList(objectIDs, UserInfo.class);Flowable.create(new FlowableOnSubscribe<UserInfo>() {@Overridepublic void subscribe(FlowableEmitter<UserInfo> emitter) throws Exception {for (UserInfo info : objectUserInfo) {emitter.onNext(info);}}}, BackpressureStrategy.ERROR).parallel().runOn(Schedulers.io()).filter(userInfo -> {Optional<InteractError> interactErrorOptional = personFacade.interactRestrict(myUserInfo, userInfo);if (interactErrorOptional.isPresent()) {return Boolean.FALSE;}Boolean isTrue =wooerFacade.addOrUpdatePush2(myUserInfo.getMemberID(), userInfo.getMemberID(),1, batchFollowForm.getSource(), HeadUaUtils.getPlatform()) > 0;if (isTrue == null || !isTrue) {return Boolean.FALSE;}return Boolean.TRUE;}).runOn(Schedulers.computation()).sequential().subscribe(new Consumer<UserInfo>() {@Overridepublic void accept(UserInfo userInfo) throws Exception {limitedPushFacade.followPush(myUserInfo, userInfo);MomentListStoreParam momentListStoreParam =new MomentListStoreParam(Arrays.asList(Long.valueOf(userInfo.getMemberID())),userInfo.getMemberID().longValue());log.info("batchFollow afterFilter param:{}", JSON.toJSONString(momentListStoreParam));momentManagerFacade.storeMomentMongoByMomentId(momentListStoreParam);}}, new Consumer<Throwable>() {@Overridepublic void accept(Throwable throwable) throws Exception {log.error("batch follow error exception:{}", throwable.getMessage());}});return MessageResponse.success(FOLLOW_SUCCESS);}
  • 测试批量关注接口,相同容器环境在同一个namespace上,并且相同条件的男性账号,关注同一批13个异性用户:

    • 普通callback异步方式:

      1560259175545

    • Rxjava方式

      1560259587687

  • backPressure

    • 以上的例子中每次都用到了BackpressureStrategy操作符这个是Rxjava2.x后为了解决背压问题的一个操作符,所谓背压,即异步的情况下发射数据的速度大于消费数据的速度带来的问题。
    • BackPressure现象说明:Flowable无限的生产事件,但是SubScribe消费的速度很慢,导致事件堆积,当堆积到一定程度将会造成OOM,我们模拟一下这种情况。
public static void oomDemo(){Observable.create(new ObservableOnSubscribe<Integer>() {@Overridepublic void subscribe(ObservableEmitter<Integer> emitter) throws Exception {for (int i=0;;i++){System.out.println(Thread.currentThread().getName() + " onNext : "+ i);emitter.onNext(i);}}}).subscribeOn(Schedulers.io()).observeOn(Schedulers.single()).subscribe(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) throws Exception {Thread.sleep(2000);System.out.println(Thread.currentThread().getName() + " consumer : "+ integer);}});}
  • 让发射数据在多个线程中执行,让消费数据在一个线程中执行并且每两秒才消费一个,这样会导致发射的数据不断的累积在内存中,最终可能会导致oom,我们通过内存信息来看他执行之后一段时间的堆内存信息

1560306631728

  • PSYoiungGen 年轻态区,总共1024k,使用了511k
  • eden区域是新对象区,已经被沾满
  • from和to区域 大学是一样,在gc时候会遍历from或者to区域,将不能清除的拷贝到另外一个区,然后清除本区域留下的,然后循环
  • paroldGen 老年代区域也已经被占满
  • 这种状态下Observable因内存不够已经oom,停止运行了,只有消费线程在消费数据。
io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | java.lang.OutOfMemoryError: GC overhead limit exceededat io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:69)at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)at java.util.concurrent.FutureTask.run(FutureTask.java:266)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
  • 我们用一个Flowable的例子来看他如何解决这个oom的问题:
public static void oomDemoFix(){Flowable.create(new FlowableOnSubscribe<Integer>() {@Overridepublic void subscribe(FlowableEmitter<Integer> emitter) throws Exception {for (int i=0;;i++){System.out.println(Thread.currentThread().getName() + " onNext : "+ i);emitter.onNext(i);}}}, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).subscribe(new Subscriber<Integer>() {@Overridepublic void onSubscribe(Subscription subscription) {subscription.request(50);}@Overridepublic void onNext(Integer integer) {System.out.println(Thread.currentThread().getName() + "消费数据: "+ integer);}@Overridepublic void onError(Throwable throwable) {}@Overridepublic void onComplete() {}});}
  • 我们在创建Flowable的时候增加了一个参数 BackpressureStrategy.ERROR,这个参数指定了在处理背压问题时候执行的一个策略,当内存满时候接下来继续发射的数据将会抛出MissingBackpressureException 异常,其余的策略稍等介绍
  • 还有另外一个不同 onSubscribe中传递的不是Disposable变成了Subscription,而且还执行了这句代码subscription.request(50)。因为在Flowable中采用了一个新的思路,响应获取发射数据的方法来解决流量不均匀而造成的oom的问题,也就是我要消费多少我就取多少,这里我们从发射数据中取出了50条。其他的还是会存储在内存中。

Flowable中主要有这几个策略

  • BackpressureStrategy.ERROR:如果缓存池(默认128)溢出会立刻抛异常MissingBackpressureexception
  • BackpressureStrategy.BUFFER:RxJava中有一个默认能够存储128个事件的缓存池,可以调节大小,生产者生产事件,并且将处理不了的事件缓存。(谨慎使用,因为依赖对消费者消费能力,耗内存)
  • BackpressureStrategy.DROP:消费不了就丢弃,比如先生产200个,并没有消费,而是在缓存,然后消费者request(200),但是缓存只有128个,所以其他的事件都丢弃了。
  • BackpressureStrategy.LATEST:和DROP功能基本一致,处理不了丢弃,区别在于LATEST能够让消费者接收到生产者产生的最后一个事件。
  • BackpressureStrategy.MISSING:生产的事件没有进行缓存和丢弃,下游接收到的事件必须进行消费或者处理!

感觉这些都是缓兵之计,能否按照我的消费能力来发射数据呢,这样才完美。

  • Rxjava2.x后有一个FlowableEmitter 这个接口:
 public static void fix(Flowable flowable){flowable.observeOn(Schedulers.computation()).subscribe(new Subscriber<Integer>() {@Overridepublic void onSubscribe(Subscription subscription) {subscription.request(20);}@Overridepublic void onNext(Integer integer) {System.out.println("消费数据: "+ 100);}@Overridepublic void onError(Throwable throwable) {}@Overridepublic void onComplete() {}});}public static Flowable flowableEmitterDemo(){Flowable flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {@Overridepublic void subscribe(FlowableEmitter<Integer> emitter) throws Exception {while (emitter.requested() > 0){System.out.println("下游处理能力:"+ emitter.requested());emitter.onNext(100);}}}, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io());return flowable;}
  • 我可以在Flowable发射数据之前通过requested来获取下游Subscriber的消费能力,依据这个来进行数据的发射,这样既可以控制发射以及消费数据的速度,也能够避免数据的丢失

现在我们看下开始时候从官网摘抄的Rx的几个优点:

  • 首先函数式风格,这种编程模式和常规的方式比较的确简化了不少代码比如第一个案例,但是感觉我们用stream表达式加上lambda也可以达到这种效果,而且对于map,flatmap,filter等这些操作符对于没有函数式编程的人来说并不好理解不觉得这是优势

  • 简化代码,这点主要体现在异步编程模式时候,不管和我们java中的异步编程用的Thread和Runnable相比,还是我们框架中的异步编程框架比较的确代码都更加简单,只需要通过几个异步线程切换的操作符便可以达到目的,但是缺点也很明显,需要引入新的jar,新的技术对不熟悉这块技术的同事并不友好有一定学习成本不利于维护。

  • 异步错误处理,轻松使用并发:通过onError捕获异常信息,通过操作法切换线程,的确也是优势所在。

  • 在之前的实践中还有这种业务模型下使用Rxjava会更具优势,当我们需要从多个网络环境来获取各自信息从中筛选出符合我们预期的并对其进行组合,我们可以通过Rxjava的丰富的操作符以及异步操作来完成。来一个简单的例子

 public static Flowable getIOData1(){return Flowable.create(new FlowableOnSubscribe<Integer>() {@Overridepublic void subscribe(@NonNull FlowableEmitter<Integer> flowableEmitter) throws Exception {for (int i = 0; i < 10; i++) {flowableEmitter.onNext(i);}System.out.println(Thread.currentThread().getName());}}, BackpressureStrategy.DROP).subscribeOn(Schedulers.io()).filter(temp -> temp > 2);}public static Flowable getIOData2(){return Flowable.create(new FlowableOnSubscribe<Integer>() {@Overridepublic void subscribe(@NonNull FlowableEmitter<Integer> flowableEmitter) throws Exception {for (int i = 10; i < 21; i++) {flowableEmitter.onNext(i);}System.out.println(Thread.currentThread().getName());}}, BackpressureStrategy.DROP).subscribeOn(Schedulers.io()).filter(temp -> temp > 12);}public static Flowable getIOData3(){return Flowable.create(new FlowableOnSubscribe<Integer>() {@Overridepublic void subscribe(@NonNull FlowableEmitter<Integer> flowableEmitter) throws Exception {for (int i = 20; i < 30; i++) {flowableEmitter.onNext(i);}System.out.println(Thread.currentThread().getName());}}, BackpressureStrategy.DROP).subscribeOn(Schedulers.io()).filter(temp -> temp > 22);}public static Flowable getIOData4(){return Flowable.create(new FlowableOnSubscribe<Integer>() {@Overridepublic void subscribe(@NonNull FlowableEmitter<Integer> flowableEmitter) throws Exception {for (int i = 30; i < 41; i++) {flowableEmitter.onNext(i);}System.out.println(Thread.currentThread().getName());}}, BackpressureStrategy.DROP).subscribeOn(Schedulers.io()).filter(temp -> temp > 32);}public static void mergeDemo(){Flowable.merge(getIOData1(), getIOData2(), getIOData3(), getIOData4()).map(temp -> "onNext"+ temp).flatMap(new Function() {@Overridepublic Object apply(@NonNull Object o) throws Exception {return Flowable.just(o).subscribeOn(Schedulers.io());}}).subscribeOn(Schedulers.newThread()).observeOn(Schedulers.computation()).subscribe(new Subscriber() {@Overridepublic void onSubscribe(Subscription subscription) {subscription.request(Long.MAX_VALUE);}@Overridepublic void onNext(Object o) {System.out.println("onNext: "+ o);}@Overridepublic void onError(Throwable throwable) {}@Overridepublic void onComplete() {}});}
  • 我们定义N个Flowable用异步方式分别请求各个第三方接口来获取对应的数据并且用filter过滤出我们需要的信息,然后通过merge操作法将所有获取到的数据组合到同一个Flowable中,进行统一的封装处理以及之后的一些业务操作。
  • 如果我们用传统的方式,我们不得不定义N个变量来获取四个异步线程数据,然后等都获取完毕之后,在分别对四个变量中保存的信息进行筛选,之后通过逻辑操作合并到一起,和rxjava相比显然要逊色很多。
  • 这种方式就是Flowable通过内置操作符对自身发射的数据在空间维度上重新组织,或者和其他Flowable发射的数据一起在空间维度上进行重新组织,是的观察者的逻辑变得更加的简单直观因为直接看操作符就能知道具体做了什么,不需要关心数据从哪里来这部分由Flowable屏蔽了,从而使得观察者更加专注于下游逻辑。

RxJava的响应式优势只有在异步逻辑占主导时才会体现出来.

  • wiki地址:https://github.com/ReactiveX/RxJava/wiki
  • reactivex官网:http://reactivex.io/

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

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

相关文章

中国速度之二神山建设(4):全能运维,召之即来,来之即战 | IDCF DevOps案例研究...

内容来源&#xff1a;DevOps案例深度研究第4期 – 火神山雷神山 DevOps实践研究战队&#xff08;本文只展示部分PPT及研究成果&#xff0c;全程视频请移步文末&#xff09;本案例内容贡献者&#xff1a;赖泽薇、张扬、邓茜芸、韦一、刘德权、候利涛、冯利娟、常相宇、张力、韩丰…

Spring Cloud部分源码分析Eureka,Ribbon,Feign,Zuul

Eureka SpringCloud Eureka使用NetFlix Eureka来实现的&#xff0c;它包括了服务端组件和客户端组件&#xff0c;并且都是用java 编写的。 Eureka服务端就是服务注册中心&#xff0c;Eureka客户端主要处理服务的注册发现&#xff0c;通过注解和参数配置的方式&#xff0c;客户…

C++ class实现邻接表存储的图(完整代码)

代码如下: #include <iostream> #include <queue>using namespace std; const int MaxVertexNum 30; typedef int InfoType; typedef int VertexType;class Node {friend class VNode;friend class ALGraph; private:int adjvertex;InfoType info;Node *next; };…

世界上首个被数学证明安全的OS微内核seL4成立基金会

Linux 基金会正在与澳大利亚国家科学机构 CSIRO 合作&#xff0c;打造 seL4 操作系统微内核生态。近日 Linux 基金会宣布托管 seL4 基金会&#xff0c;该基金会以澳大利亚国家科学机构 CSIRO 的数字机构 Data61 创建的 seL4 操作系统微内核为基础项目。seL4 是一个安全操作系统…

数据结构(哈夫曼树,哈夫曼编码)入门篇,JAVA实现

什么是哈夫曼树 哈夫曼树就是一种最优判定树&#xff0c;举个例子&#xff0c;如下一个判断逻辑 if(s<60) g1; else if(s<70) g2 else if(s<80) g3 else if(s<90) g4 else g5; 分数概率图如下 如果按照代码从上到下顺序构造判定树&#xff0c;那么如下图所示&am…

C++ class实现十字链表存储的图(完整代码)

代码如下: #include <iostream> #include <queue> using namespace std; typedef int InfoType; typedef int VertexType; const int MaxVertexNum 30;class ArcNode {friend class OLGraph;friend class VertexNode; private:int tailvertex, headvertex;ArcNod…

hystrix隔离策略对比

hystrix隔离策略 zuul的隔离实现是基于hystrix实现的&#xff0c;hystrix支持线程池隔离和信号量的隔离 # 信号量隔离&#xff1a; it executes on the calling thread and concurrent requests are limited by the semaphore count --引自官网单每次调用线程&#xff0c;当…

中国速度之二神山建设(1):坚强的领导核心,“小团队大后台”组织结构 | IDCF DevOps案例研究...

内容来源&#xff1a;DevOps案例深度研究第4期 – 火神山雷神山 DevOps实践研究战队&#xff08;本文只展示部分PPT及研究成果&#xff0c;全程视频请移步文末&#xff09;本案例内容贡献者&#xff1a;赖泽薇、张扬、邓茜芸、韦一、刘德权、候利涛、冯利娟、常相宇、张力、韩丰…

通过Java编写一个服务器理解动态Web,静态Web

静态Web 说到Servlet自然就要说到Web,Web分为静态Web和动态Web&#xff0c;之前我一直都傻傻分不清两者的区别&#xff0c;直到用JAVA编写了一个服务器后才明白二者的区别&#xff0c;所谓静态Web&#xff0c;实际上就是指&#xff0c;客户端要请求的资源文件&#xff0c;服务器…

Hystrix在网关Zuul使用中遇到问题

Hystrix在网关Zuul使用中遇到问题 Zuul默认隔离策略级别是信号量隔离&#xff0c;默认最大隔离信号量是100 信号量隔离和线程隔离的区别&#xff1a;https://blog.csdn.net/liaojiamin0102/article/details/94394956默认的设置如源码&#xff1a; //在ZuulProperties 类下游…

C++ 实现无向图的最小生成树Prim算法(附完整代码)

实现Prim算法&#xff0c;需设置两个辅助一维数组lowcost和closevertex。 其中lowcost用来保存集合V-U中各顶点与集合U中各顶点构成的边中具有最小权值的边的权值&#xff1b;数组closevertex用来保存依附于该边的在集合U中的顶点。 过程: 假设初始状态时&#xff0c;U{u0}&a…

中国速度之二神山建设(2):完善的项目计划,高效能价值流 | IDCF DevOps案例研究...

内容来源&#xff1a;DevOps案例深度研究第4期 – 火神山雷神山 DevOps实践研究战队&#xff08;本文只展示部分PPT及研究成果&#xff0c;全程视频请移步文末。&#xff09;本案例内容贡献者&#xff1a;赖泽薇、张扬、邓茜芸、韦一、刘德权、候利涛、冯利娟、常相宇、张力、韩…

C++ 实现无向图的最小生成树Kruskal算法(完整代码)

按照Kruskal思想&#xff0c;n个结点的生成树有n-1条边&#xff0c;故反复上述过程&#xff0c;直到选取了n-1条边为止&#xff0c;就构成了一棵最小生成树。 实现Kruskal算法的关键问题是&#xff1a; 当一条边加入T的边集中后&#xff0c;如何判断是否构成回路。 一种解决方…

MySql 内连接,外连接查询方式区别

MySql 内连接&#xff0c;外连接查询方式 CREATE TABLE question_test (q_id int(11) DEFAULT NULL,q_name varchar(10) DEFAULT NULL,q_part varchar(10) DEFAULT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8CREATE TABLE answer_test (a_id int(11) DEFAULT NULL,a_name varch…

让我的 .NET Core 博客系统支持 Docker

点击上方蓝字关注“汪宇杰博客”导语我的博客&#xff08;https://edi.wang&#xff09;所使用的博客系统 Moonglade 开源已经一年多了。目前已有至少4位社区朋友使用此系统在 Azure、阿里云上部署了自己的博客。可惜长久以来该系统一直缺乏 Docker 支持&#xff0c;而 .NET Co…

C++ 实现带权有向图的单源点最短路径Dijkstra算法(完整代码)

首先&#xff0c;引进一个辅助向量D&#xff0c;它的每个分量D[i]表示当前所找到的从始点v0到每个终点vi的最短路径的长度。 它的初态为&#xff1a;若从v0到vi有弧&#xff0c;则D[i]为弧上的权值&#xff1b;否则&#xff0c;置D[i]为∞。 显然&#xff0c;长度为 D[j]Min{…

[ASP.NET Core MVC] 如何实现运行时动态定义Controller类型?

昨天有个朋友在微信上问我一个问题&#xff1a;他希望通过动态脚本的形式实现对ASP.NET Core MVC应用的扩展&#xff0c;比如在程序运行过程中上传一段C#脚本将其中定义的Controller类型注册到应用中&#xff0c;问我是否有好解决方案。这是一个挺有意思的问题&#xff0c;我们…

C++ 实现带权有向图的每对顶点之间的最短路径Floyd算法(完整代码)

基本思想是&#xff1a; 假设求从顶点vi到vj的最短路径。 如果从vi到vj有弧&#xff0c;则从vi到vj存在一条长度为arcs[i][j]的路径&#xff0c;该路径不一定是最短路径&#xff0c;尚需进行n次试探。 首先考虑路径&#xff08;vi, v0, vj&#xff09;是否存在&#xff08;判别…

等额本息,等额本金区别

等额本金&#xff0c;等额本息区别 买房银行贷款分为两种&#xff1a; 等额本金和等额本息 等额本息 等额本息定义&#xff1a;还款本金占比逐月递增&#xff0c;利息占比逐月递减&#xff0c;月还款数不变由于每月的还款额度是一样的&#xff0c;其中每个月的还款包括了根…

【视频回放与课件】Build your AI solution with MLOps

4月8日在Global AI Community on Vitural Tour与大家分享了Build your AI solution with MLOps的专题&#xff0c;本课程主要介绍了微软Azure Machine Learning如何使用 , 以及如何通过Azure Machine Learning 结合 MLOps的概念完成机器学习项目的工作。本次Global AI Communit…