• 关于 Java远程方法调用有什么用 的搜索结果

问题

基于Java容器的多应用部署技术实践【精品问答集锦】

管理贝贝 2019-12-01 20:28:27 52422 浏览量 回答数 55

问题

[精品问答]Java一百问第一期

问问小秘 2019-12-01 21:51:20 791 浏览量 回答数 1

问题

【Java学习全家桶】1460道Java热门问题,阿里百位技术专家答疑解惑

管理贝贝 2019-12-01 20:07:15 27612 浏览量 回答数 19

阿里云限量爆款产品特惠抢购

最新性价比爆款,每日10:00限量抢购!还可领取多种产品代金券福利,限量神券抢完即止。

问题

【精品问答】Java技术1000问(1)

问问小秘 2019-12-01 21:57:43 24636 浏览量 回答数 7

回答

跨域请求需要用webservice,远程方法调用!######能详细一些吗?怎么跨域的意思,我http请求不也就行了吗?怎么存在跨域了...还有远程方法调用,###### 传统的webservice是提供wsdl格式的内容,通过解析这样的返回内容可以得到一些对象和方法。需要特定的client去接收并解析 而广义上的webservice则可以将SpringMVC 遵循rest规范的API也纳入范畴,对于controller提供的API可以通过简单的httpclient进行接收和请求 ######那你意思上,广义来说,他们都是一个样咯。哈。###### 有一个地方很明显不一样: webService可以调用其他系统中的class的一个方法。controller中调用不了其他系统中的类的方法吧? 另外一个上面上面提到的一个跨域问题,一般都是js出现跨域问题。不用webService也可以解决(webService可以解决这个问题吗?一直不清楚。)。js请求本地controller,controller再通过httpClient请求其他域的接口。或者js请求jsp,jsp再请求其他的域接口都行。 ######【webService可以调用其他系统中的class的一个方法】,调用都是做一些业务处理,数据库操作吧,开放一个接口出来也可以呀。会不会因为安全问题,不对外开放接口,只允许代码调用??? 对,关于跨域的,如你所说就可以解决啦,只是代码任务上多了些操作...###### 首先,WebService肯定不是MVC里的Controller,虽然这两种东西都具有接口的性质,但这不代表他们就可以用“类似”来比较。 其次,WebService本身就是编程实现的,你用不同的程序语言当然也能实现了。但是注意,这里不是说用Struts、SpringMVC之类的某某框架去实现,因为你实现的过程就已经撇开了框架,是建立在Java(或者某种语言)的基础上,这个关系也不要混淆。 最后,那为什么不自己去实现呢?自己去实现当然是可以的,但是你真正觉得你能做到专门做WebService团队的水平吗?开发的时间能保证不会得不偿失吗?代码质量和运行速度有保证吗?显然是不行的,将WebService独立出来,其实是模块化的一个例子,这些东西留给WebService去考虑,而不是你来考虑。 ######像你所说的,比如一个快递公司,它内部一个查询快递单号的系统, 这个系统只对内使用。但对外互联网用户就重新的弄一套WebService应用使用???######mark

kun坤 2020-05-29 23:09:26 0 浏览量 回答数 0

问题

荆门开诊断证明-scc

游客5k2abgdj3m2ti 2019-12-01 22:09:00 1 浏览量 回答数 0

问题

【精品问答】dubbo必备面试题集

游客pklijor6gytpx 2019-12-01 21:54:02 54 浏览量 回答数 1

问题

【精品问答】Java实战200例(附源码)

珍宝珠 2020-02-14 11:55:46 11594 浏览量 回答数 6

回答

window下还要用emacs,真是苦逼了~######我也是没办法我不是开发人员我只是个爱好者,还在读书,学习教学的都是基于win平台,就好像全中国的都用qq.我就算不喜欢但是跟别人交流也只能使用qq咯.######搞Java啊?爱好者?那还是用Eclipse吧。######@云溪 : 只要你有信心,就坚持下去吧。 我最开始接触Emasc的时候,朋友跟我说,它比我的年龄还大。哈哈。######@puras : emacs的远程功能能替代我使用其他远程软件,emacs的dired模式能替代linux下的文件管理命令,emacs有shell模式,emacs有上网 播放MP3功能等等所以我真的很喜欢这个瑞士军刀######@云溪 : Emacs确实是个好东西,以前我还用过好久呢。不过开发Java,还是用Eclipse之类的IDE要快一些######我很喜欢emacs的定制性,并且我是个懒人,不喜欢经常换开发软件, 普通的用emacs写java是没什么问题的了,只是想进行扩展,拥有一些IDE的功能而已.######这样折腾为什么不直接用Linux呢?OpenJDK很好的 ######linux下的话我也是面临这个问题,只是系统换了而已,linux+emacs23+openJDK 如何成为开发环境 首先我会使用emacs编辑java并且会进入shell模式调用java工具进行编译,不过我自己是想emacs具有IDE的某些功能,所以想进行扩展~~~需要用到的软件包我都下载好了就是不会配置######你如果不是长期的emacs忠实用户,习惯了用它快速编辑文本,则完全没必要选择emacs。 你应该选择一个主流IDE,比如Eclipse, IntelliJ IDEA, Netbeans。 我想你之所以会听到这个东西,可能是问了某个“所谓”的高手,推荐了你个装B的做法。 其实对于真正的程序员来说,如果你的打字速度制约你的开发速度,只能说明你做的工作还停留在Ctrl+C Ctrl+V的阶段######@吀夜 : 这个我修改的键盘键位用得正爽呢.vi不是不好啊,问题是我已经有emacs了,我不能始乱终弃!######@云溪 : 可爱的少年,用鼠标会打断你的思路,难道Ctrl,Alt,Shift这些边缘按键不会打断你的思路? 回到Vi的怀抱吧,少年######其实我使用Emacs其中有一个很重要的原因那就是我很讨厌鼠标,经常使用鼠标点击按键会有一种打断思路的感觉.######有你把emacs折腾好,折腾完善的时间,你都不知道能开发多少个java项目出来了 不要为了使用emacs而去使用emacs,少年!######emacs就像毒药,我无药可救了........,你还是放弃我吧- -######想清楚了..Emacs投入的成本非常大,因为你要为这东西学习一门语言(elisp),而且你在Windows用得到的回报少得可怜。这里还是不计较损失了编译型语言的即时错误提示。 如果刚入门+下定决心要学习的话,可以找我拿点资料:)  ######@Sanatir : 你的资料我下载了,我的问题虽然还没解决,不过还是说声谢谢.######@云溪 : 并不是说emacs和elisp在win下用得少,只不过使用体验不同而已。另外,我自己用得不多,不能给予多大帮助XD..######反正我是那种越折腾越痛快的人,不折腾浑身不舒服,至于你说的win下能用emacs跟elisp的地方很少,我是win7系统,vmware虚拟机,我是想使用emacs的远程功能去操作linux虚拟机的,win下的emacs远程问题我到现在还没搞定呢,我现在是使用putty远程登录linux的shell,然后在shell打开emacs的,其实说什么都是假,想找个人指点下才是真的.怎样找你要资料?######CEDET的安装方法: You can install all these packagees at once with the CEDET build and install scripts: 1) Copy source files somewhere. 2) Byte compile    There are several ways to get CEDET compiled:    a) make    b) make EMACS=<your favorite emacs>     You might also have trouble with makeinfo.  If you need to upgrade     makeinfo, you can do this:   c) make MAKEINFO=/usr/local/bin/makeinfo     Note: For speedbar, and older versions of Emacs, you may also need           to byte-compile the version of INFO and RMAIL that come with           your version of emacs.   d) make MAKEINFO=echo     To skip making the doc.   e) cedet-build.el     If you do not have "make", are on Windows, or otherwise cannot use     the Makefiles, you can build CEDET from within Emacs.  See the     commentary in cedet-build.el 以下我对yasnippet的配置,或许对你有用 ;;yasnippet设置 ;;web site: http://code.google.com/p/yasnippet/ (add-to-list 'load-path                   "~/.emacs.d/plugins/yasnippet") (require 'yasnippet) ;; not yasnippet-bundle (yas/initialize) (yas/load-directory "~/.emacs.d/plugins/yasnippet/snippets")######JAVA,Eclipse王道 ######@云溪 : 加油######我就想用而已我有不是让人看我多厉害######装B######emacs是什么道?######楼主想用Emacs就用吧,做好孤独的准备 ######我的同学个个都用ide~就我自己用emacs,我确实是被孤立了,我不懂只能自己找答案,同学有时候会鄙视我,说我装模作样的呢,可问题emacs我觉得真的很优秀,我很喜欢.######为什么不来最简单的,用emacs编辑好源文件,命令行下编译就是了 BTW.楼主忽视那些苦口婆心劝你不要用emacs换这个IDE那个IDE的吧,你现在学会了emacs,一辈子都是你的技能.而所有的IDE都可以在你以后工作之后很快学会.在学校,多学一点是一点.在学校,时间往往很多,知识一般不够多.你感兴趣的,就去学.管别人怎么说,说你装逼也罢,二逼也好,和他解释那么多,不如多记两个emacs命令.哈哈哈.... 以上的所有"emacs"都可以换成"vim".######嗯我一直都坚持自己的想法~~,直接编译我已经会了,不过就是如果代码多了的话是需要IDE这些东西的,况且我也是想学习怎么配置呵呵.

kun坤 2020-06-05 13:16:50 0 浏览量 回答数 0

回答

"window下还要用emacs,真是苦逼了~######我也是没办法我不是开发人员我只是个爱好者,还在读书,学习教学的都是基于win平台,就好像全中国的都用qq.我就算不喜欢但是跟别人交流也只能使用qq咯.######搞Java啊?爱好者?那还是用Eclipse吧。######<a href=""http://my.oschina.net/PokerFace"" class=""referer"" target=""_blank"">@云溪 : 只要你有信心,就坚持下去吧。 我最开始接触Emasc的时候,朋友跟我说,它比我的年龄还大。哈哈。######<a href=""http://my.oschina.net/puras"" class=""referer"" target=""_blank"">@puras : emacs的远程功能能替代我使用其他远程软件,emacs的dired模式能替代linux下的文件管理命令,emacs有shell模式,emacs有上网 播放MP3功能等等所以我真的很喜欢这个瑞士军刀######<a href=""http://my.oschina.net/PokerFace"" class=""referer"" target=""_blank"">@云溪 : Emacs确实是个好东西,以前我还用过好久呢。不过开发Java,还是用Eclipse之类的IDE要快一些######我很喜欢emacs的定制性,并且我是个懒人,不喜欢经常换开发软件, 普通的用emacs写java是没什么问题的了,只是想进行扩展,拥有一些IDE的功能而已.######这样折腾为什么不直接用Linux呢?OpenJDK很好的 ######linux下的话我也是面临这个问题,只是系统换了而已,linux+emacs23+openJDK 如何成为开发环境 首先我会使用emacs编辑java并且会进入shell模式调用java工具进行编译,不过我自己是想emacs具有IDE的某些功能,所以想进行扩展~~~需要用到的软件包我都下载好了就是不会配置###### 你如果不是长期的emacs忠实用户,习惯了用它快速编辑文本,则完全没必要选择emacs。 你应该选择一个主流IDE,比如Eclipse, IntelliJ IDEA, Netbeans。 我想你之所以会听到这个东西,可能是问了某个“所谓”的高手,推荐了你个装B的做法。 其实对于真正的程序员来说,如果你的打字速度制约你的开发速度,只能说明你做的工作还停留在Ctrl+C Ctrl+V的阶段###### @吀夜 : 这个我修改的键盘键位用得正爽呢.vi不是不好啊,问题是我已经有emacs了,我不能始乱终弃!###### @云溪 : 可爱的少年,用鼠标会打断你的思路,难道Ctrl,Alt,Shift这些边缘按键不会打断你的思路? 回到Vi的怀抱吧,少年######其实我使用Emacs其中有一个很重要的原因那就是我很讨厌鼠标,经常使用鼠标点击按键会有一种打断思路的感觉.###### 有你把emacs折腾好,折腾完善的时间,你都不知道能开发多少个java项目出来了 不要为了使用emacs而去使用emacs,少年!######emacs就像毒药,我无药可救了........,你还是放弃我吧- -###### 想清楚了..Emacs投入的成本非常大,因为你要为这东西学习一门语言(elisp),而且你在Windows用得到的回报少得可怜。这里还是不计较损失了编译型语言的即时错误提示。 如果刚入门+下定决心要学习的话,可以找我拿点资料:)  ###### @Sanatir : 你的资料我下载了,我的问题虽然还没解决,不过还是说声谢谢.###### @云溪 : 并不是说emacs和elisp在win下用得少,只不过使用体验不同而已。另外,我自己用得不多,不能给予多大帮助XD..######反正我是那种越折腾越痛快的人,不折腾浑身不舒服,至于你说的win下能用emacs跟elisp的地方很少,我是win7系统,vmware虚拟机,我是想使用emacs的远程功能去操作linux虚拟机的,win下的emacs远程问题我到现在还没搞定呢,我现在是使用putty远程登录linux的shell,然后在shell打开emacs的,其实说什么都是假,想找个人指点下才是真的.怎样找你要资料?###### CEDET的安装方法: You can install all these packagees at once with the CEDET build and install scripts: 1) Copy source files somewhere. 2) Byte compile    There are several ways to get CEDET compiled:    a) make    b) make EMACS=<your favorite emacs>     You might also have trouble with makeinfo.  If you need to upgrade     makeinfo, you can do this:   c) make MAKEINFO=/usr/local/bin/makeinfo     Note: For speedbar, and older versions of Emacs, you may also need           to byte-compile the version of INFO and RMAIL that come with           your version of emacs.   d) make MAKEINFO=echo     To skip making the doc.   e) cedet-build.el     If you do not have "make", are on Windows, or otherwise cannot use     the Makefiles, you can build CEDET from within Emacs.  See the     commentary in cedet-build.el 以下我对yasnippet的配置,或许对你有用 ;;yasnippet设置 ;;web site: http://code.google.com/p/yasnippet/ (add-to-list 'load-path                   "~/.emacs.d/plugins/yasnippet") (require 'yasnippet) ;; not yasnippet-bundle (yas/initialize) (yas/load-directory "~/.emacs.d/plugins/yasnippet/snippets")######JAVA,Eclipse王道 ###### @云溪 : 加油######我就想用而已~~我有不是让人看我多厉害~~######装B######emacs是什么道?######楼主想用Emacs就用吧,做好孤独的准备 ######我的同学个个都用ide~~~就我自己用emacs,我确实是被孤立了,我不懂只能自己找答案,同学有时候会鄙视我,说我装模作样的呢,可问题emacs我觉得真的很优秀,我很喜欢.###### 为什么不来最简单的,用emacs编辑好源文件,命令行下编译就是了~~ BTW.楼主忽视那些苦口婆心劝你不要用emacs换这个IDE那个IDE的吧,你现在学会了emacs,一辈子都是你的技能.而所有的IDE都可以在你以后工作之后很快学会.在学校,多学一点是一点.在学校,时间往往很多,知识一般不够多.你感兴趣的,就去学.管别人怎么说,说你装逼也罢,二逼也好,和他解释那么多,不如多记两个emacs命令.哈哈哈.... 以上的所有"emacs"都可以换成"vim".######嗯我一直都坚持自己的想法~~,直接编译我已经会了,不过就是如果代码多了的话是需要IDE这些东西的,况且我也是想学习怎么配置呵呵." ![image.png](https://ucc.alicdn.com/pic/developer-ecology/4c6836169d074e608530252736da036d.png)

python小菜菜 2020-06-02 17:32:44 0 浏览量 回答数 0

问题

求大神们帮忙看看一个关于监控设备的BS架构的解决方案

因为相信,所以看见。 2020-05-24 22:06:34 2 浏览量 回答数 0

问题

应用 AXIS 开始 Web 服务之旅:报错

kun坤 2020-06-08 11:01:46 3 浏览量 回答数 1

回答

除了可以使用传统的 XML 配置方式开发 Dubbo 应用,还可以使用 Spring Boot 开发 Dubbo 应用,特别对于 Java 技术薄弱和 Maven 经验少,且又不熟悉 Dubbo 框架的开发者更为适合。本文以全新开发过程,向您展示如何使用 Spring Boot 开发 Dubbo 应用,并使用 SAE 服务注册中心实现服务注册与发现。 前提条件 下载 Maven并设置环境变量。 下载最新版本的 Nacos Server。 启动 Nacos Server。 解压下载的 Nacos Server 压缩包 进入nacos/bin目录,启动 Nacos Server。 Linux/Unix/Mac 系统:执行命令sh startup.sh -m standalone。 Windows 系统:双击执行startup.cmd文件。 在本地开发应用时,可以使用 Alibaba Cloud Toolkit 插件实现本地应用和部署在 EDAS 中的应用的相互调用,即端云互联,而无需搭建 VPN,帮助您提升开发效率。详情请参见为 EDAS 应用设置端云互联。 为什么使用 Spring Boot 开发 Dubbo 应用 Spring Boot 简化了微服务应用的配置和部署,同时 Nacos 又同时提供了服务注册发现和配置管理功能,两者结合的方式能够帮助您快速搭建基于 Spring 的 Dubbo 服务,相比 xml 的开发方式,大幅提升开发效率。 全新场景使用 Spring Boot 开发 Dubbo 应用有两种主要的方式: 使用 xml 开发。 使用 Spring Boot 的注解方式开发。 使用 xml 方式请参考将 Dubbo 应用托管到 SAE。文本档介绍如何使用 Spring Boot 的注解方式开发 Dubbo 服务。 视频教程 本视频仅介绍使用 Spring Boot 开发 Dubbo 应用,部署部分请参见在SAE控制台部署应用。 示例工程 您可以按照本文的逐步搭建工程,也可以选择直接下载本文对应的示例工程,或者使用 Git 来 clone: git clone https://github.com/aliyun/alibabacloud-microservice-demo.git 该项目包含了众多了示例工程,本文对应的示例工程位于 alibabacloud-microservice-demo/microservice-doc-demo/dubbo-samples-spring-boot。 创建服务提供者 创建命名为spring-boot-dubbo-provider的 Maven 工程。 在pom.xml文件中添加所需的依赖。 这里以 Spring Boot 2.0.6.RELEASE 为例。 org.springframework.boot spring-boot-dependencies 2.0.6.RELEASE pom import org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-actuator org.apache.dubbo dubbo-spring-boot-starter 2.7.3 com.alibaba.nacos nacos-client 1.1.1 开发 Dubbo 服务提供者。 Dubbo 中服务都是以接口的形式提供的。 在src/main/java路径下创建一个 package com.alibaba.edas.boot。 在com.alibaba.edas.boot下创建一个接口(interface) IHelloService,里面包含一个 SayHello 方法。 package com.alibaba.edas.boot; public interface IHelloService { String sayHello(String str); } 在com.alibaba.edas.boot下创建一个类IHelloServiceImpl,实现此接口。 package com.alibaba.edas.boot; import com.alibaba.dubbo.config.annotation.Service; @Service public class IHelloServiceImpl implements IHelloService { public String sayHello(String name) { return "Hello, " + name + " (from Dubbo with Spring Boot)"; } } 说明 这里的 Service 注解是 Dubbo 提供的一个注解类,类的全名称为:com.alibaba.dubbo.config.annotation.Service 。 配置 Dubbo 服务。 在 src/main/resources路径下创建application.properties或application.yaml文件并打开。 在application.properties或application.yaml中添加如下配置。 Base packages to scan Dubbo Components (e.g @Service , @Reference) dubbo.scan.basePackages=com.alibaba.edas.boot dubbo.application.name=dubbo-provider-demo dubbo.registry.address=nacos://127.0.0.1:8848 说明 以上三个配置没有默认值,必须要给出具体的配置。 dubbo.scan.basePackages的值是开发的代码中含有com.alibaba.dubbo.config.annotation.Service和com.alibaba.dubbo.config.annotation.Reference注解所在的包。多个包之间用逗号隔开。 dubbo.registry.address的值前缀必须以 nacos:// 开头,后面的 IP 地址和端口指的是 Nacos Server 的地址。代码示例中为本地地址,如果您将 Nacos Server 部署在其它机器上,请修改为实际的 IP 地址。 开发并启动 Spring Boot 入口类DubboProvider。 package com.alibaba.edas.boot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DubboProvider { public static void main(String[] args) { SpringApplication.run(DubboProvider.class, args); } } 登录 Nacos 控制台 http://127.0.0.1:8848,在左侧导航栏中单击服务列表 ,查看提供者列表。 可以看到服务提供者里已经包含了com.alibaba.edas.boot.IHelloService,且可以查询该服务的服务分组和提供者 IP。 创建服务消费者 创建一个 Maven 工程,命名为spring-boot-dubbo-consumer。 在pom.xml文件中添加相关依赖。 这里以 Spring Boot 2.0.6.RELEASE 为例。 org.springframework.boot spring-boot-dependencies 2.0.6.RELEASE pom import org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-actuator org.apache.dubbo dubbo-spring-boot-starter 2.7.3 com.alibaba.nacos nacos-client 1.1.1 如果您需要选择使用 Spring Boot 1.x 的版本,请使用 Spring Boot 1.5.x 版本,对应的 com.alibaba.boot:dubbo-spring-boot-starter 版本为 0.1.0。 说明 Spring Boot 1.x 版本的生命周期即将在 2019 年 8 月 结束,推荐使用新版本开发您的应用。 开发 Dubbo 消费者。 在src/main/java路径下创建 package com.alibaba.edas.boot。 在com.alibaba.edas.boot下创建一个接口(interface) IHelloService,里面包含一个 SayHello 方法。 package com.alibaba.edas.boot; public interface IHelloService { String sayHello(String str); } 开发 Dubbo 服务调用。 例如需要在 Controller 中调用一次远程 Dubbo 服务,开发的代码如下所示。 package com.alibaba.edas.boot; import com.alibaba.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class DemoConsumerController { @Reference private IHelloService demoService; @RequestMapping("/sayHello/{name}") public String sayHello(@PathVariable String name) { return demoService.sayHello(name); } } 说明 这里的 Reference 注解是 com.alibaba.dubbo.config.annotation.Reference 。 在application.properties/application.yaml配置文件中新增以下配置。 dubbo.application.name=dubbo-consumer-demo dubbo.registry.address=nacos://127.0.0.1:8848 说明 以上两个配置没有默认值,必须要给出具体的配置。 dubbo.registry.address的值前缀必须以 nacos:// 开头,后面的 IP 地址和端口为 Nacos Server 的地址。代码示例中为本地地址,如果您将 Nacos Server 部署在其它机器上,请修改为实际的 IP 地址。 开发并启动 Spring Boot 入口类DubboConsumer。 package com.alibaba.edas.boot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DubboConsumer { public static void main(String[] args) { SpringApplication.run(DubboConsumer.class, args); } } 登录 Nacos 控制台 http://127.0.0.1:8848,在左侧导航栏中单击服务列表,再在服务列表页面单击调用者列表页签,查看调用者列表。 可以看到包含了com.alibaba.edas.boot.IHelloService,且可以查看该服务的服务分组和调用者 IP。 结果验证 curl http://localhost:8080/sayHello/EDAS Hello, EDAS (from Dubbo with Spring Boot) 部署到 SAE 本地使用 Nacos 作为注册中心的应用,可以直接部署到 SAE 中,无需做任何修改,注册中心会被自动替换为 SAE上的注册中心。 您可以根据实际需求选择部署途径(控制台或工具),详情请参见应用部署概述。 使用控制台部署前,请参见如下操作将应用程序编译为可运行的JAR包、WAR包。 在pom.xml文件中添加以下打包插件的配置。 Provider org.springframework.boot spring-boot-maven-plugin repackage spring-boot com.alibaba.edas.boot.DubboProvider Consumer org.springframework.boot spring-boot-maven-plugin repackage spring-boot com.alibaba.edas.boot.DubboConsumer 执行 mvn clean package 将本地的程序打成 JAR 包。 更多信息 除 Spring Boot 外,还可以通过 XML 的方式开发 Dubbo 微服务应用,详情请参见将 Dubbo 应用托管到 SAE。 应用部署到 SAE 后,您可以对应用进行管理、绑定 SLB 等操作。 应用部署 应用管理 监控管理 日志管理

1934890530796658 2020-03-27 12:50:55 0 浏览量 回答数 0

回答

重试作用: 对于重试是有场景限制的,不是什么场景都适合重试,比如参数校验不合法、写操作等(要考虑写是否幂等)都不适合重试。 远程调用超时、网络突然中断可以重试。在微服务治理框架中,通常都有自己的重试与超时配置,比如dubbo可以设置retries=1,timeout=500调用失败只重试1次,超过500ms调用仍未返回则调用失败。 比如外部 RPC 调用,或者数据入库等操作,如果一次操作失败,可以进行多次重试,提高调用成功的可能性。 优雅的重试机制要具备几点: 无侵入:这个好理解,不改动当前的业务逻辑,对于需要重试的地方,可以很简单的实现 可配置:包括重试次数,重试的间隔时间,是否使用异步方式等 通用性:最好是无改动(或者很小改动)的支持绝大部分的场景,拿过来直接可用 优雅重试共性和原理: 正常和重试优雅解耦,重试断言条件实例或逻辑异常实例是两者沟通的媒介。 约定重试间隔,差异性重试策略,设置重试超时时间,进一步保证重试有效性以及重试流程稳定性。 都使用了命令设计模式,通过委托重试对象完成相应的逻辑操作,同时内部封装实现重试逻辑。 Spring-tryer和guava-tryer工具都是线程安全的重试,能够支持并发业务场景的重试逻辑正确性。 优雅重试适用场景: 功能逻辑中存在不稳定依赖场景,需要使用重试获取预期结果或者尝试重新执行逻辑不立即结束。比如远程接口访问,数据加载访问,数据上传校验等等。 对于异常场景存在需要重试场景,同时希望把正常逻辑和重试逻辑解耦。 对于需要基于数据媒介交互,希望通过重试轮询检测执行逻辑场景也可以考虑重试方案。 优雅重试解决思路: 切面方式 这个思路比较清晰,在需要添加重试的方法上添加一个用于重试的自定义注解,然后在切面中实现重试的逻辑,主要的配置参数则根据注解中的选项来初始化 优点: 真正的无侵入 缺点: 某些方法无法被切面拦截的场景无法覆盖(如spring-aop无法切私有方法,final方法) 直接使用aspecj则有些小复杂;如果用spring-aop,则只能切被spring容器管理的bean 消息总线方式 这个也比较容易理解,在需要重试的方法中,发送一个消息,并将业务逻辑作为回调方法传入;由一个订阅了重试消息的consumer来执行重试的业务逻辑 优点: 重试机制不受任何限制,即在任何地方你都可以使用 利用EventBus框架,可以非常容易把框架搭起来 缺点: 业务侵入,需要在重试的业务处,主动发起一条重试消息 调试理解复杂(消息总线方式的最大优点和缺点,就是过于灵活了,你可能都不知道什么地方处理这个消息,特别是新的童鞋来维护这段代码时) 如果要获取返回结果,不太好处理, 上下文参数不好处理 模板方式 优点: 简单(依赖简单:引入一个类就可以了; 使用简单:实现抽象类,讲业务逻辑填充即可;) 灵活(这个是真正的灵活了,你想怎么干都可以,完全由你控制) 缺点: 强侵入 代码臃肿 把这个单独捞出来,主要是某些时候我就一两个地方要用到重试,简单的实现下就好了,也没有必用用到上面这么重的方式;而且我希望可以针对代码快进行重试 这个的设计还是非常简单的,基本上代码都可以直接贴出来,一目了然: 复制代码 public abstract class RetryTemplate { private static final int DEFAULT_RETRY_TIME = 1; private int retryTime = DEFAULT_RETRY_TIME; private int sleepTime = 0;// 重试的睡眠时间 public int getSleepTime() { return sleepTime; } public RetryTemplate setSleepTime(int sleepTime) { if(sleepTime < 0) { throw new IllegalArgumentException("sleepTime should equal or bigger than 0"); } this.sleepTime = sleepTime; return this; } public int getRetryTime() { return retryTime; } public RetryTemplate setRetryTime(int retryTime) { if (retryTime <= 0) { throw new IllegalArgumentException("retryTime should bigger than 0"); } this.retryTime = retryTime; return this; } /** * 重试的业务执行代码 * 失败时请抛出一个异常 * * todo 确定返回的封装类,根据返回结果的状态来判定是否需要重试 * * @return */ protected abstract Object doBiz() throws Exception; //预留一个doBiz方法由业务方来实现,在其中书写需要重试的业务代码,然后执行即可 public Object execute() throws InterruptedException { for (int i = 0; i < retryTime; i++) { try { return doBiz(); } catch (Exception e) { log.error("业务执行出现异常,e: {}", e); Thread.sleep(sleepTime); } } return null; } public Object submit(ExecutorService executorService) { if (executorService == null) { throw new IllegalArgumentException("please choose executorService!"); } return executorService.submit((Callable) () -> execute()); } } 复制代码 使用示例: 复制代码 public void retryDemo() throws InterruptedException { Object ans = new RetryTemplate() { @Override protected Object doBiz() throws Exception { int temp = (int) (Math.random() * 10); System.out.println(temp); if (temp > 3) { throw new Exception("generate value bigger then 3! need retry"); } return temp; } }.setRetryTime(10).setSleepTime(10).execute(); System.out.println(ans); } 复制代码 spring-retry Spring Retry 为 Spring 应用程序提供了声明性重试支持。 它用于Spring批处理、Spring集成、Apache Hadoop(等等)的Spring。 在分布式系统中,为了保证数据分布式事务的强一致性,在调用RPC接口或者发送MQ时,针对可能会出现网络抖动请求超时情况采取一下重试操作。 用的最多的重试方式就是MQ了,但是如果你的项目中没有引入MQ,就不方便了。 还有一种方式,是开发者自己编写重试机制,但是大多不够优雅。 缺陷 spring-retry 工具虽能优雅实现重试,但是存在两个不友好设计: 一个是重试实体限定为 Throwable 子类,说明重试针对的是可捕捉的功能异常为设计前提的,但是我们希望依赖某个数据对象实体作为重试实体, 但 sping-retry框架必须强制转换为Throwable子类。 另一个是重试根源的断言对象使用的是 doWithRetry 的 Exception 异常实例,不符合正常内部断言的返回设计。 Spring Retry 提倡以注解的方式对方法进行重试,重试逻辑是同步执行的,当抛出相关异常后执行重试, 如果你要以返回值的某个状态来判定是否需要重试,可能只能通过自己判断返回值然后显式抛出异常了。只读操作可以重试,幂等写操作可以重试,但是非幂等写操作不能重试,重试可能导致脏写,或产生重复数据。 @Recover 注解在使用时无法指定方法,如果一个类中多个重试方法,就会很麻烦。 spring-retry 结构 BackOff:补偿值,一般指失败后多久进行重试的延迟值。 Sleeper:暂停应用的工具,通常用来应用补偿值。 RetryState:重试状态,通常包含一个重试的键值。 RetryCallback:封装你需要重试的业务逻辑(上文中的doSth) RecoverCallback:封装了多次重试都失败后你需要执行的业务逻辑(上文中的doSthWhenStillFail) RetryContext:重试语境下的上下文,代表了能被重试动作使用的资源。可用于在多次Retry或者Retry 和Recover之间传递参数或状态(在多次doSth或者doSth与doSthWhenStillFail之间传递参数) RetryOperations: 定义了“重试”的模板(重试的API),要求传入RetryCallback,可选传入RecoveryCallback; RetryTemplate :RetryOperations的具体实现,组合了RetryListener[],BackOffPolicy,RetryPolicy。 RetryListener:用来监控Retry的执行情况,并生成统计信息。 RetryPolicy:重试的策略或条件,可以简单的进行多次重试,可以是指定超时时间进行重试(上文中的someCondition),决定失败能否重试。 BackOffPolicy: 重试的回退策略,在业务逻辑执行发生异常时。如果需要重试,我们可能需要等一段时间(可能服务器过于繁忙,如果一直不间隔重试可能拖垮服务器),当然这段时间可以是0,也可以是固定的,可以是随机的(参见tcp的拥塞控制算法中的回退策略)。回退策略在上文中体现为wait(); RetryPolicy提供了如下策略实现: NeverRetryPolicy:只允许调用RetryCallback一次,不允许重试; AlwaysRetryPolicy:允许无限重试,直到成功,此方式逻辑不当会导致死循环; SimpleRetryPolicy:固定次数重试策略,默认重试最大次数为3次,RetryTemplate默认使用的策略; TimeoutRetryPolicy:超时时间重试策略,默认超时时间为1秒,在指定的超时时间内允许重试; CircuitBreakerRetryPolicy:有熔断功能的重试策略,需设置3个参数openTimeout、resetTimeout和delegate delegate:是真正判断是否重试的策略,当重试失败时,则执行熔断策略;应该配置基于次数的SimpleRetryPolicy或者基于超时的TimeoutRetryPolicy策略,且策略都是全局模式,而非局部模式,所以要注意次数或超时的配置合理性。 openTimeout:openWindow,配置熔断器电路打开的超时时间,当超过openTimeout之后熔断器电路变成半打开状态(主要有一次重试成功,则闭合电路); resetTimeout:timeout,配置重置熔断器重新闭合的超时时间 CompositeRetryPolicy:组合重试策略,有两种组合方式,乐观组合重试策略是指只要有一个策略允许重试即可以,悲观组合重试策略是指只要有一个策略不允许重试即可以,但不管哪种组合方式,组合中的每一个策略都会执行。 BackOffPolicy 提供了如下策略实现: NoBackOffPolicy:无退避算法策略,即当重试时是立即重试; FixedBackOffPolicy:固定时间的退避策略,需设置参数sleeper(指定等待策略,默认是Thread.sleep,即线程休眠)、backOffPeriod(休眠时间,默认1秒); UniformRandomBackOffPolicy:随机时间退避策略,需设置sleeper、minBackOffPeriod、maxBackOffPeriod,该策略在[minBackOffPeriod,maxBackOffPeriod之间取一个随机休眠时间,minBackOffPeriod默认500毫秒,maxBackOffPeriod默认1500毫秒; ExponentialBackOffPolicy:指数退避策略,需设置参数sleeper、initialInterval、maxInterval和multiplier。initialInterval指定初始休眠时间,默认100毫秒,maxInterval指定最大休眠时间,默认30秒,multiplier指定乘数,即下一次休眠时间为当前休眠时间*multiplier; ExponentialRandomBackOffPolicy:随机指数退避策略,引入随机乘数,固定乘数可能会引起很多服务同时重试导致DDos,使用随机休眠时间来避免这种情况。 RetryTemplate主要流程实现: 复制代码 //示例一 public void upload(final Map<String, Object> map) throws Exception { // 构建重试模板实例 RetryTemplate retryTemplate = new RetryTemplate(); // 设置重试策略,主要设置重试次数 SimpleRetryPolicy policy =         new SimpleRetryPolicy(3, Collections.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)); // 设置重试回退操作策略,主要设置重试间隔时间 FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); fixedBackOffPolicy.setBackOffPeriod(100); retryTemplate.setRetryPolicy(policy); retryTemplate.setBackOffPolicy(fixedBackOffPolicy); // 通过RetryCallback 重试回调实例包装正常逻辑逻辑,第一次执行和重试执行执行的都是这段逻辑 final RetryCallback<Object, Exception> retryCallback = new RetryCallback<Object, Exception>() { //RetryContext 重试操作上下文约定,统一spring-try包装 public Object doWithRetry(RetryContext context) throws Exception { System.out.println("do some thing"); Exception e = uploadToOdps(map); System.out.println(context.getRetryCount()); throw e;//这个点特别注意,重试的根源通过Exception返回 } }; // 通过RecoveryCallback 重试流程正常结束或者达到重试上限后的退出恢复操作实例 final RecoveryCallback recoveryCallback = new RecoveryCallback() { public Object recover(RetryContext context) throws Exception { System.out.println("do recory operation"); return null; } }; try { // 由retryTemplate 执行execute方法开始逻辑执行 retryTemplate.execute(retryCallback, recoveryCallback); } catch (Exception e) { e.printStackTrace(); } } //示例二 protected <T, E extends Throwable> T doExecute(RetryCallback<T, E> retryCallback,RecoveryCallback recoveryCallback,   RetryState state) throws E, ExhaustedRetryException { //重试策略 RetryPolicy retryPolicy = this.retryPolicy; //退避策略 BackOffPolicy backOffPolicy = this.backOffPolicy; //重试上下文,当前重试次数等都记录在上下文中 RetryContext context = open(retryPolicy, state); try { //拦截器模式,执行RetryListener#open boolean running = doOpenInterceptors(retryCallback, context); //判断是否可以重试执行 while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) { try {//执行RetryCallback回调 return retryCallback.doWithRetry(context); } catch (Throwable e) {//异常时,要进行下一次重试准备 //遇到异常后,注册该异常的失败次数 registerThrowable(retryPolicy, state, context, e); //执行RetryListener#onError doOnErrorInterceptors(retryCallback, context, e); //如果可以重试,执行退避算法,比如休眠一小段时间后再重试 if (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) { backOffPolicy.backOff(backOffContext); } //state != null && state.rollbackFor(context.getLastThrowable()) //在有状态重试时,如果是需要执行回滚操作的异常,则立即抛出异常 if (shouldRethrow(retryPolicy, context, state)) { throw RetryTemplate. wrapIfNecessary(e); } } //如果是有状态重试,且有GLOBAL_STATE属性,则立即跳出重试终止;       //当抛出的异常是非需要执行回滚操作的异常时,才会执行到此处,CircuitBreakerRetryPolicy会在此跳出循环; if (state != null && context.hasAttribute(GLOBAL_STATE)) { break; } } //重试失败后,如果有RecoveryCallback,则执行此回调,否则抛出异常 return handleRetryExhausted(recoveryCallback, context, state); } catch (Throwable e) { throw RetryTemplate. wrapIfNecessary(e); } finally { //清理环境 close(retryPolicy, context, state, lastException == null || exhausted); //执行RetryListener#close,比如统计重试信息 doCloseInterceptors(retryCallback, context, lastException); } } 复制代码 有状态or无状态 无状态重试,是在一个循环中执行完重试策略,即重试上下文保持在一个线程上下文中,在一次调用中进行完整的重试策略判断。如远程调用某个查询方法时是最常见的无状态重试: 复制代码 RetryTemplate template = new RetryTemplate(); //重试策略:次数重试策略 RetryPolicy retryPolicy = new SimpleRetryPolicy(3); template.setRetryPolicy(retryPolicy); //退避策略:指数退避策略 ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy(); backOffPolicy.setInitialInterval(100); backOffPolicy.setMaxInterval(3000); backOffPolicy.setMultiplier(2); backOffPolicy.setSleeper(new ThreadWaitSleeper()); template.setBackOffPolicy(backOffPolicy); //当重试失败后,抛出异常 String result = template.execute(new RetryCallback<String, RuntimeException>() { @Override public String doWithRetry(RetryContext context) throws RuntimeException { throw new RuntimeException("timeout"); } }); //当重试失败后,执行RecoveryCallback String result = template.execute(new RetryCallback<String, RuntimeException>() { @Override public String doWithRetry(RetryContext context) throws RuntimeException { System.out.println("retry count:" + context.getRetryCount()); throw new RuntimeException("timeout"); } }, new RecoveryCallback () { @Override public String recover(RetryContext context) throws Exception { return "default"; } }); 复制代码 有状态重试,有两种情况需要使用有状态重试,事务操作需要回滚、熔断器模式。 事务操作需要回滚场景时,当整个操作中抛出的是数据库异常DataAccessException,则不能进行重试需要回滚,而抛出其他异常则可以进行重试,可以通过RetryState实现: 复制代码 //当前状态的名称,当把状态放入缓存时,通过该key查询获取 Object key = "mykey"; //是否每次都重新生成上下文还是从缓存中查询,即全局模式(如熔断器策略时从缓存中查询) boolean isForceRefresh = true; //对DataAccessException进行回滚 BinaryExceptionClassifier rollbackClassifier = new BinaryExceptionClassifier(Collections.<Class<? extends Throwable>>singleton(DataAccessException.class)); RetryState state = new DefaultRetryState(key, isForceRefresh, rollbackClassifier); String result = template.execute(new RetryCallback<String, RuntimeException>() { @Override public String doWithRetry(RetryContext context) throws RuntimeException { System.out.println("retry count:" + context.getRetryCount()); throw new TypeMismatchDataAccessException(""); } }, new RecoveryCallback () { @Override public String recover(RetryContext context) throws Exception { return "default"; } }, state); 复制代码 RetryTemplate中在有状态重试时,回滚场景时直接抛出异常处理代码: //state != null && state.rollbackFor(context.getLastThrowable()) //在有状态重试时,如果是需要执行回滚操作的异常,则立即抛出异常 if (shouldRethrow(retryPolicy,context, state)) { throw RetryTemplate. wrapIfNecessary(e); } 熔断器场景。在有状态重试时,且是全局模式,不在当前循环中处理重试,而是全局重试模式(不是线程上下文),如熔断器策略时测试代码如下所示。 复制代码 RetryTemplate template = new RetryTemplate(); CircuitBreakerRetryPolicy retryPolicy = new CircuitBreakerRetryPolicy(new SimpleRetryPolicy(3)); retryPolicy.setOpenTimeout(5000); retryPolicy.setResetTimeout(20000); template.setRetryPolicy(retryPolicy); for (int i = 0; i < 10; i++) { try { Object key = "circuit"; boolean isForceRefresh = false; RetryState state = new DefaultRetryState(key, isForceRefresh); String result = template.execute(new RetryCallback<String, RuntimeException>() { @Override public String doWithRetry(RetryContext context) throws RuntimeException { System.out.println("retry count:" + context.getRetryCount()); throw new RuntimeException("timeout"); } }, new RecoveryCallback () { @Override public String recover(RetryContext context) throws Exception { return "default"; } }, state); System.out.println(result); } catch (Exception e) { System.out.println(e); } } 复制代码 为什么说是全局模式呢?我们配置了isForceRefresh为false,则在获取上下文时是根据key “circuit”从缓存中获取,从而拿到同一个上下文。 Object key = "circuit"; boolean isForceRefresh = false; RetryState state = new DefaultRetryState(key,isForceRefresh); 如下RetryTemplate代码说明在有状态模式下,不会在循环中进行重试。 if (state != null && context.hasAttribute(GLOBAL_STATE)) { break; } 判断熔断器电路是否打开的代码: 复制代码 public boolean isOpen() { long time = System.currentTimeMillis() - this.start; boolean retryable = this.policy.canRetry(this.context); if (!retryable) {//重试失败 //在重置熔断器超时后,熔断器器电路闭合,重置上下文 if (time > this.timeout) { this.context = createDelegateContext(policy, getParent()); this.start = System.currentTimeMillis(); retryable = this.policy.canRetry(this.context); } else if (time < this.openWindow) { //当在熔断器打开状态时,熔断器电路打开,立即熔断 if ((Boolean) getAttribute(CIRCUIT_OPEN) == false) { setAttribute(CIRCUIT_OPEN, true); } this.start = System.currentTimeMillis(); return true; } } else {//重试成功 //在熔断器电路半打开状态时,断路器电路闭合,重置上下文 if (time > this.openWindow) { this.start = System.currentTimeMillis(); this.context = createDelegateContext(policy, getParent()); } } setAttribute(CIRCUIT_OPEN, !retryable); return !retryable; } 复制代码 从如上代码可看出spring-retry的熔断策略相对简单: 当重试失败,且在熔断器打开时间窗口[0,openWindow) 内,立即熔断; 当重试失败,且在指定超时时间后(>timeout),熔断器电路重新闭合; 在熔断器半打开状态[openWindow, timeout] 时,只要重试成功则重置上下文,断路器闭合。 注解介绍 @EnableRetry 表示是否开始重试。 序号 属性 类型 默认值 说明 1 proxyTargetClass boolean false 指示是否要创建基于子类的(CGLIB)代理,而不是创建标准的基于Java接口的代理。当proxyTargetClass属性为true时,使用CGLIB代理。默认使用标准JAVA注解 @Retryable 标注此注解的方法在发生异常时会进行重试 序号 属性 类型 默认值 说明 1 interceptor String ”” 将 interceptor 的 bean 名称应用到 retryable() 2 value class[] {} 可重试的异常类型 3 include class[] {} 和value一样,默认空,当exclude也为空时,所有异常都重试 4 exclude class[] {} 指定异常不重试,默认空,当include也为空时,所有异常都重试 5 label String ”” 统计报告的唯一标签。如果没有提供,调用者可以选择忽略它,或者提供默认值。 6 maxAttempts int 3 尝试的最大次数(包括第一次失败),默认为3次。 7 backoff @Backoff @Backoff() 重试补偿机制,指定用于重试此操作的backoff属性。默认为空 @Backoff 不设置参数时,默认使用FixedBackOffPolicy(指定等待时间),重试等待1000ms 序号 属性 类型 默认值 说明 1 delay long 0 指定延迟后重试 ,如果不设置则默认使用 1000 milliseconds 2 maxDelay long 0 最大重试等待时间 3 multiplier long 0 指定延迟的倍数,比如delay=5000l,multiplier=2时,第一次重试为5秒后,第二次为10秒,第三次为20秒(大于0生效) 4 random boolean false 随机重试等待时间 @Recover 用于恢复处理程序的方法调用的注释。返回类型必须与@retryable方法匹配。 可抛出的第一个参数是可选的(但是没有它的方法只会被调用)。 从失败方法的参数列表按顺序填充后续的参数。 用于@Retryable重试失败后处理方法,此注解注释的方法参数一定要是@Retryable抛出的异常,否则无法识别,可以在该方法中进行日志处理。 说明: 使用了@Retryable的方法不能在本类被调用,不然重试机制不会生效。也就是要标记为@Service,然后在其它类使用@Autowired注入或者@Bean去实例才能生效。 要触发@Recover方法,那么在@Retryable方法上不能有返回值,只能是void才能生效。 使用了@Retryable的方法里面不能使用try...catch包裹,要在发放上抛出异常,不然不会触发。 在重试期间这个方法是同步的,如果使用类似Spring Cloud这种框架的熔断机制时,可以结合重试机制来重试后返回结果。 Spring Retry不只能注入方式去实现,还可以通过API的方式实现,类似熔断处理的机制就基于API方式实现会比较宽松。 转载于:https://www.cnblogs.com/whatarewords/p/10656514.html

养狐狸的猫 2019-12-02 02:11:54 0 浏览量 回答数 0

回答

转自:思否 本文作者:Michael van der Gulik 原文链接:《Why WebAssembly is a big deal》 译者:敖小剑 WebAssembly 是每个程序员都应该关注的技术。WebAssembly 会变得更流行。 WebAssembly 将取代 JavaScript。WebAssembly 将取代 HTML 和 CSS。 WebAssembly 将取代手机应用。WebAssembly 将取代桌面应用。在 10 年内,我保证每个程序员至少需要知道如何使用工具来操作 WebAssembly 并理解它是如何工作的。 你可能会说,“太离谱了!” 好吧,请继续阅读。 什么是 WebAssembly 当前形式的 WebAssembly 是 Web 浏览器的新扩展,可以运行预编译代码…快速地。在 C ++ 中编写了一些小代码,然后使用 Emscripten 编译器将该代码编译为 WebAssembly。通过一些 Javascript 粘合,就可以在 Web 浏览器中调用这一小段代码,例如,运行粒子模拟。 WebAssembly 文件,扩展名为.wasm,本身是包含可执行指令的二进制格式。要使用该文件,必须编写一个运行某些 Javascript 的 HTML 文件来获取、编译和执行 WebAssembly 文件。WebAssembly 文件在基于堆栈的虚拟机上执行,并使用共享内存与其 JavaScript 包装器进行通信。 到目前为止,这似乎并不有趣。它看起来只不过是 JavaScript 的加速器。但是,聪明的读者会对 WebAssembly 可能成为什么有所了解。 WebAssembly 将成为什么? 第一个重要发现是 WebAssembly 是一个安全的沙盒虚拟机。可以从 Internet 运行喜欢的 WebAssembly 代码,而确保它不会接管 PC 或服务器。四个主流 Web 浏览器对它的安全性非常有信心,它已经默认实现并启用了。它的真正安全性还有待观察,但安全性是 WebAssembly 的核心设计目标。 第二个重要发现是 WebAssembly 是一个通用的编译目标。它的原始编译器是一个 C 编译器,这个编译器很好地指示了 WebAssembly 虚拟机的低级和可重定向性。许多编程语言都使用 C 语言编写虚拟机,其他一些语言甚至使用 C 本身作为编译目标。 此时,有人整理了一个可以编译为 WebAssembly 的编程语言列表。这份名单将在未来很多年中继续增长。 WebAssembly 允许使用任何编程语言编写代码,然后让其他人在任何平台上安全地运行该代码,无需安装任何内容。朋友们,这是美好梦想的开始。 部署问题 我们来谈谈如何将软件提供给用户。 为新项目选择编程语言的一个重要因素是如何将项目部署到客户。您的程序员喜欢用 Haskell,Python,Visual Basic 或其他语言编写应用程序,具体取决于他们的喜好。要使用喜欢的语言,他们需要编译应用,制作一些可安装的软件包,并以某种方式将其安装在客户端的计算机上。有许多方法可以提供软件 - 包管理器,可执行安装程序或安装服务,如 Steam,Apple App Store,Google Play 或 Microsoft store。 每一个安装机制都意味着痛苦,从应用商店安装时的轻微疼痛,到管理员要求在他的 PC 上运行一些旧的 COBOL 代码时的集群头痛。 部署是一个问题。对于开发人员和系统管理员来说,部署一直是一个痛点。我们使用的编程语言与我们所针对的平台密切相关。如果大量用户在 PC 或移动设备上,我们使用 HTML 和 Javascript。如果用户是 Apple 移动设备用户,我们使用……呃…… Swift?(我实际上不知道)。如果用户在 Android 设备上,我们使用 Java 或 Kotlin。如果用户在真实计算机上并且愿意处理掉他们的部署问题,那么我们开发人员才能在我们使用的编程语言中有更多选择。 WebAssembly 有可能解决部署问题。 有了 WebAssembly,您可以使用任何编程语言编写应用,只要这些编程语言可以支持 WebAssembly,而应用可以在任何设备和任何具有现代 Web 浏览器的操作系统上运行。 硬件垄断 想购买台式机或笔记本电脑。有什么选择?好吧,有英特尔,有 AMD。多年来一直是双寡头垄断。保持这种双寡头垄断的一个原因是 x86 架构只在这两家公司之间交叉许可,而且通常预编译的代码需要 x86 或 x86-64(也就是 AMD-64)架构。还有其他因素,例如设计世界上最快的 CPU 是一件很艰难但也很昂贵的事情。 WebAssembly 是一种可让您在任何平台上运行代码的技术(之一)。如果它成为下一个风口,硬件市场将变得商品化。应用编译为 WebAssembly,就可以在任何东西上运行 - x86,ARM,RISC-V,SPARC。即便是操作系统市场也会商品化;您所需要的只是一个支持 WebAssembly 的浏览器,以便在硬件可以运行时运行最苛刻的应用程序。 编者注:Second State 研发的专为服务端优化的 WebAssembly 引擎 SSVM 已经可以运行在高通骁龙芯片上。Github 链接:https://github.com/second-sta... 云计算 但等等,还有更多。云计算成为IT经理办公室的流行词已有一段时间,WebAssembly 可以直接迎合它。 WebAssembly 在安全沙箱中执行。可以制作一个容器,它可以在服务器上接受和执行 WebAssembly 模块,而资源开销很小。对于提供的每个服务,无需在虚拟机上运行完整的操作系统。托管提供商只提供对可以上传代码的WebAssembly 容器的访问权限。它可以是一个原始容器,接收 socket 并解析自己的 HTTP 连接,也可以是一个完整的 Web 服务容器,其中 WebAssembly 模块只需要处理预解析的HTTP请求。 这还不存在。如果有人想变得富有,那么可以考虑这个想法。 编者注:目前已经有人正在实现这个想法,Byte Alliance 计划将WebAssembly 带到浏览器之外,Second State 已经发布了为服务端设计的WebAssembly 引擎开发者预览版。 不是云计算 WebAssembly 足以取代 PC 上本地安装的大多数应用程序。我们已经使用 WebGL(又名OpenGL ES 2.0)移植了游戏。我预测不久之后,受益于WebAssembly,像 LibreOffice 这样的大型应用可以直接从网站上获得,而无需安装。 在这种情况下,在本地安装应用没什么意义。本地安装的应用和 WebAssembly 应用之间几乎没有区别。WebAssembly 应用已经可以使用屏幕,键盘和鼠标进行交互。它可以在 2D 或 OpenGL 中进行图形处理,并使用硬件对视频流进行解码。可以播放和录制声音。可以访问网络摄像头。可以使用 WebSockets。可以使用 IndexedDB 存储大量数据在本地磁盘上。这些已经是 Web 浏览器中的标准功能,并且都可以使用 JavaScript 向 WebAssembly 暴露。 目前唯一困难的地方是 WebAssembly 无法访问本地文件系统。好吧,可以通过 HTML 使用文件上传对话,但这不算。最终,总会有人为此创建 API,并可能称之为 “WASI”。 “从互联网上运行应用程序!?胡说八道!“,你说。好吧,这是使用 Qt 和 WebAssembly 实现的文本编辑器 (以及更多)。 这是一个简单的例子。复杂的例子是在 WebBrowser 中运行的 Adobe Premier Pro 或 Blender。或者考虑像 Steam 游戏一样可以直接从网络上运行。这听起来像小说,但从技术上说这并非不能发生。 它会来的。 让我们裸奔! 目前,WebAssembly 在包含 HTML 和 Javascript 包装器的环境中执行。为什么不脱掉这些?有了 WebAssembly,为什么还要在浏览器中包含 HTML 渲染器和 JavaScript 引擎? 通过为所有服务提供标准化 API,这些服务通常是 Web 浏览器提供的,可以创建裸 WebAssembly。就是没有 HTML和 Javascript 包装来管理的 WebAssembly。访问的网页是 .wasm 文件,浏览器会抓取并运行该文件。浏览器为WebAssembly 模块提供画布,事件处理程序以及对浏览器提供的所有服务的访问。 这目前还不存在。如果现在使用 Web 浏览器直接访问 .wasm 文件,它会询问是否要下载它。我假设将设计所需的 API 并使其工作。 结果是 Web 可以发展。网站不再局限于 HTML,CSS 和 Javascript。可以创建全新的文档描述语言。可以发明全新的布局引擎。而且,对于像我这样的 polyglots 最相关,我们可以选择任何编程语言来实现在线服务。 可访问性 但我听到了强烈抗议!可访问性怎么样??搜索引擎怎么办? 好吧,我还没有一个好的答案。但我可以想象几种技术解决方案。 一个解决方案是我们保留内容和表现的分离。内容以标准化格式编写,例如 HTML。演示文稿由 WebAssembly 应用管理,该应用可以获取并显示内容。这允许网页设计师使用想要的任何技术进行任意演示 - 不需要 CSS,而搜索引擎和需要不同类型的可访问性的用户仍然可以访问内容。 请记住,许多 WebAssembly 应用并不是可以通过文本访问的,例如游戏和许多应用。盲人不会从图像编辑器中获得太多好处。 另一个解决方案是发明一个 API,它可以作为 WebAssembly 模块,来提供想在屏幕上呈现的 DOM,供屏幕阅读器或搜索引擎使用。基本上会有两种表示形式:一种是在图形画布上,另一种是产生结构化文本输出。 第三种解决方案是使用屏幕阅读器或搜索引擎可以使用的元数据来增强画布。执行 WebAssembly 并在画布上呈现内容,其中包含描述渲染内容的额外元数据。例如,该元数据将包括屏幕上的区域是否是菜单以及存在哪些选项,或者区域是否想要文本输入,以及屏幕上的区域的自然排序(也称为标签顺序)是什么。基本上,曾经在 HTML 中描述的内容现在被描述为具有元数据的画布区域。同样,这只是一个想法,它可能在实践中很糟糕。 可能是什么 1995年,Sun Microsystems 发布了 Java,带有 Java applets 和大量的宣传。有史以来第一次,网页可以做一些比 和 GIF 动画更有趣的事情。开发人员可以使应用完全在用户的 Web 浏览器中运行。它们没有集成到浏览器中,而是实现为繁重的插件,需要安装整个 JVM。1995年,这不是一个小的安装。applets 也需要一段时间来加载并使用大量内存。我们现在凭借大量内存,这不再是一个问题,但在 Java 生命的第一个十年里,它让体验变得令人厌烦。 applets 也不可靠。无法保证它们会运行,尤其是在用户使用 Microsoft 的实现时。他们也不安全,这是棺材里的最后一颗钉子。 以 JVM 为荣,其他语言最终演变为在 JVM 上运行。但现在,那艘船航行了。 FutureSplash / Macromedia / Adobe Flash 也是一个竞争者,但是是专有的,具有专有工具集和专有语言的专有格式。我读到他们确实在2009年开启了文件格式。最终从浏览器中删除了支持,因为它存在安全风险。 这里的结论是,如果希望您的技术存在于每个人的机器上,那么安全性就需要正视。我真诚地希望 WebAssembly 作为标准对安全问题做出很好的反应。 需要什么? WebAssembly 仍处于初期阶段。它目前能很好的运行代码,而规范版本是 1.0,二进制格式定型。目前正在开展SIMD 指令支持。通过 Web Workers 进行多线程处理也正在进行中。 工具可用,并将在未来几年不断改进。浏览器已经让你窥视 WebAssembly 文件。至少 Firefox 允许查看WebAssembly 字节码,设置断点并查看调用堆栈。我听说浏览器也有 profiling 支持。 语言支持包括一套不错的语言集合–C,C++和Rust是一流的公民。C#,Go和Lua显然有稳定的支持。Python,Scala,Ruby,Java和Typescript都有实验性支持。这可能是一个傲慢的陈述,但我真的相信任何想要在21世纪存在的语言都需要能够在 WebAssembly 上编译或运行。 在访问外部设备的 API 支持方面,我所知道的唯一可用于裸 WebAssembly 的 API 是 WASI,它允许文件和流访问等核心功能,允许 WebAssembly 在浏览器外运行。否则,任何访问外部世界的 API 都需要在浏览器中的 Javascript 中实现。除了本地机器上的文件访问,打印机访问和其他新颖的硬件访问(例如非标准蓝牙或USB设备)之外,应用所需的一切几乎都可以满足。“裸WebAssembly”并不是它成功的必要条件; 它只是一个小的优化,不需要浏览器包含对 HTML,CSS 或 Javascript 的支持。 我不确定在桌面环境中让 WebAssembly 成为一等公民需要什么。需要良好的复制和粘贴支持,拖放支持,本地化和国际化,窗口管理事件以及创建通知的功能。也许这些已经可以从网络浏览器中获得; 我经常惊讶与已经可能的事情。 引发爆炸的火花是创建允许现有应用移植的环境。如果创造了“用于 WebAssembly 的 Linux 子系统”,那么可以将大量现有的开源软件移植到 WebAssembly 上。它需要模拟一个文件系统 - 可以通过将文件系统的所有只读部分都缓存为 HTTP 请求来完成,并且所有可写部分都可以在内存中,远程存储或使用浏览器可以提供的任何文件访问。图形支持可以通过移植 X11 或 Wayland 的实现来使用 WebGL(我理解已经作为 AIGLX 存在?)。 一些 SDL 游戏已经被移植到 WebAssembly - 最着名的是官方演示。 一旦 JVM 在 WebAssembly 中运行,就可以在浏览器中运行大量的 Java 软件。同样适用于其他虚拟机和使用它们的语言。 与 Windows 软件的巨大世界一样,我没有答案。WINE 和 ReactOS 都需要底层的 x86 或 x86-64 机器,所以唯一的选择是获取源代码并移植它,或者使用 x86 模拟器。 尾声 WebAssembly 即将到来。 它来得很慢,但现在所有的部分都可以在你正在使用的浏览器上使用。现在我们等待构建用于从各种编程语言中定位 WebAssembly 的基础设施。一旦构建完成,我们将摆脱 HTML,CSS 和 Javascript 的束缚。 加入阿里云钉钉群享福利:每周技术直播,定期群内有奖活动、大咖问答 阿里云开发者社区

茶什i 2020-01-07 10:32:35 0 浏览量 回答数 0

问题

程序员报错QA大分享(1)

问问小秘 2020-06-18 15:46:14 8 浏览量 回答数 1

问题

网站技术职位之我见:报错

kun坤 2020-06-09 13:55:57 0 浏览量 回答数 1

回答

如何掌握牢靠Go语言的容器? 容器相对来说更偏重细节一些,如果想掌握的更牢靠的话呢,还是要多看一下代码,重点给大家几个提示 Go语言的并发初步有哪两个特别重要的特点? **GO语言的协程并发操作或者说协程的资源池,其调度策略有两个: ** 1、没有优先级,没有API能设置优先级,正是因为它一切都是靠Go语言自身的一个调度器来听调度,才能保证它的高效率,这点非常重要。 2、调度的策略是可抢占的,假如说一个任务它长时间的占用CPU,那么它是有可能被购入天的这个调度器给其抢占过来,让其其的任务来做运行,这是两个最重要的特点。 GO语言调度的单元goroutine的应用场景是什么? 使用JAVA或者C编写网络程序时,一个线程来处理一个http请求, 但是对于资源的利用率不高。而Go语言实现了轻量级线程的机制,GO语言在底层封装了所有的系统调用,自己实现了一个调度器,这种设计在操作系统的代码中非常多见。比如现代的操作系统基本都会封装一个软件的Timer,同时可以提供上万个软Timer同时工作,而这只是基于数量很少的硬件timer实现的,而GO语言中的并发也是如此,他是基于线程的调度池,这种调度的单元在Go语言中被称为goroutine。 GO语言与其它并发模型最大的区别是什么? 宏观GO语言与其它并发模型最大的不同,就是其推荐使用通信的这种方式来替代共享内存。当资源需要在goroutine之间进行共享的时候,实际上就是这个资源,或者说这个信息通过通道在goroutine之间进行通信的过程。因为这个锁,一般来说都是用在这个共享内存当中的,因为如果说大家阅读GO语言的相关代码,就可以看到这个channel,它实际上是基于锁来保证并发安全。 然而,这也不代表GO语言当中只能使用channel来进行一些操作,其也具备锁这方面的知识。因为现实当中,这个锁还是有一定它现实的意义和现实的要求,因为这个锁它最关键的一个意义就是它能保证资源能在并发的操作当中有一个合理的调度情况和调度策略。其中跟这个最重要,或者说最关联性最强的一个概念就是原子操作。 GO语言中的原子操作具体实现过程是怎样的? 对于原子操作,在其逻辑下,按照它书面的定义上来讲,是指不会被调度器打断的操作。对原子操作实际上就是不存在中间状态的一种操作,要不就全成功,要不全失败,这个在我们在用并发方式来调动某任务,或者说来设计某种并发系统的情况下,这种名字操作我发现是非常重要的设计理念之一。 并发与并行具体概念及实际区分是怎样的? 有一个比较重要的一个概念,就是并发与并行,其实并发与并行,它实际上具体的含义是不一样的,并发实际上是把任务在不同的时间点交给同样一个处理器来进行处理,在同一个时间点,任务不会同时进行,只是任务感觉自己正在执行,因为其那会儿可能正在堵塞状态或者说是就绪状态,其不知道自己被暂停了,以为已经被调度走了,可能自己没有感知,但是实际上CPU所有权已经不在这个任务身上了。 并行比并发更高级一些,它实际上是把每个任务都交给独立的处理器去进行完成,但同一时间点,任务在一定程度上实际上是同时在执行的。一般来说,并发的性能是要比并行更重要一些,在1.5版本之前,我们需要人工去设置GO调度器最多能运行在多少个CPU上,但是在最新的GO版本当中,已经不需要这个相关的操作。 详细介绍一下并发程序中的竞争态? 并发系统设计最初始的这一个概念就是并发程序设计当中一个竞合的概念,或者也叫竞争态。假如说我要记录一个文件的阅读量,但是这个文件或者说这个网页,可能它的阅读渠道有非常多,有可能通过引擎通过微信通过APP等等这些渠道,这些渠道的话呢,它的阅读也都是并发的,这就会涉及到同样一个变量,被多个协程的所共同访问的情况。具体代码如下: 对于GO语言并发体系中的主推的通信机制是什么? channel是GO语言并发体系中的主推的通信机制,它可以让一个 goroutine 通过它给另一个 goroutine 发送值信息。每个 channel 都有一个特殊的类型,也就是 channels 可发送数据的类型。一个可以发送 int 类型数据的 channel 一般写为 chan int。 GO语言当中,它实际上是大家协同的机制,通过这种方式让几个goroutine之间做达到一个协调的效果,那么每个goroutine当中,实际上channel都是一个特殊的类型,它实际上是可以发送数据。比如现在想发送一个int类型的数据,那么channel就要定义一个发送int数据的一个管道。 那么GO语言当中,提倡使用通讯的方式来代替共享内存的方式来做goroutine,或者说并发之间的一个协同。channel如果我们后续阅读它的代码就会知道,它是保证协程安全,并且它遵循这个先入先出的原则来让这个储蓄方读取获得数据,而且它能保证顺序,正是这两个特性,可以让这个channel替代共享内存,因为它的如果顺序有所改变的话,它实际上也是有会有问题。 详细介绍GO语言中关于通道的声明涉及哪些方面? 1.经典方式声明 通过使用chan类型,其声明方式如下: var name chan type 其中type表示通道内的数据类型;name:通道的变量名称,不过这样创建的通道只是空值 nil,一般来说都是通道都是通过make函数创建的。 2.make方式 make函数可以创建通道格式如下: name := make(chan type) 3.创建带有缓冲的通道 后面会讲到缓冲通道的概念,这里先说他的定义方式 name := make(chan type, size) 其中type表示通道内的数据类型;name:通道的变量名称,size代表缓冲的长度。 具体介绍通道数据收发的详细过程有哪些? 通道的数据发送 通道当中发送数据的操作服务是这样的这样的一个大于号加上一个减号。 chan <- value 注意,如果是发送给一个没有缓冲的一个通道。假如说数据没有被接收的话,那么这个发送操作将持续被注册,也就是说就是channel这个语句就直接被注册到这,假如说没有任何的协程去读到他或者其他语句去读到这个产品,那么这个语句就被注册掉了。但GO语言是能发现的,如果其一直在堵塞的话,那实际上就造成死锁,GO语言的编译器实际上能发现的有点错误。 假如说,首先创建一个int型的通道,然后直接尝试发送一个数据给它,编译会报错,然后呢,数据的这个数据的接收的话,实际上就是把这个点号的位置跟那个大于号的位置做了一个调换。其实把这个双方的位置做了一个调换之后,是实际上就是都做了一个允许的操作。这其中的话呢,还有一种比较特殊的一个读取操作是其可以忽略到接收到的数据,因为不管管道中发出的数据,如果没读的话就堵塞到这,那么如果你觉得这个语句你也不需要,那么你可以把那个变量给它忽略掉。 2.通道的数据接收 通道接收数据的操作符也是<-,具体有以下几种方式 - 1) 阻塞接收数据 阻塞模式接收数据时,将接收变量作为<-操作符的左值,格式如下: data := <-ch 执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。 如需要忽略接收的数据,则将data变量省略,具体格式如下: <-ch - 2) 非阻塞接收数据 使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下: data, ok := <-ch 非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。一般只配合select语句配合定时器做超时检测时使用。 关于通道数据收发有哪些需要注意的事项? 通道数据在进行输入收发的时候,必须要在两个不同的goroutine当中进行,因在同一个goroutine当中,收发的这些语句实际上都是堵塞的,你可能在同一个goroutine当中,它的这个函数已经在那边阻塞住了,或者说程序已经在那边阻塞住了,它已经停在那了,你后面有一句你能执行不到,所以说通道的收发必须在两个不同的goroutine之间来进行,在同一个goroutine之间的这个收发操作的话,实际上是没有意义的。 接收将持续堵塞,直到发送方发送出去,如果接收方接收,然后通道中没有发送方数据时,接收方也会发送,直到发送方到发送数据为止。就是刚才说的这个一体两面,这个发送方假如说没有人读的话,发送方会堵塞,假如说没有人写的话,那么接收方也会发生堵塞,这两边实际上都会有一个堵塞的情况。那么这个通道的收发的话呢,一般来说一次只能收一一个元素,假如说这个是一个有缓冲的一个通道,我通过一次不操作的话,实际上也只不过读出一个元素。不能把它一些缓冲区所有元素都读出来。 聊一下生产者消费者模式具体内容有哪些? 介绍一下生产者消费者模式,从GO语言的这个并发模型来看,也就是说假如说咱们站在一个比较高的一个高度来看,其实利用channel的确能达到共享内存的目的。这个channel的性质与在读写状态且保证顺序的共享内存并无不同。甚至我们可以说这个是基于消息队列的封装程度可以比共享内存来的更安全,所以说呢,这个在这个GO语言当中,或者说在GO语言的这个设计风格当中的话呢,其这个生产者消费者模式实现起来会相对来说比较简单一些。我们先介绍一下什么是生产者消费者。 就这个这这张图当中的话呢,就是一个典型的那种消费的问题, 就是说我是生产者的话我会生产一些产品,然后放到这个仓库当中,消费者的话会从那个仓库当中去取商品,这个可以说是消息队列,还有包括卡夫卡那些比较经典的相应队列当中,都会用到的这么一个设计模式,或者说其们从本质上来说的话,都是基于这样一个设计模式,交易的生产者是谁?消费者是谁?这个消息队列的话是。这个生产者消费者模式的话呢,实际上也成为有缓冲有限缓冲问题,它是一个并发的一个经典的案例,因为我们知道这个商品仓库的库房大小是有限的,也就是说生产者不能无限的去生产商品,一旦这个库房爆掉的话,它是它是必须要中止自己的生产,消费者也是不能无限地获取消息。 假如仓库是空的话,那这个消费者的这个相关的情况也需要被阻塞。那么怎么在这个生产者跟消费者之间保证商品不丢失。这就是生产者与消费者之间最核心的内容。先来看一下这个Java当中生产者消费者的这种实现到底是什么样的。这个可以说是一个最经典的这么样一个实现。这个Java当中是没有channel,那么它只能通过什么呢,只能通过信号量和一个一个log,也就是说一个忽视服务态度,这两个这两个配合信号量和所配合才能共同完成,这样一个生产者消费者这么一个相关的工作。 GO语言并发实战详细过程梳理 在现在这个远程办公的这一个大的背景下,积累了大量重复的文件,因为很可能大家都不断的在不同的群里发相同的文件,发相同的这个报表,以及一些相同的视频等等这些需要学习的材料,那么怎么把这些文件都找出来,然后把这些相同文件都给删掉了,这实际上是并发课的一个实践的一个内容,因为这个创业型的这个方案的话,它的代码相对来说比较长。 如何使用GO语言清理PC机中的文件,详细代码及注释如下: package main import ( // "fmt" // fmt 包使用函数实现 I/O 格式化(类似于 C 的 printf 和 scanf 的函数), 格式化参数源自C,但更简单 "io/ioutil" //"sync" //"time" ) func PrintRepreatFile(path string, fileNameSizeMap map[string]int64, exFileList []string) { fs, _ := ioutil.ReadDir(path) for _, file := range fs { if file.IsDir() { PrintRepreatFile(path+"/"+file.Name(), fileNameSizeMap, exFileList)//遍历整个文件系统,如果是目录则递归调用 } else { if file.Size() > 1000000 {//设定文件清理阈值,如果大于一定大小再进行清理 fileSize := fileNameSizeMap[file.Name()]//通过查哈希表的方式来确定,有无重名且大小相同的文件。 if fileSize == file.Size() { fmt.Println(path + "/" + file.Name())//如果有则打印出来 exFileList = append(exFileList, path+file.Name())//将结果记入切片当中 } else { fileNameSizeMap[file.Name()] = file.Size() } } } } } func main() { //方式一 fileNameSizeMap := make(map[string]int64, 10000) exFileList := make([]string, 100, 1000) PrintRepreatFile("E:/test", fileNameSizeMap, exFileList) } 这个程序在GO语言的环境下可以直接运行使用,其中有几个知识点,也是咱们前文提到过的,首先是切片的大小一定要设定的相对合适一些,如果容量不够大造成频繁扩容非常浪费资源。二是哈希表也就是map没有并发安全的属于,在我们这个未引入并发的程序中可以使用,如果有并发操作,那么map不再适用了。 可能很多人被GO语言的在并发性能所吸引入坑的,GO语言之父也就是UNIX之父Ken Thompson明显给出了很多建议,根据笔者在操作系统方面的相关经验来看,GO语言设计中经常参考UNIX内核的设计思路。比如硬定时器的数量有限,无法满足系统实际运行需要,所以在内核代码中就会看到基于硬件定时器的软件定时器的方案,而软件定时器的数量可以比硬件定时器多几百倍。 这样的理念明显融合到了 goroutine之中,由于其它编程语言往往直接通过系统级别的线程来实现并发功能,但是这样的方式往往会是大马拉小车,造成系统资源的浪费。因此GO语言封装了所有的系统操作,实现了更加轻量级的协程-goroutine。只要使用关键字(go)就可以启动协程,对比C++、JAVA的多线程并发模型,GO的协程更简单明了。 当然协程之间的消息通信与并发控制也是非常重要的一环。在GO语言借鉴了Message Queue的消息队列机制替代共享内存的方式进行协程间通信,其中管道channel作为基本的数据类型,保证并发时的操作安全。而且管道的引入还带来很多实践中非常实用的功能,比如可以方便实现生产者、消费者等并发设计模式,而这些设计模式在其它使用共享存内存的并发模型中实现起相关功能来非常的繁锁。 在GO语言中在调用函数前加入go 关键字,就能启动一个协程,也就是一个并发,但是我们上面的程序如果把调用方式改为: go PrintRepreatFile("E:/test", fileNameSizeMap, exFileList) 你会发现程序会直接退出,什么都没做,所以GO语言的并发对于初学者来说还是有一定门槛的,比如上例中如果想设计成一个并行的程序,如何让多个协程共同来帮忙找出重复的文件其实还是要费一番周折的。

剑曼红尘 2020-04-13 11:06:46 0 浏览量 回答数 0

回答

把配置文件拿出来看看? ###### 引用来自“李玉珏”的评论把配置文件拿出来看看? <?xml version="1.0" encoding="UTF8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration"> <!-- Set to true to enable distributed class loading for examples, default is false. --> <property name="peerClassLoadingEnabled" value="true"/> <property name="discoverySpi"> <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi"> <property name="ipFinder"> <!-- Ignite provides several options for automatic discovery that can be used instead os static IP based discovery. For information on all options refer to our documentation: http://apacheignite.readme.io/docs/cluster-config --> <!-- Uncomment static IP finder to enable static-based discovery of initial nodes. --> <!--<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">--> <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder"> <property name="addresses"> <list> <!-- In distributed environment, replace with actual host IP address. --> <value>127.0.0.1:48500..48509</value> </list> </property> </bean> </property> </bean> </property> <!-- Explicitly configure TCP communication SPI changing local port number for the nodes from the first cluster. --> <property name="communicationSpi"> <bean class="org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi"> <property name="localPort" value="48100"/> </bean> </property> <!-- Enable task execution events for examples. --> <property name="includeEventTypes"> <list> <!--Task execution events--> <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_STARTED"/> <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_FINISHED"/> <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_FAILED"/> <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_TIMEDOUT"/> <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_SESSION_ATTR_SET"/> <util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_REDUCED"/> <!--Cache events--> <util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_PUT"/> <util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_READ"/> <util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_REMOVED"/> </list> </property> </bean> <bean id="igniteCacheFactory" class="com.ignite.IgniteCacheFactory"> <constructor-arg index="0" ref="typesMap" /> <constructor-arg index="1" ref="queryEntitiesMap" /> <constructor-arg index="2" ref="ignite.cfg" /> <constructor-arg index="3" value="REPLICATED" /> </bean> <bean id="typesMap" class="com.ignite.TypesMap"> <property name="list"> <list> <bean class="org.apache.ignite.cache.store.jdbc.JdbcType"> <property name="databaseSchema" value="mishu"/> <property name="databaseTable" value="cert_aqrz_copy"/> <property name="keyType" value="com.toubiaomishu.mojo.CertAqrzCopyKey"/> <property name="valueType" value="com.toubiaomishu.mojo.CertAqrzCopy"/> <property name="keyFields"> <list> <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField"> <property name="databaseFieldType"> <util:constant static-field="java.sql.Types.VARCHAR"/> </property> <property name="databaseFieldName" value="srcUUid"/> <property name="javaFieldType" value="java.lang.String"/> <property name="javaFieldName" value="srcuuid"/> </bean> </list> </property> <property name="valueFields"> <list> <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField"> <property name="databaseFieldType"> <util:constant static-field="java.sql.Types.VARCHAR"/> </property> <property name="databaseFieldName" value="srcUUid"/> <property name="javaFieldType" value="java.lang.String"/> <property name="javaFieldName" value="srcuuid"/> </bean> <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField"> <property name="databaseFieldType"> <util:constant static-field="java.sql.Types.VARCHAR"/> </property> <property name="databaseFieldName" value="secureLevel"/> <property name="javaFieldType" value="java.lang.String"/> <property name="javaFieldName" value="securelevel"/> </bean> <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField"> <property name="databaseFieldType"> <util:constant static-field="java.sql.Types.VARCHAR"/> </property> <property name="databaseFieldName" value="secureRank"/> <property name="javaFieldType" value="java.lang.String"/> <property name="javaFieldName" value="securerank"/> </bean> <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField"> <property name="databaseFieldType"> <util:constant static-field="java.sql.Types.VARCHAR"/> </property> <property name="databaseFieldName" value="yxqTime"/> <property name="javaFieldType" value="java.lang.String"/> <property name="javaFieldName" value="yxqtime"/> </bean> <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField"> <property name="databaseFieldType"> <util:constant static-field="java.sql.Types.VARCHAR"/> </property> <property name="databaseFieldName" value="zhUUid"/> <property name="javaFieldType" value="java.lang.String"/> <property name="javaFieldName" value="zhuuid"/> </bean> </list> </property> </bean> </list> </property> </bean> <bean id="queryEntitiesMap" class="com.ignite.QueryEntitiesMap"> <property name="list"> <list> <bean class="org.apache.ignite.cache.QueryEntity"> <property name="keyType" value="com.toubiaomishu.mojo.CertAqrzCopyKey"/> <property name="valueType" value="com.toubiaomishu.mojo.CertAqrzCopy"/> <property name="fields"> <util:map map-class="java.util.LinkedHashMap"> <entry key="srcuuid" value="java.lang.String"/> <entry key="securelevel" value="java.lang.String"/> <entry key="securerank" value="java.lang.String"/> <entry key="yxqtime" value="java.lang.String"/> <entry key="zhuuid" value="java.lang.String"/> </util:map> </property> <property name="indexes"> <list> <bean class="org.apache.ignite.cache.QueryIndex"> <property name="name" value="PRIMARY"/> <property name="indexType"> <util:constant static-field="org.apache.ignite.cache.QueryIndexType.SORTED"/> </property> <property name="fields"> <map> <entry key="srcuuid" value="true"/> </map> </property> </bean> <bean class="org.apache.ignite.cache.QueryIndex"> <property name="name" value="srcUUid"/> <property name="indexType"> <util:constant static-field="org.apache.ignite.cache.QueryIndexType.SORTED"/> </property> <property name="fields"> <map> <entry key="srcuuid" value="true"/> <entry key="yxqtime" value="true"/> <entry key="zhuuid" value="true"/> </map> </property> </bean> </list> </property> </bean> </list> </property> </bean> <!-- Datasource for sample in-memory mysql database. --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mishu?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/> <property name="username" value="root"/> <property name="password" value="5201128blue"/> <!-- 连接池启动时的初始值 --> <property name="initialSize" value="10"/> <!-- 连接池的最大值 --> <property name="maxActive" value="100"/> <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 --> <property name="maxIdle" value="10"/> <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 --> <property name="minIdle" value="5"/> <property name="removeAbandoned" value="true"/> <property name="removeAbandonedTimeout" value="10"/> <property name="testWhileIdle"> <value>true</value> </property> <property name="testOnBorrow"> <value>true</value> </property> <property name="testOnReturn"> <value>true</value> </property> <property name="validationQuery"> <value>SELECT 1</value> </property> <property name="validationQueryTimeout"> <value>1</value> </property> <property name="timeBetweenEvictionRunsMillis"> <value>3600000</value> </property> <property name="minEvictableIdleTimeMillis"> <value>60000</value> </property> <property name="numTestsPerEvictionRun"> <value>5</value> </property> </bean> </beans> package com.ignite; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; import org.apache.ignite.Ignition; import org.apache.ignite.cache.CacheMode; import org.apache.ignite.cache.QueryEntity; import org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStoreFactory; import org.apache.ignite.cache.store.jdbc.JdbcType; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; //这个类根据JdbcType和QueryEntity创建缓存 public class IgniteCacheFactory { public IgniteCacheFactory(TypesMap typesMap,QueryEntitiesMap queryEntitiesMap,IgniteConfiguration conf,CacheMode cacheMode){ init(typesMap.getList(),queryEntitiesMap,conf,cacheMode); } public void init(List<JdbcType> list,QueryEntitiesMap queryEntitiesMap,IgniteConfiguration conf,CacheMode cacheMode) { CacheConfiguration[] cacheConflist = new CacheConfiguration[list.size()]; for(int i=0;i<list.size();i++){ JdbcType qe=list.get(i); String cacheName=qe.getDatabaseTable(); qe.setCacheName(cacheName); String valueType=qe.getValueType(); CacheConfiguration cfg=IgniteCacheConfigurationFactory.create(cacheName); CacheJdbcPojoStoreFactory sf= new CacheJdbcPojoStoreFactory(); sf.setDataSourceBean("dataSource"); sf.setTypes(qe); cfg.setCacheMode(cacheMode);//CacheMode.PARTITIONED cfg.setCacheStoreFactory(sf); cacheConflist[i]=cfg; List<QueryEntity> qlist=new ArrayList<>(); qlist.add(queryEntitiesMap.get(valueType)); cfg.setQueryEntities(qlist); } conf.setCacheConfiguration(cacheConflist); System.out.println("----------------->>------------------"); } } ######你开启那么多事件做什么呢?###### 调用任务代码,先加载,在执行分布式任务: public static void main(String[] args) throws IgniteException{ //Ignition.setClientMode(true); Ignite ignite=Ignition.start("ignite2.xml"); IgniteCluster cluster = ignite.cluster(); // 启动之后调用,直接加载所有定的表数据进缓存 Object[] list= (Object[]) ignite.cacheNames().toArray(); for(int i=0;i<list.length;i++){ String cacheName=String.valueOf(list[i]); IgniteCache cahce= ignite.cache(cacheName); cahce.loadCache(null); } CreditCalculate c=new CreditCalculate(); c.setGc(3); c.setSc(5); c.setGb(new BigDecimal(20)); c.setSb(new BigDecimal(20)); c.setSqlstr("(SELECT.... THEN ...."); c.setAq("1"); c.setIdd(new BigDecimal(3)); c.setProjTy("房程"); c.setXydjDqq("xydjDqq"); IgniteCompute compute = ignite.compute(cluster.forRemotes()); // Execute task on the clustr and wait for its completion. List<String> cnt = compute.execute(CreditCalculateTask.class,c); //System.out.println(">>> Total number of characters in the phrase is '" + cnt.size() + "'."); //System.out.println(">>>"+ cnt.get(0)); System.out.println("----------------mapreduce end-------------------"); } ###### 引用来自“李玉珏”的评论把配置文件拿出来看看? 半夜醒来:会不会是因为我缓存是在任务外声明的,所以执行任务是引用都是同一台机器上的缓存 // Inject Ignite instance. @IgniteInstanceResource private Ignite ignite; IgniteCache certSrcCache = ignite.cache("cert_src");//******这里声明 @Override public List<ComputeJob> split(int gridSize, CreditCalculate arg) { List<ComputeJob> jobs = new ArrayList<>(); SqlQuery sql2 = new SqlQuery(CertSrc.class, "from ..."); QueryCursor<Cache.Entry<Long, CertSrc>> companyList = certSrcCache.query(sql2); ... for (Cache.Entry<Long, CertSrc> e : companyList) { jobs.add(new ComputeJobAdapter() { @Override public Object execute() { .... SqlFieldsQuery sql = new SqlFieldsQuery(sql1); try (QueryCursor<List<?>> cursor = certSrcCache.query(sql)) {//*******这里在ComputeJobAdapter内部引用 ######回复 @fir01 : 如果默认的端口被占用的话,会使用一个随机的端口,我估计你看看节点启动时的控制台输出应该会有的。######回复 @fir01 : 任务数没有数量限制,这个看你的内存配置了。######回复 @李玉珏 : 端口好像是随机的,怎么从本机访问远程机器的h2控制台,或者在哪里配置固定的端口呢?######回复 @李玉珏 : 谢谢回复。好像是搞定了,估计mapReduce有bug或者要配置,在ubuntu里面就是单线程的,windows是多线程。换executorService就都可以异步多线程了,但是ubuntu里面执行看打印还是会执行一会就停个6秒,为什么呢?是不是存放任务的队列有大小限制?任务总数10823######回复 @fir01 : SHARED是默认的部署模式。###### 我想我还要测试一下是不是我本机发送任务速度慢。7519个任务1秒钟就发送完毕。######任务数不要太多,你可以换一个维度,控制下任务的总数量,大量的任务处于排队状态,是很占用资源的。######都是用ignite自己的broadcast方法异步发布任务就又快又流畅,无卡顿现象。看样子非官方的,说可以用的东西最好别用。

kun坤 2020-06-06 23:16:57 0 浏览量 回答数 0

问题

Web测试方法

技术小菜鸟 2019-12-01 21:41:32 7022 浏览量 回答数 1
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 SSL证书 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站 2020中国云原生 阿里云云栖号