1.环境要求

.Net Framework 4.8

.Net Core 版本: https://www.cnblogs.com/dennisdong/p/17120990.html

2.Stub和Proto

2.1 新建类库

GrpcCommon

2.2 新建文件夹和配置文件

文件夹:Certs,Helpers,Models,Protos\Google,Stubs\Example

class类:AppConfigs.cs

  1. using System;
  2. using System.Configuration;
  3. namespace GrpcCommon
  4. {
  5. public class AppConfigs
  6. {
  7. public static string Host = GetValue("host");
  8. public static int HttpPort = Convert.ToInt32(GetValue("httpPort"));
  9. public static int HttpsPort = Convert.ToInt32(GetValue("httpsPort"));
  10. public static string Issuer = GetValue("issuer");
  11. public static int Expire = Convert.ToInt32(GetValue("expire"));
  12. public static string SecurityKey = GetValue("securityKey");
  13. public static string GetValue(string key)
  14. {
  15. try
  16. {
  17. return ConfigurationManager.AppSettings[key].Trim();
  18. }
  19. catch (Exception e)
  20. {
  21. throw new Exception($"AppConfig 配置获取异常,{e.StackTrace}");
  22. }
  23. }
  24. public static T GetValue<T>(string key) where T : class
  25. {
  26. try
  27. {
  28. return ConfigurationManager.AppSettings[key].Trim() as T;
  29. }
  30. catch (Exception e)
  31. {
  32. throw new Exception($"AppConfig 配置获取异常,{e.StackTrace}");
  33. }
  34. }
  35. }
  36. }

添加程序集引用:System.Configuration

2.3 新建proto文件

Protos下新建example.proto文件

  1. syntax = "proto3";
  2. package example;
  3. import "Protos/Google/struct.proto";
  4. option csharp_namespace = "GrpcExample";
  5. service ExampleServer {
  6. // Unary
  7. rpc UnaryCall (ExampleRequest) returns (ExampleResponse);
  8. // Server streaming
  9. rpc StreamingFromServer (ExampleRequest) returns (stream ExampleResponse);
  10. // Client streaming
  11. rpc StreamingFromClient (stream ExampleRequest) returns (ExampleResponse);
  12. // Bi-directional streaming
  13. rpc StreamingBothWays (stream ExampleRequest) returns (stream ExampleResponse);
  14. }
  15. message ExampleRequest {
  16. string securityKey = 1;
  17. string userId = 2;
  18. google.protobuf.Struct userDetail = 3;
  19. string token = 4;
  20. }
  21. message ExampleResponse {
  22. int32 code = 1;
  23. bool result = 2;
  24. string message = 3;
  25. }

2.4 下载google的数据类型文件

https://github.com/protocolbuffers/protobuf/releases/download/v21.12/protoc-21.12-win64.zip

其他版本参考:https://github.com/protocolbuffers/protobuf/releases

下载不了的文章末尾有源码地址

下载解压后将\include\google\protobuf中的所有文件放在Protos下的Google

2.5 NuGet安装依赖包

GrpcCommon右键管理NuGet程序包安装以下依赖:

Google.Protobuf 3.21.12,Grpc.Core 2.46.6,Grpc.Tools 2.51.0,JWT 10.0.2

2.6 生成stub文件

项目根目录下按下Shift+鼠标右键在此打开命令窗口

  1. .\packages\Grpc.Tools.2.51.0\tools\windows_x64\protoc.exe -I .\GrpcCommon\ .\GrpcCommon\Protos\example.proto --csharp_out .\GrpcCommon\Stubs\Example --grpc_out .\GrpcCommon\Stubs\Example --plugin=protoc-gen-grpc=.\packages\Grpc.Tools.2.51.0\tools\windows_x64\grpc_csharp_plugin.exe

运行之后会在Example文件夹下生成Example.csExampleGrpc.cs

2.7 配置JWT

2.7.1 在Models下新建JwtToken

  1. /// <summary>
  2. /// Jwt Token
  3. /// </summary>
  4. public class JwtToken
  5. {
  6. /// <summary>
  7. /// 授权者
  8. /// </summary>
  9. public string userid { get; set; }
  10. /// <summary>
  11. /// Token过期时间
  12. /// </summary>
  13. public long exp { get; set; }
  14. /// <summary>
  15. /// Issuer
  16. /// </summary>
  17. public string iss { get; set; }
  18. }

2.7.2 在Models下新建UserDetails

  1. public class UserDetails
  2. {
  3. public string UserName { get; set; }
  4. public int Age { get; set; }
  5. public IEnumerable<string> Friends { get; set; }
  6. }

2.7.2 在Helpers下新建JwtHelper

  1. using System;
  2. using System.Text;
  3. using GrpcCommon.Models;
  4. using JWT;
  5. using JWT.Algorithms;
  6. using JWT.Exceptions;
  7. using JWT.Serializers;
  8. #pragma warning disable CS0618
  9. namespace GrpcCommon.Helpers
  10. {
  11. public class JwtHelper
  12. {
  13. /// <summary>
  14. /// 颁发JWT Token
  15. /// </summary>
  16. /// <param name="securityKey"></param>
  17. /// <param name="userName"></param>
  18. /// <returns></returns>
  19. public static string GenerateJwt(string securityKey, string userName)
  20. {
  21. //var securityKey = AppConfigs.SecurityKey;
  22. var issuer = AppConfigs.Issuer;
  23. var expire = AppConfigs.Expire;
  24. var expTime = new DateTimeOffset(DateTime.Now.AddSeconds(expire)).ToUnixTimeSeconds();
  25. //身份验证信息
  26. var jwtToken = new JwtToken { userid = userName, exp = expTime, iss = issuer };
  27. var key = Encoding.UTF8.GetBytes(securityKey);
  28. IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); //加密方式
  29. IJsonSerializer serializer = new JsonNetSerializer(); //序列化Json
  30. IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); //base64加解密
  31. IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder); //JWT编码
  32. var token = encoder.Encode(jwtToken, key); //生成令牌
  33. return token;
  34. }
  35. /// <summary>
  36. /// 校验解析Jwt Token
  37. /// </summary>
  38. /// <returns></returns>
  39. public static Tuple<bool, string> ValidateJwt(string token, string secret)
  40. {
  41. try
  42. {
  43. IJsonSerializer serializer = new JsonNetSerializer();
  44. IDateTimeProvider provider = new UtcDateTimeProvider();
  45. IJwtValidator validator = new JwtValidator(serializer, provider);
  46. IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
  47. IJwtAlgorithm alg = new HMACSHA256Algorithm();
  48. IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, alg);
  49. var payLoad = decoder.Decode(token, secret);
  50. //校验通过,返回解密后的字符串
  51. return new Tuple<bool, string>(true, payLoad);
  52. }
  53. catch (TokenExpiredException expired)
  54. {
  55. //token过期
  56. return new Tuple<bool, string>(false, expired.Message);
  57. }
  58. catch (SignatureVerificationException sve)
  59. {
  60. //签名无效
  61. return new Tuple<bool, string>(false, sve.Message);
  62. }
  63. catch (Exception err)
  64. {
  65. // 解析出错
  66. Console.WriteLine(err.StackTrace);
  67. return new Tuple<bool, string>(false, err.Message);
  68. }
  69. }
  70. }
  71. }

3.搭建grpc服务端

3.1 新建控制台应用程序

GrpcServer

3.2 安装依赖包

Google.Protobuf 3.21.12,Grpc.Core 2.46.6,Newtonsoft.Json 13.0.2

3.3 新建服务类

ExampleService.cs

  1. using System;
  2. using System.Threading.Tasks;
  3. using Grpc.Core;
  4. using GrpcCommon.Helpers;
  5. using GrpcCommon.Models;
  6. using GrpcExample;
  7. using Newtonsoft.Json;
  8. namespace GrpcServer.Services
  9. {
  10. public class ExampleService : ExampleServer.ExampleServerBase
  11. {
  12. public override Task<ExampleResponse> UnaryCall(ExampleRequest request, ServerCallContext context)
  13. {
  14. Console.WriteLine(request.ToString());
  15. var tokenRes = JwtHelper.ValidateJwt(request.Token, request.SecurityKey);
  16. // 正常响应客户端一次
  17. ExampleResponse result;
  18. if (tokenRes.Item1)
  19. {
  20. var payLoad = JsonConvert.DeserializeObject<JwtToken>(tokenRes.Item2);
  21. if (!request.UserId.Equals(payLoad.userid))
  22. {
  23. result = new ExampleResponse
  24. {
  25. Code = -1,
  26. Result = false,
  27. Message = "userid不匹配"
  28. };
  29. }
  30. else
  31. {
  32. var userDetail = JsonConvert.DeserializeObject<UserDetails>(request.UserDetail.Fields.ToString());
  33. result = new ExampleResponse
  34. {
  35. Code = 200,
  36. Result = true,
  37. Message = $"UnaryCall 单次响应: {request.UserId},{userDetail.UserName}"
  38. };
  39. }
  40. }
  41. else
  42. {
  43. // 正常响应客户端一次
  44. result = new ExampleResponse
  45. {
  46. Code = -1,
  47. Result = false,
  48. Message = tokenRes.Item2
  49. };
  50. }
  51. return Task.FromResult(result);
  52. }
  53. public override async Task StreamingFromServer(ExampleRequest request, IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
  54. {
  55. // 无限响应客户端
  56. while (!context.CancellationToken.IsCancellationRequested)
  57. {
  58. await responseStream.WriteAsync(new ExampleResponse
  59. {
  60. Code = 200,
  61. Result = true,
  62. Message = $"StreamingFromServer 无限响应: {Guid.NewGuid()}"
  63. });
  64. await Task.Delay(TimeSpan.FromSeconds(3), context.CancellationToken);
  65. }
  66. }
  67. public override async Task<ExampleResponse> StreamingFromClient(IAsyncStreamReader<ExampleRequest> requestStream, ServerCallContext context)
  68. {
  69. // 处理请求
  70. while (await requestStream.MoveNext())
  71. {
  72. Console.WriteLine(requestStream.Current.UserId);
  73. }
  74. // 响应客户端
  75. return new ExampleResponse
  76. {
  77. Code = 200,
  78. Result = true,
  79. Message = $"StreamingFromClient 单次响应: {Guid.NewGuid()}"
  80. };
  81. }
  82. public override async Task StreamingBothWays(IAsyncStreamReader<ExampleRequest> requestStream, IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
  83. {
  84. #region 服务器响应客户端一次
  85. // 处理请求
  86. //while (await requestStream.MoveNext())
  87. //{
  88. // Console.WriteLine(requestStream.Current.UserName);
  89. //}
  90. //请求处理完成之后只响应一次
  91. //await responseStream.WriteAsync(new ExampleResponse
  92. //{
  93. // Code = 200,
  94. // Result = true,
  95. // Message = $"StreamingBothWays 单次响应: {Guid.NewGuid()}"
  96. //});
  97. //await Task.Delay(TimeSpan.FromSeconds(3), context.CancellationToken);
  98. #endregion
  99. #region 服务器响应客户端多次
  100. // 处理请求
  101. var readTask = Task.Run(async () =>
  102. {
  103. while (await requestStream.MoveNext())
  104. {
  105. Console.WriteLine(requestStream.Current.UserId);
  106. }
  107. });
  108. // 请求未处理完之前一直响应
  109. while (!readTask.IsCompleted)
  110. {
  111. await responseStream.WriteAsync(new ExampleResponse
  112. {
  113. Code = 200,
  114. Result = true,
  115. Message = $"StreamingBothWays 请求处理完之前的响应: {Guid.NewGuid()}"
  116. });
  117. await Task.Delay(TimeSpan.FromSeconds(3), context.CancellationToken);
  118. }
  119. // 也可以无限响应客户端
  120. //while (!context.CancellationToken.IsCancellationRequested)
  121. //{
  122. // await responseStream.WriteAsync(new ExampleResponse
  123. // {
  124. // Code = 200,
  125. // Result = true,
  126. // Message = $"StreamingFromServer 无限响应: {Guid.NewGuid()}"
  127. // });
  128. // await Task.Delay(TimeSpan.FromSeconds(3), context.CancellationToken);
  129. //}
  130. #endregion
  131. }
  132. }
  133. }

3.4 AppConfig添加配置

  1. <appSettings>
  2. <!--主机配置-->
  3. <add key="host" value="0.0.0.0" />
  4. <add key="httpPort" value="5000" />
  5. <add key="httpsPort" value="7000" />
  6. <!--Jwt配置-->
  7. <add key="securityKey" value="grpc.dennis.com" />
  8. <add key="issuer" value="https://grpc.dennis.com" />
  9. <!--token过期时间:分钟-->
  10. <add key="expire" value="1" />
  11. </appSettings>

3.5 修改程序入口

修改Program.cs,SSL证书文章后面有说明

  1. using System;
  2. using Grpc.Core;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using GrpcCommon;
  6. using GrpcExample;
  7. using GrpcServer.Services;
  8. private static void Main()
  9. {
  10. var host = AppConfigs.Host;
  11. var httpPort = AppConfigs.HttpPort;
  12. var httpsPort = AppConfigs.HttpsPort;
  13. var cert = File.ReadAllText("Certs\\cert.pem");
  14. var key = File.ReadAllText("Certs\\key.pem");
  15. var server = new Server
  16. {
  17. Services =
  18. {
  19. ExampleServer.BindService(new ExampleService())
  20. },
  21. Ports =
  22. {
  23. new ServerPort(host, Convert.ToInt32(httpPort), ServerCredentials.Insecure),
  24. new ServerPort(host, Convert.ToInt32(httpsPort), new SslServerCredentials(
  25. new List<KeyCertificatePair>
  26. {
  27. new KeyCertificatePair(cert, key)
  28. }))
  29. }
  30. };
  31. server.Start();
  32. Console.WriteLine($"Grpc Server Listening on http://{host}:{httpPort}, https://{host}:{httpsPort}");
  33. Console.ReadLine();
  34. server.ShutdownAsync().Wait();
  35. }

4.搭建grpc客户端

4.1 新建控制台应用程序

GrpcClient

4.2 安装依赖包

Google.Protobuf 3.21.12,Grpc.Core 2.46.6,Newtonsoft.Json 13.0.2

4.3 新建测试类

ExampleUnit.cs

  1. using System;
  2. using Google.Protobuf.WellKnownTypes;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Threading.Tasks;
  6. using Grpc.Core;
  7. using GrpcExample;
  8. using GrpcCommon.Helpers;
  9. namespace GrpcClient.Test
  10. {
  11. internal class ExampleUnit
  12. {
  13. public static void Run()
  14. {
  15. // 常规请求响应
  16. UnaryCall();
  17. // 服务器流响应
  18. StreamingFromServer();
  19. // 客户端流响应
  20. StreamingFromClient();
  21. // 双向流响应
  22. StreamingBothWays();
  23. }
  24. /// <summary>
  25. /// 创建客户端链接
  26. /// </summary>
  27. /// <param name="enableSsl"></param>
  28. /// <returns></returns>
  29. private static ExampleServer.ExampleServerClient CreateClient(bool enableSsl = true)
  30. {
  31. Channel channel;
  32. if (enableSsl)
  33. {
  34. // ssl加密连接
  35. const string serverUrl = "localhost:7000";
  36. Console.WriteLine($"尝试链接服务器,https://{serverUrl}");
  37. var credentials = new SslCredentials(File.ReadAllText("Certs\\cert.pem"));
  38. channel = new Channel(serverUrl, credentials, new List<ChannelOption>
  39. {
  40. new ChannelOption(ChannelOptions.SslTargetNameOverride, "grpc.dennis.com")
  41. });
  42. }
  43. else
  44. {
  45. // 不安全连接
  46. const string serverUrl = "localhost:5000";
  47. Console.WriteLine($"尝试链接服务器,http://{serverUrl}");
  48. channel = new Channel(serverUrl, ChannelCredentials.Insecure);
  49. }
  50. Console.WriteLine("服务器链接成功");
  51. return new ExampleServer.ExampleServerClient(channel);
  52. }
  53. private static async void UnaryCall()
  54. {
  55. var client = CreateClient();
  56. var userId = Guid.NewGuid().ToString();
  57. const string securityKey = "Dennis!@#$%^";
  58. var token = JwtHelper.GenerateJwt(securityKey, userId);
  59. var result = await client.UnaryCallAsync(new ExampleRequest
  60. {
  61. SecurityKey = securityKey,
  62. UserId = userId,
  63. UserDetail = new Struct
  64. {
  65. Fields =
  66. {
  67. ["userName"] = Value.ForString("Dennis"),
  68. ["age"] = Value.ForString("18"),
  69. ["friends"] = Value.ForList(Value.ForString("Roger"), Value.ForString("YueBe"))
  70. }
  71. },
  72. Token = token
  73. });
  74. Console.WriteLine($"Code={result.Code},Result={result.Result},Message={result.Message}");
  75. }
  76. private static async void StreamingFromServer()
  77. {
  78. var client = CreateClient();
  79. var result = client.StreamingFromServer(new ExampleRequest
  80. {
  81. UserId = "Dennis"
  82. });
  83. while (await result.ResponseStream.MoveNext())
  84. {
  85. var resp = result.ResponseStream.Current;
  86. Console.WriteLine($"Code={resp.Code},Result={resp.Result},Message={resp.Message}");
  87. }
  88. }
  89. private static async void StreamingFromClient()
  90. {
  91. var client = CreateClient();
  92. var result = client.StreamingFromClient();
  93. // 发送请求
  94. for (var i = 0; i < 5; i++)
  95. {
  96. await result.RequestStream.WriteAsync(new ExampleRequest
  97. {
  98. UserId = $"StreamingFromClient 第{i}次请求"
  99. });
  100. await Task.Delay(TimeSpan.FromSeconds(1));
  101. }
  102. // 等待请求发送完毕
  103. await result.RequestStream.CompleteAsync();
  104. var resp = result.ResponseAsync.Result;
  105. Console.WriteLine($"Code={resp.Code},Result={resp.Result},Message={resp.Message}");
  106. }
  107. private static async void StreamingBothWays()
  108. {
  109. var client = CreateClient();
  110. var result = client.StreamingBothWays();
  111. // 发送请求
  112. for (var i = 0; i < 5; i++)
  113. {
  114. await result.RequestStream.WriteAsync(new ExampleRequest
  115. {
  116. UserId = $"StreamingBothWays 第{i}次请求"
  117. });
  118. await Task.Delay(TimeSpan.FromSeconds(1));
  119. }
  120. // 处理响应
  121. var respTask = Task.Run(async () =>
  122. {
  123. while (await result.ResponseStream.MoveNext())
  124. {
  125. var resp = result.ResponseStream.Current;
  126. Console.WriteLine($"Code={resp.Code},Result={resp.Result},Message={resp.Message}");
  127. }
  128. });
  129. // 等待请求发送完毕
  130. await result.RequestStream.CompleteAsync();
  131. // 等待响应处理
  132. await respTask;
  133. }
  134. }
  135. }

4.4 添加配置信息

  1. <appSettings>
  2. <!--主机配置-->
  3. <add key="host" value="0.0.0.0" />
  4. <add key="httpPort" value="5000" />
  5. <add key="httpsPort" value="7000" />
  6. <!--Jwt配置-->
  7. <add key="securityKey" value="grpc.dennis.com" />
  8. <add key="issuer" value="https://grpc.dennis.com" />
  9. <!--token过期时间:分钟-->
  10. <add key="expire" value="10" />
  11. </appSettings>

4.5 修改程序入口

修改Program.cs

  1. using System;
  2. using GrpcClient.Test;
  3. namespace GrpcClient
  4. {
  5. internal class Program
  6. {
  7. static void Main(string[] args)
  8. {
  9. // Example 测试
  10. ExampleUnit.Run();
  11. Console.ReadKey();
  12. }
  13. }
  14. }

5.SSL证书生成

5.1 下载安装openssl

参考文章:https://www.cnblogs.com/dingshaohua/p/12271280.html

5.2 生成证书

GrpcCommonCerts下右键打开命令窗口输入openssl

5.2.1 生成key

  1. genrsa -out key.pem 2048

5.2.1 生成pem证书

  1. req -new -x509 -key key.pem -out cert.pem -days 3650

5.2.1 pem证书转换成pfx证书

  1. pkcs12 -export -out cert.pfx -inkey key.pem -in cert.pem

6.运行项目

6.1 拷贝证书

把整个Certs文件夹分别拷贝到GrpcServerGrpcClient下的\bin\Debug\Certs

6.2 启动程序

先运行GrpcServer在运行GrpcClient即可

6.3 调试

右键解决方案-->属性-->启动项目-->选择多个启动项目-->F5调试即可

7.源码地址

https://gitee.com/dennisdong/net-grpc

.Net Framework创建grpc的更多相关文章

  1. unit vs2017基于nunit framework创建单元测试

    unit  vs2017基于nunit framework创建单元测试 一.简叙: 单元测试大型项目中是必备的,所以不可忽视,一个项目的成败就看是否有单元测试,对后期的扩展维护都带来了便利. 二.安装 ...

  2. [gRPC] 在 .NET Core 中创建 gRPC 服务端和客户端

    gRPC 官网:https://grpc.io/ 1. 创建服务端 1.1 基于 ASP.NET Core Web 应用程序模板创建 gRPC Server 项目. 1.2 编译并运行 2. 创建客户 ...

  3. iOS静态库及Framework 创建

    本文转自cocoachina,尊重作者的汗水. 讲述的非常透彻,有需要的朋友可以阅读实践.转载请注明出处 //=================以下留着备份==================// 在 ...

  4. Asp.Net MVC 使用Entity Framework创建模型类

    先来说说LINQ to SQL和Entity Framework的区别: LINQ to SQL和Entity Framework都是一种包含LINQ功能的对象关系映射技术.他们之间的本质区别在于EF ...

  5. Asp.Net MVC 模型(使用Entity Framework创建模型类) - Part.1

    这篇教程的目的是解释在创建ASP.NET MVC应用程序时,如何使用Microsoft Entity Framework来创建数据访问类.这篇教程假设你事先对Microsoft Entity Fram ...

  6. Asp.Net MVC 模型(使用Entity Framework创建模型类)

    这篇教程的目的是解释在创建ASP.NET MVC应用程序时,如何使用Microsoft Entity Framework来创建数据访问类.这篇教程假设你事先对Microsoft Entity Fram ...

  7. .NET Core/Framework 创建委托以大幅度提高反射调用的性能

    都知道反射伤性能,但不得不反射的时候又怎么办呢?当真的被问题逼迫的时候还是能找到解决办法的. 为反射得到的方法创建一个委托,此后调用此委托将能够提高近乎直接调用方法本身的性能.(当然 Emit 也能够 ...

  8. 使用ionic framework创建一个简单的APP

    ionic是一个以cordova为基础的html5前端框架,功能强大,能够快速做出与原生开发相似的应用. 一,安装和配置 1,安装(前提:cordova环境配置完成) npm install -g i ...

  9. 使用AssetsLibrary.Framework创建多图片选择控制器(翻译)

    系统的UIImagePickerController只能让用户选择单图片,而一般情况下,我们需要上传多张图片,这时应该可以同时选择多张图片,否则用户体验会很差.因此多图片选择器就诞生了. 在类库中,苹 ...

  10. .NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端

    .NET Core love gRPC 千呼万唤的 .NET Core 3.0 终于在 9 月份正式发布,在它的众多新特性中,除了性能得到了大大提高,比较受关注的应该是 ASP.NET Core 3. ...

随机推荐

  1. 如何利用C++使Windows蓝屏

    如何利用C++使Windows蓝屏 虽说windows非常强大,但是使它蓝屏也非常简单: 如果你想让Windows蓝屏,你一定会在运行框里输入: cmd /c for /f %I in ('wmic ...

  2. webapi+vue跨域session丢失解决方法

    前后端分离中在webapi设置可以跨域,在web.config文件中添加 <httpProtocol>       <customHeaders>         <ad ...

  3. day23 约束 & 锁 & 范式

    考点: 乐观锁=>悲观锁=>锁 表与表的对应关系 一对一:学生与手机号,一个学生对一个手机号 一对多:班级与学生,一个班级对应多个学生 多对一: 多对多:学生与科目,一个学生对应多个科目, ...

  4. MyBatis03:连接池及事务控制、xml动态SQL语句、多表操作

    今日内容: mybatis中的连接池.事务控制[原理了解,应用会用] mybatis中连接池的使用及分析 mybatis中事务控制的分析 mybatis中基于xml配置的动态SQL语句使用[会用即可] ...

  5. 【Spark】Day03-Spark SQL:DataFrame、DataSet、sql编程与转换、项目实战(区域热门商品)

    一.概述 1.介绍 将Spark SQL转换成RDD,然后提交到集群执行[对比hive] 提供2个编程抽象:DataFrame&DataSet 可以使用SQL和DatasetAPI与Spark ...

  6. input、print、字符串格式化输出

    1.使用input(), print()进行用户交互 """ 以前银行取钱只能拿着存折去柜台跟小姐姐交流才可以 你想干嘛 我想取钱 请输入密码 滴滴滴密码 想取多少钱 我 ...

  7. Python报AttributeError: module 'string' has no attribute 'join'解决方法

    报:AttributeError: module 'string' has no attribute 'join' 属性错误:模块"string"没有属性"join&qu ...

  8. python 之列表(list)处理

    列表(list) 创建一个列表,只要把逗号分隔的不同的数据项使用方括号括起来即可,一个列表中的数据类型可以各不相同,可以同时分别为整数.实数.字符串等基本类型,甚至是列表.元组.字典.集合以及其他自定 ...

  9. python进阶之路11 闭包函数 装饰器

    函数名的多种用法 函数名其实绑定的也是一块内存地址 只不过该地址里面存放的不是数据值而是一段代码 函数名加括号就会找到该代码并执行 1.可以当作变量名赋值 def index():pass res = ...

  10. [Leetcode]环形链表 II

    题目 代码 /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * Li ...