一个月前,公司的运行WCF的windows服务器down掉了,由于 AWS 没有通知,没有能第一时间发现问题。

所以,客户提出将WCF服务由C#改为JAVA,在Linux上面运行;一方面,AWS对Linux有较多的监控措施,另一方面,假如出现问题,可以设置自动重启等服务。

老旧的WCF服务

目前WCF服务,主要提供windows桌面软件的数据接口,应该有五六年的历史了。我进入公司后,WCF服务的代码,一直由我一个人来维护。存在很多历史遗留问题,也有不同版本的共存。

如果java重写的话,其中的业务逻辑代码,难免会出现各种各样的bug,增加开发和测试的工作量。听说,要移植到linux服务上后,第一时间想到的就是跨平台.net core

.net core 经过了四年的发展,到目前的 3.1 LST版本,已经是非常成熟的跨平台解决方案了。

之后,我就在网上查找,有没有WCF的.net core 版本,查询到的信息总结如下:

  1. Core WCF不打算做WCF到.NET Core的100%兼容的移植;
  2. 对于新应用程序,WCF这种SOAP技术不建议使用;
  3. 对于老的应用程序,建议将这些保留在.NET Framework上;
  4. 如果您真的想将一个旧的应用程序迁移到.NET Core并且想继续使用WCF和WF, 社区的开源项目也是可以的,但是上生产的时间表就要到了2020年.NET 5;
  5. 开源社区,也强烈建议目前不要用于生产环境。

很遗憾,想不改动代码就迁移到 Linux 上面,基本是不可能的了。

我的最理想情况,尽量少的手写代码,最好可以像WCF一样,自动生成代理类,像访问本地代码一样,来调用接口。之后,就发现了asp.net core + gRPC这种形式。

了解gRPC

gRPC 的好处非常多:高性能传输数据小,支持多语言生成工具使用HTTP2协议,这些好处网上都有大量详细的介绍,本文不做赘述。

其实我最看重的部分还是:客户端和服务端代码,都可以通过一个 proto 协议文件来自动生成

而微软官方,也建议用 ASP.NET Core gRPC。 《适用于 WCF 开发人员的 ASP.NET Core gRPC》

gRPC 的 proto 文件

为了了解 proto 文件的写法,硬着头皮看谷歌英文文档, proto3 勉强了解大概。《Language Guide (proto3)》,下面列出一些,我在使用过程中的经验总结:

  1. 一个RPC服务必须有且仅有一个入参一个出参;假如不需要的话,可以设置为空的对象google.protobuf.Empty
  2. 基本类型( string, int32 等)不能作为PRC服务的参数,可使用谷歌提供的封装对象,如:google.protobuf.StringValuegoogle.protobuf.Int32Value 详见 google/protobuf/wrappers.proto文件;
  3. proto3 不允许null值,这是由于 Protobuf 二进制序列化,空和null不能区分,利用google.protobuf.StringValue 则可以实现null值;同第2点;
  4. string name=1;这个数字必须写,用作 Protobuf 二进制序列化,并且常用的属性最好放在前12;PS: 太不习惯了,总以为是在赋值操作;
  5. 枚举类型必须从0开始,即:enum Weekday {Sunday=0;Monday=2;}
  6. 时间类型google.protobuf.Timestamp,必须是 UTC 时间;
  7. 消息体 message 不能继承,可多层嵌套,可以导入 import;
// 我的例子
syntax = "proto3"; option csharp_namespace = "GrpcServiceTest.Protos"; import "Protos/ClientInfoModel.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto"; package UserManagement;
service UserManagement {
rpc UserReset(google.protobuf.Empty) returns (google.protobuf.Empty);
rpc UserLogin(LoginRequestV2) returns(LoginResponseV2);
} message LoginRequestV2 {
string UserName = 1;
string Password = 2;
} message LoginResponseV2 {
int32 TAG = 1;
string Message = 2;
UserModelV2 UserInfo = 3; message UserModelV2 {
int64 UserID = 1;
string UserName = 2;
google.protobuf.StringValue Address = 3;
google.protobuf.Timestamp LastLoginTime = 4;
repeated PrivGroupPluginModelV2 PrivGroupPlugins = 5;
bool IsDeleted = 6; message PrivGroupPluginModelV2{
int64 Id=1;
google.protobuf.Timestamp CreateDateTime=2;
google.protobuf.Timestamp ModifyDateTime=3;
int64 PluginId=4;
int64 PrivGroupPluginID=5;
}
}
}

根据 proto 生成代码

用vs2019,选择gRPC Service项目模板,创建项目。它会自动加上nuget包Grpc.AspNetCore。如果没有的话,则需要自己安装nuget包:Grpc.coreGoogle.ProtobufGrpc.Tools

由 proto 文件生成代码有两种方式:

  1. 通过vs右键 proto文件,选择 属性Property,选择Build Action中的Protobuf complier,会看到 gRPC Stub Classes,有三个选项 Server Only , Clent Only 和 Both 按需选择;

  2. 编辑项目文件 csproj,编辑 Protobuf 属性,这种方法还可以使用路径宏通配符等,相当方便,强烈推荐
<ItemGroup>
<Protobuf Include="Protos/*.proto" OutputDir="%(ProjectDir)ServerGrpc" GrpcServices="Server" />
</ItemGroup>

asp.net core 3.1

现在,恰好赶上了net core 3.1的这个 LST版本 ( long-term-support )的发布,而 NET Core 3.0 生命周期终结于 2020年3月3日,下个大一统版本 NET 5 ,正式版本还要等到明年。至于为什么没有 NET 4.0版本,官方解释,为了避免于 .NET Framework 4.X 产生歧义。

一步步的按照官方文档的指引,跟着做就可以了。《使用 ASP.NET Core 的 gRPC 服务》《教程:在 ASP.NET Core 中创建 gRPC 客户端和服务器》

仔细回想了一下,这部分确实没有什么值得说的,官方文档已经非常的详细了。唯一不同的感受就是,net core 需要什么功能的话,需要通过nuget来安装;这点与 net framework 大有不同,framework 更像是,一次帮你全部装好。

Entity Framework Core

旧的WCF项目,数据库访问使用的是 Entity Framework + Linq + MySql。需要安装的 Nuget 包:

  • MySql.Data.EntityFrameworkCore Mysql的EF核心库;
  • Microsoft.EntityFrameworkCore.Proxies 《Lazy loading》 懒加载的插件;
  • Microsoft.EntityFrameworkCore.DesignMicrosoft.EntityFrameworkCore.Tools 这两个插件,用于生成代码;

另外,还需要下载安装 mysql-connector-net-8.0.21.msi 来访问数据库。其中有一个 Scaffold-DbContextbug 99419 TINYINT(1) 转化为 byte,而不是预期的 bool。这个问题将会在 8.0.22 版本中修复,目前只能手动修改。

EF当然是 Database First 了,生成EF代码需要在Package Manager Console用到 Scaffold-DbContext 命令,有三点需要注意:

  • Start up 启始项目一定要是引用它的项目,并且编译成功的;
  • Default project 生成后,代码存放的项目;
  • 如果生成失败,提示:“Your startup project 'XXXX' doesn't reference Microsoft.EntityFrameworkCore.Design. This package is required for the Entity Framework Core Tools to work. Ensure your startup project is correct, install the package, and try again.”。编辑项目文件 csproj 移除 <PrivateAssets>All</PrivateAssets> 从 "Microsoft.EntityFrameworkCore.Design"和"Microsoft.EntityFrameworkCore.Tools"中;

我的命令: Scaffold-DbContext -Connection "server=10.50.40.50;port=3306;user=myuser;password=123456;database=dbname" -Provider MySql.Data.EntityFrameworkCore -OutputDir "EFModel" -ContextDir "Context" -Project "DataAccess" -Context "BaseEntities" -UseDatabaseNames -Force

其他建议:

  • Library类库最好是 netstandard 方便移植;
  • 新建一个类来继承BaseEntities,覆盖 OnConfiguring 方法,可配置的数据库连接字符串;
public class Entities : BaseEntities
{
private static string _lstDBString; public static void SetDefaultDBString(string _dbString)
{
if (string.IsNullOrEmpty(_lstDBString))
{
_lstDBString = _dbString;
}
} protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseLazyLoadingProxies().UseMySQL(_lstDBString);
}
}
}
  • 最好采用 asp.net core 的框架注入;鉴于项目的原因,假如强行采用的话,改动比较大,只好放弃;
public void ConfigureServices(IServiceCollection services)
{
string _dbString = Configuration.GetConnectionString("LstDatabase");
services.AddDbContext<DataAccess.Context.Entities>(
options => options.UseLazyLoadingProxies().UseMySQL(_dbString));
services.AddGrpc();
}
  • 数据库链接字符串有多种存放的方式,有更加安全的方式;而我采用简单方式存放在 appsettings.json
{
"ConnectionStrings": {
"LstDatabase": "server=127.0.0.1;port=3306;user=myuser;password=123456;database=dbname"
},
"log4net": "log4net.config",
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}

部署到 Ubuntu

生产环境运行的服务器是 Ubuntu 14.04.6 LTS,在《ubuntu Releases wiki》上描述,14版本在去年已经停止了标准支持,而 .net core 的 runtime 最低支持也是 Ubuntu 16.04.6 LTS,只好选择最新的版本Ubuntu 20.04.1 LTS

安装Ubuntu Server系统小插曲:IT支持部门的同事,帮忙重装了两遍系统,一次14.04桌面版,一次20.04服务器版;安装20版本后,发现网卡没有启用,主机后面网线的灯都没有亮起来。

由于我和他都不熟悉Ubuntu系统,网上查找办法,然后用手机拍照,再来服务器上尝试,搞了好一会儿,才连上网络,SSH也居然没有启用。可能 Ubuntu 还是比较适合做桌面系统吧。

然后参考 《在 Ubuntu 上安装 .NET Core SDK 或 .NET Core 运行时》,安装 net core的环境,最初用的是 aspnetcore-runtime ,在测试的时候发现,gRPC需要 HTTPS。折腾了半天的 HTTPS,一会儿需要签名,一会儿还要生成密钥,一会儿还要放到指定的位置,可信任的证书还要去还要折腾。折腾了半天,脑壳一团浆糊。只好又安装了 dotnet-sdk,这个是自带开发的证书,反正是将就用把。

剩下的就比较简单了,编译发布asp.net core,打包上传到服务器,然后运行dotnet GrpcServiceLST.dll --urls "http://*:5000;http://*:5001"。打开浏览器测试访问,没毛病。

客户端的编写

在编写windows客户端的时候,遇到个问题:《.NET Core 中的 gRPC 客户端工厂集成》推荐的插件 Grpc.Net.ClientFactory 只能适用于 net core,而大部分客户的 windows7 系统不会安装 net core;如果想在 net framework 上使用 gRPC的话,只能用原生的方法来自己实现

使用 proto 文件生成代码的方法,与上面的一致,只需要把 Server Only 改为 Client Only ;代码部分要注意,部署的 HTTPS 是不受信任的,需要额外处理一下。

/// net core 3.1
private void button2_Click(object sender, EventArgs e)
{
// 取消不受信任
var httpHandler = new HttpClientHandler();
httpHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
var channel = GrpcChannel.ForAddress("https://10.50.40.237:5001", new GrpcChannelOptions { HttpHandler = httpHandler });
var client = new UserManagement.UserManagementClient(channel);
var _param = new GrpcServiceLST.Protos.LoginRequestV2()
{
UserName = "user",
Password = "123456"
};
var reply = client.UserLoginOSDShadowEx(_param);
MessageBox.Show("net core login: " + reply.Message);
} /// framework 4.0
private void button1_Click(object sender, EventArgs e)
{
var channel = new Channel("10.50.40.237:5000", ChannelCredentials.Insecure);
var client = new UserManagement.UserManagementClient(channel);
var _param = new GrpcServiceLST.Protos.LoginRequestV2()
{
UserName = "user",
Password = "123456"
};
var _reply = client.UserLoginOSDShadowEx(_param);
MessageBox.Show("framework login:" + _reply.Message);
}

经过测试发现,net core 不支持 http 的访问; net framework 的原生版本,只能访问 http 端口 5000 ,不能访问 https 端口 5001 ,不能用 http 或者 https 这样的前缀(如: http://10.50.40.237:5000),localhost这种域名也无法解析

HTTP HTTPS 域名 IP
net core x
framework x x

最最要命的是,在 win7 系统上,安装了 net core ,使用 Grpc.Net.ClientFactory 居然也不可以访问。在github上面找到了答案, win7 不会支持 http2 ,并且 win7 微软已经在2020 年1 月14 日停止提供支持。

issues : ASP.NET Core uses the operating system for HTTP/2 TLS support. macOS may support hosting servers with HTTP/2 TLS in the future, Windows 7 will not.

总结

这次WCF升级到 asp.net core + gRPC,迁移到 Linux 的部分,方案虽然可以运行。但是要放弃 win7 用户是不太可能的,只好放弃 gRPC这种方案。

幸运的是,放弃 gPRC 的那一刻,我突然意识到,为什么不用 web api ,REST Full 的方式也满足,逻辑部分的代码尽量不变。下一篇介绍,WCF 迁移到 asp.net core web api ,到目前为止,这个方案是我最为满意的。

旧 WCF 项目迁移到 asp.net core + gRPC 的尝试的更多相关文章

  1. 58HouseSearch项目迁移到asp.net core

    前言 58HouseSearch这个项目原本是基于ASP.NET MVC 4写的,开发环境是Windows+VS2015,发布平台是linux+mono+jexus,这样看来整个项目基本已经满足跨平台 ...

  2. 旧 WCF 项目成功迁移到 asp.net core web api

    背景 接上一篇,放弃了 asp.net core + gRPC 的方案后,我灵光一闪,为什么不用 web api 呢?不也是 asp.net core 的吗?虽然 RESTful 不是强约束,客户端写 ...

  3. 为什么你需要将代码迁移到ASP.NET Core 2.0?

    随着 .NET Core 2.0 的发布,.NET 开源跨平台迎来了新的时代.开发者们可以选择使用命令行.个人喜好的文本编辑器.Visual Studio 2017 15.3 和 Visual Stu ...

  4. ASP.NET Core Identity 迁移数据 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core Identity 迁移数据 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core Identity 迁移数据 上一章节中我们配置了 ...

  5. 将基于 .NET Framework 的 WPF 项目迁移到基于 .NET Core 3

    在 Connect(); 2018 大会上,微软发布了 .NET Core 3 Preview,以及基于 .NET Core 3 的 WPF:同时还发布了 Visual Studio 2019 预览版 ...

  6. ASP.NET Core 项目配置 ( Startup ) - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 项目配置 ( Startup ) - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 项目配置 ( Startup ) 前面几章节 ...

  7. ASP.NET Core 基本项目目录结构 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 基本项目目录结构 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 基本项目目录结构 上一章节中我们成功创建了一个名为 Hell ...

  8. ASP.NET Core 新建项目 - macOS 环境 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 新建项目 - macOS 环境 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 新建项目 - macOS 环境 对于任何语言和 ...

  9. Abp vNext框架 从空项目开始 使用ASP.NET Core Web Application-笔记

    参考 Abp vNext框架 从空项目开始 使用ASP.NET Core Web Application http://www.vnfan.com/helinbin/d/745b1e040c9b4f6 ...

随机推荐

  1. 手把手教你安装Office 2019 for Mac ,安装包和破解码都给你准备好了,还装不上的话,你找我!

    准备一个安装包,和一个破解工具 ​ 安装MicrosoftOffice16.23.19030902_Installer.pkg, 注意在断网情况下安装 同时不要自动更新 , 安装好之后不要打开文件!​ ...

  2. Linux/Docker 中使用 System.Drawing.Common 踩坑小计

    前言 在项目迁移到 .net core 上面后,我们可以使用 System.Drawing.Common 组件来操作 Image,Bitmap 类型,实现生成验证码.二维码,图片操作等功能.Syste ...

  3. SpringSecurity+Oauth2+Jwt实现toekn认证和刷新token

    简单描述:最近在处理鉴权这一块的东西,需求就是用户登录需要获取token,然后携带token访问接口,token认证成功接口才能返回正确的数据,如果访问接口时候token过期,就采用刷新token刷新 ...

  4. Java基础(一)基础常识

    Java开发基础流程图: 也可参考这篇博客: https://www.cnblogs.com/xdp-gacl/p/3624567.html 常用的Windows的DOS命令 : dir : 列出当前 ...

  5. springmvc(一)springmvc简介与入门程序

    springmvc概括: Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱 ...

  6. Android:沉浸式状态栏(一)工具类

    参考自Android 沉浸式状态栏完美解决方案 基本功能 状态栏深色或浅色图标切换 自定义状态栏背景 设置沉浸式状态栏 先准备几个工具类 1.SystemBarTintManager package ...

  7. Redis之NoSql入门和概述(二)

    2. 什么是NoSQL?    2.1 NoSQL 概述   NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库.随着互联网web2.0网站的兴起, ...

  8. jmeter跨线程组session保持

    @@@@@@@@@@@@@@@ 是金子早晚会被挖光的 http请求由于无状态的特性,所以在请求时需要带上身份信息,关于session和cookie的验证机制会在其他笔记中再记录,这里不讨论. 心路历程 ...

  9. PHP getrandmax() 函数

    实例 返回通过调用 rand() 函数显示的随机数的最大可能值: <?phpecho(getrandmax()); ?>高佣联盟 www.cgewang.com 定义和用法 The get ...

  10. CF R 632 div2 1333F Kate and imperfection

    赛后看了半天题 才把题目看懂 英语水平极差. 意思:定义一个集合S的权值为max{gcd(a,b)};且\(a\neq b\) 这个集合可以从1~n中选出一些数字 求出当集合大小为k时的最小价值. 无 ...