用法一 this代表当前类的实例对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
namespace Demo { public class Test { private string scope = "全局变量"; public string getResult() { string scope = "局部变量"; // this代表Test的实例对象 // 所以this.scope对应的是全局变量 // scope对应的是getResult方法内的局部变量 return this.scope + "-" + scope; } } class Program { static void Main(string[] args) { try { Test test = new Test(); Console.WriteLine(test.getResult()); } catch (Exception ex) { Console.WriteLine(ex); } finally { Console.ReadLine(); } } } } |
用法二 用this串联构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
namespace Demo { public class Test { public Test() { Console.WriteLine("无参构造函数"); } // this()对应无参构造方法Test() // 先执行Test(),后执行Test(string text) public Test(string text) : this() { Console.WriteLine(text); Console.WriteLine("有参构造函数"); } } class Program { static void Main(string[] args) { try { Test test = new Test("张三"); } catch (Exception ex) { Console.WriteLine(ex); } finally { Console.ReadLine(); } } } } |
用法三 为原始类型扩展方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
namespace Demo { public static class Extends { // string类型扩展ToJson方法 public static object ToJson(this string Json) { return Json == null ? null : JsonConvert.DeserializeObject(Json); } // object类型扩展ToJson方法 public static string ToJson(this object obj) { var timeConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" }; return JsonConvert.SerializeObject(obj, timeConverter); } public static string ToJson(this object obj, string datetimeformats) { var timeConverter = new IsoDateTimeConverter { DateTimeFormat = datetimeformats }; return JsonConvert.SerializeObject(obj, timeConverter); } public static T ToObject<T>(this string Json) { return Json == null ? default(T) : JsonConvert.DeserializeObject<T>(Json); } public static List<T> ToList<T>(this string Json) { return Json == null ? null : JsonConvert.DeserializeObject<List<T>>(Json); } public static DataTable ToTable(this string Json) { return Json == null ? null : JsonConvert.DeserializeObject<DataTable>(Json); } public static JObject ToJObject(this string Json) { return Json == null ? JObject.Parse("{}") : JObject.Parse(Json.Replace(" ", "")); } } class Program { static void Main(string[] args) { try { List<User> users = new List<User>{ new User{ID="1",Code="zs",Name="张三"}, new User{ID="2",Code="ls",Name="李四"} }; // list转化json字符串 string json = users.ToJson(); // string转化List users = json.ToList<User>(); // string转化DataTable DataTable dt = json.ToTable(); } catch (Exception ex) { Console.WriteLine(ex); } finally { Console.ReadLine(); } } } public class User { public string ID { get; set; } public string Code { get; set; } public string Name { get; set; } } } |
用法四 索引器(基于索引器封装EPList,用于优化大数据下频发的Linq查询引发的程序性能问题,通过索引从list集合中查询数据)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; namespace MyDemo.Web { /// <summary> /// EPList 支持为List创建索引 /// </summary> /// <typeparam name="T">类型</typeparam> public class EPList<T> { #region 成员变量 /// <summary> /// 索引 /// </summary> private List<string[]> m_Index = new List<string[]>(); /// <summary> /// 缓存数据 /// </summary> private Dictionary<string, List<T>> m_CachedData = new Dictionary<string, List<T>>(); /// <summary> /// List数据源 /// </summary> private List<T> m_ListData = new List<T>(); /// <summary> /// 通过索引值取数据 /// </summary> /// <param name="indexFields">索引字段</param> /// <param name="fieldValues">字段值</param> /// <returns></returns> public List<T> this[string[] indexFields] { get { string key = string.Join(",", indexFields); if (m_CachedData.ContainsKey(key)) return m_CachedData[key]; return new List<T>(); } } #endregion #region 公共方法 /// <summary> /// 创建索引 /// </summary> /// <param name="indexFields">索引字段</param> public void CreateIndex(string[] indexFields) { if (m_Index.Contains(indexFields)) return; m_Index.Add(indexFields); } /// <summary> /// 添加 /// </summary> /// <param name="record">记录</param> public void Add(T record) { m_ListData.Add(record); m_Index.ForEach(indexFields => { string key = getKey(record, indexFields); if (m_CachedData.ContainsKey(key)) { m_CachedData[key].Add(record); } else { List<T> list = new List<T> { record }; m_CachedData.Add(key, list); } }); } #endregion #region 私有方法 /// <summary> /// 获取值 /// </summary> /// <param name="record">记录</param> /// <param name="fieldName">字段名</param> /// <returns></returns> private object getValue(T record, string fieldName) { Type type = typeof(T); PropertyInfo propertyInfo = type.GetProperty(fieldName); return propertyInfo.GetValue(record, null); } /// <summary> /// 获取Key /// </summary> /// <param name="record">记录</param> /// <param name="indexFields">索引字段</param> private string getKey(T record, string[] indexFields) { List<string> values = new List<string>(); foreach (var field in indexFields) { string value = Convert.ToString(getValue(record, field)); values.Add(field + ":" + value); } return string.Join(",", values); } /// <summary> /// 获取Key /// </summary> /// <param name="indexFields">索引字段</param> /// <param name="fieldValues">字段值</param> /// <returns></returns> private string getKey(string[] indexFields, object[] fieldValues) { if (indexFields.Length != fieldValues.Length) return string.Empty; for (int i = 0; i < indexFields.Length; i++) { fieldValues[i] = indexFields[i] + ":" + fieldValues[i]; } string key = string.Join(",", fieldValues); return key; } #endregion } } |
给EPList创建索引,并添加数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private EPList<SysDepartInfo> GetEPListData() { EPList<SysDepartInfo> eplist = new EPList<SysDepartInfo>(); eplist.CreateIndex(new string[] { "ParentId" }); string sql = "select Id,ParentId,Code,Name from SysDepart"; SqlHelper.ExecuteReader(sql, null, (reader) => { SysDepartInfo record = new SysDepartInfo(); record.Id = Convert.ToString(reader["Id"]); record.ParentId = Convert.ToString(reader["ParentId"]); record.Code = Convert.ToString(reader["Code"]); record.Name = Convert.ToString(reader["Name"]); eplist.Add(record); }); return eplist; } |
通过索引高效查询数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/// <summary> /// 获取子节点 /// </summary> /// <param name="data"></param> /// <param name="parentId"></param> private IEnumerable<TreeInfo> CreateChildren(EPList<SysDepartInfo> data, TreeInfo node) { string id = node == null ? "0" : node.id; List<TreeInfo> childNodes = new List<TreeInfo>(); // ParentId字段上创建了索引,所以这里就可以通过索引值直接取出下一层子节点数据,避免Linq查询引发的效率问题 var indexValues = new string[] { "ParentId:" + id }; var childData = data[indexValues]; childData.ForEach(record => { var childNode = new TreeInfo { id = record.Id, text = record.Code + " " + record.Name }; childNodes.Add(childNode); childNode.children = CreateChildren(data, childNode); }); return childNodes.OrderBy(record => record.text); } |
from:https://www.cnblogs.com/yellowcool/p/7908607.html
View Details
1 2 3 4 5 6 7 8 9 10 11 12 |
public class test1_format { public static void main(String[] args) { BigDecimal decimal = new BigDecimal("1.12345"); System.out.println(decimal); BigDecimal setScale = decimal.setScale(4,BigDecimal.ROUND_HALF_DOWN); System.out.println(setScale); BigDecimal setScale1 = decimal.setScale(4,BigDecimal.ROUND_HALF_UP); System.out.println(setScale1); } } |
参数定义 ROUND_CEILING Rounding mode to round towards positive infinity. 向正无穷方向舍入 ROUND_DOWN Rounding mode to round towards zero. 向零方向舍入 ROUND_FLOOR Rounding mode to round towards negative infinity. 向负无穷方向舍入 ROUND_HALF_DOWN Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down. 向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向下舍入, 例如1.55 保留一位小数结果为1.5 ROUND_HALF_EVEN Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor. 向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,如果保留位数是奇数,使用ROUND_HALF_UP ,如果是偶数,使用ROUND_HALF_DOWN ROUND_HALF_UP Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in […]
View Details1、Math中四舍五入的方法 Math.ceil(double a)向上舍入,将数值向上舍入为最为接近的整数,返回值是double类型 Math.floor(double a)向下舍入,将数值向下舍入为最为接近的整数,返回值是double类型 Math.round(float a)标准舍入,将数值四舍五入为最为接近的整数,返回值是int类型 Math.round(double a)标准舍入,将数值四舍五入为最为接近的整数,返回值是long类型 2、Math中random生成随机数 Math.random()生成大于等于0,小于1的随机数 3、Random类生成随机数 两种构造方式:第一种使用默认的种子(当前时间作为种子),另一个使用long型整数为种子,Random类可以生成布尔型、浮点类型、整数等类型的随机数,还可以指定生成随机数的范围 4、BigDecimal处理小数 两种构造方式:第一种直接value写数字的值,第二种用String
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
import java.math.BigDecimal; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; public class TestNumber { public static void main(String[] args){ //ceil返回大的值 System.out.println(Math.ceil(-10.1)); //-10.0 System.out.println(Math.ceil(10.7)); //11.0 System.out.println(Math.ceil(-0.7)); //-0.0 System.out.println(Math.ceil(0.0)); //0.0 System.out.println(Math.ceil(-0.0)); //-0.0 System.out.println(Math.ceil(-1.7)); //-1.0 //floor返回小的值 System.out.println(Math.floor(-10.1)); //-11.0 System.out.println(Math.floor(10.7)); //10.0 System.out.println(Math.floor(-0.7)); //-1.0 System.out.println(Math.floor(0.0)); //0.0 System.out.println(Math.floor(-0.0)); //-0.0 System.out.println(Math.floor(-1.7)); //-2.0 //round四舍五入,float返回int,double返回long System.out.println(Math.round(10.5)); //11 System.out.println(Math.round(-10.5)); //-10 //Math生成随机数 System.out.println(Math.random()); //Random类生成随机数 Random rand=new Random(); System.out.println(rand.nextBoolean()); System.out.println(rand.nextDouble()); System.out.println(rand.nextInt()); System.out.println(rand.nextInt(10)); //Random使用当前时间作为Random的种子 Random rand2 = new Random(System.currentTimeMillis()); System.out.println(rand2.nextBoolean()); System.out.println(rand2.nextDouble()); System.out.println(rand2.nextInt()); System.out.println(rand2.nextInt(10)); System.out.println(rand2.nextInt(5)); //ThreadLocalRandom ThreadLocalRandom rand3 = ThreadLocalRandom.current(); System.out.println(rand3.nextInt(5,10)); //BigDecimal System.out.println(0.8 - 0.7); //0.10000000000000009 BigDecimal a1=new BigDecimal(0.1); BigDecimal b1=new BigDecimal(0.9); BigDecimal c1=a1.add(b1); System.out.println("a1.add(b1)="+c1); //a1.add(b1)=1.0000000000000000277555756156289135105907917022705078125 BigDecimal a2=new BigDecimal("0.1"); BigDecimal b2=new BigDecimal("0.9"); BigDecimal c2=a2.add(b2); System.out.println("a2="+a2); //a2=0.1 System.out.println("a2.add(b2)="+c2); //a2.add(b2)=1.0 } } |
from:https://www.cnblogs.com/testerlina/p/11349456.html
View Details能编译通过说明SDK导入正确,但是为啥我们点击每一个Java文件会出现好多红色的下划线 ,并提示idea cant resolve symbol 原因就是可能没有清除原来的历史缓存,导致一些错误,解决方法是 File-Invalidate Caches 然后重启IDEA,OK~困扰多年的问题解决!
View Details如题,其实这已经是以前遇到过的一个问题了。在.Net中使用Mysql的组件MySql.Data(Nuget.org的链接在这里http://www.nuget.org/packages/MySql.Data/)时需要在web.config的连接字符串中配置一些额外的属性,以最大程度地契合MS SERVER的数据类型,下面我以自己在实现工作遇到的问题为例子,来说明在连接字符串中配置的作用:Web.config连接Mysql字符串:
1 |
<add key="ConnstringMySql" value="server=xxx.xxx.xxx.xxx;database=YourDatabase;uid=xxx;pwd=xxx;pooling=false;charset=utf8;Treat Tiny As Boolean=false;Convert Zero Datetime=False" /> |
1.pooling:这个键的值设置为true,当值为True时,任何一个新创建的连接都将添加到连接池中当程序被关闭时,在下次试图开启一个相同的连接时,这个连接将从连接池中取出,如果连接字符串相同,则被认为是同一个连接。如果连接字符串不相同,则认为是不同的连接。 2.charset:这个一看应该明白,设置字符编码 3.Treat Tiny As Boolean:如果设置为True,则Mysql中的tinyint类型会被转换为MS Server中的bit类型,但有时候我们是不想要这来的转换的,所以这个可以根据自己的需要来配置 4.Convert Zero Datetime:今天就遇到了这个问题,当没有设置此属性时,如果Mysql数据库中的datetime列为null的时候,.net在转换时会抛出如下异常:Unable to convert MySQL date/time value to System.DateTime at MySql.Data.Types.MySqlDateTime.GetDateTime()这是因为.net的默认最小日期和Mysql的不匹配,导致转换出错,解决办法就是以上连接串中的(设置Convert Zero Datetime=True) 这是个人在实际操作中遇到的一些关于.NET 连接Mysql的常用设置,分享给大家,希望可以对你有一些帮助。如果你有更好的解决方案,欢迎拍砖,指正。
View DetailshttpRuntime <httpRuntime executionTimeout="90" maxRequestLength="40960" useFullyQualifiedRedirectUrl="false" minFreeThreads="8" minLocalRequestFreeThreads="4" appRequestQueueLimit="100" enableVersionHeader="false"/> httpRuntime是配置asp.net http运行时设置,以确定如何处理对asp.net应用程序的请求。 executionTimeout:表示允许执行请求的最大时间限制,单位为秒 maxRequestLength:指示 ASP.NET 支持的最大文件上载大小。该限制可用于防止因用户将大量文件传递到该服务器而导致的拒绝服务攻击。指定的大小以 KB 为单位。默认值为 4096 KB (4 MB)。 useFullyQualifiedRedirectUrl:表示指示客户端重定向是否是完全限定的(采用 "http://server/path" 格式,这是某些移动控件所必需的),或者指示是否代之以将相对重定向发送到客户端。如果为 True,则所有不是完全限定的重定向都将自动转换为完全限定的格式。false 是默认选项。 minFreeThreads:表示指定允许执行新请求的自由线程的最小数目。ASP.NET 为要求附加线程来完成其处理的请求而使指定数目的线程保持自由状态。默认值为 8。 minLocalRequestFreeThreads:表示ASP.NET 保持的允许执行新本地请求的自由线程的最小数目。该线程数目是为从本地主机传入的请求而保留的,以防某些请求在其处理期间发出对本地主机的子请求。这避免了可能的因递归重新进入 Web 服务器而导致的死锁。 appRequestQueueLimit:表示ASP.NET 将为应用程序排队的请求的最大数目。当没有足够的自由线程来处理请求时,将对请求进行排队。当队列超出了该设置中指定的限制时,将通过“503 – 服务器太忙”错误信息拒绝传入的请求。 enableVersionHeader:表示指定 ASP.NET 是否应输出版本标头。Microsoft Visual Studio 2005 使用该属性来确定当前使用的 ASP.NET 版本。对于生产环境,该属性不是必需的,可以禁用。 from:https://www.cnblogs.com/tearer/archive/2012/09/16/2687833.html
View Details今年一月份的时候,微软曾宣布对 gRPC-Web for .NET 的实验性支持,现在它已正式发布。 gRPC 是谷歌开源的高性能、通用 RPC 框架,支持包括 .NET 在内的多种编程语言。它面向移动和基于 HTTP/2 标准设计,但当前,浏览器中无法实现 gRPC HTTP/2 规范,因为没有浏览器 API 能够对请求进行足够的细粒度控制。gRPC-Web 是解决此问题并使 gRPC 在浏览器中可用的标准化协议。 gRPC-Web 即 gRPC for Web Clients。它是一个 JavaScript 库,使 Web 应用程序能够直接与后端 gRPC 服务通信,不需要 HTTP 服务器充当中介。它旨在使 gRPC 在更多情况下可用,包括但不限于: 从浏览器调用 ASP.NET Core gRPC 应用程序 JavaScript SPAs .NET Blazor Web Assembly apps 在 IIS 和 Azure App Service 中托管 ASP.NET Core gRPC 应用程序 从非 .NET Core 平台调用 gRPC —— 在所有 .NET 平台上,HttpClient 均不支持 HTTP/2,而 gRPC-Web 可用于从 Blazor 和 Xamarin 调用 gRPC 服务 微软表示正在与 Blazor 团队合作,使 gRPC-Web 在 Blazor WebAssembly 应用程序中使用时为端到端开发人员提供更好的体验。 根据微软的说法,gRPC 与 […]
View Details1. 基本的RPC模型 主要介绍RPC是什么,基本的RPC代码,RPC与REST的区别,gRPC的使用 1.1 基本概念 RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务 本地过程调用:如果需要将本地student对象的age+1,可以实现一个addAge()方法,将student对象传入,对年龄进行更新之后返回即可,本地方法调用的函数体通过函数指针来指定。 远程过程调用:上述操作的过程中,如果addAge()这个方法在服务端,执行函数的函数体在远程机器上,如何告诉机器需要调用这个方法呢? 首先客户端需要告诉服务器,需要调用的函数,这里函数和进程ID存在一个映射,客户端远程调用时,需要查一下函数,找到对应的ID,然后执行函数的代码。 客户端需要把本地参数传给远程函数,本地调用的过程中,直接压栈即可,但是在远程调用过程中不再同一个内存里,无法直接传递函数的参数,因此需要客户端把参数转换成字节流,传给服务端,然后服务端将字节流转换成自身能读取的格式,是一个序列化和反序列化的过程。 3.数据准备好了之后,如何进行传输?网络传输层需要把调用的ID和序列化后的参数传给服务端,然后把计算好的结果序列化传给客户端,因此TCP层即可完成上述过程,gRPC中采用的是HTTP2协议。 总结一下上述过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Client端 // Student student = Call(ServerAddr, addAge, student) 1. 将这个调用映射为Call ID。 2. 将Call ID,student(params)序列化,以二进制形式打包 3. 把2中得到的数据包发送给ServerAddr,这需要使用网络传输层 4. 等待服务器返回结果 5. 如果服务器调用成功,那么就将结果反序列化,并赋给student,年龄更新 // Server端 1. 在本地维护一个Call ID到函数指针的映射call_id_map,可以用Map<String, Method> callIdMap 2. 等待服务端请求 3. 得到一个请求后,将其数据包反序列化,得到Call ID 4. 通过在callIdMap中查找,得到相应的函数指针 5. 将student(params)反序列化后,在本地调用addAge()函数,得到结果 6. 将student结果序列化后通过网络返回给Client |
在微服务的设计中,一个服务A如果访问另一个Module下的服务B,可以采用HTTP REST传输数据,并在两个服务之间进行序列化和反序列化操作,服务B把执行结果返回过来。 由于HTTP在应用层中完成,整个通信的代价较高,远程过程调用中直接基于TCP进行远程调用,数据传输在传输层TCP层完成,更适合对效率要求比较高的场景,RPC主要依赖于客户端和服务端之间建立Socket链接进行,底层实现比REST更复杂。 1.2 rpc demo 系统类图 系统调用过程 客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
public class RPCClient<T> { public static <T> T getRemoteProxyObj(final Class<?> serviceInterface, final InetSocketAddress addr) { // 1.将本地的接口调用转换成JDK的动态代理,在动态代理中实现接口的远程调用 return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Socket socket = null; ObjectOutputStream output = null; ObjectInputStream input = null; try{ // 2.创建Socket客户端,根据指定地址连接远程服务提供者 socket = new Socket(); socket.connect(addr); // 3.将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者 output = new ObjectOutputStream(socket.getOutputStream()); output.writeUTF(serviceInterface.getName()); output.writeUTF(method.getName()); output.writeObject(method.getParameterTypes()); output.writeObject(args); // 4.同步阻塞等待服务器返回应答,获取应答后返回 input = new ObjectInputStream(socket.getInputStream()); return input.readObject(); }finally { if (socket != null){ socket.close(); } if (output != null){ output.close(); } if (input != null){ input.close(); } } } }); } } |
服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
public class ServiceCenter implements Server { private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); private static final HashMap<String, Class> serviceRegistry = new HashMap<String, Class>(); private static boolean isRunning = false; private static int port; public ServiceCenter(int port){ ServiceCenter.port = port; } @Override public void start() throws IOException { ServerSocket server = new ServerSocket(); server.bind(new InetSocketAddress(port)); System.out.println("Server Start ....."); try{ while(true){ executor.execute(new ServiceTask(server.accept())); } }finally { server.close(); } } @Override public void register(Class serviceInterface, Class impl) { serviceRegistry.put(serviceInterface.getName(), impl); } @Override public boolean isRunning() { return isRunning; } @Override public int getPort() { return port; } @Override public void stop() { isRunning = false; executor.shutdown(); } private static class ServiceTask implements Runnable { Socket client = null; public ServiceTask(Socket client) { this.client = client; } @Override public void run() { ObjectInputStream input = null; ObjectOutputStream output = null; try{ input = new ObjectInputStream(client.getInputStream()); String serviceName = input.readUTF(); String methodName = input.readUTF(); Class<?>[] parameterTypes = (Class<?>[]) input.readObject(); Object[] arguments = (Object[]) input.readObject(); Class serviceClass = serviceRegistry.get(serviceName); if(serviceClass == null){ throw new ClassNotFoundException(serviceName + "not found!"); } Method method = serviceClass.getMethod(methodName, parameterTypes); Object result = method.invoke(serviceClass.newInstance(), arguments); output = new ObjectOutputStream(client.getOutputStream()); output.writeObject(result); }catch (Exception e){ e.printStackTrace(); }finally { if(output!=null){ try{ output.close(); }catch (IOException e){ e.printStackTrace(); } } if (input != null) { try { input.close(); } catch (IOException e) { e.printStackTrace(); } } if (client != null) { try { client.close(); } catch (IOException e) { e.printStackTrace(); } } } } } } |
1 2 3 4 5 6 7 8 |
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ServiceProducerImpl</span> <span class="token keyword">implements</span> <span class="token class-name">ServiceProducer</span><span class="token punctuation">{</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">sendData</span><span class="token punctuation">(</span><span class="token class-name">String</span> data<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string">"I am service producer!!!, the data is "</span><span class="token operator">+</span> data<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class RPCTest { public static void main(String[] args) throws IOException { new Thread(new Runnable() { @Override public void run() { try { Server serviceServer = new ServiceCenter(8088); serviceServer.register(ServiceProducer.class, ServiceProducerImpl.class); serviceServer.start(); } catch (IOException e) { e.printStackTrace(); } } }).start(); ServiceProducer service = RPCClient.getRemoteProxyObj(ServiceProducer.class, new InetSocketAddress("localhost", 8088)); System.out.println(service.sendData("test")); } } |
1.3 完整源码 RPCdemo 1.4 分析 这里客户端只需要知道Server端的接口ServiceProducer即可,服务端在执行的时候,会根据具体实例调用实际的方法ServiceProducerImpl,符合面向对象过程中父类引用指向子类对象。 2. gRPC的使用 2.1. gRPC与REST REST通常以业务为导向,将业务对象上执行的操作映射到HTTP动词,格式非常简单,可以使用浏览器进行扩展和传输,通过JSON数据完成客户端和服务端之间的消息通信,直接支持请求/响应方式的通信。不需要中间的代理,简化了系统的架构,不同系统之间只需要对JSON进行解析和序列化即可完成数据的传递。 但是REST也存在一些弊端,比如只支持请求/响应这种单一的通信方式,对象和字符串之间的序列化操作也会影响消息传递速度,客户端需要通过服务发现的方式,知道服务实例的位置,在单个请求获取多个资源时存在着挑战,而且有时候很难将所有的动作都映射到HTTP动词。 正是因为REST面临一些问题,因此可以采用gRPC作为一种替代方案,gRPC 是一种基于二进制流的消息协议,可以采用基于Protocol Buffer的IDL定义grpc API,这是Google公司用于序列化结构化数据提供的一套语言中立的序列化机制,客户端和服务端使用HTTP/2以Protocol Buffer格式交换二进制消息。 gRPC的优势是,设计复杂更新操作的API非常简单,具有高效紧凑的进程通信机制,在交换大量消息时效率高,远程过程调用和消息传递时可以采用双向的流式消息方式,同时客户端和服务端支持多种语言编写,互操作性强;不过gRPC的缺点是不方便与JavaScript集成,某些防火墙不支持该协议。 注册中心:当项目中有很多服务时,可以把所有的服务在启动的时候注册到一个注册中心里面,用于维护服务和服务器之间的列表,当注册中心接收到客户端请求时,去找到该服务是否远程可以调用,如果可以调用需要提供服务地址返回给客户端,客户端根据返回的地址和端口,去调用远程服务端的方法,执行完成之后将结果返回给客户端。这样在服务端加新功能的时候,客户端不需要直接感知服务端的方法,服务端将更新之后的结果在注册中心注册即可,而且当修改了服务端某些方法的时候,或者服务降级服务多机部署想实现负载均衡的时候,我们只需要更新注册中心的服务群即可。 RPC调用过程 2.2. gRPC与Spring Boot 这里使用SpringBoot+gRPC的形式实现RPC调用过程 项目结构分为三部分:client、grpc、server 项目结构 2.2.2 grpc pom.xml中引入依赖:
1 2 3 4 5 |
<dependency> <groupId>io.grpc</groupId> <artifactId>grpc-all</artifactId> <version>1.12.0</version> </dependency> |
引入bulid
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.4.1.Final</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.0</version> <configuration> <pluginId>grpc-java</pluginId> <protocArtifact>com.google.protobuf:protoc:3.0.2:exe:${os.detected.classifier}</protocArtifact> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.2.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build> |
创建.proto文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
syntax = "proto3"; // 语法版本 // stub选项 option java_package = "com.shgx.grpc.api"; option java_outer_classname = "RPCDateServiceApi"; option java_multiple_files = true; // 定义包名 package com.shgx.grpc.api; // 服务接口定义,服务端和客户端都要遵守该接口进行通信 service RPCDateService { rpc getDate (RPCDateRequest) returns (RPCDateResponse) {} } // 定义消息(请求) message RPCDateRequest { string userName = 1; } // 定义消息(响应) message RPCDateResponse { string serverDate = 1; } |
mvn complie 生成代码: 2.2.3 client 根据gRPC中的项目配置在client和server两个Module的pom.xml添加依赖 […]
View DetailsLinq中查询一个表中指定的几个字段: var ts = t.FindAllItems().Where(P => P.CompanyID == CurSiteUser.CompanyId).Select(s => new { BillPeriod = s.BillPeriod,FieldB=s.FieldB }).Distinct().ToList().OrderByDescending(s => s.BillPeriod).Take(24); // FindAllItems()为查询对应表的所有数据的方法; // Where 里面为查询条件 // Select 为查询的筛选条件 new{} 里面就是要查询的字段 //Distinct() 为去除重复的查询 //ToList() 为将查询转换为List<> //OrderByDescending() 表示排序字段及排序方法(倒序排列) //Take(N) 表示查询前N条数据; from:https://blog.csdn.net/linlin2294592017/article/details/27540253
View Details直接上代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
List<TestModel> testList = new List<TestModel>(); testList.Add(new TestModel { Id = 1, Name = "A" }); testList.Add(new TestModel { Id = 2, Name = "B" }); testList.Add(new TestModel { Id = 3, Name = "C" }); List<dynamic> test = new List<dynamic>(); foreach (var item in testList) { dynamic dobj = new System.Dynamic.ExpandoObject(); var dic = (IDictionary<string, object>)dobj; var t = item.GetType(); var properties = t.GetProperties(); // 循环赋值原Model中的值 foreach (var propertyInfo in properties) { var propertyName = propertyInfo.Name; dic[propertyName] = propertyInfo.GetValue(item); } // 动态扩展属性 dic["Age"] = 3; test.Add(dic); } |
示例Model
1 2 3 4 5 |
public class TestModel { public int Id { get; set; } public string Name { get; set; } } |