三味 大名店

兼虎に行ったら大雨なのに行列ができていて断念。予定変更。こんなときのためにストックしておいた、行ってみたい店の一つ『三味 天神大名店』に向かった。

元祖トマトラーメンの食券を購入。 トマトソースのパスタは旨いことが分かりきっているけど、トマトを使ったスープのラーメンはどんなもんだろうか。恐る恐る口にしてみた。

結論としては、人類にトマトラーメンは早すぎた。トマト鍋のシメのラーメンをいきなり食べるような、予想を裏切ってくれることを期待したが予想通りの味。 女性受けしそうな味でもある。実際、店内には女性客がほとんどだった。自分が極端に酸味に弱いのもあるかもなぁ。

トマトラーメンと辛めん 三味 天神大名店

食べログ トマトラーメンと辛めん 三味 天神大名店

十一 六本松店

ミュージアムウィークで福岡市美術館に行った帰りに、「十一 六本松店」でランチを食べることにした。十一、六本松に進出してたのか。豚ステーキだけでなく、牛タンも始めていたみたいだし。あと、十一六本松って、漢数字が並んでいて、加藤一二三九段みたいだな。

お目当は六本松店限定の「ハーフ&ハーフ」。豚ステーキと牛タンの両方を味わうことができる欲張りなやつ。

注文するとまずサラダが運ばれてくる。パイナップルの甘さが良いアクセントになっていて好み。

サラダを食べ終えて少し経ったころに、豚ステーキと牛タン御一行が到着。ご飯には明太子か梅をのせることができたので、明太子をチョイスした。ただ、こんなにのせる必要はないかなぁ。

コリっとした牛タンを塩レモンで味わうのは、なかなかの美味だけど、自分はやはり豚ステーキだな。熱々の鉄板で好みの焼き加減に仕上げて、辛味噌をのせて食べると美味い。おろしわさびも捨てがたいけど、辛味噌派かな。

豚ステーキはレアというか、表面だけ焼いた状態で出されるけど、無菌豚ではないらしいので、ちゃんと鉄板で焼く必要がある。要注意。以前赤坂店で食べたとき、ちゃんと焼かずに食べたかも…。子供向けには小さくカットしてからしっかり焼いてもらうこともできるので、その点は気が利いていて良かった。

関連ランキング:ステーキ | 六本松駅別府駅

福岡市美術館

福岡市のミュージアムウィーク期間中だったので、家族で福岡市美術館に行ってみた。福岡市博物館やアジア美術館なんかは期間中無料で常設展が観れるのに対し、福岡市美術館は無料じゃなかったのがイケてない。安くはなってたけど。

リニューアルオープン記念展を観たが、展示されていた作品でグッとくるものはなかったなぁ。フランスでルーブルやオルセー、イタリアでウフィッツィに行ったから目が肥えてしまったのかも。なんて。記念展が終わって常設展になったら、展示される作品が変わるだろうから、常設展を見るまで評価は保留。あと、子供に美術館は早かったみたいで、退屈そうだった。来年のミュージアムウィークで子供が付いてきてくれるかどうか、微妙な線だな。

アルスラーン戦記(11)

アルスラーン率いるパルス軍は、ついに王都を奪還するためにペシャワールを出発。表紙がクバードだったから、アルスラーンクバード口説き落とす話があるかと思いきや、クバード全く出てこなかった。残念。

そのかわりと言ってはなんだけど、エトワールが登場。秘密がありそうだとうすうす感じていたら案の定で、やっぱり女だったか、という感じ。ちなみに原作は読んでない。エトワール、じゃなくてエステルとアルスラーンの関係性がこのマンガでどう描写されるか楽しみ。

WEB+DB PRESS Vol.110

GW中に読み終えることができたので、感想などをメモしておく。

特集1:名前付け大全

「適切な名前をつけることができた機能については、その設計の8割が完成したと考えても言い過ぎでない」とまで言われるくらい重要な名前付け。ピッタリの名前が思い付けばいいが、そうでない場合、自分はGitHubで参考になりそうなコードを探したり、開発に使っている言語の標準ライブラリのAPIを参考にしている。名前の良し悪しの判断や名付け方といった、自分たちが経験でやっていることがきっちり文章としてまとめ上げられているので、ぜひとも新人に読ませたいと思った。

特集2:速習gRPC

マイクロサービス間の通信のデファクトスタンダードであり、モバイルアプリとバックエンドの通信だけでなく、Unityで開発したゲームでの採用事例も見られるgRPC。そんなgRPCの基本的な使い方をProtocolBuffersを含め一通り解説していて、gRPCはじめの一歩を踏み出すのに良い教材だと思う。ただ、欲を言えば、認証・認可をどうやるのかまで触れてほしかったな。望みすぎか。

特集3:最新TLS 1.3徹底解剖

TLSの歴史と暗号技術の基礎は、過去に自分が少し勉強したことがあったからか、それとも解説が見事だからか、すんなり頭に入ってきた。Wiresharkを使ってTLSの通信を覗きながら進める解説手法は、手を動かすぶん、文章や図だけの解説と比べて理解しやすいかった。低レイヤーに関する知識を身に付けたいと思っているところなので、TLS周りを勉強する際には導入として読み返すことになりそう。

Rubyのウラガワ【第1回】オブジェクトはどうやって表現するのか?

ポインタとしてあり得ない値を利用した埋め込み表現の工夫は涙なしに読めない(大げさ)。 こういった記事を読むと、オレオレ言語を実装してみたいという思いがフツフツと湧いてきちゃうな。

WEB+DB PRESS Vol.110

WEB+DB PRESS Vol.110

再戦 ASP.NET Core MVC vs. WCF

はじめに

以前 ASP.NET Core MVCWCFベンチマークを比較したことがあって、 そのときは WCF の方がかなり速いなぁ、という結果だった。 ASP.NET Core MVC が思いのほか遅いのが気になったけど、それに関しては当時調べず。

tnakamura.hatenablog.com

このときのベンチマークASP.NET Core MVC が遅かった原因が、コンソールにログを出力していたせいだと判明。試しに Information 以下のログを出力しないように構成したら、だいぶ結果が変わったので、ベンチマークを比較し直すことにした。

ベンチマーク結果

ただやり直すだけでは面白くないので、ASP.NET Core MVC の方はシリアライザに ProtocolBuffers を使った場合のベンチマークも追加してみた。

f:id:griefworker:20190423094647p:plain

ProtocolBuffers を使っても、NetTcpBinding を使った WCF には敵わなかったのが残念。

ベンチマークに使ったコード

ASP.NET Core MVC

WebApiContrib.Core.Formatter.Protobuf を導入することで、シリアライザに ProtocolBuffers が使えるようになる。

using System;
using System.Linq;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ProtoBuf;
using WebApiContrib.Core.Formatter.Protobuf;

namespace AspNetCoreVsWcf.AspNetCore
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .ConfigureLogging(logging =>
                {
                    logging.SetMinimumLevel(LogLevel.Warning);
                })
                .UseUrls("http://localhost:8000");
    }

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

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                .AddProtobufFormatters();
        }

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

    [ProtoContract]
    public class Book
    {
        [ProtoMember(1)]
        public string Id { get; set; }

        [ProtoMember(2)]
        public string Title { get; set; }

        [ProtoMember(3)]
        public string Description { get; set; }

        [ProtoMember(4)]
        public string Author { get; set; }

        [ProtoMember(5)]
        public int Price { get; set; }

        [ProtoMember(6)]
        public string PublishedAt { get; set; }
    }

    [Route("api/[controller]")]
    [ApiController]
    public class BooksController : ControllerBase
    {
        static readonly Book[] books;

        static BooksController()
        {
            books = Enumerable.Range(0, 50)
                .Select(i => new Book()
                {
                    Id = Guid.NewGuid().ToString(),
                    Title = $"Book{i}",
                    Author = $"Author{i}",
                    Description = $"Description{i}",
                    PublishedAt = DateTime.Today.ToString(),
                    Price = 2000,
                })
                .ToArray();
        }

        [HttpGet]
        public ActionResult<Book[]> Get()
        {
            return books;
        }
    }
}
WCF
using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;

namespace AspNetCoreVsWcf.Wcf
{
    class Program
    {
        static void Main(string[] args)
        {
            var host = new ServiceHost(typeof(BookService));
            host.AddServiceEndpoint(
                typeof(IBookService),
                new NetTcpBinding(),
                "net.tcp://localhost:8001/BookService");
            host.Open();

            Console.WriteLine("Enter で終了");
            Console.ReadLine();

            host.Close();
        }
    }

    [DataContract]
    public class Book
    {
        [DataMember]
        public string Id { get; set; }

        [DataMember]
        public string Title { get; set; }

        [DataMember]
        public string Description { get; set; }

        [DataMember]
        public string Author { get; set; }

        [DataMember]
        public int Price { get; set; }

        [DataMember]
        public string PublishedAt { get; set; }
    }

    [ServiceContract]
    public interface IBookService
    {
        [OperationContract]
        Book[] GetBooks();
    }

    public class BookService : IBookService
    {
        static readonly Book[] books;

        static BookService()
        {
            books = Enumerable.Range(0, 50)
                .Select(i => new Book()
                {
                    Id = Guid.NewGuid().ToString(),
                    Title = $"Book{i}",
                    Author = $"Author{i}",
                    Description = $"Description{i}",
                    PublishedAt = DateTime.Today.ToString(),
                    Price = 2000,
                })
                .ToArray();
        }

        public Book[] GetBooks()
        {
            return books;
        }
    }
}
ベンチマーク
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using Newtonsoft.Json;
using ProtoBuf;

namespace AspNetCoreVsWcf.Benchmark
{
    class Program
    {
        static void Main(string[] args)
        {
            BenchmarkRunner.Run<AspNetCoreVsWcfBenchmark>();

            Console.ReadLine();
        }
    }

    [ProtoContract]
    [DataContract]
    public class Book
    {
        [DataMember]
        [ProtoMember(1)]
        public string Id { get; set; }

        [DataMember]
        [ProtoMember(2)]
        public string Title { get; set; }

        [DataMember]
        [ProtoMember(3)]
        public string Description { get; set; }

        [DataMember]
        [ProtoMember(4)]
        public string Author { get; set; }

        [DataMember]
        [ProtoMember(5)]
        public int Price { get; set; }

        [DataMember]
        [ProtoMember(6)]
        public string PublishedAt { get; set; }
    }

    [ServiceContract]
    public interface IBookService
    {
        [OperationContract]
        Book[] GetBooks();
    }

    public class AspNetCoreVsWcfBenchmark
    {
        IBookService tcpChannel;
        HttpClient httpClient;
        JsonSerializer jsonSerializer;

        [GlobalSetup]
        public void GlobalSetup()
        {
            tcpChannel = ChannelFactory<IBookService>.CreateChannel(
                new NetTcpBinding(),
                new EndpointAddress("net.tcp://localhost:8001/BookService"));
            httpClient = new HttpClient();
            jsonSerializer = JsonSerializer.CreateDefault();
        }

        [GlobalCleanup]
        public void GlobalCleanup()
        {
            ((IClientChannel)tcpChannel).Close();
            httpClient.Dispose();
        }

        [Benchmark]
        public Book[] WcfTcp()
        {
            return tcpChannel.GetBooks();
        }

        [Benchmark]
        public async Task<Book[]> AspNetCoreJson()
        {
            var request = new HttpRequestMessage(
                HttpMethod.Get,
                "http://localhost:8000/api/books");
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var response = await httpClient.SendAsync(request);
            var stream = await response.Content.ReadAsStreamAsync();
            using (var streamReader = new StreamReader(stream))
            using (var jsonReader = new JsonTextReader(streamReader))
            {
                return jsonSerializer.Deserialize<Book[]>(jsonReader);
            }
        }

        [Benchmark]
        public async Task<Book[]> AspNetCoreProtobuf()
        {
            var request = new HttpRequestMessage(
                HttpMethod.Get,
                "http://localhost:8000/api/books");
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-protobuf"));
            var response = await httpClient.SendAsync(request);
            var stream = await response.Content.ReadAsStreamAsync();
            return Serializer.Deserialize<Book[]>(stream);
        }
    }
}

服を着るならこんなふうに(7)

さすがに7巻ともなるとネタも無くなりつつあるのか、服の買い時・雨具・洗濯・古着・髪型・服の捨て時と、ファッションの周辺知識が詰められていた。服の捨て時では、最初に買った黒のスキニーとお別れかという展開で、いよいよ最終回かと思ったが、まだ続きそう。