原文: http://scud.blogjava.net

CXF是一个比较流行的Web Service框架. ( 当然如果追求更高效, 还可以去搜索ice, thrift, protobuff之类的)
近一个月, 断断续续地又好好看了看CXF的一些代码, CXF的文档还是很欠缺,特别是关于内部实现的东西. 从我的感觉来说, 内部实现还是挺复杂的. Inteceptor, Feature, ConduitSelector 这些概念一大堆, 又差不多可以做类似的事情, 真是让人头晕.

CXF本身提供了一个FailoverFeature, 可以在调用服务出错时切换到其他服务器, 但是无法做到负载均衡, 我研究了几天, 在FailoverFeature的基础上改出来一个LoadBalanceFeature, 当然也同时支持Failover.

首先我们来看看如何使用CXF的FailoverFeature: (下载示例中包括使用xml和代码两种方式, 当然CXF自己还提供了使用wsdl内部定义的方式)

我们需要先准备一个HelloService, 非常简单的一个Web Service, 这里不在贴出, 具体可以看下载包
    调用代码示例:

package org.javascud.extensions.cxf.testfailover;

import org.apache.cxf.clustering.FailoverFeature;
import org.apache.cxf.clustering.RandomStrategy;
import org.apache.cxf.feature.AbstractFeature;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.javascud.extensions.cxf.service.Hello;

import java.util.ArrayList;
import java.util.List;

public class HelloServiceFailOverClient
{
    public static void main(String[] args)
    {
        String helloFirst = "http://localhost:8080/service/Hello";
        String helloSecond = "http://localhost:8081/service/Hello";
        String helloThird = "http://localhost:8082/service/Hello";
        String helloFour = "http://localhost:8083/service/Hello";

List<String> serviceList = new ArrayList<String>();
        serviceList.add(helloFirst);
        serviceList.add(helloSecond);
        serviceList.add(helloThird);
        //serviceList.add(helloFour);

RandomStrategy strategy = new RandomStrategy();
        strategy.setAlternateAddresses(serviceList);

FailoverFeature ff = new FailoverFeature();
        ff.setStrategy(strategy);

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

List<AbstractFeature> features = new ArrayList<AbstractFeature>();
        features.add(ff);

factory.setFeatures(features);
        factory.initFeatures();

factory.setServiceClass(Hello.class);
        //factory.setAddress("http://localhost:8080/service/Hello");

Hello client = (Hello) factory.create();
        String result = client.sayHello("felix");
        System.out.println("result is: " + result);
    }
}

在遇到错误时可以自动使用下一个服务器, 但是必须要自己设置一个地址, 如果不设置的话也可以, 但是会出错然后failover.

下面我们自己来看看我们的 LoadBalanceFeature

1. 首先我们创建一个LoadBalanceFeature (完全和FailoverFeature一样)

Feature是用来定制Server, Client, Bus的一个组件, 具体可以查看AbstractFeature, 我们使用initialize方法来定制Client, 修改Client的Conduit选择器达到负载均衡的目的.

LoadBalanceFeature代码如下:

/**
 * This feature may be applied to a Client so as to enable
 * load balance , use any compatible endpoint for the target service.
 *
 * @author Felix Zhang   Date:2010-10-3 22:58
 * @see org.apache.cxf.clustering.FailoverFeature
 */
public class LoadBalanceFeature extends AbstractFeature {
    private LoadBalanceStrategy loadBalanceStrategy;

@Override
    public void initialize(Client client, Bus bus) {
        LoadBalanceTargetSelector selector = new LoadBalanceTargetSelector();
        selector.setEndpoint(client.getEndpoint());
        selector.setStrategy(getStrategy());
        client.setConduitSelector(selector);
    }

public void setStrategy(LoadBalanceStrategy strategy) {
        loadBalanceStrategy = strategy;
    }

public LoadBalanceStrategy getStrategy() {
        return loadBalanceStrategy;
    }

}

2. 定制一个LoadBalanceStrategy 负载均衡策略
负载均衡策略有很多种, 例如随机选择, 顺序选择等, FailoverFeature提供了三种策略, 总之很简单, 我们在这里就先实现随机策略, 其他的策略都很简单, 几行代码就可以实现了.

这个类主要用来设置/获取所有的提供服务的地址列表, 为了方便控制, 我新增了2个选项:
    A: alwaysChangeEndpoint 是否每次请求都切换地址: 如果只有一个客户端, 可以分担负载. 缺省为true
    B: removeFailedEndpoint 是否从全局的地址列表中移除失败服务地址 -- 如果你没有监测服务器状态的程序

关于动态增删服务地址

  • 可以使用zookeeper等服务实时监测服务器状态, 或者自己写程序实现, 调用strategy.setAlternateAddresses即可.
  • removeFailedEndpoint 如果设置为true, 但没有监测服务器状态的程序, 新增的或者复活的服务器则无法被恢复到地址列表中.
  • 考虑到效率和支持failover, 设置地址列表, 移除地址等没有同步锁.
  • 自动移除失败服务地址时, 目前仅支持手动地址列表, 没有考虑wsdl中的多服务地址.
  • 后续我会写一个使用zookeeper增删服务地址列表的示例. (最近也在看zookeeper)

主要的代码都在AbstractLoadBalanceStrategy 中, 基本和 AbstractStaticFailoverStrategy 一样, 添加了一个removeAlternateAddress 用于移除失败的服务地址.

LoadBalanceStrategy 接口的代码如下:

/**
 * Supports pluggable strategies for alternate endpoint selection on
 * load balance.
 * <p/>
 * Random, Retries, Mod (later)
 * <p/>
 * 1. support load balance  2.support fail over.
 *
 * @author Felix Zhang   Date:2010-10-1 18:14
 * @see org.apache.cxf.clustering.FailoverStrategy
 */
public interface LoadBalanceStrategy {

/**
     * Get the alternate endpoints for this invocation.
     *
     * @param exchange the current Exchange
     * @return a failover endpoint if one is available
     */
    List<Endpoint> getAlternateEndpoints(Exchange exchange);

/**
     * Select one of the alternate endpoints for a retried invocation.
     *
     * @param alternates List of alternate endpoints if available
     * @return the selected endpoint
     */
    Endpoint selectAlternateEndpoint(List<Endpoint> alternates);

/**
     * Get the alternate addresses for this invocation.
     * These addresses over-ride any addresses specified in the WSDL.
     *
     * @param exchange the current Exchange
     * @return a failover endpoint if one is available
     */
    List<String> getAlternateAddresses(Exchange exchange);

/**
     * Select one of the alternate addresses for a retried invocation.
     *
     * @param addresses List of alternate addresses if available
     * @return the selected address
     */
    String selectAlternateAddress(List<String> addresses);

/**
     * should remove failed endpoint or not.
     * only work for user defined addresses list.
     * @return true or false
     */
    boolean isRemoveFailedEndpoint();

/**
     * change endpoint every time or not.
     * @return boolean
     */
    boolean isAlwaysChangeEndpoint();

/**
     * remove failed address from list.
     * @param address the failed address
     */
    void removeAlternateAddress(String address);
}

RandomLoadBalanceStrategy继承自 AbstractLoadBalanceStrategy, 和 RandomStrategy的区别就是获取下一个服务地址时并不从列表中移除此地址, 否则就做不到负载均衡了.

3. 最重要的 LoadBalanceTargetSelector
    A: 这个类比较复杂, 我们为了实现负载均衡, 修改了prepare来动态设置调用的endpoint, 替换策略取决于LoadBalanceStrategy
    主要代码如下:

            boolean existsEndpoint = false;
            //check current endpoint is not null
            Endpoint theEndpoint = exchange.get(Endpoint.class);
            if (theEndpoint.getEndpointInfo().getAddress() != null) {
                existsEndpoint = true;
            }

Endpoint nextEndpoint;
            if (getStrategy().isAlwaysChangeEndpoint() || !existsEndpoint) {
                //get a endpoint and set to current endpoint
                Endpoint loadBalanceTarget = getLoadBalanceTarget(exchange);
                if (loadBalanceTarget != null) {
                    logger.info("switch to next target: " + loadBalanceTarget.getEndpointInfo().getAddress());
                    setEndpoint(loadBalanceTarget);

//update exchange.org.apache.cxf.message.Message.ENDPOINT_ADDRESS --- 不设置这个就用上次的奇怪
                    message.put(Message.ENDPOINT_ADDRESS, loadBalanceTarget.getEndpointInfo().getAddress());
                }

nextEndpoint = loadBalanceTarget;
            } else {
                //use current endpoint
                nextEndpoint = theEndpoint;
            }

B:为了和原有Failover特性兼容, 我们修改了 getFailoverTarget函数, 在此函数中要移除失败的服务地址, 因为在之前我们修改了LoadBalanceStrategy, 它在获取地址时不再移除当前地址, 所以我们需要手动移除.

部分代码如下:

            String currentAddress = getEndpoint().getEndpointInfo().getAddress();

//failover should remove current endpoint first, then get next -- 根据定义的策略来决定是否从全局地址列表中移除
            if (getStrategy().isRemoveFailedEndpoint()) {
                logger.warn("remove current failed address: " + currentAddress);
                //remove for client, not for current invocation -- 没有同步锁
                getStrategy().removeAlternateAddress(currentAddress);
            }

//remove for current invocation: 当前请求中总是移除失败服务地址
            alternateAddresses.remove(currentAddress);

String alternateAddress =
                    getStrategy().selectAlternateAddress(alternateAddresses);

4. 调用实例:

此处我们采用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:jaxws="http://cxf.apache.org/jaxws"
       xmlns:clustering="http://cxf.apache.org/clustering"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

<util:list id="addressList">
        <value>http://localhost:8081/service/Hello</value>
        <value>http://localhost:8082/service/Hello</value>
        <value>http://localhost:8083/service/Hello</value>
        <value>http://localhost:8086/service/Hello</value>
        <value>http://localhost:8087/service/Hello</value>
        <value>http://localhost:8088/service/Hello</value>
    </util:list>

<bean id="SequentialAddresses" class="org.apache.cxf.clustering.SequentialStrategy">
        <property name="alternateAddresses">
            <ref bean="addressList"/>
        </property>
    </bean>

<bean id="randomAddresses" class="org.javascud.extensions.cxf.RandomLoadBalanceStrategy">
        <property name="alternateAddresses">
            <ref bean="addressList"/>
        </property>
        <property name="removeFailedEndpoint" value="true" />
    </bean>

<bean id="loadBalanceFeature" class="org.javascud.extensions.cxf.LoadBalanceFeature">
        <property name="strategy" ref="randomAddresses" />
    </bean>

<jaxws:client name="helloClient"
                  serviceClass="org.javascud.extensions.cxf.service.Hello"            >
        <jaxws:features>
            <ref bean="loadBalanceFeature" />
        </jaxws:features>
    </jaxws:client>

</beans>

8081, 8082, 8083是实际存在的服务, 其他的不存在.

调用的Java代码:

package org.javascud.extensions.cxf.loadbalance;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.javascud.extensions.cxf.LoadBalanceStrategy;
import org.javascud.extensions.cxf.service.Hello;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HelloLoadBalanceAndFailOverClientByXML
{
    public static void main(String[] args)
    {
        ClassPathXmlApplicationContext context
                = new ClassPathXmlApplicationContext(new String[]
                {"org/javascud/extensions/cxf/loadbalance/loadbalance_fail.xml"});
        Hello client = (Hello) context.getBean("helloClient");

LoadBalanceStrategy strategy = (LoadBalanceStrategy) context.getBean("randomAddresses");

Client myclient = ClientProxy.getClient(client);
        String address = myclient.getEndpoint().getEndpointInfo().getAddress();

System.out.println(address);

for(int i=1; i<=20; i++)
        {
            String result1 = client.sayHello("Felix" +i);
            System.out.println("Call " + i +": " + result1);

int left = strategy.getAlternateAddresses(null).size();
            System.out.println("================== left " + left + " ===========================");
        }

}
}

此处仅仅为模拟测试.

5. 关于测试用例
    没想好如何写单元测试, test里面目前都是随意测试的代码, 基本照顾到所有功能.

6. 下载
代码下载: http://cnscud.googlecode.com/files/extensions-cxf_20101015.zip
源码位置: http://cnscud.googlecode.com/svn/trunk/extensions/  其中cxf目录是此文章相关的源码.

7. 有任何问题请留言.

转载于:https://www.cnblogs.com/yangjin-55/archive/2012/07/16/2786528.html

扩展CXF, 支持LoadBalance负载均衡相关推荐

  1. springcloud ribbon @LoadBalance负载均衡源码流程分析

    一.编写示例 1.服务端 pom.xml <properties><java.version>1.8</java.version><spring-cloud. ...

  2. 【逗老师带你学IT】PRTG自定义脚本获取H3C Loadbalance负载均衡链路状态

    本文介绍如何使PRTG监控系统的自定义脚本功能,ssh登录网络设备,抓取很多snmp无法获取的监控指标. 本文主要涉及的技术点: 1.python paramiko模块应用 2.paramiko模块回 ...

  3. restTemplate loadbalance 负载均衡使用demo 案例 原理以及全网最细源码解析

    restTemplate 是spring 提供的http请求工具,类似于httpclient, 默认情况下与其他的http 工具类没有区别 但是当添加了@Loadbalance 注解之后,则具备了负载 ...

  4. kbengine 的 nginx反向代理https/wss 配置 支持kbe负载均衡

    微信开发要求 必须用https 和 wss连接, 这里给出nginx反向代理配置, 自己使用一切正常,希望能帮助大家.域名一定要在微信开发后台设置,不然连接不上, 域名要备案,不然 审核不通过. ng ...

  5. 小白入门:大型网站技术架构负载均衡技术介绍及学习资源推荐

    十年间,负载均衡的前沿技术层出不穷,令用户眼花缭乱.经常在技术网站.文档中出现的"四层负载均衡"."七层负载均衡"字眼有什么含义?有什么区别?对客户网络有哪些不 ...

  6. 浅谈Kubernetes Service负载均衡实现机制

    女主宣言 Kubernetes Serivce是一组具有相同label Pod集合的抽象(可以简单的理解为集群内的LB),集群内外的各个服务可以通过Service进行互相通信.但是Service的类型 ...

  7. 关于负载均衡的几个问题

    本文来说下负载均衡的知识与内容 文章目录 负载均衡简介 大型网站面临的挑战 什么是负载均衡 负载均衡的分类 载体维度分类 硬件负载均衡 软件负载均衡 网络通信分类 DNS负载均衡 HTTP负载均衡 反 ...

  8. 前置:API:DSP:核心交换机:边界网关协议:边界:(防御)防火墙:负载均衡:摆渡机:名词解释

    前置:API:DSP:核心交换机:边界网关协议:边界:(防御)防火墙:负载均衡:摆渡机:名词解释 前置: 前置,拼音qián zhì,游戏术语,在即时战略游戏(RTS)中,将建筑物建造在作战前线(例如 ...

  9. 详解【负载均衡】(负载均衡算法、一致性hash、负载均衡架构分析)

    作者:duktig 博客:https://duktig.cn 优秀还努力.愿你付出甘之如饴,所得归于欢喜. 本文源码参看:https://github.com/duktig666/distribute ...

最新文章

  1. SLAM中的卡尔曼滤波:究竟滤了谁?
  2. java ssh文件下载_Java使用SSH从远程服务器下载文件
  3. jqprintsetup已经安装还会提示_英雄联盟PBE服务器安装指南 抢先体验新模式“云顶之弈”不用等...
  4. PyQt5利用QPainter绘制各种图形
  5. boost::process::windows相关的测试程序
  6. JSP中动态添加 “添加附件选择框”
  7. 芋道 Spring Boot 自动配置原理
  8. kafka基础之介绍和分布式集群搭建
  9. 关于Windows系统中一些实用的修改常识
  10. BZOJ 1037 [ZJOI2008]生日聚会Party(单调DP)
  11. 几种程序的反汇编代码入口特征
  12. [Cocos2dx] CCCamera照相机 详解
  13. Sigar介绍与使用
  14. C# Xamarin For Android移动开发基础实战演练
  15. 一文搞懂VOS3000如何配置MicroSIP网络电话
  16. pe擦除服务器硬盘,老毛桃winpe分区助手删除分区后如何用Gutmann算法擦除硬盘数据?...
  17. python爬取千图网_python 爬取 花瓣网图片,千图网图片
  18. 网络调试助手连接远程服务器
  19. React的箭头函数详解
  20. VB.NET中的回车换行

热门文章

  1. OpenGL 字体颜色问题
  2. 不要在 Spring Boot 集成测试中使用 @Transactional
  3. Cocos2d-x项目开发时在Eclipse中配置环境编译C++
  4. VS201“.NET研究”0实践RUP4+1架构模型
  5. OWIN and Katana
  6. Android Service学习之本地服务
  7. synchronized关键字原理
  8. iOS -数据库网络之xml解析之远程解析XML
  9. Apache下PHP的几种工作方式
  10. 关于编译原理的一点看法