Thrift搭建分布式微服务(四)
第一篇 《连接配置》
第二篇 《连接池》
第三篇 《标准通信》
第四篇 快速暴露接口
之前的文章,我们介绍了如何使用连接池管理Thrift节点,以及使用Thrift搭建微服务用到的标准输入输出。这一篇,我将介绍如何快速暴露服务接口,并对服务端进行错误处理。
从代码图上看,开发者在使用Thrift.Utility搭建微服务时,两个类围绕着标准输入输出,是最常用的两个类,ThriftClient上一篇已经讲过,用于客户端,与服务端建立连接,并访问服务端接口,返回值。ThriftService用于服务端,用来暴露服务接口,我们看一下ThriftService.cs:
1 public abstract class ThriftService 2 { 3 protected virtual string Excute<Q>(StandRequest<Q> request, Func<StandRequest<Q>, string> func) 4 { 5 if (request.IsValid()) 6 { 7 try 8 { 9 string result = string.Empty; 10 if (func != null) 11 { 12 result = func(request); 13 } 14 StandResponse<string> response = new StandResponse<string> 15 { 16 Code = "0", 17 Desc = "SUCCESS", 18 Data = result 19 }; 20 return SerializeHelper.JsonSerialize2(response); 21 } 22 catch (Exception ex) 23 { 24 CatchException(ex); 25 StandResponse<string> response = new StandResponse<string> 26 { 27 Code = "-2", 28 Desc = "服务端异常", 29 Data = string.Empty 30 }; 31 return SerializeHelper.JsonSerialize2(response); 32 } 33 } 34 StandResponse<string> res = new StandResponse<string> 35 { 36 Code = "-1", 37 Desc = "请求数据异常", 38 Data = string.Empty 39 }; 40 return SerializeHelper.JsonSerialize2(res); 41 } 42 43 /// <summary> 44 /// 异常处理 45 /// </summary> 46 /// <param name="ex"></param> 47 protected abstract void CatchException(Exception ex); 48 }
这个类做了两件事,第一件事就是提供了供服务端暴露接口时使用的方法Excute(StandRequest<Q> request, Func<StandRequest<Q>, string> func),只需将得到的请求参数反序列化成StandRequest<Q>,传入Excute,Excute将返回序列化好的响应。第二件事就是,在Excute里提供了错误处理机制,避免开发者进行重复的劳动,开发者只需要重写CatchException方法。
先看一下传统的调用一个本地的方法是怎么做的:
1 List<BrandInfo> brans = ProductService.GetBrandByVendorSysNo(32);
再看看用Thrift.Utility如何调用远端的服务:
1 using (var tc = new ThriftClient<ProductService.Client>("ProductService")) 2 { 3 List<BrandInfo> brands = tc.Invoke<int, List<BrandInfo>>("GetBrandByVendorSysNo", 32); 4 }
很简洁吧,如何做到的呢?
1 public class ProductServiceImpl :ThriftService, ProductService.Iface 2 { 3 public string GetBrandByVendorSysNo(string request) 4 { 5 var req = SerializationUtility.JsonDeserialize2<StandRequest<int>>(request); 6 return Excute(req, (arg) => 7 { 8 //调用Service,这是没有使用Thrift之前的方法,通过string GetBrandByVendorSysNo(string request)将其暴露给客户端调用,就这4、5行代码就可以暴露一个服务接口。 9 List<BrandInfo> brans = ProductService.GetBrandByVendorSysNo(req.Data); 10 return SerializationUtility.JsonSerialize2(brans); 11 }); 12 }
14 15 protected override void CatchException(Exception ex) //开发者自己实现服务端错误处理 16 { 17 if (ex is BusinessException) 18 { 19 20 } 21 else 22 { 23 24 } 25 } 26 }
使用本框架,暴露服务端接口就是这么简单。再看看客户端的配置:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <ThriftConfig> 3 <MonitorType>Demo.RPCClient.ConnectionPoolMonitor,Demo.RPCClient</MonitorType> 4 <ServiceArray> 5 <Service Name="ProductService" IP="127.0.0.1" Port="7911" MaxActive="100" MaxIdle="20" MinIdle="10" WaitingTimeout="1000"/> 6 <Service Name="SOService" IP="127.0.0.1" Port="7912" MaxActive="100" MaxIdle="20" MinIdle="10" WaitingTimeout="1000"/> 7 </ServiceArray> 8 </ThriftConfig>
客户端自定义模拟器示例:
1 public class ConnectionPoolMonitor:IThriftFactoryMonitor 2 { 3 public void Monitor(List<Tuple<string, int, int>> tuples) 4 { 5 foreach (var t in tuples) 6 { 7 Console.WriteLine(string.Format("自定义{0}连接池,空闲连接数量:{1},激活连接数量:{2}", t.Item1, t.Item2, t.Item3)); 8 } 9 } 10 11 public void TimeoutNotify(string serviceName, int timeOut) 12 { 13 Console.WriteLine(string.Format("自定义{0}连接池等待连接超时{1}", serviceName, timeOut)); 14 } 15 }
Demo的代码没有提供给大家,感兴趣的可以照着示例自己写一个。
到此整个系列就完结了,之所以写这个系列的文章,是希望和园友们进行交流讨论,如果代码中有什么缺点或没考虑到的地方,还希望园友们能提出来,加以改善,让这个框架更完美。
Thrift微服务代码下载Thrift.Utility