Dubbo的客户端负载均衡关键过程

String applicationConfig = "consumer-applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(applicationConfig);
UserInfoService userInfoService = (UserInfoService) context.getBean("userInfoService");
//负载均衡的处理是一个典型的懒加载模式,只有在第一次调用接口时,才进行负载均衡的处理。
System.out.println(userInfoService.sayHello("zhangsan"));

1.在进入sayHello的调用时,堆栈信息和帧代码如下


其中Constants.LOADBALANCE_KEY的值是“loadbalance”,而Constants.DEFAULT_LOADBALANCE的值是“random”,每个接口(reference)均可以灵活配置一个均衡方式,默认不配置的情况下都是random的。

不配置loadbalance时,是random的模式。

2.假如当前的loadbalance设置了leastactive时,它是如何把名字和类LeastActiveLoadBalance的实例关联起来呢?
3.首先它在com.alibaba.dubbo.rpc.cluster.loadbalance目录下实现了4种均衡方式,如下:

ConsistentHashLoadBalance:一致哈希
LeastActiveLoadBalance:最久不使用
RandomLoadBalance:随机
RoundRobinLoadBalance:顺序循环

注:上1点中的loadbalance返回的则是上述的其中一个均衡策略类实例。
4.其它dubbo实现了一套类似spi的服务加载机制如下:

private void loadFile(Map> extensionClasses, String dir) {
        String fileName = dir + type.getName();
        try {
            Enumeration urls;
            ClassLoader classLoader = findClassLoader();
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL url = urls.nextElement();
                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
                        try {
                            String line = null;
                            while ((line = reader.readLine()) != null) {
                                final int ci = line.indexOf('#');
                                if (ci >= 0) line = line.substring(0, ci);
                                line = line.trim();
                                if (line.length() > 0) {
                                    try {
                                        String name = null;
                                        int i = line.indexOf('=');
                                        if (i > 0) {
                                            name = line.substring(0, i).trim();
                                            line = line.substring(i + 1).trim();
                                        }
                                        if (line.length() > 0) {
                                            Class clazz = Class.forName(line, true, classLoader);
                                            if (! type.isAssignableFrom(clazz)) {
                                                throw new IllegalStateException("Error when load extension class(interface: " +
                                                        type + ", class line: " + clazz.getName() + "), class " 
                                                        + clazz.getName() + "is not subtype of interface.");
                                            }
                                            if (clazz.isAnnotationPresent(Adaptive.class)) {
                                                if(cachedAdaptiveClass == null) {
                                                    cachedAdaptiveClass = clazz;
                                                } else if (! cachedAdaptiveClass.equals(clazz)) {
                                                    throw new IllegalStateException("More than 1 adaptive class found: "
                                                            + cachedAdaptiveClass.getClass().getName()
                                                            + ", " + clazz.getClass().getName());
                                                }
                                            } else {
                                                try {
                                                    clazz.getConstructor(type);
                                                    Set> wrappers = cachedWrapperClasses;
                                                    if (wrappers == null) {
                                                        cachedWrapperClasses = new ConcurrentHashSet>();
                                                        wrappers = cachedWrapperClasses;
                                                    }
                                                    wrappers.add(clazz);
                                                } catch (NoSuchMethodException e) {
                                                    clazz.getConstructor();
                                                    if (name == null || name.length() == 0) {
                                                        name = findAnnotationName(clazz);
                                                        if (name == null || name.length() == 0) {
                                                            if (clazz.getSimpleName().length() > type.getSimpleName().length()
                                                                    && clazz.getSimpleName().endsWith(type.getSimpleName())) {
                                                                name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
                                                            } else {
                                                                throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
                                                            }
                                                        }
                                                    }
                                                    String[] names = NAME_SEPARATOR.split(name);
                                                    if (names != null && names.length > 0) {
                                                        Activate activate = clazz.getAnnotation(Activate.class);
                                                        if (activate != null) {
                                                            cachedActivates.put(names[0], activate);
                                                        }
                                                        for (String n : names) {
                                                            if (! cachedNames.containsKey(clazz)) {
                                                                cachedNames.put(clazz, n);
                                                            }
                                                            Class c = extensionClasses.get(n);
                                                            if (c == null) {
                                                                extensionClasses.put(n, clazz);
                                                            } else if (c != clazz) {
                                                                throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    } catch (Throwable t) {
                                        IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
                                        exceptions.put(line, e);
                                    }
                                }
                            } // end of while read lines
                        } finally {
                            reader.close();
                        }
                    } catch (Throwable t) {
                        logger.error("Exception when load extension class(interface: " +
                                            type + ", class file: " + url + ") in " + url, t);
                    }
                } // end of while urls
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

5.每个均衡策略类均实现了select接口,通过该接口从多个可用的后端服务中返回其中一个服务。

 Invoker select(List> invokers, URL url, Invocation invocation) throws RpcException;

Dubbo客户端的RPC构造关键过程

1.Dubbo的客户端请求配置和调用代码如下:

    
    
    
        String applicationConfig = "consumer-applicationContext.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(applicationConfig);

注:在上一文章,曾介绍xml表的命名空间间解析过程,而dubbo:service是服务端配置,而dubbo:reference是客户端配置。
2.构造beanmap列表时,每一行dubbo::refence对应一个ReferenceBean配置。Reference会创建三个对象,如下:
FailoverClusterInvoker:失败转移使用,如服务器宕机等,是从com.alibaba.dubbo.common.Node接口派生的。
MockClusterInvoker:如其名,接口集群化,同一个接口,存在多个服务器为其提供服务支持时,需要一个均衡策略地访问这些服务器。
InvokerInvocationHandler:这个接口是真正的动态代理,它是从InvocationHandler中派生出来的,Failover和MockCluster都是从Node接口派生,用于管理集群的。
3.如下调用代码

UserInfoService userInfoService = (UserInfoService) context.getBean("userInfoService");
System.out.println(userInfoService.sayHello("zhangsan"));


4.如下代码,通过元数据RpcInvocation打包函数名和函数参数,然后通过RPC通信把请求发送到服务器端。

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }

Dubbo的xml命名空间解析关键

在Spring中的applicationContext.xml经常看到如下标签





1.Dubbo创建了一个dubbo.xsd文件。
2.并为dubbo.xsd创建了另外两个文件:spring.handlers和spring.schemas(这是Spring的命名解析规范要求),Spring在解析过程中会主动搜索这两资源文件。

3.spring.handlers文件内容如下

http://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler

4.spring.schemas文件内容如下

http://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd

5.在执行下面代码时,会主动触发相关文件解析。

String configure = "provider-applicationContext.xml";
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(configure);



6.具体如下:

public class DubboNamespaceHandler extends NamespaceHandlerSupport {
	static {
		Version.checkDuplicate(DubboNamespaceHandler.class);
	}
	public void init() {
	    registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
    }
}

7.上述步骤,已经完成了beanmap列表的建立,但仍需要下述代码,触发Dubbo的建立RPC服务,其中xxxConfig是在xxxBean之前先创建的。

context.start();
context.registerShutdownHook();

8.在6步骤中的的ServiceBean中监听了ApplicationEvent如下:

public void onApplicationEvent(ApplicationEvent event) {
        if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
        	if (isDelay() && ! isExported() && ! isUnexported()) {
                if (logger.isInfoEnabled()) {
                    logger.info("The service ready on spring started. service: " + getInterface());
                }
                会把xxxConfig的内容,初始化ServiceBean里的对象。
                export();
            }
        }
    }

9.serviceBean是多个的,每个service对象,对应一个,故每个对象都监听了ApplicationEvent。


    
    
    
    
    

动态 IP 、固定 IP 、实体 IP 与虚拟 IP

看到论坛中的一段介绍,感觉豁然开即朗的感觉,简单总结一下:
从个人感觉:外网IP就是真实IP,而内网IP或局域网IP就是虚拟IP。

以下是摘录别人的:

动态 IP 、固定 IP 、实体 IP 与虚拟 IP都讲解一下,加深理解和知识扩展

实体 IP:在网络的世界里,为了要辨识每一部计算机的位置,因此有了计算机 IP 位址的定义。一个 IP 就好似一个门牌!例如,你要去微软的网站的话,就要去『 207.46.197.101 』这个 IP 位置!这些可以直接在网际网络上沟通的 IP 就被称为『实体 IP 』了。

虚拟 IP:不过,众所皆知的,IP 位址仅为 xxx.xxx.xxx.xxx 的资料型态,其中, xxx 为 1-255 间的整数,由于近来计算机的成长速度太快,实体的 IP 已经有点不足了,好在早在规划 IP 时就已经预留了三个网段的 IP 做为内部网域的虚拟 IP 之用。这三个预留的 IP 分别为:

A级:10.0.0.0 – 10.255.255.255
B级:172.16.0.0 – 172.31.255.255
C级:192.168.0.0 – 192.168.255.255

上述中最常用的是192.168.0.0这一组。不过,由于是虚拟 IP ,所以当您使用这些地址的时候﹐当然是有所限制的,限制如下:

私有位址的路由信息不能对外散播
使用私有位址作为来源或目的地址的封包﹐不能透过Internet来转送
关于私有位址的参考纪录(如DNS)﹐只能限于内部网络使用

由于虚拟 IP 的计算机并不能直接连上 Internet ,因此需要特别的功能才能上网。不过,这给我们架设IP网络做成很大的方便﹐比如﹕即使您目前的公司还没有连上Internet﹐但不保证将来不会啊。如果使用公共IP的话﹐如果没经过注册﹐等到以后真正要连上网络的时候﹐就很可能和别人冲突了。也正如前面所分析的﹐到时候再重新规划IP的话﹐将是件非常头痛的问题。这时候﹐我们可以先利用私有位址来架设网络﹐等到真要连上intetnet的时候﹐我们可以使用IP转换协定﹐如 NAT (Network Addresss Translation)等技术﹐配合新注册的IP就可以了。

固定 IP 与 动态 IP:基本上,这两个东西是由于近来网络公司大量的成长下的产物,例如,你如果向中华电信申请一个商业型态的 ADSL 专线,那他会给你一个固定的实体 IP ,这个实体 IP 就被称为『固定 IP 』了。而若你是申请计时制的 ADSL ,那由于你的 IP 可能是由数十人共同使用,因此你每次重新开机上网时,你这部计算机的 IP 都不会是固定的!于是就被称为『动态 IP』或者是『浮动式IP』。基本上,这两个都是『实体IP』,只是网络公司用来分配给用户的方法不同而产生不同的名称而已

@resource和@Autowired的区别

@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。
@Resource装配顺序
1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;

fastxml.json的序列化

专门分析一下fastxml.json的序列化,偶有所得,截图如下:


另一个示例

public class User {
    public interface WithoutPasswordView {};
    public interface WithPasswordView extends WithoutPasswordView {};

    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @JsonView(WithoutPasswordView.class)
    public String getUsername() {
        return this.username;
    }

    @JsonView(WithPasswordView.class)
    public String getPassword() {
        return this.password;
    }

    public static void main(String[] args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        //创建对象
        User user = new User("isea533","123456");
        //序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        objectMapper.writerWithView(User.WithoutPasswordView.class).writeValue(bos, user);
        System.out.println(bos.toString());

        bos.reset();
        objectMapper.writerWithView(User.WithPasswordView.class).writeValue(bos, user);
        System.out.println(bos.toString());
    }
}

输出结果

{"username":"isea533"}
{"username":"isea533","password":"123456"}
        
	
		com.fasterxml.jackson.core
		jackson-core
		${jackson.version}
	

	
		com.fasterxml.jackson.core
		jackson-databind
		${jackson.version}
	

@EnableWebMvc在做什么?


如红框所示,在构建beanmap的列表过程中,会主动加载该类,构建默认bean列表。在DelegatingWebMvcConfiguration中,会建立以下默认bean列表。
HandlerMapping:
Bean: requestMappingHandlerMapping
Bean: viewControllerHandlerMapping
Bean: beanNameHandlerMapping
Bean: resourceHandlerMapping
Bean: defaultServletHandlerMapping
HandlerAdapter:
Bean: requestMappingHandlerAdapter
Bean: httpRequestHandlerAdapter
Bean: simpleControllerHandlerAdapter
ExceptionResolver
Bean: handlerExceptionResolver
Other:
Bean: mvcConversionService
Bean: mvcValidator
以上bean也是可以通过基于WebMvcConfigurationSupport派生新配置类来进行高级的修改上述bean的配置。

@Configuration
@EnableWebMvc
//@ComponentScan(basePackages = "web.api.module", useDefaultFilters = false, includeFilters = {
//        @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class})
//})
@ComponentScan("web.api.module.*")
@Import({ SecurityConfig.class })
public class WebConfig extends WebMvcConfigurationSupport {
	
	private static final Logger logger = Logger.getLogger(WebConfig.class);

	public WebConfig(){

	}

	@Bean
	public ViewResolver viewResolver() {
		logger.info("ViewResolver");
		InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
		viewResolver.setPrefix("/WEB-INF/views/");
		viewResolver.setSuffix(".jsp");
		return viewResolver;
	}

    @Bean
    public MessageSource messageSource() {
    	logger.info("MessageSource");
    	ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    	messageSource.setBasename("config.messages.messages");
    	
    	return messageSource;
    }

    @Bean
    public HandlerAdapter servletHandlerAdapter(){
    	logger.info("HandlerAdapter");
    	return new SimpleServletHandlerAdapter();
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor(){
    	logger.info("LocaleChangeInterceptor");
    	return new LocaleChangeInterceptor();
    }

    @Bean(name="localeResolver")
    public CookieLocaleResolver cookieLocaleResolver(){
    	logger.info("CookieLocaleResolver");
    	return new CookieLocaleResolver();
    }

	@Bean
	public CsrfIntercepter initializingCsrfInterceptor(){
		logger.info("CsrfIntercepter");
		return new CsrfIntercepter();
	}
    
    /**                                                          
    * 描述 : <注册自定义拦截器>. 
*

<使用方法说明>

* @return */ @Bean public LoginCheckInterceptor initializingInterceptor(){ logger.info("LoginCheckInterceptor"); return new LoginCheckInterceptor(); } /** * 描述 : .
*

<这个比较奇怪,理论上应该是不需要的>

* @return */ @Bean public RequestMappingHandlerMapping requestMappingHandlerMapping() { logger.info("RequestMappingHandlerMapping"); return super.requestMappingHandlerMapping(); } @Override protected void addInterceptors(InterceptorRegistry registry) { // TODO Auto-generated method stub logger.info("addInterceptors start"); registry.addInterceptor(localeChangeInterceptor()); registry.addInterceptor(initializingInterceptor()); // registry.addInterceptor(initializingCsrfInterceptor()); logger.info("addInterceptors end"); } @Bean public HandlerMapping resourceHandlerMapping() { logger.info("HandlerMapping"); return super.resourceHandlerMapping(); } /** * 描述 : <资源访问处理器>.
*

<可以在jsp中使用/static/**的方式访问/WEB-INF/static/下的内容>

* @param registry */ @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { logger.info("addResourceHandlers"); registry.addResourceHandler("/static/**").addResourceLocations("/WEB-INF/static/"); } /** * 描述 : <异常处理器>.
*

<系统运行时遇到指定的异常将会跳转到指定的页面>

* @return */ @Bean(name="exceptionResolver") public ExceptionResolver simpleMappingExceptionResolver(){ logger.info("ExceptionResolver"); ExceptionResolver simpleMappingExceptionResolver= new ExceptionResolver(); simpleMappingExceptionResolver.setDefaultErrorView("common_error"); simpleMappingExceptionResolver.setExceptionAttribute("exception"); Properties properties = new Properties(); properties.setProperty("java.lang.RuntimeException", "common_error"); simpleMappingExceptionResolver.setExceptionMappings(properties); return simpleMappingExceptionResolver; } /** * 描述 : .
*

<这个比较奇怪,理论上应该是不需要的>

* @return */ @Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { logger.info("RequestMappingHandlerAdapter"); return super.requestMappingHandlerAdapter(); } @Override protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() { logger.info("ConfigurableWebBindingInitializer"); ConfigurableWebBindingInitializer initializer = super.getConfigurableWebBindingInitializer(); return initializer; } }