264 lines
8.4 KiB
C#
264 lines
8.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Xml.Serialization;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
using Renci.SshNet;
|
|
using StackExchange.Redis;
|
|
using StackExchange.Redis.Extensions.Core;
|
|
using StackExchange.Redis.Extensions.Core.Abstractions;
|
|
using StackExchange.Redis.Extensions.Core.Configuration;
|
|
using StackExchange.Redis.Extensions.Core.Implementations;
|
|
|
|
|
|
|
|
namespace ServerCore;
|
|
|
|
|
|
// HANDOVER: AWS S3 SDK Wrapper 클래스 이다.
|
|
// S3 저장소에 관리할 Bucket 정보를 관리해준다.
|
|
|
|
|
|
public abstract class RedisConnectorBase
|
|
{
|
|
private readonly string m_connection_string = string.Empty;
|
|
private readonly RedisConfiguration m_redis_config;
|
|
|
|
private ConnectionMultiplexer? m_connector;
|
|
|
|
private SshClient? m_ssh_tuneling;
|
|
private ForwardedPortLocal? m_forwarded_port_local;
|
|
|
|
public RedisConnectorBase(string connectionString)
|
|
{
|
|
m_redis_config = new RedisConfiguration()
|
|
{
|
|
ConnectionString = connectionString,
|
|
};
|
|
|
|
m_connection_string = connectionString;
|
|
}
|
|
|
|
public async Task<bool> tryConnectAsync()
|
|
{
|
|
if (false == isConfigureConnectionInfo())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (false == isConnected())
|
|
{
|
|
ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true);
|
|
var config_options = ConfigurationOptions.Parse(m_connection_string);
|
|
|
|
var connection = await ConnectionMultiplexer.ConnectAsync(config_options);
|
|
if (null == connection)
|
|
{
|
|
var err_msg = $"Failed to connectAsync RedisServer !!! : ConnectString:{m_connection_string}";
|
|
throw new Exception(err_msg);
|
|
}
|
|
|
|
m_connector = connection;
|
|
|
|
connection.ConnectionFailed += onConnectfailed;
|
|
connection.ErrorMessage += onError;
|
|
connection.InternalError += onInternalError;
|
|
connection.ConnectionRestored += onConnectionRestored;
|
|
connection.ConfigurationChanged += onConfigurationChanged;
|
|
connection.ConfigurationChangedBroadcast += onMasterSlaveChanged;
|
|
}
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
var err_msg = $"Exception !!!, Failed to perform in tryConnectAsync() !!! : exception:{e}, connectionString:{m_connection_string} - {toBasicString()}";
|
|
Log.getLogger().error(err_msg);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected void setConnectionMultiplexer(ConnectionMultiplexer connector) => m_connector = connector;
|
|
|
|
public bool tryConnect()
|
|
{
|
|
if (false == isConfigureConnectionInfo())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (false == isConnected())
|
|
{
|
|
var config_options = ConfigurationOptions.Parse(m_connection_string);
|
|
|
|
var connection = ConnectionMultiplexer.Connect(config_options);
|
|
if (null == connection)
|
|
{
|
|
var err_msg = $"Failed to connect RedisServer !!! : ConnectString:{m_connection_string}";
|
|
throw new Exception(err_msg);
|
|
}
|
|
|
|
m_connector = connection;
|
|
|
|
connection.ConnectionFailed += onConnectfailed;
|
|
connection.ErrorMessage += onError;
|
|
connection.InternalError += onInternalError;
|
|
connection.ConnectionRestored += onConnectionRestored;
|
|
connection.ConfigurationChanged += onConfigurationChanged;
|
|
connection.ConfigurationChangedBroadcast += onMasterSlaveChanged;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
var err_msg = $"Exception !!!, try connectAsync failed !!! : Exception:{e} - {toBasicString()}";
|
|
Log.getLogger().error(err_msg);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//=========================================================================================
|
|
// sshHost : ec2-54-69-223-245.us-west-2.compute.amazonaws.com // SSH 서버 (Jump Host or Bastion)
|
|
// sshPort : 2211 // SSH 포트
|
|
// sshUsername : "ubuntu" // SSH 사용자 이름
|
|
// privateKeyPath : @"D:\SVN\Caliverse\Metaverse\trunk\Caliverse\Server\Security\SSH-Keys\pem\USWest2-KeyPair.pem" // Private Key 보안 파일 참조 경로
|
|
// localPort : 16379 // 로컬에서 사용할 포트
|
|
// redisHost : "clustercfg.metaverse-qa-cluster.ocif0u.usw2.cache.amazonaws.com" // Redis 클러스터 호스트
|
|
// redisPort : 6379 // Redis 사용 포트
|
|
// redisPassword : "wiUaVvNwX4PhBj&8"
|
|
//=========================================================================================
|
|
public bool startTuneling( string sshHost, int sshPort, string sshUserName, string privateKeyPath
|
|
, int localPort, string redisHost, int redisPort, string redisPassword )
|
|
{
|
|
// SSH Private Key 설정
|
|
PrivateKeyFile privateKey = new PrivateKeyFile(privateKeyPath);
|
|
var keyFiles = new[] { privateKey };
|
|
|
|
try
|
|
{
|
|
var ssh_client = new SshClient(sshHost, sshPort, sshUserName, keyFiles);
|
|
|
|
// SSH 연결 시도
|
|
ssh_client.Connect();
|
|
|
|
// 포트 포워딩 설정 (로컬 포트 -> 원격 Redis 호스트)
|
|
var portForward = new ForwardedPortLocal("127.0.0.1", (uint)localPort, redisHost, (uint)redisPort);
|
|
ssh_client.AddForwardedPort(portForward);
|
|
|
|
portForward.Start();
|
|
|
|
m_forwarded_port_local = portForward;
|
|
m_ssh_tuneling = ssh_client;
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
var err_msg = $"Exception !!!, Failed to function in startTuneling() !!! : Exception:{e}";
|
|
Log.getLogger().error(err_msg);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void stopTuneling()
|
|
{
|
|
if (null != m_forwarded_port_local)
|
|
{
|
|
m_forwarded_port_local.Stop();
|
|
m_forwarded_port_local = null;
|
|
}
|
|
|
|
if(null != m_ssh_tuneling)
|
|
{
|
|
// SSH 연결 종료
|
|
m_ssh_tuneling.Disconnect();
|
|
m_ssh_tuneling = null;
|
|
}
|
|
}
|
|
|
|
protected bool isConfigureConnectionInfo()
|
|
{
|
|
if ( 0 >= m_connection_string.Length )
|
|
{
|
|
var err_msg = $"Invalid Configure of RedisConnector, Empty m_connectionString !!! - {toBasicString()}";
|
|
Log.getLogger().error(err_msg);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool isConnected()
|
|
{
|
|
if( null == m_connector
|
|
|| false == m_connector.IsConnected)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public IDatabase? getDatabase()
|
|
{
|
|
if(false == isConnected())
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return m_connector?.GetDatabase();
|
|
}
|
|
|
|
public ConnectionMultiplexer? getConnectionMultiplexer() => m_connector;
|
|
|
|
#region event
|
|
protected virtual void onConnectionRestored(object? sender, ConnectionFailedEventArgs e)
|
|
{
|
|
Log.getLogger().warn($"Redis connection restored !!! : {e} - {toBasicString()}");
|
|
}
|
|
|
|
protected void onConnectfailed(object? sender, ConnectionFailedEventArgs e)
|
|
{
|
|
Log.getLogger().error($"Redis connect failed !!! : ConnectionFailed:{e.Exception} - {toBasicString()}");
|
|
}
|
|
|
|
protected void onError(object? sender, RedisErrorEventArgs e)
|
|
{
|
|
Log.getLogger().error($"Redis connection restored !!! : RedisError:{e} - {toBasicString()}");
|
|
}
|
|
|
|
protected void onInternalError(object? sender, InternalErrorEventArgs e)
|
|
{
|
|
Log.getLogger().error($"Redis internal error !!! : InternalError:{e} - {toBasicString()}");
|
|
}
|
|
|
|
protected void onConfigurationChanged(object? sender, EndPointEventArgs e)
|
|
{
|
|
Log.getLogger().warn($"Redis configuration changed !!! : EndPoint:{e} - {toBasicString()}");
|
|
}
|
|
|
|
protected void onMasterSlaveChanged(object? sender, EndPointEventArgs e)
|
|
{
|
|
Log.getLogger().warn($"Redis changed master-slave !!! : EndPoint:{e.EndPoint} - {toBasicString()}");
|
|
}
|
|
#endregion event
|
|
|
|
|
|
public string toBasicString()
|
|
{
|
|
return $"RedisConnector - Class:{this.getTypeName()}, ConnectString:{m_connection_string}";
|
|
}
|
|
}
|