需求:

         传统web程序实现负载均衡基本都是通过web容器或者防火墙设备,比如:nginx、apache的负载均衡,防火墙负载均衡,再加上tomcat或者其他容器的集群。

       那么有没有通过代码方式实现负载均衡的方式呢?

实现:

       spring boot为我们提供了一直实现方式。主要通过均衡管理服务器(server)+功能实现服务器(client)+对外服务服务器(consumer)。

开发软件:

       Intellij IDEA 14.1.4

实现详细:

      一、负载均衡服务器(server)

      1、新建项目,所属框架勾选Cloud Discovery下Eureka Server

      项目启动类:

package com.my.balanceserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class BalanceServerApplication {

   public static void main(String[] args) {
      SpringApplication.run(BalanceServerApplication.class, args);
   }

}

 可以看到,很简洁,只需要另外加注解:@EnableEurekaServer

2、项目配置文件

 application.properties

server.port=8080
eureka.instance.hostname=127.0.0.1
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

需要注意的是服务不需要注册自己

二、实际提供功能服务器(client)

    该服务器是系统所有业务操作的所在,所有和数据库相关的操作都在这里执行。

1、新建项目,所属框架勾选Cloud Discovery下Eureka Server

      项目启动类:

@SpringBootApplication
@EnableEurekaClient
public class BalanceClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(BalanceClientApplication.class, args);
    }
}

 可以看到,也很简洁,只需要另外加注解:@EnableEurekaClient

2、项目配置文件

 application.properties

eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8080/eureka/
server.port=8088
spring.application.name=service-provider

需要填写负载均衡服务器地址和端口号,以及该服务的地址

 

三、对外提供接口项目(consumer)

consumer是对外提供接口,这里就涉及到安全问题,可以采取https方式,在银行系统等安全性比较高的项目中会应用到,也可以使用http+token令牌方式。

这里我们采取https方式

1、先生成秘钥文件:tomcat.key

在jdk bin下运行下面命令

keytool -genkeypair -alias tomcat -keyalg RSA -keystore D:/tomcat.key

然后按照操作输入对应相关信息即可,我这边密码设置为一样的。

2、新建项目,所属框架勾选spring web 和 Cloud Discovery下Eureka Server

      项目启动类:

    

@SpringBootApplication
@EnableEurekaClient
public class BalanceConsumerApplication {

   @Value("${tomcat.port}")
   private Integer tomcatPort;

   @Value("${https.port}")
   private Integer port;

   @Value("${https.ssl.key-password}")
   private String keyStorePassword;

   @Value("${https.ssl.key-password}")
   private String keyPassword;

   @Value("${https.ssl.key-type}")
   private String keyType;

   public static void main(String[] args) {
      SpringApplication.run(BalanceConsumerApplication.class, args);
   }

   @Bean
   public ServletWebServerFactory servletContainer() {
      TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
      tomcat.addAdditionalTomcatConnectors(createSslConnector()); 
      return tomcat;
   }

   private Connector createSslConnector() {
      Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
      Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
      try {
         File keystore = new ClassPathResource("tomcat.key").getFile();
            /*File truststore = new ClassPathResource("sample.jks").getFile();*/
         connector.setScheme("https");
         connector.setSecure(true);
         connector.setPort(port);
         protocol.setSSLEnabled(true);
         protocol.setKeystoreType(keyType);
         protocol.setKeystoreFile(keystore.getAbsolutePath());
         protocol.setKeystorePass(keyStorePassword);
         protocol.setKeyPass(keyStorePassword);
         return connector;
      }
      catch (IOException ex) {
         throw new IllegalStateException("can't access keystore: [" + "keystore"
               + "] or truststore: [" + "keystore" + "]", ex);
      }
   }

   @Bean
   public Connector httpConnector() {
      Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
      connector.setScheme("http");
      connector.setPort(tomcatPort);
      connector.setSecure(false);
      connector.setRedirectPort(port);
      return connector;
   }
}

  这里把端口从配置文件中读取:

3、编写http过滤类,把http请求转换成https请求

@Configuration
@WebFilter
public class SslFilter extends OncePerRequestFilter {
    @Value("${tomcat.port}")
    private Integer tomcatPort;

    @Value("${https.port}")
    private Integer port;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String requestURL = request.getRequestURL().toString();
        String protocol = requestURL.split("://")[0];
        if ("http".equals(protocol)) {
            requestURL = requestURL.replace("http", "https").replace(Integer.toString(tomcatPort), Integer.toString(port));
            response.sendRedirect(requestURL);
        }
        filterChain.doFilter(request, response);
    }
}

4、配置文件

 application.properties

server.port=88
tomcat.port=88
https.port=89
https.ssl.key-store=classpath:tomcat.key
https.ssl.key-type=JKS
https.ssl.key-password=nankang123456
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8080/eureka/
spring.application.name=service-consumer

5、编写reset对外暴露的接口类

 这里只写了一个测试接口:

@RestController
public class ResetController {
    @Autowired
    RestTemplate restTemplate;
    /**
     * 实例化RestTemplate
     * @return
     */
    @LoadBalanced
    @Bean
    public RestTemplate rest() {
        return new RestTemplate();
    }

    @RequestMapping(value = "/api/{ope}/{params}",method = RequestMethod.POST)
    @ResponseBody
    public Result api(@PathVariable int ope,@PathVariable String params){
        Result result=new Result();
        String data = (String)restTemplate.getForObject("http://service-provider/api/"+ope+"/"+params,String.class);
        result.setData(data);
        return result;
    }
}

 

另外还可以写一个http junit的测试类,用httpclient测试接口,这个请看下回分解。

代码github地址

顺便给大家推荐给开源客服系统,小倍客服

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐