Nginx负载均衡流量切换
目前在用的方案是:
使用Nginx+Tomcat配置负载均衡,nginx.conf文件配置upstream主机群,供装载两个地址,其中一台设定主服务,另一台设定备用服务,当主服务启动时,所有的请求都转发到主服务,当主服务挂掉时,再将后续的服务转发到备用服务。
- 下面是nginx配置
upstream tomcatserver2
{
#服务群组
#server 主机地址:服务端口 权重[weight=1] ;
server 127.0.0.1:6901 ;#weight 1权重配置 1表示访问次数
server 127.0.0.1:6902 backup; #服务地址
}
目前遇到的一个问题是:
当我要更新服务时,必须先更新备用服务,然后更新主服务,当备用服务更新后,主服务却不敢关了,因为仍担心有未处理的任务。
实现方法有两种
1.第一种是需要两台以上服务器,在每个服务器都配置nginx,然后每次更新时,需要使用
nginx -s quit
停用主服务nginx,这是很好的办法,但是服务器资源有限的话,这个方案就不能采用了。
2.第二种需要开发一些程序,需要servlet拦截和nginx配合处理
根据现在服务器的资源情况,采用了第二套方案
- 下图为设计简略图
第一步:在Servlet配置
- 1.1增加拦截器属性
public class SecurityServlet extends HttpServlet implements Filter
{
//拦截器状态:0正常状态;其它为不通过
private static int filterStatus = 0;
/**
* 设置拦截器状态
*/
public static void setFilterStatus(int filterStatus)
{
SecurityServlet.filterStatus = filterStatus;
}
/**
* 获取拦截器状态
*/
public static int getFilterStatus()
{
return filterStatus;
}
}
- 1.2 增加URL请求状态
//记录未完成的请求URI
private static final Map<String, Date> uriMap = new HashMap<String, Date>();
/**
* 获取所有未完成的请求
*/
public static Map<String, Date> getUrimap()
{
return uriMap;
}
- 1.3拦截器配置
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException
{
HttpServletRequest request = (HttpServletRequest)arg0;
HttpServletResponse response = (HttpServletResponse)arg1;
String url = request.getRequestURI();
url = url == null ? "" : url;
//状态不正常则拦截请求
if (SecurityServlet.filterStatus != 0 && url.indexOf("setServletFilterStatus") < 0
&& url.indexOf("getInProgressRequestInfo") < 0)
{
//设置《网关超时》状态
response.setStatus(504);
return;
}
//保存未完成的请求
uriMap.put(url, new Date());
arg2.doFilter(request, arg1);
//移除已完成的请求
uriMap.remove(url);
}
第二步:配置nginx
http
{
location /
{
proxy_pass http://tomcatserver2; #调用的服务群组
proxy_connect_timeout 1s; #如果连接超时的时间超过1秒,则更换另一个服务器。
#如果后端的服务器返回502、504、执行超时等错误,自动将请求转发到upstream负载均衡池中的另一台服务器,实现故障转移。
#invalid_header:服务器返回空响应或无效响应;
#non_idempotent:通常,如果请求已经被发送到上游服务器(1.9.13),则具有非幂等方法的请求(POST,LOCK,PATCH)不被传递到下一个服务器;启用此选项明确允许重试此类请求;
proxy_next_upstream http_504 non_idempotent;
}
}
第三步:动态配置开发
- 3.1后台
/**
* 设置服务过滤器状态
*/
@RequestMapping(value = {"setServletFilterStatus"}, method = RequestMethod.POST)
public void setServletFilterStatus(@RequestBody
JSONObject requestParams, HttpServletRequest request, HttpServletResponse response)
{
JSONObject jsonObject = new JSONObject();
try
{
if (requestParams.getInteger("filterStatus") == null)
throw new Exception("无参数:状态");
int filterStatus = requestParams.getIntValue("filterStatus");
if (filterStatus < 0)
throw new Exception("状态不可小于0");
//查询所有的群集服务配置
List<String> paramnames = new ArrayList<String>();
paramnames.add("SERVER_BACKUP");
paramnames.add("SERVER_MAIN");
List<THParam> paramList = paramdao.get_One_Param(paramnames);
String hostname = InetAddress.getLocalHost().getHostName();//本机IP
int port = CommonFun.getTomcatPort();//当前服务端口
String hostInfo = hostname + ":" + port;//主机信息
boolean exist = false;
for (THParam param : paramList)
{
JSONObject server = CommonFun.parseJSONObject(param.getParamdesc2());
if (hostInfo.equals(server.getString("hostname") + ":" + server.getString("port")))
continue;
exist = TelnetUtil.telnet(server.getString("hostname"), server.getIntValue("port"));
if (exist)
break;
}
if (!exist && filterStatus != 0)
{
throw new Exception("当前只有一台服务在用,不可拦截服务");
}
//设置状态
SecurityServlet.setFilterStatus(filterStatus);
jsonObject.put("success_Info", "success");
}
catch (Exception ex)
{
jsonObject.put("wrong_Info", CommonFun.getExceptionInfo(ex));
}
InteractiveWithAjax printWriteAjax = new InteractiveWithAjax();
printWriteAjax.printWriter(jsonObject, response);
}
/**
* 获取所有正在进行中的uri请求信息
*/
@RequestMapping(value = {"getInProgressRequestInfo"}, method = RequestMethod.POST)
public void getInProgressRequestInfo(HttpServletRequest request, HttpServletResponse response)
{
JSONObject jsonObject = new JSONObject();
try
{
Map<String, Date> uriMap = SecurityServlet.getUrimap();//正在执行的URI请求进程
Integer filterStatus = SecurityServlet.getFilterStatus();//过滤器状态
jsonObject.put("success_Info", "success");
jsonObject.put("uriMap", uriMap);
jsonObject.put("filterStatus", filterStatus);
}
catch (Exception ex)
{
}
InteractiveWithAjax printWriteAjax = new InteractiveWithAjax();
printWriteAjax.printWriter(jsonObject, response);
}
- 3.2前端
每秒加载未完成的任务信息,直至没有任务为止,方可停止主服务
图:前端设计页面