微软正式推出 gRPC-Web for .NET
今年一月份的时候,微软曾宣布对 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 Details什么是RPC?
1. 基本的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 Details巧用正则和document.location.search获取URL参数的值
要获取当前页面URL的参数,可能大家第一个想到是使用 window.location.href 或者是document.location.href ,获取结果诸如http://www.xxx.com/?aa=xx&bb=xx ;但是其实我们需要的只是:?aa=xx&bb=xx。这种形式可以使用 document.location.search 这个属性获取。 如果我想要获取该URL后面参数aa的值该怎么弄呢?常见的方式可能是这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function( param ){ var url = window . location . toString (); url = url . split ('?' ); if (typeof (url [ 1 ]) == 'string' ) { url = url [ 1 ]. split ('&' ); for (i = 0 ;i < url . length ;i ++ ) { s= url [ i ]. split ("=" ); if( s[0 ] == "param" ) return s[1]; } } return null; } |
改用document.location.search和正则获取参数将使代码更加简洁:
1 2 3 4 5 6 7 8 |
function getParameter (sProp ) { var re = new RegExp (sProp + "=([^\&]*)" , "i" ); var a = re . exec (document . location . search ); if (a == null ) return null ; return a [ 1 ]; }; |
from:https://www.cnblogs.com/codebean/archive/2011/05/27/2059901.html
View Details上传文件报413 Request Entity Too Large错误解决办法
产生这种原因是因为服务器限制了上传大小 1、nginx服务器的解决办法 修改nginx.conf的值就可以解决了 将以下代码粘贴到nginx.conf内
1 |
client_max_body_size 20M; |
可以选择在http{ }中设置:client_max_body_size 20m; 也可以选择在server{ }中设置:client_max_body_size 20m; 还可以选择在location{ }中设置:client_max_body_size 20m; 三者有区别 设置到http{}内,控制全局nginx所有请求报文大小 设置到server{}内,控制该server的所有请求报文大小 设置到location{}内,控制满足该路由规则的请求报文大小 同时记得修改php.ini内的上传限制 upload_max_filesize = 20M 注意:如果以上修改完毕后还会出现413错误的话 , 可能是域名问题 , 本人遇到过此类情况 , 记录 2、apache服务器修改 在apache环境中上传较大软件的时候,有时候会出现413错误,出现这个错误的原因,是因为apache的配置不当造成的,找到apache的配置文件目录也就是conf目录,和这个目录平行的一个目录叫conf.d打开这个conf.d,里面有一个php.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
目录内容如下: # # PHP is an HTML-embedded scripting language which attempts to make it # easy for developers to write dynamically generated webpages. # LoadModule php4_module modules/libphp4.so # # Cause the PHP interpreter handle files with a .php extension. # SetOutputFilter PHP SetInputFilter PHP LimitRequestBody 6550000 # # Add index.php to the list of files that will be served as directory # indexes. # DirectoryIndex index.php |
误就发生在这个LimitRequestBody配置上,将这个的值改大到超过你的软件大小就可以了 如果没有这个配置文件请将
1 2 3 |
SetOutputFilter PHP SetInputFilter PHP LimitRequestBody 6550000 |
写到apache的配置文件里面即可。 3、IIS服务器(Windows Server 2003系统IIS6) 先停止IIS Admin Service服务,然后 找到windows\system32\inesrv\下的metabase.xml,打开,找到ASPMaxRequestEntityAllowed 修改为需要的值,然后重启IIS Admin Service服务 1、在web服务扩展 允许active server pages和在服务器端的包含文档 2、修改各站点的属性 主目录-配置-选项-启用父路径 3、使之可以上传大文档(修改成您想要的大小就可以了,以字节为单位) c:\WINDOWS\system32\inetsrv\MetaBase.xml !企业版的windows2003在第592行 默认的预设置值 AspMaxRequestEntityAllowed="204800" 即200K 将其加两个0,即改为,现在最大就可以上传20M了。 AspMaxRequestEntityAllowed="20480000" 作者:烂孩子 链接:https://www.jianshu.com/p/3851c3d6eaf1 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
View DetailsLinq 中查询一个表中指定的字段
Linq中查询一个表中指定的几个字段: 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基于dynamic,动态给强类型Model扩展属性
直接上代码
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; } } |
js判断对象是否为空对象的几种方法
1.将json对象转化为json字符串,再判断该字符串是否为"{}" var data = {}; var b = (JSON.stringify(data) == "{}"); alert(b);//true 2.for in 循环判断 var obj = {}; var b = function() { for(var key in obj) { return false; } return true; } alert(b());//true 3.jquery的isEmptyObject方法 此方法是jquery将2方法(for in)进行封装,使用时需要依赖jquery var data = {}; var b = $.isEmptyObject(data); alert(b);//true 4.Object.getOwnPropertyNames()方法 此方法是使用Object对象的getOwnPropertyNames方法,获取到对象中的属性名,存到一个数组中,返回数组对象,我们可以通过判断数组的length来判断此对象是否为空 注意:此方法不兼容ie8,其余浏览器没有测试 var data = {}; var arr = Object.getOwnPropertyNames(data); // Object.keys(objTest).length alert(arr.length == 0);//true 5.使用ES6的Object.keys()方法 与4方法类似,是ES6的新方法, 返回值也是对象中属性名组成的数组 var data = {}; var arr = Object.keys(data); alert(arr.length == 0);//true from:https://www.cnblogs.com/jpfss/p/9105119.html
View DetailsforEach()里面使用异步函数,那如何等所有的异步函数都执行完再 进行下一步
两种方法 方法一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var arry = [...]; Promise.all(arry.map(function(elem){ return new Promise(function(resolve, reject){ ... resolve(result); }) })).then(function(data){ //在这就可以等所有的返回结果可以得到 }) |
方法二
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var arry = [...]; var counter = 0; arry.forEach(function(elem){ //异步回调中 counter++; if(counter === arr.length){ //在这执行所有执行的完后的 } }) |
from:https://www.cnblogs.com/ttjm/p/13065240.html
View DetailsExtJs4发送同步请求的store
ExtJs的store默认是异步的,但有时候我们必须要发送同步请求才能满足需求,比如在多线程中,要实现同步很简单,只需继承Ext.data.proxy.Ajax,重写的同步类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Ext.define('Ext.ux.data.proxy.Ajax', { extend: 'Ext.data.proxy.Ajax', async:true, doRequest: function(operation, callback, scope) { var writer = this.getWriter(), request = this.buildRequest(operation); if (operation.allowWrite()) { request = writer.write(request); } Ext.apply(request, { async : this.async, binary : this.binary, headers : this.headers, timeout : this.timeout, scope : this, callback : this.createRequestCallback(request, operation, callback, scope), method : this.getMethod(request), disableCaching: false }); Ext.Ajax.request(request); return request; } }); |
使用的时候,只需修改代理即可,async:false表示同步,true为异步:
1 2 3 4 5 6 7 8 |
proxy: Ext.create("Ext.ux.data.proxy.Ajax",{ async:false, url:" ", reader: { type: 'json', root: 'root' } }) |
from:https://my.oschina.net/wangdaoliang/blog/776567
View DetailsCentOS7 SpringBoot 注册服务
1.服务配置文件
1 2 |
cd /etc/systemd/system touch your_service_name.service |
1 2 3 4 5 6 |
[Unit] Description=zaomianbao [Service] ExecStart= /your_java_path/bin/java -jar /your_app_path/your_app_name.jar [Install] WantedBy=multi-user.target |
2.启动服务
1 |
systemctl start your_service_name.service |
参考: https://blog.csdn.net/weixin_37490221/article/details/80758276
View Details