因原版的项目比较久远 还采用的是web.xml来初始化配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<display-name>Dubbo monitor</display-name>
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.handu.open.dubbo.monitor.config.MonitorConfig</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*
代码中可以看出除了一些基本spring配置额外的就是加载了MonitorConfig这个配置文件
/**
* Copyright 2006-2015 handu.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.handu.open.dubbo.monitor.config;
import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
/**
* MonitorConfig
*
* @author Jinkai.Ma
*/
@Configuration
@ComponentScan(basePackages = {"com.handu.open.dubbo.monitor"}, includeFilters = {@ComponentScan.Filter(value = Service.class)})
@Import({WebConfig.class, DubboConfig.class, MyBatisConfig.class, Security.class})
@PropertySource("classpath:/application.properties")
public class MonitorConfig {
}
点进去发现其实就是
1.初始化了包扫描 2.加载了一些基本的配置类 3.加载了application.properties文件
与dubbo有关系的其实主要就是DubboConfig.class 和application.properties这两个文件
/**
* Copyright 2006-2015 handu.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.handu.open.dubbo.monitor.config;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.AnnotationBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* DubboConfig
*
* @author Jinkai.Ma
*/
@Configuration
public class DubboConfig {
private static final String REGISTRY_ADDRESS = "dubbo.registry.address";
private static final String APPLICATION_NAME = "dubbo.application.name";
private static final String APPLICATION_OWNER = "dubbo.application.owner";
private static final String PROTOCOL_PORT = "dubbo.protocol.port";
@Autowired
private Environment env;
//向spring容器中注册一个包扫描的bean
@Bean
public static AnnotationBean annotationBean() {
AnnotationBean annotationBean = new AnnotationBean();
annotationBean.setPackage("com.handu.open.dubbo.monitor");
return annotationBean;
}
//向spring容器注册一个服务配置bean声明服务的名字和服务的所属 这两个值无所谓只是用来区别在dubbo中的服务名
@Bean
public ApplicationConfig applicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName(env.getProperty(APPLICATION_NAME, "dubbo-monitor"));
applicationConfig.setOwner(env.getProperty(APPLICATION_OWNER));
return applicationConfig;
}
//向spring中注册一个注册配置bean声明需要连接的注册中心的位置
@Bean
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress(env.getProperty(REGISTRY_ADDRESS));
return registryConfig;
}
//向spring中注册一个协议设置bean声明连接的协议和连接的端口
@Bean
public ProtocolConfig protocolConfig() {
ProtocolConfig protocolConfig = new ProtocolConfig("dubbo");
protocolConfig.setPort(Integer.parseInt(env.getProperty(PROTOCOL_PORT, "20880")));
return protocolConfig;
}
}
##
# Copyright 2006-2014 handu.com.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
dubbo.application.name=dubbo-monitor
dubbo.application.owner=dubbo-monitor
dubbo.registry.address=zookeeper://192.168.52.128:2181
dubbo.protocol.port=6060
# Database Settings
db.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&transformedBitIsBoolean=true&useSSL=false&autoReconnect=true&serverTimezone=Asia/Shanghai
db.username=root
db.password=自己的数据库密码
db.maxActive=500
db.driverClass=com.mysql.cj.jdbc.Driver
# System Manager
manager.username=admin
manager.password=admin
这两个配置其实也就是在spring初始化的时候加载一些基本的zookeeper的信息,方便程序连接zookeeper 和从zookeeper中获取数据
@Import({MonitorConfig.class})
@SpringBootApplication
/**
* Copyright 2006-2015 handu.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zyzh.zz.dubbo.config;
import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
/**
* MonitorConfig
*
* @author Jinkai.Ma
*/
@Configuration
@ComponentScan(basePackages = {"com.**"}, includeFilters = {@ComponentScan.Filter(value = Service.class)})
@Import({ DubboConfig.class})
@PropertySource("classpath:dubbo.properties")
public class MonitorConfig {
}
dubbo.properties文件
dubbo.application.name=dubbo-monitor
dubbo.application.owner=dubbo-monitor
dubbo.registry.address=zookeeper://192.168.52.128:2181
dubbo.protocol.port=7070
韩都衣舍的dubbo-monitoring之所以可以监控dubbo服务 其实主题核心在两个java文件中
其中DubboMonitorService 主要负责记录日志将监控信息写到数据库中 因为不需要所以没有多做研究
重点就放在了RegistryContainer这个类中
建议阅读顺序 应该先阅读start方法 如果start方法弄懂了其他一切都很简单
/**
* Copyright 2006-2015 handu.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zyzh.zz.dubbo;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.registry.NotifyListener;
import com.alibaba.dubbo.registry.RegistryService;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author GEP
* 连接dubbo服务的zookeeper注册中心
* 将dubbo服务的信息动态的封装到本地变量中
* 为dubbo监控服务的核心类
*/
@Service
public class RegistryContainer {
/**
* 封装所有的服务名称的集合
*/
private final Set<String> applications = new ConcurrentHashSet<String>();
/**
* 封装所有的提供者接口的map集合 格式为<接口的全类名,<服务的名称>>
*/
private final Map<String, Set<String>> providerServiceApplications = new ConcurrentHashMap<String, Set<String>>();
/**
* 封装所有的提供者接口的map集合 格式为<服务的名称,<接口的全类名>>
*/
private final Map<String, Set<String>> providerApplicationServices = new ConcurrentHashMap<String, Set<String>>();
/**
* 封装所有的消费者接口的map集合 格式为<接口的全类名,<服务的名称>>
*/
private final Map<String, Set<String>> consumerServiceApplications = new ConcurrentHashMap<String, Set<String>>();
/**
* 封装所有的消费者接口的map集合 格式为<接口的全类名,<服务的名称>>
*/
private final Map<String, Set<String>> consumerApplicationServices = new ConcurrentHashMap<String, Set<String>>();
/**
* 封装所有的服务的接口的全类名的集合
*/
private final Set<String> services = new ConcurrentHashSet<String>();
/**
* 封装所有提供者的服务map结构为<接口的全类名,> 该变量为重型变量 所以应该尽量避免频繁使用
*/
private final Map<String, List<URL>> serviceProviders = new ConcurrentHashMap<String, List<URL>>();
/**
* 封装所有消费者的服务map结构为<接口的全类名,> 该变量为重型变量 所以应该尽量避免频繁使用
*/
private final Map<String, List<URL>> serviceConsumers = new ConcurrentHashMap<String, List<URL>>();
/**
* registry为curator包中的一个代理对象
* 要想使用该对象 需要先先引入org.apache.curator的相关依赖
* 其主要作用是对zookeeper中心进行操作
*/
@Reference
private RegistryService registry;
public RegistryService getRegistry() {
return registry;
}
/**
* 获得所有的服务名称
* @return 服务名称集合
*/
public Set<String> getApplications() {
return Collections.unmodifiableSet(applications);
}
/**
* 查看引用关系 根据服务名称 获得对应的提供(消费)的服务名称
* @param application 服务的名称
* @param reverse true:提供者-->消费者 false:消费者-->提供者
* @return 对应的服务列表
*/
public Set<String> getDependencies(String application, boolean reverse) {
//判断流向
if (reverse) {
Set<String> dependencies = new HashSet<String>();
//获得所有的提供方接口列表
Set<String> services = providerApplicationServices.get(application);
//如果列表不为空
if (services != null && services.size() > 0) {
for (String service : services) {
//根据接口列表获得消费的服务列表
Set<String> applications = consumerServiceApplications.get(service);
if (applications != null && applications.size() > 0) {
//封装数据
dependencies.addAll(applications);
}
}
}
//返回
return dependencies;
} else {
Set<String> dependencies = new HashSet<String>();
//根据消费者的服务名称获得接口列表
Set<String> services = consumerApplicationServices.get(application);
if (services != null && services.size() > 0) {
for (String service : services) {
//根据接口列表获得所有的提供者服务名称
Set<String> applications = providerServiceApplications.get(service);
if (applications != null && applications.size() > 0) {
//封装数据
dependencies.addAll(applications);
}
}
}
return dependencies;
}
}
/**
* 获得所有的接口名称
* @return 接口的集合
*/
public Set<String> getServices() {
return Collections.unmodifiableSet(services);
}
/**
* 获得所有的提供者信息
* @return 提供者信息
*/
public Map<String, List<URL>> getServiceProviders() {
return Collections.unmodifiableMap(serviceProviders);
}
/**
* 根据接口名称获得提供者的URL列表对象
* 获得的值无法进行修改
* @param service 接口名称
* @return URL列表对象
*/
public List<URL> getProvidersByService(String service) {
List<URL> urls = serviceProviders.get(service);
return urls == null ? null : Collections.unmodifiableList(urls);
}
/**
* 根据ip获得提供者的url列表对象
* @param host ip地址
* @return url列表对象
*/
public List<URL> getProvidersByHost(String host) {
List<URL> urls = new ArrayList<URL>();
if (host != null && host.length() > 0) {
for (List<URL> providers : serviceProviders.values()) {
for (URL url : providers) {
if (host.equals(url.getHost())) {
urls.add(url);
}
}
}
}
return urls;
}
/**
* 根据服务名称获得提供者的url列表对象
* @param application 服务名称
* @return url列表对象
*/
public List<URL> getProvidersByApplication(String application) {
List<URL> urls = new ArrayList<URL>();
if (application != null && application.length() > 0) {
for (List<URL> providers : serviceProviders.values()) {
for (URL url : providers) {
if (application.equals(url.getParameter(Constants.APPLICATION_KEY))) {
urls.add(url);
}
}
}
}
return urls;
}
/**
* 获得所有的ip列表
* @return ip列表
*/
public Set<String> getHosts() {
Set<String> addresses = new HashSet<String>();
for (List<URL> providers : serviceProviders.values()) {
for (URL url : providers) {
addresses.add(url.getHost());
}
}
for (List<URL> providers : serviceConsumers.values()) {
for (URL url : providers) {
addresses.add(url.getHost());
}
}
return addresses;
}
/**
* 获得所有的消费者列表
* @return 消费者服务列表
*/
public Map<String, List<URL>> getServiceConsumers() {
return Collections.unmodifiableMap(serviceConsumers);
}
/**
* 根据接口名称获得所有的消费者url列表
* @param service 接口名称
* @return 消费者url列表
*/
public List<URL> getConsumersByService(String service) {
List<URL> urls = serviceConsumers.get(service);
return urls == null ? null : Collections.unmodifiableList(urls);
}
/**
* 根据ip信息获得所有的消费者的url列表
* @param host 接口名称
* @return 消费者的url列表
*/
public List<URL> getConsumersByHost(String host) {
List<URL> urls = new ArrayList<URL>();
if (host != null && host.length() > 0) {
for (List<URL> consumers : serviceConsumers.values()) {
for (URL url : consumers) {
if (host.equals(url.getHost())) {
urls.add(url);
}
}
}
}
return Collections.unmodifiableList(urls);
}
/**
* 根据所有的服务名称获得所有的消费者的url列表
* @param application 服务名称
* @return 消费者的url列表
*/
public List<URL> getConsumersByApplication(String application) {
List<URL> urls = new ArrayList<URL>();
if (application != null && application.length() > 0) {
for (List<URL> consumers : serviceConsumers.values()) {
for (URL url : consumers) {
if (application.equals(url.getParameter(Constants.APPLICATION_KEY))) {
urls.add(url);
}
}
}
}
return urls;
}
/**
* 容器初始化的时候执行的方法
* 会在servlet容器初始化的时候请求注册中心将注册中心中的数据动态的封装到本地的变量中
*/
@PostConstruct
public void start() {
//创建指向注册中心zookeeper的url对象 该对象声明返回需要的数据
//其中的Constants.ADMIN_PROTOCOL...其实只是些固定的参数就是定义好的字符串常量
//不要在这些上面浪费大量时间理解意思即可
URL subscribeUrl = new URL(Constants.ADMIN_PROTOCOL, NetUtils.getLocalHost(), 0, "",
Constants.INTERFACE_KEY, Constants.ANY_VALUE,
Constants.GROUP_KEY, Constants.ANY_VALUE,
Constants.VERSION_KEY, Constants.ANY_VALUE,
Constants.CLASSIFIER_KEY, Constants.ANY_VALUE,
Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY + ","
+ Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false));
//执行认证 并设置监听类动态的接收从注册中心返回的数据
registry.subscribe(subscribeUrl, new NotifyListener() {
//返回的数据封装在urls中
public void notify(List<URL> urls) {
//判断urls是否为空 如果为空 直接结束方法 不做任何处理
if (urls == null || urls.size() == 0) {
return;
}
//服务提供者map key值为接口全类名
Map<String, List<URL>> proivderMap = new HashMap<String, List<URL>>();
//服务消费者map key值为接口全类名
Map<String, List<URL>> consumerMap = new HashMap<String, List<URL>>();
for (URL url : urls) {
//从url中获得服务的名称
//url中内部维护了存储参数map集合parameters该集合封装了一些特定的参数 详情请参照dubbo源码
String application = url.getParameter(Constants.APPLICATION_KEY);
//将所有的服务名称封装到本地的集合中
//由于该监听需要考虑多个线程的问题监控多个服务中心 所以所声明的变量均为线程安全的Concurrent*
if (application != null && application.length() > 0) {
applications.add(application);
}
//获得服务的接口名称并封装到集合中 该名称为接口的全类名
String service = url.getServiceInterface();
services.add(service);
//获得url服务的种类 默认为服务的提供者 默认值是通过方法的第二个参数来进行设定的
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
//如果返回的url确实为服务的提供者
if (Constants.PROVIDERS_CATEGORY.equals(category)) {
//如果服务的url的协议为"empty" 说明提供者的服务挂掉了 就将服务从服务列表中进行移除
if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
serviceProviders.remove(service);
} else {
//服务提供者map中是否已经存在url列表如果不存在 就重新创建一个新的集合并放在 服务提供者map中 避免之后添加的时候出现空指针 主要用在新注册服务的添加上
List<URL> list = proivderMap.get(service);
if (list == null) {
list = new ArrayList<URL>();
//将服务的接口名称作为key值进行存储
proivderMap.put(service, list);
}
//将数据添加到集合中 因为对list中进行元素添加只是操作内存中的数据 所以相应的服务提供者map中也会有值
list.add(url);
//将服务名称添加到服务提供者的集合中
if (application != null && application.length() > 0) {
//判断服务提供者集合中是否存在该服务 如果不存在经进行创建 原理与上面一样
Set<String> serviceApplications = providerServiceApplications.get(service);
if (serviceApplications == null) {
providerServiceApplications.put(service, new ConcurrentHashSet<String>());
serviceApplications = providerServiceApplications.get(service);
}
serviceApplications.add(application);
//判断服务提供者集合中是否存在该服务 如果不存在经进行创建 该集合和上面的key-value形式刚好相反
Set<String> applicationServices = providerApplicationServices.get(application);
if (applicationServices == null) {
providerApplicationServices.put(application, new ConcurrentHashSet<String>());
applicationServices = providerApplicationServices.get(application);
}
applicationServices.add(service);
}
}
//判断消费者 原理和判断提供者相同 不做详细注解
} else if (Constants.CONSUMERS_CATEGORY.equals(category)) {
//判断服务是否发生异常
if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
//移除服务
serviceConsumers.remove(service);
} else {
//添加消费者的url
List<URL> list = consumerMap.get(service);
if (list == null) {
list = new ArrayList<URL>();
consumerMap.put(service, list);
}
list.add(url);
//向消费者集合中添加数据
if (application != null && application.length() > 0) {
Set<String> serviceApplications = consumerServiceApplications.get(service);
if (serviceApplications == null) {
consumerServiceApplications.put(service, new ConcurrentHashSet<String>());
serviceApplications = consumerServiceApplications.get(service);
}
serviceApplications.add(application);
Set<String> applicationServices = consumerApplicationServices.get(application);
if (applicationServices == null) {
consumerApplicationServices.put(application, new ConcurrentHashSet<String>());
applicationServices = consumerApplicationServices.get(application);
}
applicationServices.add(service);
}
}
}
}
//将申明的方法变量添加到类变量中
if (proivderMap != null && proivderMap.size() > 0) {
serviceProviders.putAll(proivderMap);
}
if (consumerMap != null && consumerMap.size() > 0) {
serviceConsumers.putAll(consumerMap);
}
}
});
}
@PreDestroy
public void stop() {
}
}
这样就将dubbo中的注册中心的服务信息封装到本地的集合中
其他在调用的时候只需要将其注入到需要调用的类中即可获得dubbo的服务列表
直接通过autowired引用即可获得注册信息