我个人对GRPC是比较感兴趣的,最近在玩通过前端调用GRPC。通过前端调用GRPC业界有两种方式:GRPC Web和GRPC JSON转码。
GRPC Web
通过JS或者Blazor WASM调用GRPC,微软在这方面做的还是很好的,从.NET Core3.0之后就提供了两种实现GRPC Web的方式(Grpc.AspNetCore.Web与Envoy)。我在之前的一篇里也写过如何通过Blazor WASM调用GRPC Web。
GRPC JSON
通过Restful api调用一个代理服务,代理服务将数据转发到GRPC Server就是GRPC JSON。微软从.NET7开始也正式提供了GRPC JSON转码的方式。
既然有了GRPC Web与GRPC Json,那我为啥还要再造这么一个轮子?
原因是有位同行看了如何通过Blazor WASM调用GRPC Web 这篇文章后,告诉我微信小程序目前没办法通过这种方式调用GRPC。我当时觉得很奇怪,微信小程序也属于前端,为啥不能调用GRPC呢?
只是听说还不能确认,要自己试一试,于是我用GRPC Web的方式让小程序调用GRPC,首先需要生成GRPC JS Client代码:
protoc.exe -I=. test.proto --js_out=import_style=commonjs:.\grpcjs\ --plugin=protoc-gen-grpc=.\protoc-gen-grpc-web.exe --grpc-web_out=import_style=commonjs,mode=grpcwebtext:.\grpcjs\
然后将生成的代码引入小程序端,发现确实有问题,微信小程序编译后无法正常识别GRPC的namespace,会报以下错误:
proto is not defined
去查了下原因,应该是因为小程序目前不支持protobuf序列化。然后我通过一种取巧的方式手动在生成的GRPC JS中添加了proto变量
var proto = {}
再次尝试,虽然proto能找到,但是又找不到其他对象,并且最主要的是GRPC JS Client是通过proto工具生成的,每次生成手动定义proto变量也不现实。
GRPC Web+小程序遇到问题总结:
既然小程序通过GRPC Web方式调用GRPC失败,那还有GRPC Json。
我使用了Envoy来充当restful代理,调用GRPC。我在之前有一篇通过Envoy JSON代理GRPC的帖子。按这个帖子来了一遍。
计划通过docker-compose方式运行GRPC Server和Envoy代理。
既然用GRPC,那肯定用http2/http2,在docker里运行.net core必然需要证书,没有证书就自己搞一个自签证书。
openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.cer
openssl pkcs12 -export -in server.cer -inkey server.key -out server.pfx
证书有了,在GRPC里配置https
builder.WebHost.ConfigureKestrel(o =>
{
o.ListenAnyIP(1111, p =>
{
p.Protocols = HttpProtocols.Http2;
p.UseHttps("/app/server.pfx", "123456");
});
});
然后就开始配置envoy
首先生成grpc proto描述符
protoc.exe -I=. --descriptor_set_out=.\test.pb --include_imports .\test.proto --proto_path=.
然后定义envoy配置文件
admin:
address:
socket_address: {address: 0.0.0.0, port_value: 9901}
static_resources:
listeners:
- name: listener1
address:
socket_address: {address: 0.0.0.0, port_value: 10000}
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: grpc_json
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match: {prefix: "/test"}
route:
cluster: grpc
http_filters:
- name: envoy.filters.http.grpc_json_transcoder
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
proto_descriptor: "/etc/envoy/test.pb"
services: ["test"]
print_options:
add_whitespace: true
always_print_primitive_fields: true
always_print_enums_as_ints: false
preserve_proto_field_names: false
auto_mapping: true
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: grpc
type: static
connect_timeout: 15s
lb_policy: ROUND_ROBIN
dns_lookup_family: V4_ONLY
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http2_protocol_options: {}
load_assignment:
cluster_name: grpc
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 某ip
port_value: 1111
下面就定义envoy的dockerfile,主要是信任自签证书
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM envoyproxy/envoy-dev:e834c24e061b710348ffd72016d5d1069698b4ff
COPY ["server.crt","/usr/local/share/ca-certificates/"]
RUN ["update-ca-certificates"]
最后就是定义docker-compsoe.yaml
version: '3.4'
services:
myenvoy:
image: myenvoy
container_name: myenvoy
command: "-c /etc/envoy/envoy.yaml --log-level debug"
build:
context: .
dockerfile: GrpcServer/DockerfileEnvoy
volumes:
- "grpcpbs/:/etc/envoy/"
- "grpcpbs/logs:/logs"
ports:
- "9901:9901"
- "10000:10000"
depends_on:
- grpcserver
networks:
- mynetwork
grpcserver:
image: grpcserver
container_name: grpcserver
networks:
- mynetwork
build:
context: .
dockerfile: GrpcServer/Dockerfile
ports:
- "1111:1111"
networks:
mynetwork:
最后通过docker-compsoe up -d运行,但是postman调用的时候,envoy与grpcserver的通信连接成功了,但是数据传输时总是被 connection reset,去github上找原因也没找到。至此grpc json+envoy又失败了。
GRPC JSON+Envoy+小程序遇到问题总结:
既然envoy走不通不行,那就自己造一个吧。
GRPC JSON的形式,原理就是通过一个web api接收restful请求,将请求数据转发到GRPC Server。
首先创建一个web api命名为GrpcGateway,并引入proto文件,生成grpc client代码
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.20.1" />
<PackageReference Include="Grpc.Net.Client" Version="2.46.0" />
<PackageReference Include="Grpc.Tools" Version="2.46.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="..\*.proto" GrpcServices="Client" />
</ItemGroup>
然后创建一个控制器去接受restful请求,而grpc client可采用反射来创建。
[ApiController]
[Route("[controller]")]
public class ProcessGrpcRequestController : ControllerBase
{
private readonly ILogger<ProcessGrpcRequestController> _logger;
private readonly Func<string, ClientBase> _getGrpcClient;
public ProcessGrpcRequestController(ILogger<ProcessGrpcRequestController> logger, Func<string, ClientBase> getGrpcClient)
{
_logger = logger;
_getGrpcClient = getGrpcClient;
}
/// <summary>
/// 调用grpc
/// </summary>
/// <param name="serviceName">Grpc Service Name 从proto文件中查询</param>
/// <param name="method">Grpc Method Name 从proto文件中查询</param>
/// <returns></returns>
[HttpPost("serviceName/{serviceName}/method/{method}")]
public async Task<IActionResult> ProcessAsync(string serviceName, string method)
{
try
{
if (string.IsNullOrEmpty(serviceName))
{
return BadRequest("serviceName不能为空");
}
if (string.IsNullOrEmpty(method))
{
return BadRequest("method不能为空");
}
using var sr = new StreamReader(Request.Body, leaveOpen: true, encoding: Encoding.UTF8);
var paramJson = await sr.ReadToEndAsync();
if (string.IsNullOrEmpty(paramJson))
{
return BadRequest("参数不能为空");
}
var client = _getGrpcClient(serviceName);
if (client == null)
{
return NotFound();
}
Type t = client.GetType();
var processMethod = t.GetMethods().Where(e => e.Name == method).FirstOrDefault();
if (processMethod == null)
{
return NotFound();
}
var parameters = processMethod.GetParameters();
if (parameters == null)
{
return NotFound();
}
var param = JsonConvert.DeserializeObject(paramJson, parameters[0].ParameterType);
if (param == null)
{
return BadRequest("参数不能为空");
}
var pt = param.GetType();
var headers = new Metadata();
if (Request.Headers.Keys.Contains("Authorization"))
{
headers.Add("Authorization", Request.Headers["Authorization"]);
}
var result = processMethod.Invoke(client, new object[] { param, headers, null, null });
return Ok(result);
}
catch(Exception ex) when (
ex.InnerException !=null && ex.InnerException !=null && ex.InnerException is RpcException &&
((ex.InnerException as RpcException).StatusCode == Grpc.Core.StatusCode.Unauthenticated ||
((ex.InnerException as RpcException).StatusCode == Grpc.Core.StatusCode.PermissionDenied)))
{
_logger.LogError(ex, ex.ToString());
return Unauthorized();
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
return BadRequest(ex.ToString());
}
}
}
然后注入动态反射创建grpc client的方法
services.AddScoped(p => {
Func<string, ClientBase> func = serviceName =>
{
var channel = GrpcChannel.ForAddress(grpcServerAddress);
var parentClassName = $"{serviceName}";
var assembly = Assembly.Load("你的dll名字");
var parentType = assembly.GetType(parentClassName);
var clientType= parentType.GetNestedType($"{serviceName}Client");
if (clientType == null)
{
throw new Exception($"serviceName:{serviceName}不存在");
}
var client = Activator.CreateInstance(clientType, new object[] { channel });
return (ClientBase)client;
};
return func;
});
然后定义grpc gateway dockerfile ,最主要需要信任证书
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 16666
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["MyGateway/MyGateway.csproj", "MyGateway/"]
COPY . .
WORKDIR "/src/MyGateway"
FROM build AS publish
RUN dotnet publish "MyGateway.csproj" -c Release -o /app/publish
FROM base AS final
COPY ["server.crt","/usr/local/share/ca-certificates/"]
RUN ["update-ca-certificates"]
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyGateway.dll"]
最后通过定义docker-compose
version: '3.4'
services:
mygateway:
image: mygateway
container_name: mygateway
networks:
- mynetwork
build:
context: .
dockerfile: MyGateway/Dockerfile
ports:
- "2222:2222"
grpcserver:
image: grpcserver
container_name: grpcserver
networks:
- mynetwork
build:
context: .
dockerfile: GrpcServer/Dockerfile
ports:
- "1111:1111"
networks:
mynetwork:
通过docker-compsoe up -d 启动
通过postman调用,看到200状态码,终于成功了,最后试了下小程序也能通过这种方式调用后端GRPC了,整个人都舒服了...

几乎是一夜之间,微信小游戏《羊了个羊》火了。这个依靠寻找相同元素消除方块的小游戏,凭借其“变态级别”的游戏难度成功破圈,闯入了无数人的休闲时间,并数次冲上热搜。当然,很多人在微博、朋友圈对它的评价主要是:连第一关都过不了!▲ 《羊了个羊》游戏界面对于这样一个规模不大的小游戏开发团队来说,收获超高人气的同时,头疼的事情发生了:▲《羊了个羊》官方微博通告是的,突然涌入的大量玩家致使游戏服务器异常,而且问题出现了不止一次。这也导致不少玩家在微博上疯狂吐槽:好不容易被人安利了这款游戏,结果发现根本进不去!也有一些人在微博等渠道向开发团队提出改进建议,但又不确定能否被官方看到。其实,不仅是《羊了个羊》
我有以下使用JS和CSS旋转轮子的代码:varprefix=(function(){if(document.body.style.MozTransform!==undefined){return"MozTransform";}elseif(document.body.style.WebkitTransform!==undefined){return"WebkitTransform";}elseif(document.body.style.OTransform!==undefined){return"OTransform";}else{return"transform";}}()),ro
最近,一款小程序游戏《羊了个羊》火了,多次冲上社交平台热搜。但同一时间却被众多网友与玩家吐槽——感觉被收割了。据游戏官方介绍,这是一款超难的闯关消除小游戏,通关率不到0.1%。具体方法是,玩家们点击上方卡牌,被选中的卡牌会下移到底部的框中,框内最多储存7张卡牌,当有3张相同的卡牌同置于框内时,则可达成消除。“就是一款消消乐的游戏”,资深游戏玩家小陈表示,能否通关从发牌就决定了。官方也说了通关率不到0.1%。就是99.9%的人,是通过不了的,即便用完所有的道具。记者从社交平台看到,有网友晒出了通关的视频,当中有的就剩两三张牌,也无法全部消完。羊了个羊小游戏的玩法上区别与消消乐,经典的玩法看似简单
我有一个非常简单的问题。我正在学习Java,并被分配画一辆汽车。我在一个扩展JPanel的类中完成了这一切,并在paintComponent()中完成了绘图。我意识到这是糟糕的面向对象编程,并决定尝试对某些部分进行子类化以纠正这种情况。我尝试创建一个画轮子的类,但没有成功。本质上,我希望能够做到这一点:MainClassextendsJPanelpaintComponent{WheelleftWheel=newWheel(0,50,100);this.add(leftWheel);}这应该在JPanel中的点(0,50)处绘制一个轮子,其直径为100。但是,我不确定我应该如何控制JPa
公司最近在招自动化测试岗,居然一天内就收了几百份简历!想不到吧?!都快面吐了,想招一个合适的技术同学太不容易了,需要去挖的细节太多了。一般来说,很多人都会被问接口工具、aap自动化、测试框架、测试平台等内容,这是在评估普通面试者的知识结构。但!今天我是面试面试官,那就不是这样去聊的了,比如我会问这些:在自动化测试中,你是如何选择和设计测试用例的?你使用过哪些自动化测试工具,如何选择自动化测试工具?你熟悉哪些自动化测试工具和框架?你使用过哪些编程语言用于测试开发?如何区分黑盒测试、白盒测试和灰盒测试,并举例说明?如何设计并实现一个自动化测试框架?请列举几个关键步骤。你如何处理性能测试方面的问题?
我刚刚发现自己创建了一个类templatestructinvoker{voidoperator()(T&it)const{it();}};所以我可以通过invoker到想要调用invoker::operator()(foo&)的东西(不在我的控制之下)反复使用不同的foo实例,让它将这些调用转发给foo的foo::operator()()方法。我知道它只有几行,但这似乎是STL的函数式或boost::bind可能已经提供的那种东西不知何故。除了我看不到诀窍,如果有的话。(我确定我不是第一个使用非常类似的东西的人;它有名字吗?) 最佳答案
国际金融报的报道,小程序游戏《羊了个羊》在社交媒体上引发大量关注,短短几天,这款游戏已经激起无数玩家的“该死的胜负欲”。那么,这款不“充钱”但需要通过观看广告才能使用道具的小游戏,真的是薅了用户的羊毛么?1、“羊了个羊”背后有哪些技术实现点?1、小程序自带推广,因为以微信平台做载体,利用微信的社交进行推广2、触手可及用完就走,不需要下载节省手机空间3、成本降低,小程序可以降低开发和运营的成本,小程序的成本只有APP开发的十分之一,对于创业者来说优势更大4、小程序更加注重用户的体验,流畅程度几乎可以和APP媲美2、“羊了个羊”商业模式 一般是先用低门槛吸引玩家入坑,再通过各自的商业模式(广告、道
需要提交。官网:uni_modules|uni-app官网https://uniapp.dcloud.net.cn/plugin/uni_modules.html什么是uni_modulesuni_modules是uni-app的插件模块化规范(HBuilderX3.1.0+支持),通常是对一组jssdk、组件、页面、uniCloud云函数、公共模块等的封装,用于嵌入到uni-app项目中使用,也支持直接封装为项目模板。为什么有了node_modules,还需要再发明一个uni_modules的轮子?node_modules 不满足云端一体的需求。uniCloud的云函数、公共模块、schem
此代码的最后一行无法使用castingAndTernary.cpp:15进行编译:错误:不同指针类型“D1*”和“D2*”之间的条件表达式缺少强制转换一个真正聪明的编译器可能没有任何困难,因为两者都可以安全地转换为B*(基类)。我不愿意使用static_cast和dynamic_cast等等——我担心有一天我会混淆这些类并得到未定义的行为。这就是我创建up_cast模板的原因。该模板在允许的转换方面做了最低限度的工作。有更简单的方法吗?还有其他解决方法,但我不禁想到我可以使用更简单、更安全的方法吗?structB{};structD1:publicB{};structD2:public
今天上班开早会就是新人见面仪式,听说来了个很厉害的大佬,年纪还不大,是上家公司离职过来的,薪资已经达到中高等水平,很多人都好奇不已,能拿到这个薪资应该人不简单,果然,自我介绍的时候都惊讶到我们了,让我见识到了什么叫真正的测试天花板…了解过后这大佬是有5年的测试开发经验,不仅有熟练测试业务能力,而且还会编程,测试框架,测试工具开发,还能全面掌握数据库等方面的技能,甚至熟悉分布式组件等高级技能。在上家公司也是管理一个项目的组长。果然人有能力就是不一样,在哪里都发光!!!这两天和朋友说起这件事情,感叹现在的年轻人都这么优秀,说到底,软件测试行业还是属于技术岗位,随着不断地转行人员以及毕业的大学生疯狂