跨平臺開源通訊元件elastic communication

smark發表於2015-11-30

    elastic communication是基於c#開發支援.net和mono的通訊元件(簡稱EC),EC的主要目的簡化mono和.net下的通訊開發難度,通過EC可以非常快速地開發基於mono和.net的通訊互動應用。EC抽取的基礎的通訊協議預設支援protobuf,msgpack的資料物件進行通訊互動,開發者不可以根據自己的制要制訂更多的序列化方式支援;EC不緊緊支援簡單的對像傳輸,還提供了控制器,方法控制器和更方便的遠端介面呼叫功能。

    以下是ES希望實現的最終目標

    

    藉助於Xamarin實現不同移動端的實現,由於這一塊個人工作原因還沒細化實現,通過開源相關程式碼可以讓感興趣的人更好進一步去完成相關功能。

功能簡介

    EC實現通訊功能是非常方便的事情,下面通訊簡單的幾種場景來介紹一下EC在通訊上的應用。

HelloWord

  • 服務端
    namespace HelloWord.Server
    {
        [Controller]
        public class Program
        {
            static void Main(string[] args)
            {
                ECServer.Open();
                System.Threading.Thread.Sleep(-1);
            }
    
            public string HelloWord(ISession session,Hello e)
            {
                return string.Format("hello {0} [say time:{1}]", e.Name, DateTime.Now);
            }
        }
    
        [MessageID(0x1)]
        [ProtoContract]
        public class Hello
        {
            [ProtoMember(1)]
            public string Name { get; set; }
        }
    }
  • 客戶端
    private EC.ProtoSyncClient mClient = new ProtoSyncClient("127.0.0.1");
    mClient.Send<string>(new Hello { Name=textBox1.Text })

遠端方法訪問

  EC支援遠端方法呼叫,如果用過wcf那對這功能感覺應該不會陌生,而EC也是通過介面的方式來定義遠端呼叫行為;wcf同一方法過載需要重新定義名稱,而ec則支援同一方法多個過載版本。為了滿足更復雜的需要,EC的遠端呼叫同樣支援out和ref引數。

  • 介面定義
    namespace Remoting.Service
    {
        public interface IUserService
        {
            User Register(string name, string email);
        }
    }
  • 服務端
    [SOAService(typeof(Service.IUserService))]
        class Program : IUserService
        {
            static void Main(string[] args)
            {
                ECServer.Open();
                System.Threading.Thread.Sleep(-1);
            }
    
            public Service.User Register(string name, string email)
            {
                User user = new User();
                user.EMail = email;
                user.Name = name;
                user.CreateTime = DateTime.Now;
                return user;
            }
        }
  • 客戶端
      public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private ProtoClient mClient = new ProtoClient("192.168.7.111");
    
            private IUserService UserService;
    
            private void Form1_Load(object sender, EventArgs e)
            {
                UserService = mClient.CreateInstance<IUserService>();
            }
    
            private void cmdRegister_Click(object sender, EventArgs e)
            {
                User user= UserService.Register(txtName.Text, txtEMail.Text);
                txtCreateTime.Text = user.CreateTime.ToString();
            }
        }

資料訪問示例

   資料訪問應該是最常見的一種應用場,以下是定義一個簡單的資料查詢示例。

  • 服務端
     [EC.Controller]
        public class Program
        {
            static void Main(string[] args)
            {
                DBContext.SetConnectionDriver<SqliteDriver>(DB.DB1);
                DBContext.SetConnectionString(DB.DB1, "Data Source=northwindEF.db;Pooling=true;FailIfMissing=false;");
                ECServer.Open();
                System.Threading.Thread.Sleep(-1);
            }
    
            public IList<Employee> OnEmployeeSearch(ISession session, EmployeeSearch e)
            {
                return new Expression().List<Models.Employees, Employee>();
            }
    
            public IList<Customer> OnCustomerSearch(ISession session, CustomerSearch e)
            {
                return new Expression().List<Models.Customers, Customer>();
            }
    
            public IList<Order> OnOrderSearch(ISession session, OrderSearch e)
            {
                Expression exp = new Expression();
                if (e.CustomerID != null)
                    exp &= Models.Orders.customerID == e.CustomerID;
                if (e.EmployeeID > 0)
                    exp &= Models.Orders.employeeID == e.EmployeeID;
                return exp.List<Models.Orders, Order>();
            }
    
            public IList<OrderDetail> GetOrderDetail(ISession session, GetDetail e)
            {
                Expression exp = Models.OrderDetails.orderID == e.OrderID;
                JoinTable jt = Models.OrderDetails.productID.InnerJoin(Models.Products.productID);
                jt.Select("OrderDetails.*", Models.Products.productName.Name);
                return exp.List<OrderDetail>(jt);
            }
    
        }
  • 客戶端
    private ProtoSyncClient mClient = new ProtoSyncClient("127.0.0.1");
    
            private void FrmMain_Load(object sender, EventArgs e)
            {
                cbEmployees.Items.Add(new Employee());
                foreach (Employee item in mClient.Send<IList<Employee>>(new EmployeeSearch()))
                {
                    cbEmployees.Items.Add(item);
                }
                cbCustomers.Items.Add(new Customer());
                foreach (Customer item in mClient.Send<IList<Customer>>(new CustomerSearch()))
                {
                    cbCustomers.Items.Add(item);
                }
                
            }
    
            private void cmdSearch_Click(object sender, EventArgs e)
            {
                OrderSearch os = new OrderSearch();
                if (cbCustomers.SelectedItem != null)
                    os.CustomerID = ((Customer)cbCustomers.SelectedItem).CustomerID;
                if (cbEmployees.SelectedItem != null)
                    os.EmployeeID = ((Employee)cbEmployees.SelectedItem).EmployeeID;
                gdOrder.DataSource = mClient.Send<IList<Order>>(os);
            }
    
            private void gdOrder_SelectionChanged(object sender, EventArgs e)
            {
                if (gdOrder.SelectedRows.Count > 0)
                {
                    Order order = (Order)gdOrder.SelectedRows[0].DataBoundItem;
                    GetDetail getdetail = new GetDetail();
                    getdetail.OrderID = order.OrderID;
                    gdDetail.DataSource = mClient.Send<IList<OrderDetail>>(getdetail);
                }
            }

AppModule

     AppModule類似於asp.net的httpModule,它可以在EC服務中載實始化的時候進行載入,通過AppModule可以實現訊息處理,處理日誌,全域性資訊定義等相關主要功能。

    public class FilterModel : IAppModel
    {

        public string Name
        {
            get { return "Filter"; }
        }

        private System.Threading.Timer mTimer;

        public void Init(IApplication application)
        {
            //application.Filters.Add(new LoginFilter());
            application.Disconnected += (o, e) =>
            {
                "{0} disposed applicaion event".Log4Info(e.Session.Channel.EndPoint);

            };
            application.Connected += (o, e) =>
            {

                "{0} connect applicaion event".Log4Info(e.ChannelConnectArgs.Channel.EndPoint);

            };
              application.SendCompleted += (o, e) =>
              {
                 "{0} send completed applicaion event".Log4Info(e.Session.Channel.EndPoint);
              };
             application.MethodProcess += (o, e) =>
             {
                 //application
                 e.Application["Path"] = @"c:\";
                 //sexxion
                 e.Session["folder"] = "aaa";
             };
            application.Error += (o, e) =>
            {
                "{0} channel error {1}".Log4Error(e.Info.Channel.EndPoint, e.Info.Error.Message);
            };
            mTimer = new System.Threading.Timer(o =>
            {
                application.Server.Send(new User { Name = Guid.NewGuid().ToString("N"),CreateTime = DateTime.Now }, application.Server.GetOnlines());
            }, null, 1000, 1000);

        }
        public string Command(string cmd)
        {
            throw new NotImplementedException();
        }
    }

Filter

    這個特性在EC上是提供比較有用的功能,通訊filter可以對呼叫方法加入許可權,日誌,攔載等邏輯功能。  

public class AdminFilter : FilterAttribute
    {
        public override void Execute(IMethodContext context)
        {
            "admin filter ->{0}".Log4Debug(context.Handler);
            base.Execute(context);
        }
    }
    [Controller]
    public class Controller
    {
        [SkipFilter(typeof(LoginFilter))]
        [ThreadPool]
        public User Regisetr(ISession session, User user)
        {
            user.CreateTime = DateTime.Now;
            "Register invoke[Name:{0} Email:{1}]".Log4Debug(user.Name, user.EMail);

            return user;
        }

        [AdminFilter]
        public IList<User> Search(ISession session, Query query)
        {
            "Search invoke".Log4Debug();
            List<User> users = new List<User>();
            users.Add(new User());
            users.Add(new User());
            return users;
        }
    }

開源地址

  https://github.com/IKende/ec/

  https://github.com/IKende/ec/tree/master/Samples

開源原因

    其實我個人不太多喜歡走開源路線,主要是工作和手頭上的一些產品太多,而這個產品可以說完善度已經達到90%,如果扔在那裡感覺有點可惜了,所以把產品開源出來,如果有興趣的朋友可以複製完善它。

相關文章