发布服务 ServiceBean
发布入口
- ServiceBean实现接口InitializingBean的afterPropertiesSet()方法.
public void afterPropertiesSet() throws Exception {
//设置 provider,application,registries....
//发布服务
if (!isDelay()) {
export();
}
}
- 如果还没有发布服务,那么开始发布
public void onApplicationEvent(ApplicationEvent event) {
if (ContextRefreshedEvent.class.getName().equals(
event.getClass().getName())) {
if (isDelay() && ! isExported() && ! isUnexported()) {
export();
}
}
}
真正开始发布服务的是doExportUrls();
真正的开始发布服务
- 获取注册中心的url地址
- 循环协议,向协议注册url
private void doExportUrls() {
//获取注册中心的url地址
List<URL> registryURLs = loadRegistries(true);
//<dubbo:protocol name="dubbo" port="20880" id="dubbo" />
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
获取注册中心的url地址
<dubbo:registry protocol="zookeeper" address="10.118.22.25:2181" />
/**registry://224.5.6.7:1234/com.alibaba.dubbo.registry
.RegistryService?application=demo-provider&dubbo=2.0.0&pid=2964
&qos.port=22222®istry=multicast×tamp=1523178188368
**/
protected List<URL> loadRegistries(boolean provider) {
List<URL> registryList = new ArrayList<URL>();
if (registries != null && !registries.isEmpty()) {
for (RegistryConfig config : registries) {
String address = config.getAddress();
Map<String, String> map = new HashMap<String, String>();
//组装dubbo版本号、protocol为dubbo、application...
registryList.add(url);
}
}
}
向协议注册url
a) 组装dubbo版本号、application、interface、methods以及ip地址和端口号
dubbo://10.118.14.178:20890/com.test.ITestService?anyhost=true&application=testservice&default.cluster=failfast&default.timeout=60000&dubbo=2.8.4
&generic=false
&interface=com.test.ITestService&logger=slf4j&methods=test,save&pid=8832&revision=1.0-SNAPSHOT&side=provider×tamp=1523602774419&version=1.0.0
b)暴露服务
I) 如果有注册中心的话,将组装好的url地址组装成
registry://10.118.22.25:2181/com.alibaba.dubbo.registry.RegistryService?application=testservice&dubbo=2.8.4&export= dubbo://10.118.14.178:20890/com.test.ITestService?anyhost=true&application=testservice&default.cluster=failfast&default.timeout=60000&dubbo=2.8.4
&generic=false
&interface=com.test.ITestService&logger=slf4j&methods=test,save&pid=8832&revision=1.0-SNAPSHOT&side=provider×tamp=1523602774419&version=1.0.0
走RegistryProtocol协议.
II) 没有没有注册中心,那么根据协议dubbo,走DubboProtocol
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig,
List<URL> registryURLs) {
//组装url
//默认是dubbo协议
String name = protocolConfig.getName();
if (name == null || name.length() == 0) {
name = "dubbo";
}
Map<String, String> map = new HashMap<String, String>();
//组装dubbo版本号、application、interface(com.alibaba.dubbo.demo.DemoService)
//、side (provider)...
//获取到接口中所有的方法名称
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames()
//组装接口中的方法methods(sayHello)
HashSet<String>(Arrays.asList(methods)), ","));
//组装绑定ip
String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
//组装端口号20880
Integer port = this.findConfigedPorts(protocolConfig, name, map);
URL url = new URL(name, host, port, (contextPath == null
|| contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
//<dubbo:protocol name="dubbo" port="20890" id="dubbo" />
//如果协议是本地的话,那就不需要注册
if ("injvm".equals(protocolConfig.getName())) {
protocolConfig.setRegister(false);
map.put("notify", "false");
}
//暴露服务
String scope = url.getParameter(Constants.SCOPE_KEY);
//配置为none不暴露
if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
//配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
exportLocal(url);
}
//如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
//走Registry协议
if (registryURLs != null && registryURLs.size() > 0
&& url.getParameter("register", true)) {
for (URL registryURL : registryURLs) {
Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);
}
}else {
//走dubbo协议
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class)
interfaceClass, url);
Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);
}
}
}
}
暴露本地服务, 不做操作
private void exportLocal(URL url) {
if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
/**组装本地的url
injvm://127.0.0.1/com.test.ITestService?anyhost=true&application=testService&
default.cluster=failfast&default.timeout=60000&dubbo=2.8.4&generic=false
&interface=com.test.ITestService&logger=slf4j&methods=
test,save&version=1.0.0
**/
URL local = URL.valueOf(url.toFullString())
.setProtocol("injvm")
.setHost("127.0.0.1")
.setPort(0);
//将接口的实现类放入到ThreadLocal中com.test.ITestService
ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
Exporter<?> exporter = protocol.export(
proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
exporters.add(exporter);
}
}
获取主机ip绑定
1) 取dubbo:protocol中的host
2) 取本地地址InetAddress.getLocalHost().getHostAddress();
3)循环注册中心的地址,连接成功就是那个ip
private String findConfigedHosts(ProtocolConfig protocolConfig,
List<URL> registryURLs, Map<String, String> map) {
//1. <dubbo:protocol host="">
String host = protocolConfig.getHost();
if (provider != null && (host == null || host.length() == 0)) {
host = provider.getHost();
}
boolean anyhost = false;
if (NetUtils.isInvalidLocalHost(host)) {
anyhost = true;
try {
// 2. 获取本地
host = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
logger.warn(e.getMessage(), e);
}
if (NetUtils.isInvalidLocalHost(host)) {
//3.循环注册中心的地址,是否能连接
if (registryURLs != null && registryURLs.size() > 0) {
for (URL registryURL : registryURLs) {
try {
Socket socket = new Socket();
try {
SocketAddress addr = new InetSocketAddress(registryURL.getHost(),
registryURL.getPort());
socket.connect(addr, 1000);
host = socket.getLocalAddress().getHostAddress();
break;
} finally {
try {
socket.close();
} catch (Throwable e) {}
}
} catch (Exception e) {
logger.warn(e.getMessage(), e);
}
}
}
if (NetUtils.isInvalidLocalHost(host)) {
host = NetUtils.getLocalHost();
}
}
}
map.put("bind_ip", hostToBind);
}