Top page  1/43
14
2022
10
2022

Redisの負荷対策

CATEGORY開発全般
以前Laravelでの負荷対策とかの記事書いたけど、今度はキャッシュとかに使うRedisの負荷対策について。

Redisは、いわゆるNoSQLのインメモリデータストア。
過去の記事でも書いたように、そもそもキャッシュとかにRedisを使うこと自体が性能対策なんだけど、Redisも万能じゃないので負荷が増えると(稀に負荷が増える前からも)問題を起こすケースがある。
今回は自分が経験した問題と対策を一通りメモがてらまとめておく。

なお、Redisはデータストアなのでアプリ側の言語は問わないが、以下は主にASP.NET Core (StackExchange.Redis)、次いでPHP (Laravel/predis) とNode.js (node-redis) での経験になる。
Redis本体の問題以外の、ライブラリや言語の問題も一緒に書いておくのでその認識で。

Tag: Redis ASP.NET プログラミング 性能問題

07
2021

Dockerでpsやtop, vmstat, pingコマンドを使う (Debian系)

CATEGORY開発環境
Debian系のDockerコンテナで、psやpingコマンドが入っていなくて毎回ググってるので、十番煎じぐらいだけどメモ。

ps, top, vmstat

参考)DockerHubのnginx:latestイメージでpsコマンド叩いたら「そんなもの無い」って言われたのでインストールする
apt update && apt install -y procps

ping

参考)docker イメージの中にpingとかないとき
apt update && apt install -y iputils-ping net-tools


以上。これだけ。

Tag: Docker Debian

05
2021

MagicOnionでもASP.NET Core標準のバリデーションが使いたい

CATEGORY.NET
またまたMagicOnionの話。ASP.NET Coreには標準でバリデーションの仕組みがあり、APIの引数やDTOのプロパティに属性を付けることでバリデーションが出来て便利なんだけど、MagicOnionは残念ながらそういうの対応していないっぽい。
そこで、自力でValidationFilterみたいなのを作って、同じようなことができるようにやってみたので、その内容を書いておく。確認したバージョンは4.2.0。

結論から書くと、フィルタの実装はこんな感じ。
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using MagicOnion;
using MagicOnion.Server;
using MessagePack;

public class ValidationFilterAttribute : MagicOnionFilterAttribute
{
public override async ValueTask Invoke(ServiceContext context, Func<ServiceContext, ValueTask> next)
{
this.Validate(context);
await next(context);
}

private void Validate(ServiceContext context)
{
var parameterTypes = context.MethodInfo.GetParameters();
if (parameterTypes.Length == 0)
{
return;
}

var resolver = context.SerializerOptions.Resolver;
var requestType = MagicOnionMarshallers.CreateRequestTypeAndSetResolver(context.CallContext.Method, parameterTypes, ref resolver);
var param = MessagePackSerializer.Deserialize(requestType, context.GetRawRequest(), context.SerializerOptions);
ValidateParameters(parameterTypes, param);
}

private static void ValidateParameters(ParameterInfo[] parameterTypes, object valueOrTuple)
{
var valueType = valueOrTuple != null ? valueOrTuple.GetType() : typeof(object);
if (valueType.IsGenericType && valueType.Name == typeof(DynamicArgumentTuple<,>).Name)
{
for (var i = 0; i < parameterTypes.Length; i++)
{
var prop = valueType.GetField("Item" + (i + 1));
ValidateParameter(parameterTypes[i], prop != null ? prop.GetValue(valueOrTuple) : null);
}
}
else
{
ValidateParameter(parameterTypes[0], valueOrTuple);
}
}

private static void ValidateParameter(ParameterInfo type, object value)
{
var customAttributes = type.GetCustomAttributes<ValidationAttribute>(true);
if (customAttributes.Any())
{
Validator.ValidateValue(value, new ValidationContext(value ?? new object()), customAttributes);
}

if (type.ParameterType.IsClass && value != null)
{
Validator.ValidateObject(value, new ValidationContext(value), true);
}
}
}

これで、こんな感じに引数やDTOのプロパティに属性を付けてバリデーションが出来る。
public async UnaryResult Login([Required] string id, [Required] string password)
{
// 中略
}

以下解説。

Tag: .NET MagicOnion

03
2021

MagicOnion v4のSwagger設定でハマる

CATEGORY.NET
v3の頃から使っている.NET用のgRPCフレームワークのMagicOnion。2020年末に出たv4で新たにサーバーを立てたら、いろいろハマって解決に時間が掛かったので、ちょっとその解決方法をまとめておく。

まず、MagicOnion v4だが、v3までと違って独自のサーバーを起動するのではなく、ASP.NET CoreのgRPCサーバーの上に乗っかるものになっているようだ。なので、設定とかはv3以前とまるっきり異なる。

で、README.mdにはもちろんv4での設定方法が記載されているのだが、その通りにやっただけだとプロジェクト構成によっては動かずハマることになる。
分かってしまえば、ASP.NET CoreやgRPCの仕組みを考えれば、あーそりゃそうだ、って話なんだが、その辺把握できてないと普通にハマる。
なので、解決方法を書いておく。まず最初に結論から書くとこういう話。
  1. MagicOnionはサーバーにHTTP/2で接続できないと繋がらない。
  2. Swagger UIはサーバーにHTTP/1で接続できないと繋がらない。
  3. ASP.NET CoreのHTTP/1とHTTP/2の混在モード (Http1AndHttp2) は、SSL環境じゃないと動作しない。
1,2はまあ当然として、3が一番落とし穴かな。
まず、ASP.NET Core 5.0でgRPCプロジェクトを作ると、デフォルトではHTTP/2オンリーの設定になる様子。
なので、README.md通りにSwaggerを設定しても、当然繋がらない。

次に、じゃあHttp1AndHttp2に設定して両方動くようにすればいいじゃん、とやると、今度はSwaggerは開くものの3が原因でMagicOnionと通信ができない。
(SSLを有効にしてあるプロジェクトならこの段階で動くかもしれないけど未確認。とはいえ、アプリサーバーでSSLを終端させる、というのは普通はやらないイメージが。)

最終的にポートを分けてHTTP/2とHTTP/1両方動かして、SwaggerからはHTTP/2のポートに接続するようにすると、ようやく解決する。
具体的には、README.mdの「Use HTTP unencrypted connection without TLS」をやった上で、「Swagger」のとこにあるコードのHTTPゲートウェイの接続先だけ、この場合は http://localhost:5000 に変えればいい。


以上、分かってしまえば別に大した話じゃないんだけど、とりあえずREADME.mdの手順でSwagger動かしてみるかとやっているとハマるという話でした。同じ問題にハマった方の参考になればとm(__)m

Tag: .NET MagicOnion Swagger