Luo San Dong LogsLuo San Dong Logs
主页
  • Go语言程序设计
  • .NET 程序设计语言
  • JavaScript程序设计
  • Python编程
  • Linux 基础
  • CentOS
  • Docker
  • Nginx
  • 核心的概念
关于我
主页
  • Go语言程序设计
  • .NET 程序设计语言
  • JavaScript程序设计
  • Python编程
  • Linux 基础
  • CentOS
  • Docker
  • Nginx
  • 核心的概念
关于我
  • .NET 程序设计语言

.NET 程序设计语言

LDAPS

    [Route("QueryUser")]
    [HttpGet]
    public AdUser QueryAd()
    {
        AdUser adUser = new AdUser();
        string username = "administrator@wlync.com";
        string password = "Win@2012R2";
        string searchFilter = "(sAMAccountName=luo.sd)";
        LdapConnection connection = new LdapConnection(new LdapDirectoryIdentifier("192.168.10.21", 636, false, false));
        connection.AuthType = AuthType.Basic;
        connection.SessionOptions.ProtocolVersion = 3;
        connection.SessionOptions.SecureSocketLayer = true;

        // 如果设置VerifyServerCertificate 处理证书将会收到: The LDAP server is unavailable
        //connection.SessionOptions.VerifyServerCertificate += (sender, e) => true;

        connection.Credential = new NetworkCredential(username, password);
       
        try
        {
            connection.Bind();
            Console.WriteLine("Connected to AD successfully.");
            SearchRequest searchRequest = new SearchRequest("OU=Exrecall,OU=lsd,DC=wlync,DC=com", searchFilter, SearchScope.Subtree);
            SearchResponse searchResponse = (SearchResponse)connection.SendRequest(searchRequest);
            foreach (SearchResultEntry entry in searchResponse.Entries)
            {
                adUser.Name = entry.DistinguishedName;
                adUser.SamAccountName = entry.Attributes["samAccountName"][0].ToString();
            }
        }
        catch (LdapException ex)
        {
            adUser.Description = ex.Message;
        }
        finally
        {
            connection.Dispose();
        }
        return adUser;
    }
}

Cron 表达式

Cron 表达式用于指定定期任务的调度时间,一般包含以下五段(标准 cron 表达式)或六段(带年字段的扩展 cron 表达式)。每段的具体含义如下:

标准 Cron 表达式(五段)

  1. 分钟(0-59):表示任务执行的分钟。
  2. 小时(0-23):表示任务执行的小时(0表示午夜)。
  3. 日期(1-31):表示任务执行的日期。
  4. 月份(1-12 或者 JAN-DEC):表示任务执行的月份。
  5. 周几(0-7 或者 SUN-SAT):表示任务执行的星期几(0 和 7 都表示星期日)。

示例:

0 12 15 * * ? 表示每天中午12点执行任务,但仅在每月的15日。

扩展 Cron 表达式(六段,带年字段)

在一些系统中,cron 表达式会增加第六段,表示年份(通常是为了兼容更多的调度需求)。

  1. 分钟(0-59)
  2. 小时(0-23)
  3. 日期(1-31)
  4. 月份(1-12 或 JAN-DEC)
  5. 周几(0-7 或 SUN-SAT)
  6. 年(可选,用于指定特定年份)

示例:

0 0 1 1 ? 2024 表示在2024年1月1日的00:00执行任务。

缺省值的表示

  • \*(星号):表示任何值。例如,* 在分钟字段中表示“每一分钟”。
  • ?:表示“不指定”,通常用于日期和星期几字段中,当你只关心其中一个时,另一个使用 ? 表示忽略。比如在日期和星期几都指定时,你只能用一个来进行明确设置,另一个用 ? 来表示不关心。
  • -(连字符):表示一个范围。例如,1-5 表示从1到5。
  • ,(逗号):表示列举值。例如,1,3,5 表示第1、第3和第5个时间点。
  • /(斜杠):表示步进值。例如,0/15 在分钟字段中表示每15分钟。
  • L:表示“最后”,通常用于日期字段。例如,L 在日期字段中表示“每月最后一天”。
  • W:表示最近的工作日。例如,15W 表示接近15号的最近的工作日。

示例解析

  1. 0 0 12 \* \* ?:每天中午12点执行任务。
  2. 0 15 10 ? \* 2:每周二上午10:15执行任务。
  3. 0 0 1 1 \* ?:每年1月1日午夜执行任务。

了解这些基本的 cron 表达式规则可以帮助你更好地配置和管理定期任务。

Quartz Scheduler 的Cron

Quartz Scheduler 使用的 cron 表达式与传统的 Unix/Linux cron 表达式有所不同,尤其是在字段的数量和含义上。Quartz 的 cron 表达式提供了更多的灵活性和功能,允许更复杂的调度需求。下面是 Quartz 与标准 cron 表达式的主要区别:

标准 Cron 表达式(五段)

标准的 cron 表达式通常有以下五个字段:

  1. 分钟(0-59)
  2. 小时(0-23)
  3. 日期(1-31)
  4. 月份(1-12 或 JAN-DEC)
  5. 周几(0-7 或 SUN-SAT)

Quartz 的 Cron 表达式(六段或七段)

Quartz Scheduler 扩展了标准 cron 表达式,使用了六个或七个字段:

  1. 秒(0-59)
  2. 分钟(0-59)
  3. 小时(0-23)
  4. 日期(1-31)
  5. 月份(1-12 或 JAN-DEC)
  6. 周几(0-7 或 SUN-SAT)
  7. 年份(可选)

Quartz 的 Cron 表达式示例

六段式 Cron 表达式

0 0 23 * * ?
  • 秒(0):每分钟的第0秒。
  • 分钟(0):每小时的第0分钟。
  • 小时(23):每天的第23小时,即晚上11点。
  • 日期(*):每个月的每一天。
  • 月份(*):每年的每个月。
  • 周几(?):不指定具体的星期几。

七段式 Cron 表达式

0 0 23 1 1 ? 2024
  • 秒(0):每分钟的第0秒。
  • 分钟(0):每小时的第0分钟。
  • 小时(23):每天的第23小时,即晚上11点。
  • 日期(1):1月1日。
  • 月份(1):1月。
  • 周几(?):不指定具体的星期几。
  • 年份(2024):指定2024年。

主要区别

  1. 秒字段:Quartz 表达式包括秒字段,而标准的 cron 表达式没有。
  2. 年份字段:Quartz 的表达式可以包含年份字段,但标准的 cron 表达式没有。
  3. 问号 ?:在 Quartz 表达式中,? 用于“周几”字段,以避免与“日期”字段的冲突,表示不指定具体的星期几。这在标准 cron 表达式中没有。

总结

Quartz Scheduler 确实使用了一个不完全与传统标准相同的 cron 表达式语法。虽然它在基本概念上与标准 cron 表达式相似,但在功能和灵活性上提供了额外的扩展。这使得 Quartz 更适合处理复杂的调度需求。如果你在使用 Quartz Scheduler,理解这些扩展和功能是很重要的。如果有其他问题或需要进一步的帮助,请随时告诉我!

JWT的应用

创建 JWT Token 示例

首先,我们创建一个简单的方法来生成 JWT Token。在实际应用中,你需要根据具体的需求来配置 JWT 的内容和签名密钥。

using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace JwtDemo
{
    //nuget Microsoft.IdentityModel.Tokens
    // nuget Microsoft.IdentityModel.JsonWebTokens
    public class AccessToken
    {
        public string Token { get; set; }
        public DateTime Expiration { get; set; }
        public bool Success { get; set; }
    }

    public class UserModel
    {
        public string UserName { get; set; }
        public string Password { get; set; }
    }
    public class JwtService
    {
        private string secretKey = "72Q+x7x088CyZx7445LsQz2CUHInC4piAIrDevGBdVs="; // 替换为你的实际密钥
        //string keyStr = "72Q+x7x088CyZx7445LsQz2CUHInC4piAIrDevGBdVs=";
        //public JwtService(IConfiguration configuration)
        //{
        //    keyStr = configuration.GetValue<string>("SecretKey");
        //}

        public string GenerateJwtToken(string username)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.UTF8.GetBytes(secretKey);

            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[]
                {
                new Claim(ClaimTypes.Name, username)
                }),
                Expires = DateTime.UtcNow.AddHours(1), // Token 有效期为1小时
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };

            var token = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(token);
        }

        public bool ValidateJwtToken(string token)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.UTF8.GetBytes(secretKey);

            var validationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
                ValidateLifetime = true
            };

            try
            {
                SecurityToken validatedToken;
                ClaimsPrincipal principal = tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
                return true; // Token is valid
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Token validation failed: {ex.Message}");
                return false; // Token validation failed
            }
        }


        public async Task<AccessToken> GenerateTokenAsync(UserModel userModel)
        {

            if (userModel.UserName == "admin" && userModel.Password == "P@ssw0rd!")
            {
                var claims = new[]
                {
                    new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Sub,"luo_sd@hotmail.com"),
                    new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString()),
                    new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.UniqueName,"admin")
                };
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature
                    );
                var token = new JwtSecurityToken("localhost", "localhost", claims,
                    expires: DateTime.Now.AddMinutes(10), signingCredentials: creds);

                return new AccessToken
                {
                    Token = new JwtSecurityTokenHandler().WriteToken(token),
                    Expiration = token.ValidTo,
                    Success = true
                };
            }
            return new AccessToken
            {
                Success = false
            };
        }

  

        public bool ValidateToken(string token)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var validationParameters = GetValidationParameters();

            try
            {
                SecurityToken validatedToken;
                var claimsPrincipal = tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
                return true; // Token is valid
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Token validation failed: {ex.Message}");
                return false; // Token validation failed
            }
        }

        private TokenValidationParameters GetValidationParameters()
        {
            return new TokenValidationParameters
            {
                ValidateIssuer = true, // 此处设置为true时需要指定一个Issuer
                ValidIssuer= "localhost",
                ValidateAudience = false, // You may need to change audience validation according to your requirements
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey))
            };
        }
    }
}

验证 JWT Token 示例

namespace JwtDemo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string keyStr = "72Q+x7x088CyZx7445LsQz2CUHInC4piAIrDevGBdVs=";
            var userModel = new UserModel() { UserName="admin",Password= "P@ssw0rd!" };
            var jwtService = new JwtService();
            var token = jwtService.GenerateTokenAsync(userModel).GetAwaiter().GetResult();
            Console.WriteLine(token.Token);
            var isOk = jwtService.ValidateToken(token.Token);


            // 生成一个 JWT Token
            string username = "alice";
            string accessToken = jwtService.GenerateJwtToken(username);
            Console.WriteLine($"Generated JWT Token: {token}");

            // 验证 JWT Token
            bool isValid = jwtService.ValidateJwtToken(accessToken);
            Console.WriteLine($"Token validation result: {isValid}");



            Console.WriteLine($"{isValid} press any key to exit!");
            Console.ReadKey();
             
        }
    }
}

EWS开发笔记

EWS-跟踪SOAP请求

  1. 输出SOAP请求跟踪信息

    public class TraceListener : ITraceListener
        {
            public void Trace(string traceType, string traceMessage)
            {
                CreateXMLTextFile(traceType, traceMessage);
            }
    
            private void CreateXMLTextFile(string fileName, string traceContent)
            {
                try
                {
                    if (!Directory.Exists(@"..\\TraceOutput"))
                    {
                        Directory.CreateDirectory(@"..\\TraceOutput");
                    }
    
                    System.IO.File.WriteAllText(@"..\\TraceOutput\\" + fileName + DateTime.Now.Ticks + ".txt", traceContent);
                }
                catch (IOException ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
        public class EwsInvokeWithSoap
        {
            public void Init()
            {
                ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
    
                service.Credentials = new NetworkCredential("jq@wlync.com", "1234.com");
                service.Url = new Uri("https://mail.wlync.com/ews/exchange.asmx");
                try
                {
                    // 调用 FindItem 方法并获取生成的 SOAP 消息
                    service.TraceEnabled = true;
                    service.TraceFlags = TraceFlags.All;
                    service.TraceListener = new TraceListener();
    
                    var item =Item.Bind(service, "AAMkADBmMGRlYTc4LWQ4YTQtNDlhNC1iM2ZhLTNjYzVkZjdiZDA=");
                }
                catch (Exception ex)
                {
                    // 处理异常
                    Console.WriteLine(ex.ToString());
                }
            }
        }
    

EWS-在SOAP中设置模拟

<soap:Header>
    <t:RequestServerVersion Version="Exchange2013" />
    <!-- The following causes the request to run as alfred@contoso.com -->
    <t:ExchangeImpersonation>
        <t:ConnectingSID>
        	<t:SmtpAddress>alfred@contoso.com</t:SmtpAddress>
    	</t:ConnectingSID>
    </t:ExchangeImpersonation>
</soap:Header>

EWS-OAUTH-21V 获取令牌

var cca = ConfidentialClientApplicationBuilder
    .Create(ConfigurationManager.AppSettings["appId"])
    .WithClientSecret(ConfigurationManager.AppSettings["clientSecret"])
    .WithTenantId(ConfigurationManager.AppSettings["tenantId"])
    //仅在 21V 版本中,添加下一行代码
    .WithAuthority(AzureCloudInstance.AzureChina, ConfigurationManager.AppSettings["tenantId"])
    .Build();
 var ewsScopes = new string[] { "https://partner.outlook.cn/.default" };
 var authResult = await
cca.AcquireTokenForClient(ewsScopes).ExecuteAsync();

var ewsClient = new ExchangeService();
ewsClient.Url = new Uri("https://partner.outlook.cn/EWS/Exchange.asmx");
ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
ewsClient.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "user@abc.com");  


使用用户自己的身份访问邮件

  • 先获取到一个用于访问EWS的accesstoken
// Link to full sample: https://raw.githubusercontent.com/OfficeDev/office-js-snippets/prod/samples/outlook/85-tokens-and-service-calls/user-callback-token.yaml
Office.context.mailbox.getCallbackTokenAsync(function (result) {
    if (result.status !== Office.AsyncResultStatus.Succeeded) {
        console.error(`Token retrieval failed with message: ${result.error.message}`);
    } else {
        console.log(result.value);
    }
});
  • 使用accesstoken访问EWS
var token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkFwSWhPRlV2NmsxOXlGN2VQdy1mb1VaTnlhWSJ9.eyJuYW1laWQiOiIxNDVjMzZmNS1mNzA3LTRkMjYtODQ0Ni0wMTFiZTcwNWJiNmJAd2x5bmMuY29tIiwidmVyIjoiRXhjaGFuZ2UuQ2FsbGJhY2suVjEiLCJhcHBjdHhzZW5kZXIiOiJodHRwczovL2F0cC5seW5jbXAuY29tL2luZGV4Lmh0bWxAd2x5bmMuY29tIiwiYXBwY3R4Ijoie1wic210cFwiOlwia2RAd2x5bmMuY29tXCIsXCJzY29wZVwiOlwiUGFyZW50SXRlbUlkOkFBTWtBR1ZrTXpRek5qTXpMV1ZpTURBdE5ERTVZUzFoTVRZNExUWTNNV1F5TkdReU1UTmpPQUJHQUFBQUFBRHBiUjRMNllZNVQ0QlhDaHZ4SXB5ZUJ3Q2FhNEN5R1R5QlNJRkhTbHVhYU5uVEFBQUFBQUVNQUFDYWE0Q3lHVHlCU0lGSFNsdWFhTm5UQUFBUlJRQ3JBQUE9XCJ9IiwiaXNzIjoiMDAwMDAwMDItMDAwMC0wZmYxLWNlMDAtMDAwMDAwMDAwMDAwQHdseW5jLmNvbSIsImF1ZCI6IjAwMDAwMDAyLTAwMDAtMGZmMS1jZTAwLTAwMDAwMDAwMDAwMC9tYWlsLndseW5jLmNvbUB3bHluYy5jb20iLCJleHAiOjE3MDg0OTM3OTcsIm5iZiI6MTcwODQ5MzQ5N30.J8dtGkxEHuwz0UzFi0IEdZpAWaUVgipj-NYFSAGOBNtdxX9sccbBStouiCHekMQTOEFuY7UoEd13QfcH5aG1khJBLchI_LCiFjPuVbfks8QVROSv0_ooG6VixOB4EM7IUyWQU8ay0ehaVldM7CHGqDAOcIJdCnQZrRIlqDwYEpTACEPR9l9XIRruW4jvPnLT1bYAbMAT3rJw1p_r-hjVvap2kz51YfgqhSkDPm3d6k_99gQlhvMVzlqt02CwEcEBXiA6fUgLiou6qk-IbQJ8cgQUEfGryt7DKmg-UdTK2sDPh-2kpcsNp58vQ6xwI0eb5JNvG2VZv4W_rGCQ02K6WQ";

var itemId = "AAMkAGVkMzQzNjMzLWViMDAtNDE5YS1hMTY4LTY3MWQyNGQyMTNjOABGAAAAAADpbR4L6YY5T4BXChvxIpyeBwCaa4CyGTyBSIFHSluaaNnTAAAAAAEMAACaa4CyGTyBSIFHSluaaNnTAAARRQCrAAA=";



ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);

//var Credentials = new OAuthCredentials(token);
//service.Credentials = Credentials;
service.Url = new Uri("https://mail.wlync.com/ews/exchange.asmx");
service.HttpHeaders.Add("Authorization", "Bearer" +token);

var item = Item.Bind(service, itemId);

完整示例-获取日历信息

using Microsoft.Exchange.WebServices.Data;
using Microsoft.Identity.Client;
using System;
using System.Configuration;

namespace EwsOAuth
{
    class Program
    {
        static async System.Threading.Tasks.Task Main(string[] args)
        {
            // Using Microsoft.Identity.Client 4.22.0
            var cca = ConfidentialClientApplicationBuilder
                .Create(ConfigurationManager.AppSettings["appId"])
                .WithClientSecret(ConfigurationManager.AppSettings["clientSecret"])
                .WithTenantId(ConfigurationManager.AppSettings["tenantId"])
                // Use the next line of code only in a 21V environment
                .WithAuthority(AzureCloudInstance.AzureChina, ConfigurationManager.AppSettings["tenantId"])
                .Build();

            //the permission scope required for EWS access
            var ewsScopes = new string[] { "https://partner.outlook.cn/.default" };
            try
            {
                //make the token request
                var authResult = await cca.AcquireTokenForClient(ewsScopes).ExecuteAsync();

                // Configure the ExchangeService with the access token
                var ewsClient = new ExchangeService();
                ewsClient.Url = new Uri("https://partner.outlook.cn/EWS/Exchange.asmx");
                ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
                ewsClient.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "S02@ms10.online");

                //Include x-anchormailbox header
                ewsClient.HttpHeaders.Add("X-AnchorMailbox", "S02@ms10.online");
                // Make an EWS call                             
                DateTime endDate = DateTime.Now;
                DateTime startDate = endDate.AddDays(-30);
                CalendarFolder calendar = CalendarFolder.Bind(ewsClient, WellKnownFolderName.Calendar, new PropertySet());
                CalendarView cView = new CalendarView(startDate, endDate);
                cView.PropertySet = new PropertySet(AppointmentSchema.Subject, AppointmentSchema.Start, AppointmentSchema.End);

                FindItemsResults<Appointment> appointments = calendar.FindAppointments(cView);
                Console.WriteLine("\nThe first " + " appointments on your calendar from " + startDate.Date.ToShortDateString() +

             " to " + endDate.Date.ToShortDateString() + " are: \n");
                foreach (Appointment a in appointments)
                {
                    Console.Write("Subject: " + a.Subject.ToString() + " ");
                    Console.Write("Start: " + a.Start.ToString() + " ");
                    Console.Write("End: " + a.End.ToString());
                    Console.WriteLine();
                }
            }
            catch (MsalException ex)
            {
                Console.WriteLine($"Error acquiring access token: {ex}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex}");
            }
            if (System.Diagnostics.Debugger.IsAttached)
            {
                Console.WriteLine("Hit any key to exit...");
                Console.ReadKey();
            }
        }
    }
}

1. NET CORE

1.1 RabbitMQ配置

交换配置

基本

客户端应用程序使用交换和队列,这些交换和队列必须以某种方式相互“声明”和“绑定”,然后才能使用。 还可以使用其他参数自定义队列和交换。此库允许您执行此例程,只需调用向其传递其他参数的方法即可。 您可以配置绑定多个队列的多个交易所。AddExchange

您的代码将如下所示。Startup

public class Startup
{
    public static IConfiguration Configuration;

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        var clientConfiguration = Configuration.GetSection("RabbitMq");
        var exchangeConfiguration = Configuration.GetSection("RabbitMqExchange");
        services.AddRabbitMqClient(clientConfiguration)
            .AddExchange("ExchangeName", isConsuming: true, exchangeConfiguration);
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
    }
}

文件将是这样的。appsettings.json

{
 "RabbitMqExchange": {
    "Type": "direct",
    "Durable": true,
    "AutoDelete": false,
    "DeadLetterExchange": "default.dlx.exchange",
    "RequeueFailedMessages": true,
    "RequeueTimeoutMilliseconds": 200,
    "RequeueAttempts": 2,
    "Arguments": { "key": "value" },
    "Queues": [
      {
        "Name": "MyQueueName",
        "Durable": true,
        "AutoDelete": false,
        "Exclusive": false,
        "Arguments": { "key": "value" },
        "RoutingKeys": [ "first.routing.key", "second.routing.key" ]
      }
    ]
  }
}

此示例中未指定 RabbitMQ 客户端配置部分,有关更多信息,请参阅文档文件。

可以使用属性配置交换:

  • Type- 交换类型(直接、主题、扇出)。默认值为 。"direct"
  • Durable- 耐用性选项。默认值为 。true
  • AutoDelete- 交换自动删除的选项。默认值为 。false
  • Arguments- 附加参数的字典。默认值为 。null
  • RequeueFailedMessages- 一个选项,用于指定通过死信交换以一定延迟重新排队失败消息的行为。默认值为 。文档中介绍了发送延迟消息的机制。true
  • RequeueTimeoutMilliseconds- 超时(以毫秒为单位),之后消息将重新排队。默认值为 200。
  • RequeueAttempts- 排队服务将尝试重新排队消息的次数。默认值为 2。
  • DeadLetterExchange- 死信交换的值。死信交换名称的默认值为 。"default.dlx.exchange"
  • Queues- 绑定到交易所的队列集合。

队列选项:

  • Name- 队列名称。
  • Durable- 耐用性选项。默认值为 。true
  • AutoDelete- 队列自动删除选项。默认值为 。false
  • Exclusive- 独家选项。默认值为 。false
  • Arguments- 附加参数的字典。默认值为 。null
  • RoutingKeys- 队列“侦听”的路由密钥集合。

考虑到您可以跳过的所有默认值,配置将如下所示。

{
 "RabbitMqExchange": {
    "Type": "direct",
    "Queues": [
      {
        "Name": "MyQueueName",
        "RoutingKeys": [ "first.routing.key", "second.routing.key" ]
      }
    ]
  }
}

如果要使用与队列名称匹配的路由键,则可以跳过该选项,队列将通过其名称绑定到交换。"RoutingKeys"

{
 "RabbitMqExchange": {
    "Type": "direct",
    "Queues": [
      {
        "Name": "queue.name.as.routing.key"
      }
    ]
  }
}

生产和消费Exchange

此库中有两种自定义的交换“类型”——生产和****消费。生产交换应该只在生成消息的应用程序中使用。消费交换既针对生产和消费。 为什么甚至有必要进行这样的划分?您可以控制多用途应用程序中一组交换的行为,这些交换使用和使用消息,并避免收到不需要的消息。

要定义交换的自定义“类型”,您必须设置方法接受的参数。如果该值为 ,则应用程序将从绑定到该交换的队列中获取(使用)消息。如果是交换将仅用于生成消息。isConsuming``AddExchange``true``false

services.AddRabbitMqClient(clientConfiguration)
    .AddExchange("ExchangeName", isConsuming: true, exchangeConfiguration);

您也可以使用 or 但在引擎盖下,它与使用带有参数的方法相同。AddConsumptionExchange``AddProductionExchange``AddExchange``isConsuming

services.AddRabbitMqClient(clientConfiguration)
    .AddConsumptionExchange("ConsumptionExchange", exchangeConfiguration);

// And the other method.

services.AddRabbitMqClient(clientConfiguration)
    .AddProductionExchange("ProductionExchange", exchangeConfiguration);

手动配置

您还可以配置交换,手动将 和 类的实例传递给其中一个方法。RabbitMqExchangeOptions``RabbitMqQueueOptions``AddExchange

var exchangeOptions = new RabbitMqExchangeOptions
{
    Type = "topic",
    Durable = true,
    AutoDelete = true,
    Arguments = null,
    RequeueFailedMessages = true,
    DeadLetterExchange = "default.dlx.exchange",
    Queues = new List<RabbitMqQueueOptions>
    {
        new RabbitMqQueueOptions
        {
            Name = "MyQueueName",
            Durable = true,
            AutoDelete = false,
            Exclusive = false,
            Arguments = null,
            RoutingKeys = new HashSet<string> { "routing.key" }
        }
    }
};
services.AddRabbitMqClient(clientConfiguration)
     .AddExchange("ExchangeName", isConsuming: true, exchangeOptions);

相同的配置将适用于或重载。AddConsumptionExchange``AddProductionExchange

var exchangeOptions = new RabbitMqExchangeOptions
{
    Queues = new List<RabbitMqQueueOptions>
    {
        new RabbitMqQueueOptions
        {
            Name = "MyQueueName",
            RoutingKeys = new HashSet<string> { "routing.key" }
        }
    }
};
services.AddRabbitMqClient(clientConfiguration)
    .AddProductionExchange("ProductionExchange", exchangeOptions);

// Or the other method.

services.AddRabbitMqClient(clientConfiguration)
    .AddConsumptionExchange("ConsumptionExchange", exchangeConfiguration);

1.2 Linux上访问AD报错ldap.so.2

o load shared library 'ldap.so.2' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: libldap.so.2: cannot open shared object file: No such file or directory

未找到原因:换一个dll版本不报错了。 Include="LdapForNet" Version="2.7.13"

# 以CentOS为例 执行以下命令  以下的也不能解决问题

yum update 
yum install -y --no-install-recommends libldap

2. Exchange Powershell

IIS 上访问 powersehll 报错:拒绝访问,解决办法需要给应用程序池提升权限

## .net core 调用powershell 报错,。<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
	<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
  </PropertyGroup>

.NET6 在IIS上不支持PUT、Delete

在web.config中;找到<system.webServer>元素下添加以下配置:

<modules runAllManagedModulesForAllRequests="true">
    <remove name="WebDAVModule" />
</modules>

重启IIS

.NET Core开发Windows服务

  1. 使用VS新建辅助服务项目
  2. 发布应用
dotnetpublish -c Release -r win-x64 --no-self-contained -p:PublishSingleFile=true 编译生成单一 .exe 文件
  1. 部署服务使用“管理员身份运行”windows powershell命令行
  2. 使用sc.exe安装windows服务
sc.exe create "ServiceName" binpath="D:\CPULoader\WorkerService1.exe"  #安装服务

sc.exe start "ServiceName"  #启动服务

sc.exe stop "ServiceName"  #停止服务

sc.exe qfailure "ServiceName" #查看服务信息

sc.exe delete "CPU Loader" #删除windows服务


sc.exe create "NETCoreServiceTest" binpath="D:\corewinservice\WorkerService1.exe" 

cmd


sc.exe create MessageTrackingLogService binpath = "C:\Program Files\MessageTrackingLogService\ExAtp.MessageTackingLogSyncService.exe" start= auto 

示例:

// Program.cs

using WorkerService1;

IHost host = Host.CreateDefaultBuilder(args)
    .UseWindowsService(options =>
    {
        options.ServiceName = "CPU Load Logger";
    })
    .ConfigureServices(services =>
    {
        services.AddHostedService<Worker>();
    })
    .Build();

await host.RunAsync();
//Worker.cs

using Microsoft.Extensions.Logging;
using System.Diagnostics;

namespace WorkerService1
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private readonly string logPath;
        private StreamWriter cpuLogger = null!;
        public Worker(ILogger<Worker> logger, IConfiguration config)
        {
            _logger = logger;
            logPath = Path.Combine(
                config.GetValue<string>("LogPath") ??
                System.AppContext.BaseDirectory!,
                "cpu.log");
        }
        // 服務啟動時
        public override async Task StartAsync(CancellationToken stoppingToken)
        {
            cpuLogger = new StreamWriter(logPath, true);
            _logger.LogInformation("Service started");
            Log("Service started");
            // 基底類別 BackgroundService 在 StartAsync() 呼叫 ExecuteAsync、
            // 在 StopAsync() 時呼叫 stoppingToken.Cancel() 優雅結束
            await base.StartAsync(stoppingToken);
        }

        // 服務停止時
        public override async Task StopAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation("Service stopped");
            Log("Service stopped");
            cpuLogger.Dispose();
            cpuLogger = null!;
            await base.StopAsync(stoppingToken);
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            try
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    ThreadPool.QueueUserWorkItem(
                        (obj) =>
                        {
                            try
                            {
                                var cancelToken = (CancellationToken)obj!;
                                if (!stoppingToken.IsCancellationRequested)
                                {
                                    Log($"CPU: {GetCpuLoad()}%");
                                    _logger.LogInformation($"Logging CPU load");
                                }
                            }
                            catch (Exception ex)
                            {
                                _logger.LogError(ex.ToString());
                                throw;
                            }
                        }, stoppingToken);
                    await Task.Delay(5000, stoppingToken);
                }
            }
            catch (OperationCanceledException)
            {
                // When the stopping token is canceled, for example, a call made from services.msc,
                // we shouldn't exit with a non-zero exit code. In other words, this is expected...
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "{Message}", ex.Message);

                // Terminates this process and returns an exit code to the operating system.
                // This is required to avoid the 'BackgroundServiceExceptionBehavior', which
                // performs one of two scenarios:
                // 1. When set to "Ignore": will do nothing at all, errors cause zombie services.
                // 2. When set to "StopHost": will cleanly stop the host, and log errors.
                //
                // In order for the Windows Service Management system to leverage configured
                // recovery options, we need to terminate the process with a non-zero exit code.
                Environment.Exit(1);
            }
        }

        void Log(string message)
        {
            if (cpuLogger == null) return;
            cpuLogger.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {message}");
            cpuLogger.Flush();
        }

        int GetCpuLoad()
        {
            using (var p = new Process())
            {
                p.StartInfo.FileName = "wmic.exe";
                p.StartInfo.Arguments = "cpu get loadpercentage";
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardOutput = true;
                p.Start();
                int load = -1;
                var m = System.Text.RegularExpressions.Regex.Match(
                    p.StandardOutput.ReadToEnd(), @"\d+");
                if (m.Success) load = int.Parse(m.Value);
                p.WaitForExit();
                return load;
            }
        }
    }
}

在服务中启用GRPC

public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private readonly RecallWorker _recallWorker;

        private MailboxGrpcServiceImplement _mailboxGrpcService;
        private Server _server;

        public Worker(ILogger<Worker> logger, RecallWorker mailRecallWorker, MailboxGrpcServiceImplement mailboxGrpcServiceImplement)
        {
            _logger = logger;
            _mailboxGrpcService = mailboxGrpcServiceImplement;
            _recallWorker = mailRecallWorker;
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        {
            string keyPath = Path.Combine(AppContext.BaseDirectory, "cert/exatp.wlync.com.key");
            string certificatePath = Path.Combine(AppContext.BaseDirectory, "cert/exatp.wlync.com.crt");
            var key = File.ReadAllText(keyPath);
            var cert = File.ReadAllText(certificatePath);
            var keyPair = new KeyCertificatePair(cert, key);
            var credentials = new SslServerCredentials(new List<KeyCertificatePair> { keyPair });
            _server = new Server
            {
                Services = { BindService(_mailboxGrpcService) },
                Ports = { new ServerPort("0.0.0.0", 5001, credentials),
                new ServerPort("0.0.0.0", 5000, ServerCredentials.Insecure)}
            };

            _server.Start();
            _logger.LogInformation($"gRPC server is runing at http://0.0.0.0:{5000}, https://0.0.0.0:{5001} port");
            return base.StartAsync(cancellationToken);
        }

        public override Task StopAsync(CancellationToken cancellationToken)
        {
            _server?.ShutdownAsync().Wait();
            return base.StopAsync(cancellationToken);
        }


        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                if (!WorkerStatus.MailRecallWorkerStatus)
                {
                    _recallWorker.Start();
                }
                await Task.Delay(1000 * 60, stoppingToken);
            }
        }

3. 异步编程

3.1 Task

Task myTask = Task.Run(() =>
{
    // 异步任务的代码
});

// 异步任务开始后,状态变为Running
if (myTask.Status == TaskStatus.Running)
{
    // 异步任务已经开始
}

// 异步任务结束后,状态变为RanToCompletion
myTask.ContinueWith(task =>
{
    // 异步任务已经结束
});

3.2 异步等待超时示例

using System.DirectoryServices;
using System.Threading.Tasks;
using System.Threading;

async Task ConnectWithTimeoutAsync(string server, TimeSpan timeout)
{
    using var cts = new CancellationTokenSource(timeout);
    try
    {
        var task = Task.Run(() =>
        {
            var de = new DirectoryEntry("LDAP://" + server);
            // 尝试建立连接
            var obj = de.NativeObject;
            return de;
        }, cts.Token);

        return await task;
    }
    catch (OperationCanceledException)
    {
        // 处理超时
        Console.WriteLine("LDAP connection timed out.");
        return null;
    }
}

// 使用示例
var result = await ConnectWithTimeoutAsync("ldap.example.com", TimeSpan.FromSeconds(10));
if (result == null)
{
    // 处理超时情况
}

工具

文件大小友好显示转换ByteConverter

public static object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string size = "0 KB";

            if (value != null)
            {
                double byteCount;
                if (value.GetType().Name == "Single")
                {
                    byteCount = double.Parse(value.ToString());
                }
                else
                {
                    byteCount = (double)value;
                }
                

                if (byteCount >= 1073741824)
                    size = String.Format("{0:##.##}", byteCount / 1073741824) + " GB";
                else if (byteCount >= 1048576)
                    size = String.Format("{0:##.##}", byteCount / 1048576) + " MB";
                else if (byteCount >= 1024)
                    size = String.Format("{0:##.##}", byteCount / 1024) + " KB";
                else if (byteCount > 0 && byteCount < 1024)
                    size = "1 KB";    //Bytes are unimportant ;)            

            }

            return size;
        }