dubbo java环境下出现这种错误怎么解决?
往service里注入失败了。
改成:ref="CarServiceImpl"
Dubbo分布式服务框架 服务注册不上:
(1) 检查dubbo的jar包有没有在classpath中,以及有没有重复的jar包
(2) 检查有没有重复的dubboproperties配置文件
(3) 检查暴露服务的spring配置有没有加载
(4) 检查beanId或beanName有没有重复
(5) 查看有没有错误日志:
cat ~/output/logs/webxlog
(6) 在服务提供者机器上测试与注册中心的网络是否通:
telnet 17222394 9090
(7) 检查与注册中心的连接是否存在:
netstat -anp | grep 17222394
(8) 如果是预发布机,检查hosts文件有没有正确绑定:
cat /etc/hosts
(9) 实在不行,开启远程调试:
– (a) 在服务器JVM参数中加入:-Xdebug -Xnoagent -Djavacompiler=NONE -Xrunjdwp:transport=dt_socket,address=7001,server=y,suspend=y
注意线上只有7001和8080可以被线下访问,调试端口需用这两个之一,因注册是启动时行为,启动时必需挂起suspend=y
– (b) 在dubbo源码的DefaultRegistryService的registerService()方法中设置断点。
– (c) 在Eclipse的Debug按钮下拉菜单Debug Configurations中的Remote Java Applications中新增远程调试,并设置IP和端口,以及增加dubbo的源码,进行远程Debug调试。
报错日志如下:
问题原因是多个服务在该服务器内运行,两个服务的qos-server配置端口号一致,启动时提示被占用,修改其一端口号并重启。
qos是dubbo的在线运维命令,dubbo258新版本重构了telnet模块,提供了新的telnet命令支持,新版本的telnet端口与dubbo协议的端口是不同的端口,默认为22222。
qos端口冲突并影响服务消费者消费服务,但是每次程序启动总是抛出端口冲突异常
Dubbo是 Alibaba 开源的分布式服务框架远程调用框架,在网络间传输数据,就需要通信协议和序列化。
Dubbo支持dubbo、rmi、hessian、http、webservice、thrift、redis等多种协议,但是Dubbo官网是推荐我们使用Dubbo协议的,默认也是用的dubbo协议。
先介绍几种常见的协议:
缺省协议,使用基于mina117+hessian321的tbremoting交互。
连接个数:单连接
连接方式:长连接
传输协议:TCP
传输方式:NIO异步传输
序列化:Hessian二进制序列化
适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。
适用场景:常规远程服务方法调用
1、dubbo默认采用dubbo协议,dubbo协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况
2、他不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
配置如下:
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name=“dubbo” port=“9090” server=“netty” client=“netty” codec=“dubbo”
serialization=“hessian2” charset=“UTF-8” threadpool=“fixed” threads=“100” queues=“0” iothreads=“9”
buffer=“8192” accepts=“1000” payload=“8388608” />
3、Dubbo协议缺省每服务每提供者每消费者使用单一长连接,如果数据量较大,可以使用多个连接。
<dubbo:protocol name="dubbo" connections="2" />
4、为防止被大量连接撑挂,可在服务提供方限制大接收连接数,以实现服务提供方自我保护
<dubbo:protocol name="dubbo" accepts="1000" />
Java标准的远程调用协议。
连接个数:多连接
连接方式:短连接
传输协议:TCP
传输方式:同步传输
序列化:Java标准二进制序列化
适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件。
适用场景:常规远程服务方法调用,与原生RMI服务互操作
RMI协议采用JDK标准的javarmi实现,采用阻塞式短连接和JDK标准序列化方式 。
基于Hessian的远程调用协议。
连接个数:多连接
连接方式:短连接
传输协议:HTTP
传输方式:同步传输
序列化:表单序列化
适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数,暂不支持传文件。
适用场景:需同时给应用程序和浏览器JS使用的服务。
1、Hessian协议用于集成Hessian的服务,Hessian底层采用Http通讯,采用Servlet暴露服务,Dubbo缺省内嵌Jetty作为服务器实现。
2、Hessian是Caucho开源的一个RPC框架: http://hessiancauchocom ,其通讯效率高于WebService和Java自带的序列化。
基于http表单的远程调用协议。参见:[HTTP协议使用说明]
连接个数:多连接
连接方式:短连接
传输协议:HTTP
传输方式:同步传输
序列化:表单序列化
适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数,暂不支持传文件。
适用场景:需同时给应用程序和浏览器JS使用的服务。
基于WebService的远程调用协议。
连接个数:多连接
连接方式:短连接
传输协议:HTTP
传输方式:同步传输
序列化:SOAP文本序列化
适用场景:系统集成,跨语言调用
序列化是将一个对象变成一个二进制流就是序列化, 反序列化是将二进制流转换成对象
为什么要序列化?
Dubbo序列化支持java、compactedjava、nativejava、fastjson、dubbo、fst、hessian2、kryo,其中默认hessian2。其中java、compactedjava、nativejava属于原生java的序列化。
hessian2序列化:hessian是一种跨语言的高效二进制序列化方式。但这里实际不是原生的hessian2序列化,而是阿里修改过的,它是dubbo RPC默认启用的序列化方式。
json序列化:目前有两种实现,一种是采用的阿里的fastjson库,另一种是采用dubbo中自己实现的简单json库,但其实现都不是特别成熟,而且json这种文本序列化性能一般不如上面两种二进制序列化。
java序列化:主要是采用JDK自带的Java序列化实现,性能很不理想。
大家在平时开发和测试阶段定位一些bug的时候,需要调用本地启动的dubbo服务debug。如果与服务器、其他同事共用一个注册中心的话,就会调用到别人的服务或者自己本地的服务被别人调用到,造成一些调用失败或者其他异常。
解决如上问题有大致几种解决方案:
1、dubbo直连;
2、dubbo group 服务分组;
3、dubbo version 版本过渡;
在开发以及测试环境下,经常需要绕过注册中心,只测试指定服务的提供者,这时候可 以使用点对点直连的方式。点对点直连方式,将以服务接口为单位,忽略注册中心的提供者表。A接口配置点对点,不影响B接口从注册中心获取列表。
消费者应用,在注入的提供者api上添加 @Reference(version = "100", url = "dubbo://ip:port") 即可!
此时,如果提供者不希望本地的服务被别人调用到,设置:dubboregistryregister=false,默认值是true。该属性含义: 是否向此注册中心注册服务,如果设为false,将只订阅,不注册。
通过服务分组实现环境隔离,不用绕过注册中心,大家可以共用一个注册中心。服务注册分组,使跨组的服务不会相互影响,也无法相互调用,适用于环境隔离。
场景:服务A希望调用到本地的服务B(此时,B服务正常的调用远程服务C),而不是远程服务B。
本地服务B的配置设置如下: //应用全局配置 dubboprovidergroup=local-group //设置本地B所提供的dubbo服务均在local-group分组下如图:
//针对某个Api进行配置
也可以针对某个api单独做分组,例如:@Service(version = "100",group = "local-group")服务A:在注入的B dubbo服务的api上加 @Reference(version = "100", group = "local-group") 即可。
服务分组的几个属性解释:
1、dubboregistrygroup=local-group
该值自行定义,确保唯一即可,该属性含义:服务注册分组,跨组的服务不会相互影响,也无法相互调用,适用于环境隔离。 该配置不推荐配置,配置之后服务在dubbo admin上默认无法查看,也调用不到该服 务。不同环境,通过zookeeper做数据隔离。
2、dubboprovidergroup=local-group
该属性含义:服务分组,当一个接口有多个实现,可以用分组区分。该配置使当前服务所有的提供者都在local-group下。也可以只针对某个api做配置@Service(version ="100",group ="local-group");推荐后者!
3、dubboconsumergroup=local-group
该配置使当前服务只消费local-group分组下的提供者,建议只针对某个api进行配置即可,例如: @Reference(version ="100", group ="local-group")
当一个接口的实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。dubboproviderversion=10 //服务版本, 建议使用两位数字版本,如:10 ,通常在接口不兼容时版本号才需要升级。
dubboconsumerversion=10 //消费10版本的提供者
可全局配置,亦可配置到某个api服务上,此时优先级大于全局配置。
提供者服务示例:@Service(version ="your version")
消费者api服务示例: @Reference(version ="your version")
原理:首先有个服务器,提供注册服务,称之为注册中心。
服务提供方连接注册中心,将对应的服务配置到注册中心中。
服务消费方连接到注册中心,通过注册中心,调用服务提供方提供的方法或服务。
Protocol 还有一个实现分支是 AbstractProxyProtocol,如下图所示:
从图中我们可以看到:gRPC、HTTP、WebService、Hessian、Thrift 等协议对应的 Protocol 实现,都是继承自 AbstractProxyProtocol 抽象类。
目前互联网的技术栈百花齐放,很多公司会使用 Nodejs、Python、Rails、Go 等语言来开发 一些 Web 端应用,同时又有很多服务会使用 Java 技术栈实现,这就出现了大量的跨语言调用的需求。Dubbo 作为一个 RPC 框架,自然也希望能实现这种跨语言的调用,目前 Dubbo 中使用“HTTP 协议 + JSON-RPC”的方式来达到这一目的,其中 HTTP 协议和 JSON 都是天然跨语言的标准,在各种语言中都有成熟的类库。
下面就重点来分析 Dubbo 对 HTTP 协议的支持。首先,会介绍 JSON-RPC 的基础,并通过一个示例,快速入门,然后介绍 Dubbo 中 HttpProtocol 的具体实现,也就是如何将 HTTP 协议与 JSON-RPC 结合使用,实现跨语言调用的效果。
Dubbo 中支持的 HTTP 协议实际上使用的是 JSON-RPC 协议。
JSON-RPC 是基于 JSON 的跨语言远程调用协议。Dubbo 中的 dubbo-rpc-xml、dubbo-rpc-webservice 等模块支持的 XML-RPC、WebService 等协议与 JSON-RPC 一样,都是基于文本的协议,只不过 JSON 的格式比 XML、WebService 等格式更加简洁、紧凑。与 Dubbo 协议、Hessian 协议等二进制协议相比,JSON-RPC 更便于调试和实现,可见 JSON-RPC 协议还是一款非常优秀的远程调用协议。
在 Java 体系中,有很多成熟的 JSON-RPC 框架,例如 jsonrpc4j、jpoxy 等,其中,jsonrpc4j 本身体积小巧,使用方便,既可以独立使用,也可以与 Spring 无缝集合,非常适合基于 Spring 的项目。
下面先来看看 JSON-RPC 协议中请求的基本格式:
JSON-RPC请求中各个字段的含义如下:
在 JSON-RPC 的服务端收到调用请求之后,会查找到相应的方法并进行调用,然后将方法的返回值整理成如下格式,返回给客户端:
JSON-RPC响应中各个字段的含义如下:
Dubbo 使用 jsonrpc4j 库来实现 JSON-RPC 协议,下面使用 jsonrpc4j 编写一个简单的 JSON-RPC 服务端示例程序和客户端示例程序,并通过这两个示例程序说明 jsonrpc4j 最基本的使用方式。
首先,需要创建服务端和客户端都需要的 domain 类以及服务接口。先来创建一个 User 类,作为最基础的数据对象:
接下来创建一个 UserService 接口作为服务接口,其中定义了 5 个方法,分别用来创建 User、查询 User 以及相关信息、删除 User:
UserServiceImpl 是 UserService 接口的实现类,其中使用一个 ArrayList 集合管理 User 对象,具体实现如下:
整个用户管理业务的核心大致如此。下面我们来看服务端如何将 UserService 与 JSON-RPC 关联起来。
首先,创建 RpcServlet 类,它是 HttpServlet 的子类,并覆盖了 HttpServlet 的 service() 方法。我们知道,HttpServlet 在收到 GET 和 POST 请求的时候,最终会调用其 service() 方法进行处理;HttpServlet 还会将 HTTP 请求和响应封装成 HttpServletRequest 和 HttpServletResponse 传入 service() 方法之中。这里的 RpcServlet 实现之中会创建一个 JsonRpcServer,并在 service() 方法中将 HTTP 请求委托给 JsonRpcServer 进行处理:
最后,创建一个 JsonRpcServer 作为服务端的入口类,在其 main() 方法中会启动 Jetty 作为 Web 容器,具体实现如下:
这里使用到的 webxml 配置文件如下:
完成服务端的编写之后,下面再继续编写 JSON-RPC 的客户端。在 JsonRpcClient 中会创建 JsonRpcHttpClient,并通过 JsonRpcHttpClient 请求服务端:
在 AbstractProxyProtocol 的 export() 方法中,首先会根据 URL 检查 exporterMap 缓存,如果查询失败,则会调用 ProxyFactorygetProxy() 方法将 Invoker 封装成业务接口的代理类,然后通过子类实现的 doExport() 方法启动底层的 ProxyProtocolServer,并初始化 serverMap 集合。具体实现如下:
在 HttpProtocol 的 doExport() 方法中,与前面介绍的 DubboProtocol 的实现类似,也要启动一个 RemotingServer。为了适配各种 HTTP 服务器,例如,Tomcat、Jetty 等,Dubbo 在 Transporter 层抽象出了一个 HttpServer 的接口。
dubbo-remoting-http 模块的入口是 HttpBinder 接口,它被 @SPI 注解修饰,是一个扩展接口,有三个扩展实现,默认使用的是 JettyHttpBinder 实现,如下图所示:
HttpBinder 接口中的 bind() 方法被 @Adaptive 注解修饰,会根据 URL 的 server 参数选择相应的 HttpBinder 扩展实现,不同 HttpBinder 实现返回相应的 HttpServer 实现。HttpServer 的继承关系如下图所示:
这里以 JettyHttpServer 为例简单介绍 HttpServer 的实现,在 JettyHttpServer 中会初始化 Jetty Server,其中会配置 Jetty Server 使用到的线程池以及处理请求 Handler:
可以看到 JettyHttpServer 收到的全部请求将委托给 DispatcherServlet 这个 HttpServlet 实现,而 DispatcherServlet 的 service() 方法会把请求委托给对应接端口的 HttpHandler 处理:
了解了 Dubbo 对 HttpServer 的抽象以及 JettyHttpServer 的核心之后,回到 HttpProtocol 中的 doExport() 方法继续分析。
在 HttpProtocoldoExport() 方法中会通过 HttpBinder 创建前面介绍的 HttpServer 对象,并记录到 serverMap 中用来接收 HTTP 请求。这里初始化 HttpServer 以及处理请求用到的 HttpHandler 是 HttpProtocol 中的内部类,在其他使用 HTTP 协议作为基础的 RPC 协议实现中也有类似的 HttpHandler 实现类,如下图所示:
在 HttpProtocolInternalHandler 中的 handle() 实现中,会将请求委托给 skeletonMap 集合中记录的 JsonRpcServer 对象进行处理:
skeletonMap 集合中的 JsonRpcServer 是与 HttpServer 对象一同在 doExport() 方法中初始化的。最后,我们来看 HttpProtocoldoExport() 方法的实现:
介绍完 HttpProtocol 暴露服务的相关实现之后,下面再来看 HttpProtocol 中引用服务相关的方法实现,即 protocolBindinRefer() 方法实现。该方法首先通过 doRefer() 方法创建业务接口的代理,这里会使用到 jsonrpc4j 库中的 JsonProxyFactoryBean 与 Spring 进行集成,在其 afterPropertiesSet() 方法中会创建 JsonRpcHttpClient 对象:
下面来看 doRefer() 方法的具体实现:
在 AbstractProxyProtocolprotocolBindingRefer() 方法中,会通过 ProxyFactorygetInvoker() 方法将 doRefer() 方法返回的代理对象转换成 Invoker 对象,并记录到 Invokers 集合中,具体实现如下:
本文重点介绍了在 Dubbo 中如何通过“HTTP 协议 + JSON-RPC”的方案实现跨语言调用。首先介绍了 JSON-RPC 中请求和响应的基本格式,以及其实现库 jsonrpc4j 的基本使用;接下来我们还详细介绍了 Dubbo 中 AbstractProxyProtocol、HttpProtocol 等核心类,剖析了 Dubbo 中“HTTP 协议 + JSON-RPC”方案的落地实现。
直连加不发布服务
DUBBO的配置属性里面对消费端提供了不从注册中心发现服务的机制,直接配置远程接口的地址,这样可以保证消费端连接到制定的环境接口。这样消费端是解决了问题,但是服务提供端呢?如上图的B1它即是消费端也是服务提供端,它提供A1所依赖的接口,那么如果B1将它的服务发布到注册中心里面(这里需要提醒,STABLE环境机制里面所有子环境公用一个注册中心),那么势必会导致stable环境里面的A会发现B1提供的服务?势必会导致stable环境的不稳定(stable环境的机制是stable环境只能进不能出,就是不能调用外部其他子环境的服务)?所以B1不能发布服务到注册中心,dubbo也提供了相关的配置属性来支持这一点。下面我例举出通过哪些配置可以实现这种方案:
服务消费端:
DUBBO在消费端提供了一个url的属性来指定某个服务端的地址
<!--lang:xml-->
<dubbo:reference interface="comalibabadubbodemoHelloWorldService" check="false" id="helloWorldService"/>
默认的方式是从注册中心发现接口为comalibabadubbodemoHelloWorldService的服务,但是如果需要直连,可以在dubboproperties下面配置dubboreferencehelloWorldServiceurl=dubbo://ip:port/comalibabadubbodemoHelloWorldService可以通过配置dubboreferenceurl=dubbo://ip:port/来让某个消费者系统的服务都指向制定的服务器地址(关于配置信息可以参考《DUBBO配置规则详解》)
0条评论