Dubbo详解,用心看这一篇文章就够了【重点】
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
1.1 Dubbo
概述
Dubbo
是阿里巴巴开源的基于 Java
的高性能RPC
一种远程调用 分布式服务框架致力于提供高性能和透明化的RPC
远程服务调用方案以及SOA
服务治理方案。
每天为2
千多个服务提供大于30
亿次访问量支持并被广泛应用于阿里巴巴集团的各成员站点以及别的公司的业务中。
简单的说Dubbo
就是个服务框架如果没有分布式的需求其实是不需要用的只有在分布式的时候才有Dubbo
这样的分布式服务框架的需求。
并且本质上是个远程服务调用的分布式框架告别Web Service
模式中的WSdl
以服务者与消费者的方式在Dubbo
上注册
其核心部分包含
1、远程通讯提供对多种基于长连接的NIO
框架抽象封装包括多种线程模型序列化以及“请求-响应”模式的信息交换方式。
2、集群容错提供基于接口方法的透明远程过程调用包括多协议支持以及软负载均衡失败容错地址路由动态配置等集群支持。
3、自动发现基于注册中心目录服务使服务消费方能动态的查找服务提供方使地址透明使服务提供方可以平滑增加或减少机器。
1.2 Dubbo
背景
Dubbo
开始于电商系统因此在这里先从电商系统的演变讲起。
1.2.1 单一应用框架
1、单应用单服务器
2、单应用拆分成多个应用并部署到多个服务器
3、单应用拆分成多个应用并实现分布式部署
4、流动计算框架用于提高机器利用率的资源调度和治理中心
1.2.5.1 单一应用架构
当网站流量很小时只需一个应用将所有功能都部署在一起以减少部署节点和成本。 此时用于简化增删改查工作量的 数据访问框架(ORM
) 是关键。
1.2.5.2 垂直应用架构
当访问量逐渐增大单一应用增加机器带来的加速度越来越小将应用拆成互不相干的几个应用以提升效率。此时用于加速前端页面开发的 Web
框架(MVC
) 是关键。
1.2.5.3 分布式服务架构
当垂直应用越来越多应用之间交互不可避免将核心业务抽取出来作为独立的服务逐渐形成稳定的服务中心使前端应用能更快速的响应多变的市场需求。此时用于提高业务复用及整合的 分布式服务框架(RPC
) 是关键。
1.2.5.4 流动计算架构
当服务越来越多容量的评估小服务资源的浪费等问题逐渐显现此时需增加一个调度中心基于访问压力实时管理集群容量提高集群利用率。此时用于提高机器利用率的 资源调度和治理中心(SOA
) 是关键。
1.2.6 RPC
的简介
RPC(Remote Procedure Call Protocol)
远程过程调用
两台服务器A、B
分别部署不同的应用a,b
。当A
服务器想要调用B
服务器上应用b
提供的函数或方法的时候由于不在一个内存空间不能直接调用需要通过网络来表达调用的语义传达调用的数据。
说白了就是你在你的机器上写了一个程序我这边是无法直接调用的这个时候就出现了一个远程服务调用的概念。
RPC
是一种通过网络从远程计算机程序上请求服务而不需要了解底层网络技术的协议。RPC
协议假定某些传输协议的存在如TCP
或UDP
为通信程序之间携带信息数据。在OSI
网络通信模型中RPC
跨越了传输层和应用层。RPC
使得开发包括网络分布式多程序在内的应用程序更加容易。
RPC
采用客户机/服务器模式。请求程序就是一个客户机而服务提供程序就是一个服务器。首先客户机调用进程发送一个有进程参数的调用信息到服务进程然后等待应答信息。在服务器端进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达服务器获得进程参数计算结果发送答复信息然后等待下一个调用信息最后客户端调用进程接收答复信息获得进程结果然后调用执行继续进行。
1.2.6.1 RPC
需要解决的问题
- 通讯问题主要是通过在客户端和服务器之间建立TCP连接远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接调用结束后就断掉也可以是长连接多个远程过程调用共享同一个连接。
- 寻址问题A服务器上的应用怎么告诉底层的RPC框架如何连接到B服务器如主机或
IP
地址以及特定的端口方法的名称名称是什么这样才能完成调用。比如基于Web
服务协议栈的RPC
就要提供一个endpoint URI
或者是从UDDI
服务上查找。如果是RMI
调用的话还需要一个RMI Registry
来注册服务的地址。 - 序列化 与 反序列化当
A
服务器上的应用发起远程过程调用时方法的参数需要通过底层的网络协议如TCP
传递到B
服务器由于网络协议是基于二进制的内存中的参数的值要序列化成二进制的形式也就是序列化Serialize
或编组marshal
通过寻址和传输将序列化的二进制发送给B
服务器。
同理B
服务器接收参数要将参数反序列化。B服务器应用调用自己的方法处理后返回的结果也要序列化给A
服务器A
服务器接收也要经过反序列化的过程。
1.3 Dubbo
作用
我们一起来看一下Dubbo
的服务治理图
1.3.1 为什么使用Dubbo
因为是阿里开源项目国内很多互联网公司都在用已经经过很多线上考验。内部使用了Netty、Zookeeper
保证了高性能高可用性。
- 使用
Dubbo
可以将核心业务抽取出来作为独立的服务逐渐形成稳定的服务中心可用于提高业务复用灵活扩展使前端应用能更快速的响应多变的市场需求。 - 分布式架构可以承受更大规模的并发流量。
1.3.2 Dubbo
能做什么
1、透明化的远程方法调用就像调用本地方法一样调用远程方法只需简单配置没有任何API
侵入。
2、软负载均衡及容错机制可在内网替代F5
等硬件负载均衡器降低成本减少单点。
3.、服务自动注册与发现不再需要写死服务提供方地址注册中心基于接口名查询服务提供者的IP
地址并且能够平滑添加或删除服务提供者。
Dubbo
采用全Spring
配置方式透明化接入应用对应用没有任何API
侵入只需用Spring
加载Dubbo
的配置即可Dubbo
基于Spring
的Schema
扩展进行加载。
1.4 Dubbo
和 Spring Cloud
区别
1、通信方式不同Dubbo
使用的是 RPC
通信而Spring Cloud
使用的是HTTP RESTFul
方式。
2、组成不一样
dubbo
的服务注册中心为Zookeerper
服务监控中心为dubbo-monitor
无消息总线、服务跟踪、批量任务等组件Spring Cloud
的服务注册中心为spring-cloud netflix enruka
服务监控中心为spring-boot admin
有消息总线、数据流、服务跟踪、批量任务等组件
1.5 Dubbo
技术架构
首先我们一起来看一下Dubbo
官网提供的架构图
节点角色说明
节点 | 角色说明 |
---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
看了这几个概念后似乎发现其实Dubbo
的架构也是很简单的(其实现细节是复杂)为什么这么说呢有没有发现其实很像生产者-消费者模型。只是在这种模型上加上了注册中心和监控中心用于管理提供方提供的url
以及管理整个过程。
调用关系说明
1、服务容器负责启动加载运行服务提供者。
2、服务提供者在启动时向注册中心注册自己提供的服务。
3、服务消费者在启动时向注册中心订阅自己所需的服务。
4、注册中心返回服务提供者地址列表给消费者如果有变更注册中心将基于长连接推送变更数据给消费者。
5、服务消费者从提供者地址列表中基于软负载均衡算法选一台提供者进行调用如果调用失败再选另一台调用。
6、服务消费者和提供者在内存中累计调用次数和调用时间定时每分钟发送一次统计数据到监控中心。
那么整个发布-订阅的过程就非常的简单了
- 启动容器加载运行服务提供者。
- 服务提供者在启动时在注册中心发布注册自己提供的服务。
- 服务消费者在启动时在注册中心订阅自己所需的服务。
如果考虑失败或变更的情况就需要考虑下面的过程
- 注册中心返回服务提供者地址列表给消费者如果有变更注册中心将基于长连接推送变更数据给消费者。
- 服务消费者从提供者地址列表中基于软负载均衡算法选一台提供者进行调用如果调用失败再选另一台调用。
- 服务消费者和提供者在内存中累计调用次数和调用时间定时每分钟发送一次统计数据到监控中心。
1.6 Dubbo
入门案例
1.6.1 服务端
首先我们先把服务端的接口写好因为其实dubbo
的作用简单来说就是给消费端提供接口。
接口定义
/**
* xml方式服务提供者接口
*/
public interface ProviderService {
String SayHello(String word);
}
这个接口非常简单只是包含一个 SayHello
的方法。
接着定义它的实现类
/**
* xml方式服务提供者实现类
*/
public class ProviderServiceImpl implements ProviderService{
public String SayHello(String word) {
return word;
}
}
这样我们就把我们的接口写好了那么我们应该怎么将我们的服务暴露出去呢
导入 maven 依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ouyangsihai</groupId>
<artifactId>dubbo-provider</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.6</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.10</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.5</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.32.Final</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.8.0</version>
</dependency>
</dependencies>
</project>
这里使用的dubbo
的版本是2.6.6
需要注意的是如果你只导入dubbo
的包的时候是会报错的找不到netty
和curator
的依赖所以在这里我们需要把这两个的依赖加上就不会报错了。
另外这里我们使用 zookeeper
作为注册中心。
到目前为止dubbo
需要的环境就已经可以了下面我们就把上面刚刚定义的接口暴露出去。
暴露接口xml
配置方法
首先我们在我们项目的resource
目录下创建 META-INF.spring
包然后再创建 provider.xml
文件名字可以任取哦如下图所示
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--当前项目在整个分布式架构里面的唯一名称计算依赖关系的标签-->
<dubbo:application name="provider" owner="sihai">
<dubbo:parameter key="qos.enable" value="true"/>
<dubbo:parameter key="qos.accept.foreign.ip" value="false"/>
<dubbo:parameter key="qos.port" value="55555"/>
</dubbo:application>
<dubbo:monitor protocol="registry"/>
<!--dubbo这个服务所要暴露的服务地址所对应的注册中心-->
<!--<dubbo:registry address="N/A"/>-->
<dubbo:registry address="N/A" />
<!--当前服务发布所依赖的协议webservice、Thrift、Hessain、http-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--服务发布的配置需要暴露的服务接口-->
<dubbo:service
interface="com.sihai.dubbo.provider.service.ProviderService"
ref="providerService"/>
<!--Bean bean定义-->
<bean id="providerService" class="com.sihai.dubbo.provider.service.ProviderServiceImpl"/>
</beans>
说明
1、上面的文件其实就是类似 spring
的配置文件而且dubbo
底层就是 spring
。
2、节点dubbo:application
就是整个项目在分布式架构中的唯一名称可以在name
属性中配置另外还可以配置owner
字段表示属于谁。
下面的参数是可以不配置的这里配置是因为出现了端口的冲突所以配置。
3、节点dubbo:monitor
监控中心配置 用于配置连接监控中心相关信息可以不配置不是必须的参数。
4、节点dubbo:registry
配置注册中心的信息比如这里我们可以配置 zookeeper
作为我们的注册中心。address
是注册中心的地址这里我们配置的是 N/A
表示由 dubbo
自动分配地址。或者说是一种直连的方式不通过注册中心。
5、节点dubbo:protocol
服务发布的时候 dubbo
依赖什么协议可以配置dubbo
、webservice
、http
等协议。
6、节点dubbo:service
这个节点就是我们的重点了当我们服务发布的时候我们就是通过这个配置将我们的服务发布出去的。interface
是接口的包路径ref
是第 ⑦ 点配置的接口的bean
。
7、最后我们需要像配置spring
的接口一样配置接口的 bean
。
到这一步关于服务端的配置就完成了下面我们通过 main
方法将接口发布出去。
发布接口
package com.sihai.dubbo.provider;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.ServiceConfig;
import com.alibaba.dubbo.container.Main;
import com.sihai.dubbo.provider.service.ProviderService;
import com.sihai.dubbo.provider.service.ProviderServiceImpl;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
/**
* xml方式启动
*
*/
public class App
{
public static void main( String[] args ) throws IOException {
//加载xml配置文件启动
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/provider.xml");
context.start();
System.in.read(); // 按任意键退出
}
}
发布接口非常简单因为dubbo
底层就是依赖 spring
的所以我们只需要通过 ClassPathXmlApplicationContext
拿到我们刚刚配置好的 xml
然后调用 context.start()
方法就启动了。
看到下面的截图就算是启动成功了接口也就发布出去了。
你以为到这里就结束了了并不是的我们拿到 dubbo
暴露出去的 url
分析分析。
Dubbo 暴露的 URL
dubbo://192.168.234.1:20880/com.sihai.dubbo.provider.service.ProviderService?anyhost=true&application=provider&bean.name=com.sihai.dubbo.provider.service.ProviderService&bind.ip=192.168.234.1&bind.port=20880&dubbo=2.0.2&generic=false&interface=com.sihai.dubbo.provider.service.ProviderService&methods=SayHello&owner=sihai&pid=8412&qos.accept.foreign.ip=false&qos.enable=true&qos.port=55555&side=provider×tamp=1562077289380
分析如下
1、首先在形式上我们发现其实这么牛逼的 dubbo
也是用类似于 http
的协议发布自己的服务的只是这里我们用的是dubbo
协议。
2、dubbo://192.168.234.1:20880/com.sihai.dubbo.provider.service.ProviderService
上面这段链接就是 ?
之前的链接构成协议://ip:端口/接口。发现是不是也没有什么神秘的。
3、anyhost=true&application=provider&bean.name=com.sihai.dubbo.provider.service.ProviderService&bind.ip=192.168.234.1&bind.port=20880&dubbo=2.0.2&generic=false&interface=com.sihai.dubbo.provider.service.ProviderService&methods=SayHello&owner=sihai&pid=8412&qos.accept.foreign.ip=false&qos.enable=true&qos.port=55555&side=provider×tamp=1562077289380
?
之后的字符串分析后你发现这些都是刚刚在provider.xml
中配置的字段然后通过&
拼接而成的闻到了 http
的香味了吗
终于dubbo
服务端入门了。下面我们看看拿到了 url
后怎么消费呢
1.6.2 消费端
上面提到我们在服务端提供的只是点对点的方式提供服务并没有使用注册中心所以下面的配置也是会有一些不一样的。
消费端环境配置
首先我们在消费端的 resource
下建立配置文件 consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--当前项目在整个分布式架构里面的唯一名称计算依赖关系的标签-->
<dubbo:application name="consumer" owner="sihai"/>
<!--dubbo这个服务所要暴露的服务地址所对应的注册中心-->
<!--点对点的方式-->
<dubbo:registry address="N/A" />
<!--<dubbo:registry address="zookeeper://localhost:2181" check="false"/>-->
<!--生成一个远程服务的调用代理-->
<!--点对点方式-->
<dubbo:reference id="providerService"
interface="com.sihai.dubbo.provider.service.ProviderService"
url="dubbo://192.168.234.1:20880/com.sihai.dubbo.provider.service.ProviderService"/>
<!--<dubbo:reference id="providerService"
interface="com.sihai.dubbo.provider.service.ProviderService"/>-->
</beans>
分析如下所示
1、发现这里的 dubbo:application
和 dubbo:registry
是一致的
2、dubbo:reference
我们这里采用点对点的方式所以需要配置在服务端暴露的 url
maven 依赖
和服务端一样
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ouyangsihai</groupId>
<artifactId>dubbo-consumer</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.ouyangsihai</groupId>
<artifactId>dubbo-provider</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.6</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.10</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.5</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.32.Final</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.8.0</version>
</dependency>
</dependencies>
</project>
调用服务
package com.sihai.dubbo.consumer;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.sihai.dubbo.provider.service.ProviderService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
/**
* xml的方式调用
*
*/
public class App
{
public static void main( String[] args ) throws IOException {
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("consumer.xml");
context.start();
ProviderService providerService = (ProviderService) context.getBean("providerService");
String str = providerService.SayHello("hello");
System.out.println(str);
System.in.read();
}
}
这里和服务端的发布如出一辙
如此我们就成功调用接口了。
1.7 加入zookeeper
作为注册中心
在前面的案例中我们没有使用任何的注册中心而是用一种直连的方式进行的。但是实际上很多时候我们都是使用dubbo + zookeeper
的方式使用 zookeeper
作为注册中心这里我们就介绍一下 zookeeper
作为注册中心的使用方法。
这里我们在前面的入门实例中进行改造。
1.7.1 服务端
在服务端中我们只需要修改provider.xml
即可。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--当前项目在整个分布式架构里面的唯一名称计算依赖关系的标签-->
<dubbo:application name="provider" owner="sihai">
<dubbo:parameter key="qos.enable" value="true"/>
<dubbo:parameter key="qos.accept.foreign.ip" value="false"/>
<dubbo:parameter key="qos.port" value="55555"/>
</dubbo:application>
<dubbo:monitor protocol="registry"/>
<!--dubbo这个服务所要暴露的服务地址所对应的注册中心-->
<!--<dubbo:registry address="N/A"/>-->
<dubbo:registry address="zookeeper://localhost:2181" check="false"/>
<!--当前服务发布所依赖的协议webservice、Thrift、Hessain、http-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--服务发布的配置需要暴露的服务接口-->
<dubbo:service
interface="com.sihai.dubbo.provider.service.ProviderService"
ref="providerService"/>
<!--Bean bean定义-->
<bean id="providerService" class="com.sihai.dubbo.provider.service.ProviderServiceImpl"/>
</beans>
重点关注这句话
<dubbo:registry address="zookeeper://localhost:2181" />
在 address
中使用我们的 zookeeper
的地址。
如果是zookeeper
集群的话使用下面的方式。
<dubbo:registry protocol="zookeeper" address="192.168.11.129:2181,192.168.11.137:2181,192.168.11.138:2181"/>
服务端的配置就好了其他的跟 入门案例 一样。
1.7.2 消费端
跟服务端一样在消费端我们也只需要修改 consumer.xml
即可。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--当前项目在整个分布式架构里面的唯一名称计算依赖关系的标签-->
<dubbo:application name="consumer" owner="sihai"/>
<!--dubbo这个服务所要暴露的服务地址所对应的注册中心-->
<!--点对点的方式-->
<!--<dubbo:registry address="N/A" />-->
<dubbo:registry address="zookeeper://localhost:2181" check="false"/>
<!--生成一个远程服务的调用代理-->
<!--点对点方式-->
<!--<dubbo:reference id="providerService"
interface="com.sihai.dubbo.provider.service.ProviderService"
url="dubbo://192.168.234.1:20880/com.sihai.dubbo.provider.service.ProviderService"/>-->
<dubbo:reference id="providerService"
interface="com.sihai.dubbo.provider.service.ProviderService"/>
</beans>
1、注册中心配置跟服务端一样
<dubbo:registry address="zookeeper://localhost:2181"/>
2、dubbo:reference
由于我们这里使用 zookeeper
作为注册中心所以跟点对点的方式是不一样的这里不再需要 dubbo
服务端提供的 url 了只需要直接引用服务端提供的接口即可
<dubbo:reference id="providerService"
interface="com.sihai.dubbo.provider.service.ProviderService"/>
好了消费端也配置好了这样就可以使用修改的入门案例重新启动运行了。
同样成功了。
这时候的区别在于将 dubbo
发布的url
注册到了 zookeeper
消费端从 zookeeper
消费zookeeper
相当于一个中介给消费者提供服务。
你以为这就完了不好戏才刚刚开始呢。
1.8 多种配置方式
在入门实例的时候我们使用的是 xml
配置的方式对 dubbo
的环境进行了配置但是官方还提供了其他的配置方式这里我们也一一分解。
1.8.1 API
配置方式
这种方式其实官方是不太推荐的官方推荐使用xml
配置的方式但是在有的时候测试的时候还是可以用的到的另外为了保证完整性这些内容还是有必要讲讲的。
首先还是回到服务端工程。
服务端
这里我们使用 api
的方式配置所以provider.xml
这个配置文件就暂时不需要了我们只需要在上面的 AppApi
这个类中的 main
方法中用api
配置及启动即可。
package com.sihai.dubbo.provider;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.ServiceConfig;
import com.sihai.dubbo.provider.service.ProviderService;
import com.sihai.dubbo.provider.service.ProviderServiceImpl;
import java.io.IOException;
/**
* Api方式启动
* api的方式调用不需要其他的配置只需要下面的代码即可。
* 但是需要注意官方建议
* Api方式用于测试用例使用推荐xml的方式
*/
public class AppApi
{
public static void main( String[] args ) throws IOException {
// 服务实现
ProviderService providerService = new ProviderServiceImpl();
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("provider");
application.setOwner("sihai");
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("zookeeper://localhost:2181");
// registry.setUsername("aaa");
// registry.setPassword("bbb");
// 服务提供者协议配置
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName("dubbo");
protocol.setPort(20880);
//protocol.setThreads(200);
// 注意ServiceConfig为重对象内部封装了与注册中心的连接
//以及开启服务端口
// 服务提供者暴露服务配置
// 此实例很重封装了与注册中心的连接请自行缓存
//否则可能造成内存和连接泄漏
ServiceConfig<ProviderService> service = new ServiceConfig<ProviderService>();
service.setApplication(application);
// 多个注册中心可以用setRegistries()
service.setRegistry(registry);
// 多个协议可以用setProtocols()
service.setProtocol(protocol);
service.setInterface(ProviderService.class);
service.setRef(providerService);
service.setVersion("1.0.0");
// 暴露及注册服务
service.export();
}
}
分析说明如下所示
看到上面的代码是不是云里雾里不要慌我们通过对照 xml
的方式分析一下。
registry 的 xml方式
<dubbo:registry protocol="zookeeper" address="localhost:2181"/>
API 的方式
RegistryConfig registry = new RegistryConfig();
registry.setAddress("zookeeper://localhost:2181");
dubbo:registry
节点对应RegistryConfig
xml
的属性对应 API
方式用 set
方法就可以了。对比之下你就会发现如果 API
的方式不熟悉可以对照xml
配置方式就可以。
其他 API
org.apache.dubbo.config.ServiceConfig
org.apache.dubbo.config.ReferenceConfig
org.apache.dubbo.config.ProtocolConfig
org.apache.dubbo.config.RegistryConfig
org.apache.dubbo.config.MonitorConfig
org.apache.dubbo.config.ApplicationConfig
org.apache.dubbo.config.ModuleConfig
org.apache.dubbo.config.ProviderConfig
org.apache.dubbo.config.ConsumerConfig
org.apache.dubbo.config.MethodConfig
org.apache.dubbo.config.ArgumentConfig
更详细的可以查看官方文档
http://dubbo.apache.org/zh-cn…
我们再看看我配置的消费端的Api
方式。
消费端
同样我们不需要 consumer.xml
配置文件了只需要在 main
方法中启动即可。
package com.sihai.dubbo.consumer;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.sihai.dubbo.provider.service.ProviderService;
/**
* api的方式调用
* api的方式调用不需要其他的配置只需要下面的代码即可。
* 但是需要注意官方建议
* Api方式用于测试用例使用推荐xml的方式
*/
public class AppApi {
public static void main(String[] args) {
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("consumer");
application.setOwner("sihai");
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("zookeeper://localhost:2181");
// 注意ReferenceConfig为重对象内部封装了与注册中心的连接
//以及与服务提供方的连接
// 引用远程服务
ReferenceConfig<ProviderService> reference = new ReferenceConfig<ProviderService>(); // 此实例很重封装了与注册中心的连接以及与提供者的连接请自行缓存否则可能造成内存和连接泄漏
reference.setApplication(application);
reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
reference.setInterface(ProviderService.class);
// 和本地bean一样使用xxxService
ProviderService providerService = reference.get(); // 注意此代理对象内部封装了所有通讯细节对象较重请缓存复用
providerService.SayHello("hello dubbo! I am sihai!");
}
}
这部分的 API
配置的方式就到这了注意官方推荐 xml
的配置方法
1.8.2 注解配置方式
注解配置方式还是需要了解一下的现在微服务都倾向于这种方式这也是以后发展的趋势0
配置应该是这几年的趋势。
那么如何对dubbo
使用注解的方式呢我们先看服务端。
服务端
第一步定义接口及实现类在上面的截图中的 annotation
包下
package com.sihai.dubbo.provider.service.annotation;
/**
* 注解方式接口
*/
public interface ProviderServiceAnnotation {
String SayHelloAnnotation(String word);
}
package com.sihai.dubbo.provider.service.annotation;
import com.alibaba.dubbo.config.annotation.Service;
/**
* 注解方式实现类
*/
@Service(timeout = 5000)
public class ProviderServiceImplAnnotation implements ProviderServiceAnnotation{
public String SayHelloAnnotation(String word) {
return word;
}
}
@Service
@Service
用来配置 Dubbo
的服务提供方。
第二步组装服务提供方。通过 Spring
中 Java Config
的技术@Configuration
和 annotation
扫描@EnableDubbo
来发现、组装、并向外提供Dubbo
的服务。
package com.sihai.dubbo.provider.configuration;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.ProviderConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 注解方式配置
*/
@Configuration
@EnableDubbo(scanBasePackages = "com.sihai.dubbo.provider.service.annotation")
public class DubboConfiguration {
@Bean // #1 服务提供者信息配置
public ProviderConfig providerConfig() {
ProviderConfig providerConfig = new ProviderConfig();
providerConfig.setTimeout(1000);
return providerConfig;
}
@Bean // #2 分布式应用信息配置
public ApplicationConfig applicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("dubbo-annotation-provider");
return applicationConfig;
}
@Bean // #3 注册中心信息配置
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("localhost");
registryConfig.setPort(2181);
return registryConfig;
}
@Bean // #4 使用协议配置这里使用 dubbo
public ProtocolConfig protocolConfig() {
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setName("dubbo");
protocolConfig.setPort(20880);
return protocolConfig;
}
}
分析说明如下
1、通过 @EnableDubbo
指定在com.sihai.dubbo.provider.service.annotation
下扫描所有标注有 @Service
的类
2、通过 @Configuration
将 DubboConfiguration
中所有的 @Bean
通过Java Config
的方式组装出来并注入给 Dubbo
服务也就是标注有@Service
的类。
这其中就包括了
ProviderConfig服务提供方配置
ApplicationConfig应用配置
RegistryConfig注册中心配置
ProtocolConfig协议配置
看起来很复杂其实。。。
第三步启动服务
package com.sihai.dubbo.provider;
import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan;
import com.sihai.dubbo.provider.configuration.DubboConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import sun.applet.Main;
import java.io.IOException;
/**
* 注解启动方式
*/
public class AppAnnotation {
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DubboConfiguration.class);
context.start();
System.in.read();
}
}
发现输出下面信息就表示 success
了
消费端
同样我们下看看消费端的工程有一个感性认识。
第一步引用服务
package com.sihai.dubbo.consumer.Annotation;
import com.alibaba.dubbo.config.annotation.Reference;
import com.sihai.dubbo.provider.service.annotation.ProviderServiceAnnotation;
import org.springframework.stereotype.Component;
/**
* 注解方式的service
*/
@Component("annotatedConsumer")
public class ConsumerAnnotationService {
@Reference
private ProviderServiceAnnotation providerServiceAnnotation;
public String doSayHello(String name) {
return providerServiceAnnotation.SayHelloAnnotation(name);
}
}
在 ConsumerAnnotationService
类中通过 @Reference
引用服务端提供的类然后通过方法调用这个类的方式给消费端提供接口。
注意如果这里找不到 ProviderServiceAnnotation
类请在服务端先把服务端工程用 Maven intall
一下然后将服务端的依赖放到消费端的pom
中。如下
<dependency>
<groupId>com.ouyangsihai</groupId>
<artifactId>dubbo-provider</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
解释一下引入的jar
包里面只是未被实现的接口rpc
需要在客户端服务端定义一套统一的接口然后在服务端实现接口实际上还是网络通信只不过长得像本地实现
第二步组装服务消费者
这一步和服务端是类似的这里就不在重复了。
package com.sihai.dubbo.consumer.configuration;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ConsumerConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* 注解配置类
*/
@Configuration
@EnableDubbo(scanBasePackages = "com.sihai.dubbo.consumer.Annotation")
@ComponentScan(value = {"com.sihai.dubbo.consumer.Annotation"})
public class ConsumerConfiguration {
@Bean // 应用配置
public ApplicationConfig applicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("dubbo-annotation-consumer");
Map<String, String> stringStringMap = new HashMap<String, String>();
stringStringMap.put("qos.enable","true");
stringStringMap.put("qos.accept.foreign.ip","false");
stringStringMap.put("qos.port","33333");
applicationConfig.setParameters(stringStringMap);
return applicationConfig;
}
@Bean // 服务消费者配置
public ConsumerConfig consumerConfig() {
ConsumerConfig consumerConfig = new ConsumerConfig();
consumerConfig.setTimeout(3000);
return consumerConfig;
}
@Bean // 配置注册中心
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("localhost");
registryConfig.setPort(2181);
return registryConfig;
}
}
第三步发起远程调用
在main
方法中通过启动一个Spring Context
从其中查找到组装好的Dubbo
的服务消费者并发起一次远程调用。
package com.sihai.dubbo.consumer;
import com.sihai.dubbo.consumer.Annotation.ConsumerAnnotationService;
import com.sihai.dubbo.consumer.configuration.ConsumerConfiguration;
import com.sihai.dubbo.provider.service.ProviderService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
/**
* 注解方式启动
*
*/
public class AppAnnotation
{
public static void main( String[] args ) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
context.start(); // 启动
ConsumerAnnotationService consumerAnnotationService = context.getBean(ConsumerAnnotationService.class);
String hello = consumerAnnotationService.doSayHello("annotation"); // 调用方法
System.out.println("result: " + hello); // 输出结果
}
}
结果如下所示
1.9 常用场景
在下面的讲解中都会是以xml
配置的方式来讲解的这也是dubbo
官方比较推荐的方式。以下的操作都是在服务端的 xml
配置文件和消费端的配置文件来讲解的。
1.9.1 启动时检查
Dubbo
缺省会在启动时检查依赖的服务是否可用不可用时会抛出异常阻止 Spring
初始化完成以便上线时能及早发现问题默认check="true"
。
但是有的时候我们并不是都需要启动时就检查的比如测试的时候我们是需要更快速的启动所以这种场景的时候我们是需要关闭这个功能的。
下面我们看看如何使用这个功能。
在服务端注册的时候客户端注册时同样适用
<dubbo:registry protocol="zookeeper" address="localhost:2181,localhost:2182,localhost:2183" check="false"/>
在客户端引用服务端服务的时候
<dubbo:reference check="false" id="providerService"
interface="com.sihai.dubbo.provider.service.ProviderService"/>
就是这么简单就是这么强
1.9.2 集群容错
dubbo
也是支持集群容错的同时也有很多可选的方案其中默认的方案是 failover
也就是重试机制。
首先我们先把所有的容错机制都整理一遍然后再看看使用。
集群模式 | 说明 | 使用方法 |
---|---|---|
Failover Cluster | 失败自动切换当出现失败重试其它服务器。通常用于读操作但重试会带来更长延迟。可通过retries="2" 来设置重试次数(不含第一次)。 | cluster="xxx" xxx集群模式名称 例如cluster="failover" |
Failfast Cluster | 快速失败只发起一次调用失败立即报错。通常用于非幂等性的写操作比如新增记录。 | |
Failsafe Cluster | 失败安全出现异常时直接忽略。 | |
Failback Cluster | 失败自动恢复后台记录失败请求定时重发。通常用于消息通知操作。 | |
Forking Cluster | 并行调用多个服务器只要一个成功即返回。通常用于实时性要求较高的读操作但需要浪费更多服务资源。可通过forks="2" 来设置最大并行数。 | |
Broadcast Cluster | 广播调用所有提供者逐个调用任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。 |
使用实例
在发布服务或者引用服务的时候设置
<!--服务发布的配置需要暴露的服务接口-->
<dubbo:service cluster="failover" retries="2"
interface="com.sihai.dubbo.provider.service.ProviderService"
ref="providerService"/>
<dubbo:reference cluster="failover" retries="2" check="false" id="providerService"
interface="com.sihai.dubbo.provider.service.ProviderService"/>
1.9.3 负载均衡
负载均衡想必是一个再熟悉不过的概念了所以dubbo
支持也是再正常不过了这里也总结一下dubbo
支持的负载均衡的一些方案及使用方法。
负载均衡模式 | 说明 | 使用方法 |
---|---|---|
Random LoadBalance | 随机 按权重设置随机概率 | <dubbo:service loadbalance="xxx"/> xxx负载均衡方法 |
RoundRobin LoadBalance | 轮询 按公约后的权重设置轮询比率。 | |
LeastActive LoadBalance | 最少活跃调用数 相同活跃数的随机活跃数指调用前后计数差。 | |
ConsistentHash LoadBalance | 一致性 Hash 相同参数的请求总是发到同一提供者。 当某一台提供者挂时原本发往该提供者的请求基于虚拟节点平摊到其它提供者不会引起剧烈变动。 |
1.9.4 直连提供者
在开发及测试环境下经常需要绕过注册中心只测试指定服务提供者所以这种情况下我们只需要直接连接服务端的地即可其实这种方法在前面的讲解已经使用到了第一种讲解的方式就是这种方式因为这种方式简单。
使用方法如下所示
<dubbo:reference id="providerService"
interface="com.sihai.dubbo.provider.service.ProviderService"
url="dubbo://192.168.234.1:20880/com.sihai.dubbo.provider.service.ProviderService"/>
说明可以看到只要在消费端在·dubbo:reference
节点使用url
给出服务端的方法即可。
1.9.5 只订阅
只订阅就是只能够订阅服务端的服务而不能够注册。
引用官方的使用场景如下
为方便开发测试经常会在线下共用一个所有服务可用的注册中心这时如果一个正在开发中的服务提供者注册可能会影响消费者不能正常运行。
可以让服务提供者开发方只订阅服务(开发的服务可能依赖其它服务)而不注册正在开发的服务通过直连测试正在开发的服务。
<dubbo:registry register="false" protocol="zookeeper" address="localhost:2181,localhost:2182,localhost:2183" check="false"/>
1、使用只订阅方式
当在服务提供端使用 register="false"
的时候我们使用下面的方式获取服务端的服务
<dubbo:reference cluster="failover" retries="2" check="false" id="providerService"
interface="com.sihai.dubbo.provider.service.ProviderService"/>
启动信息
发现这时候并不是向注册中心 zookeeper
注册而只是做了发布服务和启动netty
。
2、不使用只订阅方式
<dubbo:registry protocol="zookeeper" address="localhost:2181,localhost:2182,localhost:2183" check="false"/>
启动信息
可以发现这里就向注册中心 zookeeper
注册了。
1.9.6 只注册
只注册正好跟前面的只订阅相反这个时候可以向注册中心注册但是消费端却不能够读到服务。
应用场景
如果有两个镜像环境两个注册中心有一个服务只在其中一个注册中心有部署另一个注册中心还没来得及部署而两个注册中心的其它应用都需要依赖此服务。这个时候可以让服务提供者方只注册服务到另一注册中心而不从另一注册中心订阅服务。
使用说明
<dubbo:registry subscribe="false" address="localhost:2181"></dubbo:registry>
在服务端的 dubbo:registry
节点下使用 subscribe="false"
来声明这个服务是只注册的服务。
这个时候消费端调用的时候是不能调用的。
1.9.7 多协议机制
在前面我们使用的协议都是 dubbo
协议但是 dubbo
除了支持这种协议外还支持其他的协议比如rmi、hessian
等另外而且还可以用多种协议同时暴露一种服务。
使用方法
1、一种接口使用一种协议
先声明多种协议
<!--当前服务发布所依赖的协议webserovice、Thrift、Hessain、http-->
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:protocol name="rmi" port="1099" />
然后在发布接口的时候使用具体协议
<!--服务发布的配置需要暴露的服务接口-->
<dubbo:service cluster="failover" retries="2"
interface="com.sihai.dubbo.provider.service.ProviderService"
ref="providerService"/>
<dubbo:service cluster="failover" retries="2"
interface="com.sihai.dubbo.provider.service.ProviderService"
ref="providerService" protocol="rmi"/>
在输出日志中就可以找到rmi
发布的接口。
rmi://192.168.234.1:1099/com.sihai.dubbo.provider.service.ProviderService?anyhost=true&application=provider&bean.name=com.sihai.dubbo.provider.service.ProviderService&cluster=failover&dubbo=2.0.2&generic=false&interface=com.sihai.dubbo.provider.service.ProviderService&methods=SayHello&owner=sihai&pid=796&retries=2&side=provider×tamp=1564281053185, dubbo version: 2.6.6, current host: 192.168.234.1
2、一种接口使用多种协议
声明协议和上面的方式一样在发布接口的时候有一点不一样。
<dubbo:service cluster="failover" retries="2"
interface="com.sihai.dubbo.provider.service.ProviderService"
ref="providerService" protocol="rmi,dubbo"/>
说明protocol
属性可以用,
隔开使用多种协议。
1.9.8 多注册中心
Dubbo
支持同一服务向多注册中心同时注册或者不同服务分别注册到不同的注册中心上去甚至可以同时引用注册在不同注册中心上的同名服务。
服务端多注册中心发布服务
一个服务可以在不同的注册中心注册当一个注册中心出现问题时可以用其他的注册中心。
注册
<!--多注册中心-->
<dubbo:registry protocol="zookeeper" id="reg1" timeout="10000" address="localhost:2181"/>
<dubbo:registry protocol="zookeeper" id="reg2" timeout="10000" address="localhost:2182"/>
<dubbo:registry protocol="zookeeper" id="reg3" timeout="10000" address="localhost:2183"/>
发布服务
<!--服务发布的配置需要暴露的服务接口-->
<dubbo:service cluster="failover" retries="2"
interface="com.sihai.dubbo.provider.service.ProviderService"
ref="providerService" registry="reg1"/>
<dubbo:service cluster="failover" retries="2"
interface="com.sihai.dubbo.provider.service.ProviderService"
ref="providerService" protocol="rmi" registry="reg2"/>
说明使用registry="reg2"
指定该接口使用的注册中心同时也可以使用多个用隔开例如
registry="reg1,,reg2"
。
消费端多注册中心引用服务
首先先向不同注册中心注册;
<!--多注册中心-->
<dubbo:registry protocol="zookeeper" id="reg1" timeout="10000" address="localhost:2181"/>
<dubbo:registry protocol="zookeeper" id="reg2" timeout="10000" address="localhost:2182"/>
<dubbo:registry protocol="zookeeper" id="reg3" timeout="10000" address="localhost:2183"/>
其次不同的消费端服务引用使用不同的注册中心
<!--不同的服务使用不同的注册中心-->
<dubbo:reference cluster="failover" retries="2" check="false" id="providerService"
interface="com.sihai.dubbo.provider.service.ProviderService" registry="reg1"/>
<dubbo:reference cluster="failover" retries="2" check="false" id="providerService2"
interface="com.sihai.dubbo.provider.service.ProviderService" registry="reg2"/>
说明上面分别使用注册中心1
和注册中心2
。
1.9.9 多版本
不同的服务是有版本不同的版本可以更新并且升级同时不同的版本之间是不可以调用的。
<!--服务发布的配置需要暴露的服务接口-->
<dubbo:service cluster="failover" retries="2"
interface="com.sihai.dubbo.provider.service.ProviderService"
ref="providerService" registry="reg1" version="1.0.0"/>
<dubbo:service cluster="failover" retries="2"
interface="com.sihai.dubbo.provider.service.ProviderService"
ref="providerService" protocol="rmi" registry="reg2" version="1.0.0"/>
加入了版本控制。
1.9.10 日志管理
dubbo
也可以将日志信息记录或者保存到文件中的。
1、使用accesslog
输出到log4j
<dubbo:protocol accesslog="true" name="dubbo" port="20880"/>
<dubbo:protocol accesslog="true" name="rmi" port="1099" />
2、输出到文件
<dubbo:protocol accesslog="http://localhost/log.txt" name="dubbo" port="20880"/>
<dubbo:protocol accesslog="http://localhost/log2.txt" name="rmi" port="1099" />