skip to Main Content

I have this container running on my localhost:5005 docker:

enter image description here

It was created with: docker run --name mtg-redis -p 5005:6379 -d redis

I have my .NET application running on localhost:5002

Docker Compose:

version: '3.4'

services:
  mtgmvc:
    image: ${DOCKER_REGISTRY-}mtgmvc
    build:
      context: .
      dockerfile: MTGMVC/Dockerfile

Docker compose override:

version: '3.4'

services:
  mtgmvc:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_HTTP_PORTS=8080
      - ASPNETCORE_HTTPS_PORTS=8081
    ports:
      - "5001:8080"
      - "5002:8081"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro

Dockerfile:

#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 8080
EXPOSE 8081

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["MTG/MTGMVC.csproj", "MTGMVC/"]
RUN dotnet restore "./MTG/MTGMVC.csproj"
COPY . .
WORKDIR "/src/MTGMVC"
RUN dotnet build "./MTGMVC.csproj" -c $BUILD_CONFIGURATION -o /app/build

FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./MTGMVC.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MTGMVC.dll"]

appsettings:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=mylocaldb;Initial Catalog=MTGCards;Integrated Security=True",
    "Redis": "localhost:5005"
  }
}

Program:

using MTGMVC.Clients;
using MTGMVC.Repositories;
using MTGMVC.Repositories.CommandText;
using MTGMVC.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddHttpClient("MTG", (serviceProvider, httpClient) =>
{
    //var mtgSettings = serviceProvider.GetRequiredService<IOptions<MtgSettings>>().Value;
    httpClient.BaseAddress = new Uri("https://api.magicthegathering.io/v1/");
});
builder.Services.AddHttpClient("Scryfall", (serviceProvider, httpClient) =>
{
    //var mtgSettings = serviceProvider.GetRequiredService<IOptions<MtgSettings>>().Value;
    httpClient.BaseAddress = new Uri("https://api.scryfall.com/");
});

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
    options.InstanceName = "mtg-redis";
});

//Register own services
builder.Services.AddScoped<IMTGService, MTGService>();
builder.Services.AddScoped<IMTGClient, MTGClient>();
builder.Services.AddScoped<IScryfallClient, ScryfallClient>();
builder.Services.AddTransient<ICommandText, CommandText>();
builder.Services.AddTransient<ISetRepository, SetRepository>();
builder.Services.AddTransient<IMagicDataWriterService, MagicDataWriterService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Extension File for crud redis methods:

using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;

namespace MTGMVC.Extensions
{
    public static class DistributedCacheExtensions
    {
        public static async Task SetRecordAsync<T>(this IDistributedCache cache, string recordId, T data, TimeSpan? absoluteExpireTime = null, TimeSpan? unusedExpireTime = null)
        {
            var options = new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = absoluteExpireTime ?? TimeSpan.FromMinutes(1),
                SlidingExpiration = unusedExpireTime
            };
            var jsonData = JsonSerializer.Serialize(data);
            await cache.SetStringAsync(recordId, jsonData, options);
        }

        public static async Task<T?> GetRecordAsync<T>(this IDistributedCache cache, string recordId)
        {
            var jsonData = await cache.GetStringAsync(recordId);

            if (jsonData is null)
            {
                return default;
            }

            return JsonSerializer.Deserialize<T>(jsonData);
        }
    }
}

I’m trying to call the redis cache to perform a GET from here:

using Microsoft.Extensions.Caching.Distributed;
using MTGMVC.Clients;
using MTGMVC.DTOs.Scryfall.Cards;
using MTGMVC.Extensions;
using MTGMVC.Models;
using MTGMVC.Repositories;

namespace MTGMVC.Services
{
    public interface IMagicDataWriterService
    {
        Task<IList<SetModel>> GetAllSetNamesAsync();
        Task<ScryfallCardDto> GetRandomCardBySet(string setCode);
    }

    public class MagicDataWriterService : IMagicDataWriterService
    {
        private ISetRepository _setRepository;
        private IScryfallClient _scryfallClient;
        private ILogger<MagicDataWriterService> _logger;
        private IDistributedCache _cache;

        public MagicDataWriterService(ISetRepository setRepository, IScryfallClient scryfallClient, ILogger<MagicDataWriterService> logger, IDistributedCache cache)
        {
            _setRepository = setRepository;
            _scryfallClient = scryfallClient;
            _logger = logger;
            _cache = cache;
        }

        public async Task<IList<SetModel>> GetAllSetNamesAsync()
        {
            try
            {
                var cachedRecords = await _cache.GetRecordAsync<IList<SetModel>>("sets");

                if (cachedRecords == null || !cachedRecords.Any())
                {
                    var persistedSets = await _setRepository.CountSets();
                    if (persistedSets == 0)
                    {
                        var scryfallSets = await _scryfallClient.GetAllScryfallSetsAsync();
                        foreach (var set in scryfallSets.ScryfallSets)
                        {
                            await _setRepository.InsertSet(set);
                        }
                    }

                    var setNames = await _setRepository.GetAllSetNames();
                    await _cache.SetRecordAsync("sets", setNames, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(60));
                    return setNames;
                }

                return cachedRecords;
            }
            catch (Exception ex)
            {
                _logger.LogError("Unable to get sets {ex}", ex);
                throw;
            }
        }
    (..)

But I’m getting a timeout from the redis cache. As far as I understand my app is being run by docker compose on port localhost:5002 and my redis container is up and running on localhost:5005 which is what I’m pointing at on appsettings…I’m unsure of what I’m doing wrong to reach it.

Exception message:

The message timed out in the backlog attempting to send because no connection became available (5000ms) - Last Connection Exception: UnableToConnect on localhost:5005/Interactive, Initializing/NotStarted, last: NONE, origin: BeginConnectAsync, outstanding: 0, last-read: 0s ago, last-write: 0s ago, keep-alive: 60s, state: Connecting, mgr: 10 of 10 available, last-heartbeat: never, global: 0s ago, v: 2.7.33.41805, command=HMGET, timeout: 5000, inst: 0, qu: 0, qs: 0, aw: False, bw: CheckingForTimeout, last-in: 0, cur-in: 0, sync-ops: 0, async-ops: 1, serverEndpoint: localhost:5005, conn-sec: n/a, aoc: 0, mc: 1/1/0, mgr: 10 of 10 available, clientName: 14c8ae52ba5a(SE.Redis-v2.7.33.41805), IOCP: (Busy=0,Free=1000,Min=1,Max=1000), WORKER: (Busy=0,Free=32767,Min=12,Max=32767), POOL: (Threads=12,QueuedItems=0,CompletedItems=195,Timers=14), v: 2.7.33.41805 (Please take a look at this article for some common client-side issues that can cause timeouts: https://stackexchange.github.io/StackExchange.Redis/Timeouts)

2

Answers


  1. There might be several reasons.

    1. First, check if your .net container has access to Redis container. Docker compose automatically sets up a network for containers in the same compose file. However, ensure that no network restrictions or firewalls are blocking the communication. you can check it with telnet.
    2. Second, check your Redis configuration. By default, Redis binds to localhost only, so external connections are not allowed. You can modify the Redis configuration to bind to all interfaces or a specific interface. In your case, since you’re trying to connect from another Docker container, you should configure Redis to bind to all interfaces or the Docker bridge network interface. Use --bind 0.0.0.0.
    3. Third, Check your connection string. You have used localhost:5005 which refers to the current container. Use Redis container’s IP address instead.
    Login or Signup to reply.
  2. Your localhost is not the localhost of your container. Try to use this:

    host.docker.internal or your default ip of your network (192...* etc.) on your backend application for redis.

    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*",
      "ConnectionStrings": {
        "DefaultConnection": "Data Source=mylocaldb;Initial Catalog=MTGCards;Integrated Security=True",
        "Redis": "host.docker.internal:5005"
      }
    }
    

    For more check this out

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search