• [技术干货] CSE Java SDK包名切换指导
    背景信息CSE Java SDK的开源部分ServiceComb项目已于2017年12月全票通过进入Apache孵化器,根据Apache软件基金会要求,项目groupId统一由io.servicecomb调整为org.apache.servicecomb。CSE Java SDK作为ServiceComb的商业版本,除提供对接华为公有云的能力、安全、分布式数据一致性等商业能力外,其大部分组件来自于开源的ServiceComb。因此提供本指导用于支持客户顺利升级至包名变更后的CSE Java SDK版本(2.3.3+)。更改说明CSE Java SDK版本(2.3.3+)中涉及包名变化情况如下:序号包名称groupId(修改前)groupId(修改后)变化类型1CSE Java-SDK开源包65个包名变更,详见CSE Java SDK开源包列表io.servicecomborg.apache.servicecomb修改2CSE Java-SDK开源包foundation-config-cc新增config-cc,groupId为org.apache.servicecomb新增3CSE Java-SDK商业包foundation-config-cc移除foundation-config-cc删除4CSE Java-SDK商业包共计13个,详见CSE Java SDK商业包列表com.huawei.paas.cse无变化操作步骤通过配置maven setting文件以获取SDK依赖。profiles中增加如下配置。<profile>     <id>nexusProfile</id>     <repositories>         <repository>             <id>cse1</id>             <url>http://maven.huaweicse.com/nexus/content/groups/public/</url>         </repository>     </repositories> </profile>新增activeProfiles配置。<activeProfiles>     <activeProfile>nexusProfile</activeProfile> </activeProfiles>引入dependencyManagement,建议开发者在pom.xml中进行如下配置以便更好的管理三方件。 说明:版本使用2.3.3以上版本。<dependencyManagement>     <dependencies>         <dependency>             <groupId>com.huawei.paas.cse</groupId>             <artifactId>cse-dependency</artifactId>             <version>2.3.58</version>             <type>pom</type>             <scope>import</scope>         </dependency>     </dependencies> </dependencyManagement>修改pom.xml中依赖包的groupid。此处仅以引入对spring-boot-starter-provider、cse-solution-service-engine、foundation-auth的依赖为例。修改前<dependency>     <groupId>io.servicecomb</groupId>     <artifactId>spring-boot-starter-provider</artifactId> </dependency> <dependency>     <groupId>com.huawei.paas.cse</groupId>     <artifactId>cse-solution-service-engine</artifactId> </dependency> <dependency>     <groupId>com.huawei.paas.cse</groupId>     <artifactId>foundation-auth</artifactId>     <exclusions>         <exclusion>             <groupId>org.slf4j</groupId>             <artifactId>slf4j-log4j12</artifactId>         </exclusion>     </exclusions> </dependency>修改后<dependency>     <groupId>org.apache.servicecomb</groupId>     <artifactId>spring-boot-starter-provider</artifactId> </dependency> <dependency>     <groupId>com.huawei.paas.cse</groupId>     <artifactId>cse-solution-service-engine</artifactId> </dependency> <dependency>     <groupId>com.huawei.paas.cse</groupId>     <artifactId>foundation-auth</artifactId>     <exclusions>         <exclusion>             <groupId>org.slf4j</groupId>             <artifactId>slf4j-log4j12</artifactId>         </exclusion>     </exclusions> </dependency>CSE Java SDK开源包列表序号artifactIdgroupid(修改前)groupid(修改后)1common-javassistio.servicecomborg.apache.servicecomb2common-protobufio.servicecomborg.apache.servicecomb3common-restio.servicecomborg.apache.servicecomb4commonio.servicecomborg.apache.servicecomb5config-apolloio.servicecomborg.apache.servicecomb6dynamic-configio.servicecomborg.apache.servicecomb7edge-coreio.servicecomborg.apache.servicecomb8edgeio.servicecomborg.apache.servicecomb9foundation-commonio.servicecomborg.apache.servicecomb10config-ccio.servicecomborg.apache.servicecomb11foundation-configio.servicecomborg.apache.servicecomb12foundation-metricsio.servicecomborg.apache.servicecomb13foundation-sslio.servicecomborg.apache.servicecomb14foundation-test-scaffoldingio.servicecomborg.apache.servicecomb15foundation-vertxio.servicecomborg.apache.servicecomb16foundationsio.servicecomborg.apache.servicecomb17handler-bizkeeperio.servicecomborg.apache.servicecomb18handler-flowcontrol-qpsio.servicecomborg.apache.servicecomb19handler-loadbalanceio.servicecomborg.apache.servicecomb20handler-publickey-authio.servicecomborg.apache.servicecomb21handler-tracing-zipkinio.servicecomborg.apache.servicecomb22handlersio.servicecomborg.apache.servicecomb23java-chassis-coreio.servicecomborg.apache.servicecomb24java-chassis-dependenciesio.servicecomborg.apache.servicecomb25java-chassis-parentio.servicecomborg.apache.servicecomb26java-chassisio.servicecomborg.apache.servicecomb27metrics-commonio.servicecomborg.apache.servicecomb28metrics-coreio.servicecomborg.apache.servicecomb29metrics-extensionio.servicecomborg.apache.servicecomb30metrics-integrationio.servicecomborg.apache.servicecomb31metrics-prometheusio.servicecomborg.apache.servicecomb32metricsio.servicecomborg.apache.servicecomb33provider-jaxrsio.servicecomborg.apache.servicecomb34provider-pojoio.servicecomborg.apache.servicecomb35provider-rest-commonio.servicecomborg.apache.servicecomb36provider-springmvcio.servicecomborg.apache.servicecomb37providersio.servicecomborg.apache.servicecomb38service-registryio.servicecomborg.apache.servicecomb39spring-boot-starter-configurationio.servicecomborg.apache.servicecomb40spring-boot-starter-discoveryio.servicecomborg.apache.servicecomb41spring-boot-starter-parentio.servicecomborg.apache.servicecomb42spring-boot-starter-providerio.servicecomborg.apache.servicecomb43spring-boot-starter-registryio.servicecomborg.apache.servicecomb44spring-boot-starter-servicecombio.servicecomborg.apache.servicecomb45spring-boot-starter-transportio.servicecomborg.apache.servicecomb46spring-cloud-zuul-zipkinio.servicecomborg.apache.servicecomb47spring-cloud-zuulio.servicecomborg.apache.servicecomb48swagger-generator-coreio.servicecomborg.apache.servicecomb49swagger-generator-jaxrsio.servicecomborg.apache.servicecomb50swagger-generator-springmvcio.servicecomborg.apache.servicecomb51swagger-generatorio.servicecomborg.apache.servicecomb52swagger-invocation-coreio.servicecomborg.apache.servicecomb53swagger-invocation-jaxrsio.servicecomborg.apache.servicecomb54swagger-invocation-springmvcio.servicecomborg.apache.servicecomb55swagger-invocationio.servicecomborg.apache.servicecomb56swaggerio.servicecomborg.apache.servicecomb57tracing-commonio.servicecomborg.apache.servicecomb58tracing-zipkinio.servicecomborg.apache.servicecomb59tracingio.servicecomborg.apache.servicecomb60transport-highwayio.servicecomborg.apache.servicecomb61transport-rest-clientio.servicecomborg.apache.servicecomb62transport-rest-servletio.servicecomborg.apache.servicecomb63transport-rest-vertxio.servicecomborg.apache.servicecomb64transport-restio.servicecomborg.apache.servicecomb65transportsio.servicecomborg.apache.servicecombCSE Java SDK商业包列表增删说明序号artifactIdgroupid(不涉及修改)1foundation-config-cc已迁移至ServiceCombCSE Java-SDK商业包列表序号artifactIdgroupid(不涉及修改)1cse-adapter-springmvccom.huawei.paas.cse2cse-dependencycom.huawei.paas.cse3cse-handler-2pccom.huawei.paas.cse4cse-handler-cloud-extensioncom.huawei.paas.cse5cse-handler-performance-statscom.huawei.paas.cse6cse-handler-tcccom.huawei.paas.cse7cse-handler-tracing-apmcom.huawei.paas.cse8cse-handler-tracingcom.huawei.paas.cse9cse-narayanacom.huawei.paas.cse10cse-solution-service-enginecom.huawei.paas.cse11cse-solutionscom.huawei.paas.cse12foundation-authcom.huawei.paas.cse13paas-csecom.huawei.paas.cse
  • [交流吐槽] 开发者文档里,提到的java mqtt sdk何时能提供
    我手上有树莓派,想知道如何接入OC平台。提交工单后,反馈sdk不提供
  • [技术干货] CSE JAVA SDK: java.lang.IllegalStateException: Response is closed 原因和定位
    当有些业务处理比较耗时的时候,日志里面经常打印如下异常:2019-01-09 10:42:22,890 [ntloop-thread-0] ERROR Unhandled exception [io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:345)]java.lang.IllegalStateException: Response is closedat io.vertx.core.http.impl.HttpServerResponseImpl.checkValid(HttpServerResponseImpl.java:548)at io.vertx.core.http.impl.HttpServerResponseImpl.end0(HttpServerResponseImpl.java:401)at io.vertx.core.http.impl.HttpServerResponseImpl.end(HttpServerResponseImpl.java:319)at org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse.internalFlushBuffer(VertxServerResponseToHttpServletResponse.java:122)at org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse.lambda$flushBuffer$0(VertxServerResponseToHttpServletResponse.java:112)at io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:339)at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463)at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)at java.lang.Thread.run(Thread.java:745)这个异常表示的是在给请求返回结果的时候,写结果发现连接已经关闭。这种情况通常发生在业务处理比较长的情况。这个日志在服务端打印(服务端是相对的)。和这个问题相关的配置由如下几个:客户端:cse.rest.client.connection.idleTimeoutInSeconds(缺省值30s)服务端:cse.rest.server.connection.idleTimeoutInSeconds(缺省值30s)如果业务处理时间超过这两个值的最小值,那么服务端就会报告这个异常。 
  • [技术干货] Mismatched content. Msg=Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
    在使用CSE开发REST接口,并使用postman或者客户端调用,经常出现:Mismatched content. Msg=Cannot deserialize instance of `java.lang.String` out of START_OBJECT token的错误。 这个错误的含义是将HTTP body转换为 REST接口定义的参数发生的,抛出异常的是jackson API。 2018-12-22 09:49:01.440 ERROR 3048 --- [pool-1-thread-1] o.a.s.common.rest.codec.RestCodec        : Parameter is not valid for operation cse-reporting-service.ReportingEndpoint.addAlarm. Message is cause:MismatchedInputException,message:Cannot deserialize instance of `java.lang.String` out of START_OBJECT token at [Source: (org.apache.servicecomb.foundation.vertx.stream.BufferInputStream); line: 1, column: 1].2018-12-22 09:49:01.447 ERROR 3048 --- [pool-1-thread-1] o.a.s.c.rest.AbstractRestInvocation      : unknown rest exception.org.apache.servicecomb.swagger.invocation.exception.InvocationException: InvocationException: code=400;msg=CommonExceptionData [message=Parameter is not valid.]at org.apache.servicecomb.common.rest.codec.RestCodec.restToArgs(RestCodec.java:72) ~[common-rest-1.1.0.B030.jar:1.1.0.B030]at org.apache.servicecomb.common.rest.filter.inner.ServerRestArgsFilter.afterReceiveRequest(ServerRestArgsFilter.java:60) ~[common-rest-1.1.0.B030.jar:1.1.0.B030]at org.apache.servicecomb.common.rest.AbstractRestInvocation.prepareInvoke(AbstractRestInvocation.java:226) [common-rest-1.1.0.B030.jar:1.1.0.B030]at org.apache.servicecomb.common.rest.AbstractRestInvocation.invoke(AbstractRestInvocation.java:206) [common-rest-1.1.0.B030.jar:1.1.0.B030]at org.apache.servicecomb.common.rest.AbstractRestInvocation.runOnExecutor(AbstractRestInvocation.java:196) [common-rest-1.1.0.B030.jar:1.1.0.B030]at org.apache.servicecomb.common.rest.AbstractRestInvocation.lambda$scheduleInvocation$0(AbstractRestInvocation.java:159) [common-rest-1.1.0.B030.jar:1.1.0.B030]at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) ~[na:1.8.0_111]at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) ~[na:1.8.0_111]at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_111]下面解读下这个错误:"Cannot deserialize instance of `java.lang.String`" 表面目标类型是一个String类型; "out of START_OBJECT token"表面HTTP body的内容,是一个对象,比如{"name":"bobo"}String的json表示是需要用引号的,所以就报了上述错误。正确的传递参数应该是:"This is a String Json content."或者:"{\"abcd\":\"abcd\"}"
  • [分享交流] 一对一直播源码,Java 部分的案例剖析
      在java内部已有内置的观察者模式,如类 java.util.Observable和类java.util.Observer,即是被观察者和观察者。在 java.util.Observable 中,存储观察者对象的容器是Vector,此容器支持动态扩展和同步性,用法与ArrayList类似。Observable内部方法如下所示:      一对一直播源码,Java观察者模式案例简析观察者模式的内在原理:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。即:通过将一组对象实例存储在一个数组容器中,在某一时刻遍历数组并调用数组内的对象方法完成更新。被观察者Observable内部的核心方法:1 public void notifyObservers(Object arg) {2 /*3 * a temporary array buffer, used as a snapshot of the state of4 * current Observers.5 */6 Object[] arrLocal;7 8 synchronized (this) {9 if (!changed)10 return;11 arrLocal = obs.toArray();12 clearChanged();13 }14 15 for (int i = arrLocal.length-1; i>=0; i--)16 ((Observer)arrLocal).update(this, arg);17 }加同步锁synchronized可避免线程并发问题,设置changed状态可监听更新。for循环遍历执行观察者Observer内部的方法update()。                                                                                                                                     被观察者继承Observable类,动态发布消息通知观察者更新。如:复制代码1 /**2 * 被观察者类3 *4 * @author jinzifu5 * @create 2018/2/22 15:366 */7 public class ObservableClass extends Observable {8 private String message;9 10 public String getMessage() {11 return message;12 }13 14 public void setMessage(String message) {15 this.message = message;16 //被观察者数据发生变化时,通过以下两行代码通知所有的观察者17 this.setChanged();18 this.notifyObservers(message);19 }20 }复制代码观察者实现了Observer接口,支持扩展。如:                                                                                                                               复制代码1 /**2 * 观察者类3 *4 * @author jinzifu5 * @create 2018/2/22 15:146 */7 public class ObserverClass implements Observer {8 private String name;9 10 public ObserverClass(String name) {11 this.name = name;12 }13 14 @Override15 public void update(Observable o, Object arg) {16 System.out.println(name + ":" + arg);17 }18 }复制代码外部测试如   复制代码1 /**2 * @author jinzifu3 */4 public class Main {5 6 public static void main(String[] args) {7 8 ObservableClass observableClass = new ObservableClass();9 ObserverClass observerClass1 = new ObserverClass("observerClass1");10 ObserverClass observerClass2 = new ObserverClass("observerClass2");11 ObserverClass observerClass3 = new ObserverClass("observerClass3");12 ObserverClass observerClass4 = new ObserverClass("observerClass4");13 14 observableClass.addObserver(observerClass1);15 observableClass.addObserver(observerClass2);16 observableClass.addObserver(observerClass3);17 observableClass.addObserver(observerClass4);18 19 observableClass.setMessage("收到通知,完成更新。");20 }21 }                                   
  • [技术干货] MQTT设备接入及上报数据的命令行模拟器(Java)
    以下博客将持续更新模拟器及使用方法:https://bbs.huaweicloud.com/blogs/acc3919d038911e9bd5a7ca23e93a891
  • [技术干货] AgentLite Java demo快速修改实现设备上线
    一、Java demo开发工具是用eclipse,为了避免出现一些意外情况,打开源码工程应使用eclipse import导入开发。二、修改demo南向接入地址:修改第一处:修改第二处:三、修改接入设备信息,AgentLiteBind.java四、根据profile修改设备上报数据五、如果不需要注册子设备,可以注释掉相关的代码六、运行查看结果七、至此,整个demo已经可以跑起来了,如果设备无法上线,可以删掉如下文件试试。
  • [行业资讯] 聊聊Reactive Programming &amp;Actor模型
    最近刚好在infoq上看到下一代分布式的设计理念,感触颇深,二十年来,整个分布式系统架构的演进,从 C/S 到 B/S,再到分布式系统。今天,微服务,网格,云计算大行其道,虽然二十年前我刚刚上初中!以当前常见的服务架构举例:微服务常见微服务架构,其前端流量入口采用负载。应用层常见是采取一级网络 (通过配置推送的软负载) 或者二级网络 (通过应用网关负载隔离) 模式。阿里是使用前者,百度、新浪使用后者,主要取决于微服务的展现形式 (RPC or Rest-API),差异是是否需要一个专职配置中心。为保证请求无状态地实现迁移,所以使用共享数据节点 (存储各种形式的临时或中间数据) 的形式实现。数据节点往往采用分片的主备模式。而伴随微服务的增长,当前我们的系统作为2.0基于RPC的微服务架构,也衍生出很多问题,例如服务间调用关系由当初的设计自底向上,逐渐变成网状调用依赖。部署上资源限制,效率瓶颈,缺乏总体架构。大量的远程调用。爆炸式增长的碎片化应用。问题定位变得复。另外,远程调用中最大的问题就是性能问题。如何解决以上的问题,同时也是下一代架构目标:流式架构/反应式编程(Reactive Architecture/Programming)作为一种范式在整个业界正在逐步受到认可和落地,是对过往系统的业务需求理解梳理之后对系统技术设计/架构模式的提升总结。 作为JAVA程序员的你应该感到高兴,因为,Java作为一个成熟平台,对于趋势一向有些稳健的接纳和跟进能力,有着令人惊叹的生命活力:    1. Java 7提供了ForkJoinPool,支持了Java 8提供的Stream(Reactive Stream是RP的一个核心组件)。    2. 另外Java 8还提供了Lamda(有效地表达和使用RP需要FP的语言构件和理念)。    3. 有了前面的这些稳健但不失时机的准备,在Java 9中提供了面向RP的Flow API,为Java圈子提供了官方的RP API,标志着RP由集市式的自由探索阶段 向 教堂式的统一使用的转变。 响应式编程就是异步数据流编程  说了这么就,什么是响应式编程(Reactive Programming)?传统的顺序编程采用每条指令依次执行的方式,上一条指令没有执行结束,当前的线程就得等着,任你如何提升机器性能还是代码性能,如果本质不变,始终改变不了响应需要等待的现实。若要响应迅速,就得把顺序执行指令的方式换一换——同步换成异步,方法执行换做消息发送,于是,就有了上边的响应式的定义:响应式编程就是异步数据流编程,这其实是一种编程范式,是编程理念的一种思想转型。因为采用响应式编程,我们就不再将软件要处理的业务视为对象,又或者函数,而是直接透析到本质:数据流(Data Stream)。万事万物皆为流从函数式编程的角度来讲,一连串组合函数的调用,其实就是数据在流动。函数可以抽象地视为一种数据类型到另一种数据类型的转换。将各种形式的转换(map、flatMap、filter)穿起来,同时保证数据的不变性(Immutable),则数据就能非常可靠地在函数链条中流动,或者被分析,或者被转换,或者被过滤。当我们将编程的范式切换为“流(Stream)”时,我们欣喜地发现,这种方式可以在很大程度上确保数据是不变的。这就为并行开发创造了可能。然而,普通的数据流编程范式并不能满足“响应式Reactive”的本初定义。我们需要响应迅速。如何才能做到?那就是要做到没有阻塞,这就是我们通常所说的异步工作方式。因而,响应式编程的设计原则是:保持数据的不变性没有共享阻塞是有害的恰好,这三条特征也是Actor模型拥有的。Actor,这个诞生在1970的产物,遥遥领先于那个时间,知道很久以后,Erlang这种基于Acotr的模型设计的面向并发编程的新语言横空出世,并在并发领域树立一座丰碑,古老的Actor才重见天日,再次成为分布式计算领域的焦点技术之一,目前,几乎所有的主流平台都支持Actor模型:JAVA平台下的Scala的Actor类库jetlan。Actor的理念很简单,Actor是一等公民,一切皆Actor,之间通过消息发送通信,所有都是异步的,不同Actor可以自己处理各自的消息。整个系统获得良好的大规模并发处理能力。在《Scala并发编程》一书中,Aleksandar Prokopec形象地描述了Actor系统:Actor系统模仿了人类的组织,如公司、政府和其他大型机构。在软件公司中,有许多需要以并发方式达成的目标。为了实现这些目标,数百或数千名员工一起努力工作,而且这些员工通常会被组织成一种层次结构。许多员工会为级别比他们低的员工分派工作。为了高效地工作和决策,员工们使用电子邮件进行通信。当员工早上上班时,就会检查他的电子邮箱并对重要的消息做出回应。如果某封电子邮件非常重要,那么这个员工就必须立刻回复这封邮件。当员工忙着回复一封电子邮件时,可能会收到另一封电子邮件,而且后续的电子邮件都会进入他的电子邮箱中。只有当员工处理完成当前的电子邮件后,他才能继续处理下一封电子邮件。软件公司就相当于是一个ActorSystem,每位员工则是一个一个Actor。电子邮件是Actor之间彼此发送的消息(Message),一旦发送了消息,就不必等待收件人的回复,可以继续自己的工作,也就是说这种消息发送的方式是异步非阻塞的。Actor持有的MailBox正好借用了这里所谓的电子邮箱概念。因而对于每个Actor而言:每个Actor都拥有独立的MailBox;接收到的消息皆为不可变对象,且完全独立;不管是tell消息还是ask消息,Actor执行消息的方式都是异步非阻塞的。不得不提的Akka AKKA框架是一个实现Actors模型的Scala或Java平台,实现多线程安全应用。  Actors是一个轻量级的对象,通过发送消息实现交互。每个Actors在同一时间处理最多一个消息,可以发送消息给其他Actors。在同一时间可以于一个Java虚拟机存在数以百万计的参与者,构架是一个分层的父层(管理) - 子层,其中父层监控子层的行为。还可以很容易地扩展Actor运行在集群中各个节点之间 - 无需修改一行代码。每个演员都可以有内部状态(字段/变量) ,但通信只能通过消息传递,不会有共享数据结构(计数器,队列) 。Akka框架支持两种语言Java和Scala,** is cheap,show me the codeAkka actors是轻量,能够平均在一个系统创建数千个。线程是重量的,场景切换相当慢。横向扩展Scale out,Actor能够在代码没有任何修改时在远程运行。失败恢复和错误处理Actor的Java代码就如上面简介中所说的,AKKA把并发操作的各种复杂的东西都统一的做了封装.我们主要关心的是业务逻辑的实现,只需要少量的关心Actor模型的串联即可构建出高可用,高性能,高扩展的应用.我希望在后续的应用的开发中朝着这个方向努力,今天的分享就到这里。最近我对内存相关的内容比较感兴趣,下个blog我会在跟大家分享下关于内存的内容。谢谢!  部分资料来自 infoq & 简书https://www.jianshu.com/p/3bdb8dbaa35c 
  • [技术干货] 【微服务直播】Netty和Vert.x在Apache顶级项目ServiceComb中的应用
    【微服务直播】Netty和Vert.x在Apache顶级项目ServiceComb中的应用   直播时间:11月29日19:30-20:30 【直播介绍】Netty是Java高性能网络编程的明星框架,互联网公司程序员必须掌握的基础组件。Netty将Java NIO接口封装,提供了全异步编程方式,是个大Java项目的网络应用开发必备神器。Vertx,是一个基于JVM、轻量级、高性能的应用平台,非常适用于移动端后台、互联网、企业应用架构。Vert.x框架基于事件和异步,依托于全异步Java服务器Netty,并扩展了很多其他特性,以其轻量、高性能、支持多语言开发而备受开发者青睐。ServiceComb,业界第一个Apache微服务顶级项目, 是一个开源微服务框架,提供了一套包含代码框架生成,服务注册发现,负载均衡,服务可靠性(容错熔断,限流降级,调用链追踪)等功能的微服务框架。华为微服务引擎CSE(Cloud Service Engine)提供ServiceComb商业版。本期直播将为你系统讲解:Netty在Apache顶级微服务项目ServiceComb中的实践!华为软件总工程师指点迷津,让你从一个技术小白成为技术专家!【直播议题】何为Netty,Vertx,与ServiceComb有何关系?ServiceComb支持同步和异步调用,如何实现异步转同步?ServiceComb如何实现Reactive机制,与Netty的NIO线程有何关系  vs Netty的Reactor线程模型从API接口契约、协议标准化等角度解读ServiceComb同时支持的Highway和私有RPC协议   vs  内部RPC使用HTTPS的进阶之路ServiceComb与HTTP/2ServiceComb实战利器之性能统计,通信队列排队、还是工作线程阻塞,统统帮你搞定   vs  《Netty进阶之路》之性能统计Netty本身不支持HTTP连接池,ServiceComb是如何实现连接池【大咖来袭】Netty专家  Mr.李10年Java NIO通信框架、平台中间件架构设计和开发经验,《Netty进阶之路:跟着案例学Netty》《Netty权威指南》、《分布式服务框架原理与实践》作者。目前在华为终端应用市场负责业务微服务化、云化、全球化等相关设计和开发工作。福利环节:观众参与直播提问互动,将有机会获得作者签名版《Netty进阶之路》!华为软件总工程师  Mr.吴华为微服务CSE架构师,Apache全球首个微服务顶级项目ServiceComb社区首席Committer,设计和实现加载框架、通讯协议、服务契约、服务治理等核心能力。直播课程入口:http://zhibo.huaweicloud.com/watch/2578836 
  • [技术干货] Docker容器内部署Java微服务的内存限制问题
    1 前言前两天有同事发现,通过华为云 ServiceStage 的流水线部署基于模板创建的 CSEJavaSDK demo 服务时,会在容器启动过程中报错。初步排查是由于 JVM 占用的内存超出了 docker 内存配额的上限,导致容器被 kill 掉。于是我们需要排查一下问题出在哪里,为什么以前没有这类问题,而现在却发生了。2 基本定位要确定 docker 容器内存超限问题的直接原因并不难。直接进入docker容器,执行 top 命令,我们发现宿主机是一台8核16G的机器,而且 docker 并不会屏蔽这些信息,也就是 JVM 会认为自己工作于一台 16G 内存的机器上。而查看 demo 服务的 Dockerfile,发现运行服务时并没有对 JVM 的内存进行任何限制,于是 JVM 会根据默认的设置来工作 —— 最大堆内存为物理内存的1/4(这里的描述并不完全准确,因为 JVM 的默认堆内存大小限制比例其实是根据物理内存有所变化的,具体内容请自行搜索资料),而基于模板创建的 ServiceStage 流水线,在部署应用堆栈的时候会把 docker 容器的内存配额默认设置为 512M,于是容器就会在启动的时候内存超限了。至于以前没有碰到过这种问题的原因,只是因为以前没将这么高规格的 ECS 服务器用于流水线部署应用堆栈。在查询过相关资料后,我们找到了两种问题解决方案,一个是直接在 jar 包运行命令里加上 -Xmx 参数来指定最大堆内存,不过这种方式只能将 JVM 堆内存限制为一个固定的值;另一个方法是在执行 jar 包时加上 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap 参数,让 JVM 能够感知到docker容器所设置的 cgroup 限制,相应地调整自身的堆内存大小,不过这个特性是 JDK 8u131 以上的版本才具备的。最终,我们提醒 ServiceStage 流水线的同学将 CSEJavaSDK demo 的创建模板做了改进,在 Dockerfile 中将打包的基础镜像版本由原先的 java:8u111-jre-alpine 升级为了 openjdk:8u181-jdk-alpine,并且在运行服务 jar 包的命令中加上了 -Xmx256m参数。问题至此已经解决了。3 进一步的探究虽然问题已经解决,但是在好奇心的驱使下,我还是打算自己找个 demo 实际去触发一下问题,另外看看从网上搜到的解决方法到底好不好用 : )3.1 准备工作3.1.1 创建云上工程首先需要在华为云 ServiceStage 创建一个云上工程。在 ServiceStage -> 应用开发 -> 微服务开发 -> 工程管理 -> 创建云上工程中,选择“基于模板创建”,语言选择 Java, 框架选择 CSE-Java (SpringMVC),部署系统选择“云容器引擎CCE”,给你的云上工程取一个名字,比如test-memo-consuming,最后选择存放代码的仓库,就可以完成云上工程的创建了。之后云上工程会根据你的选项自动地生成脚手架代码,上传到你指定的代码仓库中,并且为你创建一条流水线,完成代码编译、构建、打包、归档镜像包的操作,并且使用打好的 docker 镜像包在 CCE 集群中部署一个应用堆栈。创建云上工程和流水线不是本文的重点,我就不详细讲操作了 : )。同一个应用堆栈的实例可以部署多个,在这里为了实验方便就按照默认值1个来部署。由于云上工程已经改进了脚手架代码的模板,不会再出现内存超限的问题,所以我们现在能看到 demo 服务已经正常的跑起来,微服务实例已经注册到服务中心了。登录到 demo 服务所部署的容器,使用curl命令可以调用 demo 服务的 helloworld 接口,可以看到此时服务已经可以正常工作。3.1.2 增加实验代码为了能够触发微服务实例消耗更多的内存,我在项目代码中增加了如下接口,当调用/allocateMemory接口时,微服务实例会不停申请内存,直到 JVM 抛出 OOM 错误或者容器内存超限被 kill 掉。private HashMap<String, long[]> cacheMap = new HashMap<>(); @GetMapping(value = "/allocateMemory") public String allocateMemory() {   LOGGER.info("allocateMemory() is called");   try {     for (long i = 0; true; ++i) {       cacheMap.put("key" + i, new long[1024 * 1024]);     }   } catch (Throwable t) {     LOGGER.info("allocateMemory() gets error", t);   }   return "allocated"; }此时用来打镜像包的基础镜像是openjdk:8u181-jdk-alpine,jar 包启动命令中加上了-Xmx256m参数。执行流水线,应用堆栈部署成功后,调用/allocateMemory接口触发微服务实例消耗内存,直到 JVM 抛出 OOM 错误,可以在 ServiceStage -> 应用上线 -> 应用管理中选择相应的应用,点击进入概览页面,查看应用使用内存的情况。应用使用的内存从 800M+ 陡然下降的时间点就是我重新打包部署的时间,而之后由于调用/allocateMemory接口,内存占用量上升到了接近 400M,并且在这个水平稳定了下来,显示-Xmx256m参数发挥了预期的作用。3.2 复现问题现在将 demo 工程中的 Dockerfile 修改一下,将基础镜像改为 java:8u111-jre-alpine,并且删除启动命令中的-Xmx256m参数,将其提交为noLimit_oldBase分支,推送到代码仓库中。然后编辑流水线,将 source 阶段的任务所使用的代码分支改为noLimit_oldBase分支,保存并重新运行流水线,将新的代码打包部署到应用堆栈中。在微服务实例列表中查询到新的微服务实例的 endpoint IP 后,调用`/allocateMemory`接口,观察内存情况,内存从接近 400M 突然掉下去一下,然后又上升到约 450M 的时间点就是修改代码后的微服务实例部署成功的时间点,之后内存占用量突然下跌就是因为调用`/allocateMemory`接口导致容器内存超限被 kill 掉了。如果你事先使用docker logs -f命令查看容器日志的话,那么日志大概是这个样子的2018-11-23 15:40:04,920  INFO SCBEngine:152 - receive MicroserviceInstanceRegisterTask event, check instance Id... 2018-11-23 15:40:04,920  INFO SCBEngine:154 - instance registry succeeds for the first time, will send AFTER_REGISTRY event. 2018-11-23 15:40:04,925  WARN VertxTLSBuilder:116 - keyStore [server.p12] file not exist, please check! 2018-11-23 15:40:04,925  WARN VertxTLSBuilder:136 - trustStore [trust.jks] file not exist, please check! 2018-11-23 15:40:04,928  INFO DataFactory:62 - Monitor data sender started. Configured data providers is {com.huawei.paas.cse.tcc.upload.TransactionMonitorDataProvider,com.huawei.paas.monitor.HealthMonitorDataProvider,} 2018-11-23 15:40:04,929  INFO ServiceCenterTask:51 - read MicroserviceInstanceRegisterTask status is FINISHED 2018-11-23 15:40:04,939  INFO TestmemoconsumingApplication:57 - Started TestmemoconsumingApplication in 34.81 seconds (JVM running for 38.752) 2018-11-23 15:40:14,943  INFO AbstractServiceRegistry:258 - find instances[1] from service center success. service=default/CseMonitoring/latest, old revision=null, new revision=28475010.1 2018-11-23 15:40:14,943  INFO AbstractServiceRegistry:266 - service id=8b09a7085f4011e89f130255ac10470c, instance id=8b160d485f4011e89f130255ac10470c, endpoints=[rest://100.125.0.198:30109?sslEnabled=true] 2018-11-23 15:40:34,937  INFO ServiceCenterTaskMonitor:39 - sc task interval changed from -1 to 30 2018-11-23 15:47:03,823  INFO SPIServiceUtils:76 - Found SPI service javax.ws.rs.core.Response$StatusType, count=0. 2018-11-23 15:47:04,657  INFO TestmemoconsumingImpl:39 - allocateMemory() is called Killed可以看到allocateMemory方法被调用,然后 JVM 还没来得及抛出 OOM 错误,整个容器就被 kill 掉了。这里也给大家提了一个醒:不要以为自己的服务容器能启动起来就万事大吉了,如果没有特定的限制,JVM 会在运行时继续申请堆内存,也有可能造成内存用量超过 docker 容器的配额!3.3 让 JVM 感知cgroup限制前文提到还有另外一种方法解决 JVM 内存超限的问题,这种方法可以让 JVM 自动感知 docker 容器的 cgroup 限制,从而动态的调整堆内存大小,感觉挺不错的。我们也来试一下这种方法,看看效果如何 ; )回到demo项目代码的master分支,将 Dockerfile 中启动命令参数的-Xmx256m替换为-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap,提交为useCGroupMemoryLimitForHeap分支,推送到代码仓库里。再次运行流水线进行构建部署。等 demo 服务部署成功后,再次调用`/allocateMemory`接口,容器的内存占用情况如上图所示(最右边的那一部分连续曲线),内存上升到一定程度后,JVM 抛出了 OOM 错误,没有继续申请堆内存。看来这种方式也是有效果的。不过,仔细观察容器的内存占用情况,可以发现容器所使用的内存仅为不到 300M,而我们对于这个容器的内存配额限制为 512M,也就是还有 200M+ 是闲置的,并不会被 JVM 利用。这个利用率,比起上文中直接设置-Xmx256m的内存利用率要低 : ( 。推测是因为 JVM 并不会感知到自己是部署在一个 docker 容器里的,所以它把当前的环境当成一个物理内存只有 512M 的物理机,按照比例来限制自己的最大堆内存,另一部分就被闲置了。如此看来,如果想要充分利用自己的服务器资源,还是得多花一点功夫,手动调整好-Xmx参数。4 参考资料Java and Docker, the limitationsJava inside docker: What you must know to not FAIL本文转载自Docker容器内部署Java微服务的内存限制问题
  • [技术干货] Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
    failed to decode response body, exception is [Cannot deserialize instance of `java.lang.String` out of START_OBJECT tokenat [Source: (org.apache.servicecomb.foundation.vertx.stream.BufferInputStream); line: 1, column: 139] (through reference chain出错原因:在edge上引用了一个业务包,业务包定义的model跟契约不一致,导致出错。排查方法,可以在DefaultHttpClientFilter打个断点,然后查看下这个目标类的classloader是不是APPClassLoader,如果是,那么就是该问题
  • [技术干货] esdk-obs-java 扩展包
    本SDK是对华为OBS原生SDK(com.huawei.storage:esdk-obs-java)包装,在其基础上,提供断点上传、断点下载、回调、进度速率监控等特性。因此,本SDK不是原生SDK的替代,而是对原生SDK的补充。开发者应首先参考原生SDK的用户手册,在熟悉原生SDK的基础上再使用本SDK。下载链接:release一、功能1. 断点上传将整个上传任务转化为分段上传,每上传完一分段,在断点文件中记录完成分段上传结果,等所有分段上传成功后,合并所有分段。如果上传过程被中断,再次上传时,会读取断点文件中记录的分段结果,对于已经上传成功的分段不用再次上传。2.  断点下载将整个下载任务转化为分段(范围)下载,每个分段被成功的写入本地文件中后,在断点文件中记录完成分段结果,所有分段写入成功,即完成下载。如果下载过程被中断,再次下载时,会读取断点文件中记录的分段结果,对于已经成功的分段不用再次下载。3.  过程信息返回(进度,速率)在上传、下载操作中,对待上传的流/下载的流使用 ReadBytesMonitorInputStream 包装,其监控读取流的字节总数,并生成某一时刻的快照信息(包括总字节数、当前已读取字节数、当前时间、上一快照已读取字节数、上一快照时间等信息), 可以通过快照信息计算出进度、速率、是否完成等。二、日志配置参考原生SDK日志配置方式,如果要打印本SDK日志,配置logger name为"com.obs.extension"即可。注:当前SDK打印的日志级别为debug。<Logger name="com.obs.extension" level="debug" additivity="false">     <AppenderRef ref="NorthInterfaceLogAppender" /> </Logger>三、初始化该SDK提供了统一的对象操作门面,IObjectOperateClient。 要初始化 IObjectOperateClient ,需要传入原生SDK的obsClient实例,obsClient实例的初始化参考原生SDK手册。示例:IObjectOperateClient objectOperateClient = ObjectOperateClientImpl.newInstance(obsClient);四、代码示例1.  文件上传分段上传文件,支持断点续传和速率监控,IParallelUploadFuture 可以获取整个文件的速率信息,也可以获取每个分段上传的速率信息。//分段下载下载,开启断点续传和速率监控,关闭完整性校验 public void downloadFileWithProcessing() throws InterruptedException { String downloadFilePath = ""; String objectKey = ""; DownloadFileRequest request = new DownloadFileRequest(bucketName, objectKey); request.setDownloadFile(downloadFilePath); request.setEnableCheckpoint(true); request.setPartSize(5242880L); IParallelDownloadFuture downloadFuture = objectOperateClient.downloadFileAsyncWithProgress(request, false, printCallback); printProcessingInfo(downloadFuture.aggregationProcessing()); objectOperateClient.shutdown(); }2.  文件下载分段下载桶中对象到本地,支持断点续传、速率监控特性。//分段下载下载,开启断点续传和速率监控,关闭完整性校验 public void downloadFileWithProcessing() throws InterruptedException { String downloadFilePath = ""; String objectKey = ""; DownloadFileRequest request = new DownloadFileRequest(bucketName, objectKey); request.setDownloadFile(downloadFilePath); request.setEnableCheckpoint(true); request.setPartSize(5242880L); IParallelDownloadFuture downloadFuture = objectOperateClient.downloadFileAsyncWithProgress(request, false, printCallback); printProcessingInfo(downloadFuture.aggregationProcessing()); objectOperateClient.shutdown(); }3. printProcessingInfo() 方法public void printProcessingInfo(IProcessing processingInfo) throws InterruptedException { while (!processingInfo.isComplete()){ System.out.println("是否支持速率信息:" + processingInfo.isSupportProgress()); if(processingInfo.isSupportProgress()) { System.out.println("总字节数: " + processingInfo.getTotalBytes()); System.out.println("进度: " + processingInfo.getProgress()); } System.out.println("已传输: " + processingInfo.getTransferedBytes()); System.out.println("速率: " +processingInfo.perSecondRate() + " bytes/s"); System.out.println("-------------"); TimeUnit.MILLISECONDS.sleep(1000); } if (processingInfo.isComplete()) { System.out.println("是否支持速率信息:" + processingInfo.isSupportProgress()); if(processingInfo.isSupportProgress()) { System.out.println("总字节数: " + processingInfo.getTotalBytes()); System.out.println("进度: " + processingInfo.getProgress()); } System.out.println("已传输: " + processingInfo.getTransferedBytes()); System.out.println("速率: " +processingInfo.perSecondRate() + " bytes/s"); System.out.println("-------------"); } }
  • [技术干货] 常用的Java OCR开源框架有哪些?急求!
    常用的Java OCR开源框架有哪些?急求!
  • [技术干货] CSEJavaSDK连sc报错 404:Not Found, &quot;error_msg&quot;:&quot;API not exist or not published in the environment&quot;
    某些时候微服务注册到华为云CSE的时候,日志里面会打印错误内容2018-11-05 17:17:04.208  WARN 12692 --- [ntloop-thread-0] o.a.s.s.c.h.ServiceRegistryClientImpl    : get response for org.apache.servicecomb.serviceregistry.api.response.GetSchemasResponse failed, 404:Not Found, {"error_msg":"API not exist or not published in the environment","error_code":"APIGW.0101","request_id":"284c5c8b68ceed2411cbe958df208149"}这是由于microservice.yaml文件里面配置的连接服务中心、配置中心等的APIGateway地址还是旧域名https://cse.cn-north-1.myhwclouds.com,改成新的地址https://cse.cn-north-1.myhuaweicloud.com:443 就可以解决问题了。 PS:旧域名的APIGateway已经不更新了,并且预计以后会停止工作。推荐大家根据CSE或ServiceStage的CSE-SDK工具下载页面上发布的地址更新自己的配置
  • [分享交流] Java动态代理机制——那些让你面试脱颖而出的技能
    retrofit是一个解耦性非常高的网络请求框架,最近在研究的时候发现了动态代理这个非常强大且实用的技术,这篇文章将作为retrofit的前置知识,让大家认识:动态代理有哪些应用场景,什么是动态代理,怎样使用,它的局限性在什么地方?#动态代理的应用场景1. AOP—面向切面编程,程序解耦简言之当你想要对一些类的内部的一些方法,在执行前和执行后做一些共同的的操作,而在方法中执行个性化操作的时候--用动态代理。在业务量庞大的时候能够降低代码量,增强可维护性。2. 想要自定义第三放类库中的某些方法我引用了一个第三方类库,但他的一些方法不满足我的需求,我想自己重写一下那几个方法,或在方法前后加一些特殊的操作--用动态代理。但需要注意的是,这些方法有局限性,我会在稍后说明。什么是动态代理以上的图太过于抽象,我们从生活中的例子开始切入。假如你是一个大房东(被代理人),你有很多套房子想要出租,而你觉得找租客太麻烦,不愿意自己弄,因而你找一个人来代理你(代理人),帮打理这些东西,而这个人(代理人也就是中介)在帮你出租房屋的时候对你收取一些相应的中介费(对房屋出租的一些额外操作)。对于租客而言,中介就是房东,代理你做一些事情。以上,就是一个代理的例子,而他为什么叫动态代理,“动态”两个字体现在什么地方?我们可以这样想,如果你的每一套房子你都请一个代理人帮你打理,每当你想再出租一套房子的时候你得再请一个,这样你会请很多的代理人,花费高额的中介成本,这可以看作常说的“静态代理”。但假如我们把所有的房子都交给一个中介来代理,让他在多套房子之间动态的切换身份,帮你应付每一个租客。这就是一个“动态代理”的过程。动态代理的一大特点就是编译阶段没有代理类在运行时才生成代理类。我们用一段代码来看一下房屋出租的操作/** *定义一个借口 **/public interface RentHouse {void rent();//房屋出租void charge(String str);//出租费用收取}房东public class HouseOwner implements RentHouse {public void rent() {     System.out.println("I want to rent my house"); }public void charge(String str) {     System.out.println("You get : " + str + " RMB HouseCharge."); } }中介public class DynamicProxy implements InvocationHandler { // 这个就是我们要代理的真实对象,即房东 private Object subject; //  构造方法,给我们要代理的真实对象赋初值 public DynamicProxy(Object subject) {     this.subject = subject; } @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {     //  在代理真实对象前我们可以添加一些自己的操作,中介收取中介费     System.out.println("before "+method.getName()+" house");     System.out.println("Method:" + method.getName());         //        如果方法是 charge 则中介收取100元中介费     if (method.getName().equals("charge")) {         method.invoke(subject, args);         System.out.println("I will get 100 RMB ProxyCharge.");     } else {             //    当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用         method.invoke(subject, args);     }         //  在代理真实对象后我们也可以添加一些自己的操作     System.out.println("after "+method.getName()+" house");         return null; }客人public class Client {public static void main(String[] args){     //    我们要代理的真实对象--房东     HouseOwner houseOwner = new HouseOwner();         //    我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的     InvocationHandler handler = new DynamicProxy(houseOwner);       /*     * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数     * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象     * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了     * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上    */     RentHouse rentHouse = (RentHouse) Proxy.newProxyInstance(handler.getClass().getClassLoader(), houseOwner             .getClass().getInterfaces(), handler);//一个动态代理类,中介     System.out.println(rentHouse.getClass().getName());     rentHouse.rent();     rentHouse.charge("10000"); } }我们来看一下输出com.sun.proxy.$Proxy0 before rent house Method:rent I want to rent my house after rent house before charge house Method:charge You get : 10000 RMB HouseCharge. I will get 100 RMB ProxyCharge. after charge house Process finished with exit code 0输出里有 before rent house以及after rent house,说明我们可以在方法的前后增加操作。再看输出 I will get 100 RMB ProxyCharge. 中介收取了100块的中介费,说明我们不仅可以增加操作,甚至可以替换该方法或者直接让该方法不执行。刚开始看代码你可能会有很多疑惑,我们通过以下的内容来看看动态代理应该怎么用。#动态代理该如何使用在java的动态代理机制中,有两个重要的类和接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。每一个动态代理类都必须要实现InvocationHandler这个接口(代码中的中介),并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke(对方法的增强就写在这里面) 方法来进行调用。Object invoke(Object proxy, Method method, Object[] args) throws Throwable我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?Object invoke(Object proxy, Method method, Object[] args) throws Throwable //proxy:  指代我们所代理的那个真实对象 //method:  指代的是我们所要调用真实对象的某个方法的Method对象 //args:  指代的是调用真实对象某个方法时接受的参数接下来我们来看看Proxy这个类public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentExceptionProxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException //loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载 //interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了 //h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上这样一来,结合上面给出的代码,我们就可以明白动态代理的使用方法了#动态代理的局限性从动态代理的使用方法中我们看到其实可以被增强的方法都是实现了借口的(不实现借口的public方法也可以通过继承被代理类来使用),代码中的HouseOwner继承了RentHouse 。而对于private方法JDK的动态代理无能为力!以上的动态代理是JDK的,对于java工程还有大名鼎鼎的CGLib,但遗憾的是CGLib并不能在android中使用,android虚拟机相对与jvm还是有区别的。结束语动态代理的使用场景远不止这些,内部原理会在以后的文章中介绍,但应用类反射临时生成代理类这一机制决定它对性能会有一定的影响。本文作为retrofit原理的前置文章并没有太过详尽,如有疏漏和错误,欢迎指正!本文转自(51cto 洛基loky)博客51CTO博客,如需转载,请自行联系原作者。原文链接(http://blog.51cto.com/13586365/2065317)