前言
Ribbon 是提供 REST 服务的区域感知负载均衡器,它在 wowza 的前端,应该部署在专业的 REST 容器下,而不是流媒体服务器 wowza 下。
然后把这个文件放在你的 classpath 下。
3. 编写 LB 调用类
这个类将会给 ribbonLB 的 REST 请求返回一台服务器地址。假设请求失败,返回对应的错误码。
6. 编辑 VHost.xml
编辑 %wowza%/conf/VHost.xml,把上边写的两个 HTTPProvider 加入进去:
7. 项目又一次打包部署
命令行切换到你的 defonds-server-module 项目文件夹下,运行
mvn package
8. 訪问接口
debug 启动 defonds-server-module,然后在浏览器訪问 http://localhost:1935/ribbonLB,以向 Ribbon 要一个服务器地址,返回结果例如以下:
{"server_uri":"http://www.csdn.net:80/"}
返回的是服务器 URL 是 http://www.csdn.net:80/。然后在浏览器訪问 http://localhost:1935/ribbonChange?server_list=www.qq.com:80,www.126.com:80。以动态调整负载均衡节点。返回结果例如以下:
{"error_code":"0"}
这个返回码说明已经调整成功。我们能够继续訪问 http://localhost:1935/ribbonLB 验证一下,返回结果是:
{"server_uri":"http://www.qq.com:80/"}
没错,确实成功了。
这样 REST 服务重新启动后。读取的负载均衡节点依然是改动前的。假设你想在 REST 服务器断电重新启动后读取改动后的。最好把 Ribbon 属性在分布式缓存服务器中进行存放和读取,比方 Redis。
Ribbon 是提供 REST 服务的区域感知负载均衡器,它在 wowza 的前端,应该部署在专业的 REST 容器下,而不是流媒体服务器 wowza 下。
本文介绍了 Ribbon 和 wowza 的集成,Ribbon 作为 wowza 的一个插件部署在了 wowza 容器下,仅供 Ribbon 开发、部署的技术參考,现实中绝不可能出现这样的情况。由于 Wowza 毕竟不是专业提供 REST 服务的容器。关于 Ribbon 和 Wowza 真实场景的架构部署,请关注作者兴许博客。
本文是在《让你的 wowza 服务器提供 RESTful web 服务》样例的基础上进一步进行研发。
1. 新建 maven 项目
參考《让你的 wowza 服务器提供 RESTful web 服务》步骤。新建的 maven 项目 defonds-server-module 例如以下:
2. 编辑 Ribbon 配置文件
依据你自己的集群。配置 Ribbon。比方作者 demo 用的配置文件例如以下:
# Max number of retries on the same server (excluding the first try) sample-client.ribbon.MaxAutoRetries=1 # Max number of next servers to retry (excluding the first server) sample-client.ribbon.MaxAutoRetriesNextServer=1 # Whether all operations can be retried for this client sample-client.ribbon.OkToRetryOnAllOperations=true # Interval to refresh the server list from the source sample-client.ribbon.ServerListRefreshInterval=2000 # Connect timeout used by Apache HttpClient sample-client.ribbon.ConnectTimeout=3000 # Read timeout used by Apache HttpClient sample-client.ribbon.ReadTimeout=3000 # Initial list of servers, can be changed via Archaius dynamic property at runtime sample-client.ribbon.listOfServers=www.baidu.com:80,www.163.com:80,www.csdn.net:80
然后把这个文件放在你的 classpath 下。
3. 编写 LB 调用类
package com.defonds.wms.module.server; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import com.netflix.client.ClientException; import com.netflix.client.ClientFactory; import com.netflix.client.http.HttpRequest; import com.netflix.client.http.HttpResponse; import com.netflix.config.ConfigurationManager; import com.netflix.niws.client.http.RestClient; import com.wowza.wms.http.HTTProvider2Base; import com.wowza.wms.http.IHTTPRequest; import com.wowza.wms.http.IHTTPResponse; import com.wowza.wms.logging.WMSLogger; import com.wowza.wms.logging.WMSLoggerFactory; import com.wowza.wms.vhost.IVHost; public class RibbonLBRestService extends HTTProvider2Base { private static final WMSLogger logger = WMSLoggerFactory.getInstance().getLoggerObj(RibbonLBRestService.class.getName()); public static RestClient restClient = null; static { try { // Load the properties file using Archaius ConfigurationManager ConfigurationManager.loadPropertiesFromResources("sample-client.properties"); // Use ClientFactory to create client and the load balancer RibbonLBRestService.restClient = (RestClient) ClientFactory.getNamedClient("sample-client"); } catch (IOException e) { logger.error(e.getMessage(), e); } } @Override public void onHTTPRequest(IVHost arg0, IHTTPRequest request, IHTTPResponse response) { String jsonObject = null; response.setHeader("Content-Type", "application/json"); try { String serverURI = RibbonLBRestService.getURI(); response.setResponseCode(200); jsonObject = "{"server_uri":"" + serverURI + ""}"; } catch (ClientException e2) { response.setResponseCode(400); jsonObject = "{"error_code":"40039"}"; logger.error(e2.getMessage(), e2); } catch (URISyntaxException e3) { response.setResponseCode(400); jsonObject = "{"error_code":"40023"}"; logger.error(e3.getMessage(), e3); } finally { // Get the printwriter object from response to write the required json object to the output stream OutputStream out = response.getOutputStream(); try { out.write(jsonObject.getBytes()); out.flush(); } catch (IOException e) { logger.error(e.getMessage(), e); } } } public synchronized static String getURI() throws ClientException, URISyntaxException { // Build the http request using the builder // Note that we only supply the path part (“/”) of the URI // The complete URI will be computed by the client once the server is chosen by the load balancer HttpRequest ribbonRequest = HttpRequest.newBuilder().uri(new URI("/")).build(); HttpResponse ribbonResponse; // Call client.executeWithLoadBalancer() API, not the execute() API ribbonResponse = RibbonLBRestService.restClient.executeWithLoadBalancer(ribbonRequest); return ribbonResponse.getRequestedURI().toString(); } public synchronized static void changeServersPoolDynamically(String serverList) throws ClientException { // Dynamically change the server pool from the configuration ConfigurationManager.getConfigInstance().setProperty( "sample-client.ribbon.listOfServers", serverList); logger.debug("changing servers ..."); try { // Wait until server list is refreshed (2 seconds refresh interval defined in properties file) Thread.sleep(3000); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } } }
这个类将会给 ribbonLB 的 REST 请求返回一台服务器地址。假设请求失败,返回对应的错误码。
这个类还提供了一个公开方法,用于动态调整负载均衡节点。
4. 编写动态改动负载均衡节点接口
package com.defonds.wms.module.server; import java.io.IOException; import java.io.OutputStream; import com.netflix.client.ClientException; import com.wowza.wms.http.HTTProvider2Base; import com.wowza.wms.http.IHTTPRequest; import com.wowza.wms.http.IHTTPResponse; import com.wowza.wms.logging.WMSLogger; import com.wowza.wms.logging.WMSLoggerFactory; import com.wowza.wms.vhost.IVHost; public class RibbonChangeInstanceService extends HTTProvider2Base { private static final WMSLogger logger = WMSLoggerFactory.getInstance().getLoggerObj(RibbonChangeInstanceService.class.getName()); @Override public void onHTTPRequest(IVHost arg0, IHTTPRequest request, IHTTPResponse response) { String serverList = request.getParameter("server_list"); // TODO Authorization // TODO serverList str check String jsonObject = null; response.setHeader("Content-Type", "application/json"); try { RibbonLBRestService.changeServersPoolDynamically(serverList); response.setResponseCode(200); jsonObject = "{"error_code":"0"}"; // server list is changed successfully } catch (ClientException e2) { response.setResponseCode(400); jsonObject = "{"error_code":"40039"}"; logger.error(e2.getMessage(), e2); } finally { // Get the printwriter object from response to write the required json object to the output stream OutputStream out = response.getOutputStream(); try { out.write(jsonObject.getBytes()); out.flush(); } catch (IOException e) { logger.error(e.getMessage(), e); } } } }
这个类提供了一个用于动态改动负载均衡节点池的接口。假设改动成功返回错误码为 0,否则为其它值。
5. 编辑 maven 依赖
编辑项目 pom.xml。将上边依赖到的包导入:
<!-- ribbon --> <dependency> <groupId>com.netflix.ribbon</groupId> <artifactId>ribbon-core</artifactId> <version>0.3.12</version> </dependency> <dependency> <groupId>com.netflix.ribbon</groupId> <artifactId>ribbon-httpclient</artifactId> <version>0.3.12</version> </dependency>
6. 编辑 VHost.xml
编辑 %wowza%/conf/VHost.xml,把上边写的两个 HTTPProvider 加入进去:
<HTTPProvider> <BaseClass>com.defonds.wms.module.server.RibbonLBRestService</BaseClass> <RequestFilters>ribbonLB*</RequestFilters> <AuthenticationMethod>none</AuthenticationMethod> </HTTPProvider> <HTTPProvider> <BaseClass>com.defonds.wms.module.server.RibbonChangeInstanceService</BaseClass> <RequestFilters>ribbonChange*</RequestFilters> <AuthenticationMethod>none</AuthenticationMethod> </HTTPProvider>
7. 项目又一次打包部署
命令行切换到你的 defonds-server-module 项目文件夹下,运行
mvn package
8. 訪问接口
debug 启动 defonds-server-module,然后在浏览器訪问 http://localhost:1935/ribbonLB,以向 Ribbon 要一个服务器地址,返回结果例如以下:
{"server_uri":"http://www.csdn.net:80/"}
返回的是服务器 URL 是 http://www.csdn.net:80/。然后在浏览器訪问 http://localhost:1935/ribbonChange?server_list=www.qq.com:80,www.126.com:80。以动态调整负载均衡节点。返回结果例如以下:
{"error_code":"0"}
这个返回码说明已经调整成功。我们能够继续訪问 http://localhost:1935/ribbonLB 验证一下,返回结果是:
{"server_uri":"http://www.qq.com:80/"}
没错,确实成功了。
注意
以上 Ribbon 动态调整的负载均衡节点是内存里的配置。服务器下的 sample-client.properties 配置的节点依然是:
sample-client.ribbon.listOfServers=www.baidu.com:80,www.163.com:80,www.csdn.net:80
这样 REST 服务重新启动后。读取的负载均衡节点依然是改动前的。假设你想在 REST 服务器断电重新启动后读取改动后的。最好把 Ribbon 属性在分布式缓存服务器中进行存放和读取,比方 Redis。
另:本文演示样例代码已上传 CSDN 资源。下载地址:http://download.csdn.net/detail/defonds/7526359。