﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-金庆的专栏-随笔分类-9. 其它</title><link>http://www.cppblog.com/jinq0123/category/5141.html</link><description /><language>zh-cn</language><lastBuildDate>Sun, 24 Apr 2022 06:08:58 GMT</lastBuildDate><pubDate>Sun, 24 Apr 2022 06:08:58 GMT</pubDate><ttl>60</ttl><item><title>TortoiseGit is OK but GitExtensions fails</title><link>http://www.cppblog.com/jinq0123/archive/2022/04/24/229292.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Sun, 24 Apr 2022 06:01:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2022/04/24/229292.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/229292.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2022/04/24/229292.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/229292.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/229292.html</trackback:ping><description><![CDATA[    <h1 id="tortoisegit-is-ok-but-gitextensions-fails">TortoiseGit is OK but GitExtensions fails</h1>
    <p>GitExtensions and Git fail:</p>
    <pre>
      <code>"git" pull --progress "origin"
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
Done
Press Enter or Esc to exit...
</code>
    </pre>
    <p>But TortoiseGit is OK.</p>
    <pre>
      <code>git.exe pull --progress -v --no-rebase "origin"
From github.com:jinq0123/recastnavigation
= [up to date]      master      -&gt; origin/master
= [up to date]      release-2.0 -&gt; origin/release-2.0
Already up to date.
</code>
    </pre>
    <p>
      <code>ssh -T</code> shows that the reason is there is no id files:</p>
    <pre>
      <code>PS d:\github&gt; ssh -vT git@github.com
OpenSSH_for_Windows
debug1: Connecting to github.com [20.205.243.166] port 22.
debug1: Connection established.
debug1: identity file C:\\Users\\jinqing/.ssh/id_rsa type -1
debug1: identity file C:\\Users\\jinqing/.ssh/id_xmss-cert type -1
debug1: Authenticating to github.com:22 as 'git'
debug1: Host 'github.com' is known and matches the ED25519 host key.
debug1: Found key in C:\\Users\\jinqing/.ssh/known_hosts:2
debug1: Will attempt key: C:\\Users\\jinqing/.ssh/id_rsa
debug1: Will attempt key: C:\\Users\\jinqing/.ssh/id_xmss
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Trying private key: C:\\Users\\jinqing/.ssh/id_rsa
debug1: Trying private key: C:\\Users\\jinqing/.ssh/id_xmss
debug1: No more authentication methods to try.
git@github.com: Permission denied (publickey).
PS d:\github&gt;
</code>
    </pre>
    <p>Copy my RSA key file rsa.ppk that TortoiseGit uses as ~/.ssh/id_rsa:</p>
    <pre>
      <code>PS D:\jinqing\github_jinq\recastnavigation&gt; ssh -T git@github.com
Load key "C:\\Users\\jinqing/.ssh/id_rsa": invalid format
git@github.com: Permission denied (publickey).
PS D:\jinqing\github_jinq\recastnavigation&gt;
</code>
    </pre>
    <p>Open TortoiseGit/bin/puttygen.exe, load rsa.ppk, then Convensionis-&gt;Export OpenSSH key as ~/.ssh/id_rsa:</p>
    <pre>
      <code>PS D:\jinqing\github_jinq\recastnavigation&gt; ssh -T git@github.com
Hi jinq0123! You've successfully authenticated, ...
</code>
    </pre>
    <p>id_rsa begins with:</p>
    <pre>
      <code>-----BEGIN RSA PRIVATE KEY-----
</code>
    </pre>
<img src ="http://www.cppblog.com/jinq0123/aggbug/229292.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2022-04-24 14:01 <a href="http://www.cppblog.com/jinq0123/archive/2022/04/24/229292.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DeathVoteExpirationTimeout in Orleans</title><link>http://www.cppblog.com/jinq0123/archive/2021/12/08/217872.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Wed, 08 Dec 2021 01:43:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2021/12/08/217872.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217872.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2021/12/08/217872.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217872.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217872.html</trackback:ping><description><![CDATA[<h1id='deathvoteexpirationtimeout-in-orleans'>DeathVoteExpirationTimeout in Orleans</h1>
<p>(Jin Qing&#39;s Column, Dec., 2021)</p>
<p>Try to find out why Orleans need a DeathVoteExpirationTimeout config.</p>
<p><ahref='https://dotnet.github.io/orleans/docs/implementation/cluster_management.html#extension-to-totally-order-membership-views'target='_blank'class='url'>https://dotnet.github.io/orleans/docs/implementation/cluster_management.html#extension-to-totally-order-membership-views</a></p>
<pre><code>DeathVoteExpirationTimeout - Expiration time in seconds for death vote in the membership table. Default is 120 seconds
</code></pre>
<h2id='getfreshvotes'><code>GetFreshVotes</code></h2>
<p>DeathVoteExpirationTimeout is only used by GetFreshVotes(), which has 3 occurence:</p>
<pre><code>    class ClusterHealthMonitor
    {
        ...
        private UpdateMonitoredSilos(...)
        {
            ...
            bool isSuspected = candidateEntry.GetFreshVotes(now, DeathVoteExpirationTimeout).Count &gt; 0;
            ...
        }
    }
</code></pre>
<pre><code>    class LocalSiloHealthMonitor
    {
        ...
        private int CheckSuspectingNodes(DateTime now, List&lt;string&gt; complaints)
        {
            ...
            var freshVotes = membershipEntry.GetFreshVotes(now, DeathVoteExpirationTimeout);
            ...
        }
        ...
    }
</code></pre>
<pre><code>    class MembershipTableManager
    {
        ...
        public TryToSuspectOrKill(SiloAddress silo)
        {
            ...
            // get all valid (non-expired) votes
            var freshVotes = entry.GetFreshVotes(DateTime.UtcNow, DeathVoteExpirationTimeout);
            ...
        }
        ...
    }
</code></pre>
<p>GetFreshVotes() uses this expiration time to ignore old voter:</p>
<pre><code>        internal GetFreshVotes(DateTime now, TimeSpan expiration)
        {
            ...
            foreach (var voter in this.SuspectTimes)
            {
                var otherVoterTime = voter.Item2;
                if (now.Subtract(otherVoterTime) &lt; expiration)
                {
                    result.Add(voter);
                }
            }
            return result.ToImmutable();
        }
</code></pre>
<img src ="http://www.cppblog.com/jinq0123/aggbug/217872.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2021-12-08 09:43 <a href="http://www.cppblog.com/jinq0123/archive/2021/12/08/217872.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>How to delete local branches of GitExtension</title><link>http://www.cppblog.com/jinq0123/archive/2021/12/07/217870.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Tue, 07 Dec 2021 09:04:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2021/12/07/217870.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217870.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2021/12/07/217870.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217870.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217870.html</trackback:ping><description><![CDATA[<h1id='how-to-delete-local-branches-of-gitextension'>How to delete local branches of GitExtension</h1>
<p>(Jin Qing&#39;s Column, Dec., 2021)</p>
<p>GitExtension is a good tool. After a long time of usage, my branch list finally reaches over a full screen, and it is hard to select the branch I want.</p>
<p>GitExtension always remembers all the branches of remote and local, even they have been deleted.</p>
<p>I tried to find a way to delete these branches which are already merged, but resulted in futile.</p>
<p>It has a plugin names &quot;Delete obsolete branches&quot;, but I don&#39;t know how to use it.</p>
<p>Finally I renamed the work directory, and cloned a new one, which clears all the branches.
It seems that these branches are stored in local .git directory.
If let GitExt open the renamed directory, these branches reappears.</p>
<p>Reference:</p>
<ul>
<li><ahref='https://talk.plesk.com/threads/git-extension-doesnt-remove-deleted-branches.351838/'target='_blank'class='url'>https://talk.plesk.com/threads/git-extension-doesnt-remove-deleted-branches.351838/</a></li>
<li><ahref='https://plesk.uservoice.com/forums/184549-feature-suggestions/suggestions/39296707-git-extension-option-to-delete-branches'target='_blank'class='url'>https://plesk.uservoice.com/forums/184549-feature-suggestions/suggestions/39296707-git-extension-option-to-delete-branches</a></li>
</ul>
<img src ="http://www.cppblog.com/jinq0123/aggbug/217870.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2021-12-07 17:04 <a href="http://www.cppblog.com/jinq0123/archive/2021/12/07/217870.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Clustering provider in Orleans</title><link>http://www.cppblog.com/jinq0123/archive/2021/11/03/217852.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Wed, 03 Nov 2021 05:36:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2021/11/03/217852.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217852.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2021/11/03/217852.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217852.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217852.html</trackback:ping><description><![CDATA[<h1 id='clustering-provider-in-orleans'>Clustering provider in Orleans</h1>
<p>(Jin Qing&#39;s Column, Nov. 3, 2021)</p>
<p>When deployed to a cluster of nodes, 
Orleans internally implements a protocol to manage it&#39;s silos,
including discovery, failure and reconfigure, 
which is called cluster membership management.</p>
<p>Orleans has clustering membership providers for: Azure, SQL server, Zookeeper.</p>
<p>Clustering provider is one of key aspects of silo configuration.</p>
<h2 id='orleansclusteringkubernetes'>Orleans.Clustering.Kubernetes</h2>
<p><a href='https://github.com/OrleansContrib/Orleans.Clustering.Kubernetes'>OrleansContrib/Orleans.Clustering.Kubernetes</a>
is a clustering provider for running Orleans cluster on Kubernetes.</p>
<p>Tell silo to use Kubernetes as the Cluster Membership Provider:</p>
<pre><code class='language-c#' lang='c#'>var silo = new SiloBuilder()
        ...
        .UseKubeMembership()
        ...
        .Build();
</code></pre>
<h3 id='interface'>Interface</h3>
<p><code>UseKubeMembership()</code> instantiates a <code>KubeMembershipTable</code> which implements 
<a href='https://github.com/dotnet/orleans/blob/main/src/Orleans.Core/SystemTargetInterfaces/IMembershipTable.cs'><code>IMembershipTable</code></a>.</p>
<pre><code class='language-c#' lang='c#'>    public interface IMembershipTable
    {
        Task InitializeMembershipTable(bool tryInitTableVersion);
        Task DeleteMembershipTableEntries(string clusterId);
        Task CleanupDefunctSiloEntries(DateTimeOffset beforeDate);
        Task&lt;MembershipTableData&gt; ReadRow(SiloAddress key);
        Task&lt;MembershipTableData&gt; ReadAll();
        Task&lt;bool&gt; InsertRow(MembershipEntry entry, TableVersion tableVersion);
        Task&lt;bool&gt; UpdateRow(MembershipEntry entry, string etag, TableVersion tableVersion);
        Task UpdateIAmAlive(MembershipEntry entry);
    }
</code></pre>
<h3 id='implement'>Implement</h3>
<p><a href='https://github.com/OrleansContrib/Orleans.Clustering.Kubernetes/blob/master/src/Orleans.Clustering.Kubernetes/KubeMembershipTable.cs'><code>KubeMembershipTable</code></a>
access Kubernetes API server to read and write silo entry CRD.</p>
<ul>
<li><p>InitializeMembershipTable()</p>
<ul>
<li><p>TryInitClusterVersion()</p>
<ul>
<li>_kubeClient.GetNamespacedCustomObjectAsync</li>
<li>_kubeClient.CreateNamespacedCustomObjectAsync</li>
</ul>
</li>
</ul>
</li>
<li><p>DeleteMembershipTableEntries(string clusterId)</p>
<ul>
<li>_kubeClient.DeleteNamespacedCustomObjectAsync</li>
</ul>
</li>
<li><p>InsertRow(...)</p>
<ul>
<li>_kubeClient.GetNamespacedCustomObjectAsync</li>
<li>_kubeClient.ReplaceNamespacedCustomObjectAsync</li>
<li>_kubeClient.CreateNamespacedCustomObjectAsync</li>
</ul>
</li>
<li><p>ReadAll()</p>
<ul>
<li><p>GetClusterVersion()</p>
<ul>
<li>_kubeClient.ListNamespacedCustomObjectAsync</li>
</ul>
</li>
<li><p>GetSilos()</p>
<ul>
<li>_kubeClient.ListNamespacedCustomObjectAsync</li>
</ul>
</li>
</ul>
</li>
<li><p>ReadRow(SiloAddress key)</p>
<ul>
<li>_kubeClient.GetNamespacedCustomObjectAsync</li>
</ul>
</li>
<li><p>UpdateIAmAlive(MembershipEntry entry)</p>
<ul>
<li>_kubeClient.GetNamespacedCustomObjectAsync</li>
<li>_kubeClient.ReplaceNamespacedCustomObjectAsync</li>
</ul>
</li>
<li><p>UpdateRow(...)</p>
<ul>
<li>_kubeClient.ReplaceNamespacedCustomObjectAsync</li>
</ul>
</li>
<li><p>CleanupDefunctSiloEntries(DateTimeOffset beforeDate)</p>
<ul>
<li>_kubeClient.DeleteNamespacedCustomObjectAsync</li>
</ul>
</li>
</ul>
<p>The operators to NamespacedCustomObject are:</p>
<ul>
<li>Delete</li>
<li>Get</li>
<li>Create</li>
<li>Replace</li>
<li>List</li>
</ul>
<h3 id='crd'>CRD</h3>
<p>Two CRDs, <code>ClusterVersion</code> and <code>Silo</code> are defined in files:</p>
<ul>
<li>ClusterVersionCRD.yaml</li>
<li>SiloEntryCRD.yaml</li>
</ul>
<p>Custom resource objects are stored in etcd of Kubernetes.</p>
<img src ="http://www.cppblog.com/jinq0123/aggbug/217852.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2021-11-03 13:36 <a href="http://www.cppblog.com/jinq0123/archive/2021/11/03/217852.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Why Orleans' actor is virutal</title><link>http://www.cppblog.com/jinq0123/archive/2021/11/02/217850.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Tue, 02 Nov 2021 07:27:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2021/11/02/217850.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217850.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2021/11/02/217850.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217850.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217850.html</trackback:ping><description><![CDATA[<h1 id="why-orleans-actor-is-virutal">Why Orleans' actor is virutal</h1>
<p>(Jin Qing's Column, Nov. 2, 2021)</p>
<p>Virtual Actor is a concept invented by Microsoft Orleans, which is a framework of distributed actor.</p>
<p><a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/Orleans-MSR-TR-2014-41.pdf">Orleans: Distributed Virtual Actors for Programmability and Scalability</a> describes the virtual programming model.</p>
<p>The virtual actor is analogous to virtual memory. Virtual actors are mapped to physical arctors instances in the running servers. Virtualization of actors in Orleans has 4 facets:</p>
<ol start="">
     <li>
     <p>Perpetual existence</p>
     <ul>
         <li>Actors always exist, virtually</li>
         <li>Actors can not be created or destroied explicitly</li>
         <li>Server failure does not affect the actors' existence</li>
     </ul>
     </li>
     <li>
     <p>Automatic instantiation</p>
     <ul>
         <li>Activation: Orleans automatically create an actor</li>
         <li>A request triggers an activation if the actor doesn't exist</li>
         <li>Unused actors are automatically reclaimed</li>
     </ul>
     </li>
     <li>
     <p>Location transparency</p>
     <ul>
         <li>Applications don't know where the physical actor is</li>
         <li>Similar to virtual memory's "paged out" and mapping </li>
     </ul>
     </li>
     <li>
     <p>Automatic scale out</p>
     <ul>
         <li>
         <p>2 activation modes:</p>
         <ul>
             <li>
             <p>Single activation (default): Only one simultaneous actor is allowed</p>
             </li>
             <li>
             <p>Stateless worker: Many activations of an actor are created</p>
             <ul>
                 <li>to increase throughput</li>
             </ul>
             </li>
         </ul>
         </li>
     </ul>
     </li>
</ol>
<p>Actor viruliaztion greatly simplifes the programming, since it gets rid of the burden of actor lifecycle control.</p>
<p>&nbsp;</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/jinq0123/aggbug/217850.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2021-11-02 15:27 <a href="http://www.cppblog.com/jinq0123/archive/2021/11/02/217850.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>What comes after microservice?</title><link>http://www.cppblog.com/jinq0123/archive/2021/10/29/217843.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Fri, 29 Oct 2021 03:16:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2021/10/29/217843.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217843.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2021/10/29/217843.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217843.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217843.html</trackback:ping><description><![CDATA[<h1 id="what-comes-after-microservice-1">What comes after microservice?</h1>
<p>(Jin Qing's Column, Oct. 25, 2021)</p>
<p>Reading "The Evolution of Distributed Systems on Kubernetes" from Bilgin Ibryam.</p>
<p><a href="https://www.infoq.com/articles/distributed-systems-kubernetes/" target="_blank" class="url">https://www.infoq.com/articles/distributed-systems-kubernetes/</a></p>
<p>What are the purpose of projects like Dapr, Istio, Knative? How can they change the world?</p>
<h2 id="modern-distributed-systems">Modern distributed systems</h2>
<p>The needs of distributed systems:</p>
<ul>
<li><p>Business logic</p>
</li>
<li><p>Other</p>
<ul>
<li>Lifecycle: deploy, rollback, upgrade, scale, stop, isolate, config</li>
<li>Network: connect, circuit break, retry, timeout, load balance, discover, security, trace</li>
<li>Resource binding: to API, protocol, message, data format</li>
<li>State: stateless, stateful, store</li>
</ul>
</li>
</ul>
<h3 id="monolithic-architecture">Monolithic architecture</h3>
<p>enterprise service bus (ESB): not distributed</p>
<h3 id="cloud-native-architecture">Cloud-native architecture</h3>
<p>Kubernetes: Lifecycle</p>
<h3 id="service-mesh">Service Mesh</h3>
<p>Istio: Networking</p>
<h3 id="serverless">Serverless</h3>
<p>Knative: scale</p>
<h3 id="dapr">Dapr</h3>
<p>Networking, resource binding, state</p>
<h2 id="trends">Trends</h2>
<ul>
<li>Lifecycle: Kubernetes + operator</li>
<li>Networking: Envoy</li>
<li>Binding: Apache Camel</li>
<li>State: Cloudstate</li>
</ul>
<h2 id="multi-runtime-microservice">Multi-runtime microservice</h2>
<p>Write business logic as another runtime. Here runtime is a process?</p>
<h2 id="what-comes-after-microservice-2">What comes after microservice</h2>
<p>Faas is not the best. Multi-runtime microservice maybe is.</p>
<p><a href="https://www.infoq.com/articles/multi-runtime-microservice-architecture/">Mecha architecture</a></p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/jinq0123/aggbug/217843.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2021-10-29 11:16 <a href="http://www.cppblog.com/jinq0123/archive/2021/10/29/217843.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Rust Deref coercion example</title><link>http://www.cppblog.com/jinq0123/archive/2021/08/22/217786.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Sun, 22 Aug 2021 04:31:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2021/08/22/217786.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217786.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2021/08/22/217786.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217786.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217786.html</trackback:ping><description><![CDATA[# Rust Deref coercion example<br /><br />https://doc.rust-lang.org/std/ops/trait.Deref.html<br /><br />```<br />use std::ops::Deref;<br /><br />struct DerefExample&lt;T&gt; {<br />&nbsp;&nbsp;&nbsp; value: T<br />}<br /><br />impl&lt;T&gt; Deref for DerefExample&lt;T&gt; {<br />&nbsp;&nbsp;&nbsp; type Target = T;<br /><br />&nbsp;&nbsp;&nbsp; fn deref(&amp;self) -&gt; &amp;Self::Target {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;self.value<br />&nbsp;&nbsp;&nbsp; }<br />}<br /><br />let x = DerefExample { value: 'a' };<br />assert_eq!('a', *x);<br />```<br /><br />Deref coercion can be used in newtype:<br />```<br />struct MyI32(i32)<br /><br />impl Deref for MyI32 {<br />&nbsp;&nbsp;&nbsp; type Target = i32;<br />&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp;&nbsp; fn deref(&amp;self) -&gt; &amp;Self::Target {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;self.0<br />&nbsp;&nbsp;&nbsp; }<br />}<br />```<br /><img src ="http://www.cppblog.com/jinq0123/aggbug/217786.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2021-08-22 12:31 <a href="http://www.cppblog.com/jinq0123/archive/2021/08/22/217786.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Rust Error Return Check Policy</title><link>http://www.cppblog.com/jinq0123/archive/2021/08/09/217775.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Mon, 09 Aug 2021 09:08:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2021/08/09/217775.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217775.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2021/08/09/217775.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217775.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217775.html</trackback:ping><description><![CDATA[<h3>Error Return Check Policy<a href="https://www.ockam.io/learn/how-to-guides/contributing/ockam_rust_code_standard#error-return-check-policy" style="box-sizing: border-box; vertical-align: baseline; text-decoration-line: none; outline: none;"></a></h3><ul e801dxl0"="" style="box-sizing: border-box; vertical-align: baseline; list-style: initial; padding-left: 1.6rem; color: #3c454e; font-family: &quot;IBM Plex Sans&quot;, sans-serif; font-size: 16px; background-color: #ffffff;"><li style="box-sizing: border-box; vertical-align: baseline; margin-bottom: 0.4rem; margin-top: 0px; margin-left: 0px; padding-left: 0px; line-height: 1.5;">Never use&nbsp;<code e1q2in260"="" style="box-sizing: border-box; font-family: &quot;IBM Plex Sans&quot;, sans-serif; font-size: 1.2rem; vertical-align: baseline; background: #e6ecf1; border-radius: 0.2rem; padding: 0.5rem 1rem; font-weight: 700;">unwrap</code>&nbsp;on&nbsp;<span e9jhbpx0"="" style="box-sizing: border-box; vertical-align: baseline;">Result</span>. If the type is&nbsp;<em style="box-sizing: border-box; vertical-align: baseline;">Err</em>, it will panic and crash the program. The only exception is if it has already been checked for error previously or in test code.</li><li style="box-sizing: border-box; vertical-align: baseline; margin-bottom: 0.4rem; margin-top: 0px; margin-left: 0px; padding-left: 0px; line-height: 1.5;">Never use&nbsp;<code e1q2in260"="" style="box-sizing: border-box; font-family: &quot;IBM Plex Sans&quot;, sans-serif; font-size: 1.2rem; vertical-align: baseline; background: #e6ecf1; border-radius: 0.2rem; padding: 0.5rem 1rem; font-weight: 700;">unwrap</code>&nbsp;on&nbsp;<span e9jhbpx0"="" style="box-sizing: border-box; vertical-align: baseline;">Option</span>&nbsp;for the same reason if the type is&nbsp;<em style="box-sizing: border-box; vertical-align: baseline;">None</em>&nbsp;as&nbsp;<span e9jhbpx0"="" style="box-sizing: border-box; vertical-align: baseline;">Result</span>&nbsp;is&nbsp;<em style="box-sizing: border-box; vertical-align: baseline;">Err</em>.</li></ul><img src ="http://www.cppblog.com/jinq0123/aggbug/217775.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2021-08-09 17:08 <a href="http://www.cppblog.com/jinq0123/archive/2021/08/09/217775.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Rust visibility</title><link>http://www.cppblog.com/jinq0123/archive/2021/08/09/217774.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Mon, 09 Aug 2021 05:45:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2021/08/09/217774.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217774.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2021/08/09/217774.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217774.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217774.html</trackback:ping><description><![CDATA[From:&nbsp;<a href="https://doc.rust-lang.org/reference/visibility-and-privacy.html">Visibility and privacy - The Rust Reference (rust-lang.org)</a><br /><h2><a href="https://doc.rust-lang.org/reference/visibility-and-privacy.html#pubin-path-pubcrate-pubsuper-and-pubself" style="text-decoration-line: none;"><code style="font-size: unset; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important;">pub(in path)</code>,&nbsp;<code style="font-size: unset; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important;">pub(crate)</code>,&nbsp;<code style="font-size: unset; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important;">pub(super)</code>, and&nbsp;<code style="font-size: unset; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important;">pub(self)</code></a></h2><p style="line-height: 1.45em; font-family: &quot;Open Sans&quot;, sans-serif; font-size: 16px; background-color: #ffffff;">In addition to public and private, Rust allows users to declare an item as visible only within a given scope. The rules for&nbsp;<code style="font-size: 0.875em; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important; display: inline; overflow-x: initial; background: #f6f7f6; color: var(--inline-code-color); padding: 0.1em 0.3em; border-radius: 3px;">pub</code>&nbsp;restrictions are as follows:</p><ul style="line-height: 1.45em; font-family: &quot;Open Sans&quot;, sans-serif; font-size: 16px; background-color: #ffffff;"><li><code style="font-size: 0.875em; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important; display: inline; overflow-x: initial; background: #f6f7f6; color: var(--inline-code-color); padding: 0.1em 0.3em; border-radius: 3px;">pub(in path)</code>&nbsp;makes an item visible within the provided&nbsp;<code style="font-size: 0.875em; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important; display: inline; overflow-x: initial; background: #f6f7f6; color: var(--inline-code-color); padding: 0.1em 0.3em; border-radius: 3px;">path</code>.&nbsp;<code style="font-size: 0.875em; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important; display: inline; overflow-x: initial; background: #f6f7f6; color: var(--inline-code-color); padding: 0.1em 0.3em; border-radius: 3px;">path</code>&nbsp;must be an ancestor module of the item whose visibility is being declared.</li><li><code style="font-size: 0.875em; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important; display: inline; overflow-x: initial; background: #f6f7f6; color: var(--inline-code-color); padding: 0.1em 0.3em; border-radius: 3px;">pub(crate)</code>&nbsp;makes an item visible within the current crate.</li><li><code style="font-size: 0.875em; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important; display: inline; overflow-x: initial; background: #f6f7f6; color: var(--inline-code-color); padding: 0.1em 0.3em; border-radius: 3px;">pub(super)</code>&nbsp;makes an item visible to the parent module. This is equivalent to&nbsp;<code style="font-size: 0.875em; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important; display: inline; overflow-x: initial; background: #f6f7f6; color: var(--inline-code-color); padding: 0.1em 0.3em; border-radius: 3px;">pub(in super)</code>.</li><li><code style="font-size: 0.875em; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important; display: inline; overflow-x: initial; background: #f6f7f6; color: var(--inline-code-color); padding: 0.1em 0.3em; border-radius: 3px;">pub(self)</code>&nbsp;makes an item visible to the current module. This is equivalent to&nbsp;<code style="font-size: 0.875em; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important; display: inline; overflow-x: initial; background: #f6f7f6; color: var(--inline-code-color); padding: 0.1em 0.3em; border-radius: 3px;">pub(in self)</code>&nbsp;or not using&nbsp;<code style="font-size: 0.875em; font-family: &quot;Source Code Pro&quot;, Consolas, &quot;Ubuntu Mono&quot;, Menlo, &quot;DejaVu Sans Mono&quot;, monospace, monospace !important; display: inline; overflow-x: initial; background: #f6f7f6; color: var(--inline-code-color); padding: 0.1em 0.3em; border-radius: 3px;">pub</code>&nbsp;at all.</li></ul><img src ="http://www.cppblog.com/jinq0123/aggbug/217774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2021-08-09 13:45 <a href="http://www.cppblog.com/jinq0123/archive/2021/08/09/217774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Why does Rust check borrow even in single thread</title><link>http://www.cppblog.com/jinq0123/archive/2021/08/07/217773.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Sat, 07 Aug 2021 08:05:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2021/08/07/217773.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217773.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2021/08/07/217773.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217773.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217773.html</trackback:ping><description><![CDATA[<div style="background-color: #ffffff; font-family: Consolas, &quot;Courier New&quot;, monospace; line-height: 19px; white-space: pre;"><div><span style="color: #800000;font-weight: bold;">#&nbsp;Why&nbsp;does&nbsp;Rust&nbsp;check&nbsp;borrow&nbsp;even&nbsp;in&nbsp;single&nbsp;thread</span></div><br /><div>(Jin&nbsp;Qing's&nbsp;Column,&nbsp;Aug.&nbsp;7,&nbsp;2021)</div><br /><div>The&nbsp;Rust&nbsp;book&nbsp;says&nbsp;borrow&nbsp;checking&nbsp;is&nbsp;to&nbsp;prevent&nbsp;data&nbsp;race.</div><div>But&nbsp;the&nbsp;borrow&nbsp;checker&nbsp;forbids&nbsp;multiply&nbsp;mutable&nbsp;borrows&nbsp;even&nbsp;in&nbsp;the&nbsp;same&nbsp;thread.</div><div>Is&nbsp;there&nbsp;data&nbsp;race&nbsp;in&nbsp;single&nbsp;thread?</div><div>Why&nbsp;does&nbsp;the&nbsp;borrow&nbsp;checker&nbsp;forbid&nbsp;it&nbsp;in&nbsp;the&nbsp;same&nbsp;thread?</div><br /><div>[<span style="color: #a31515;">The&nbsp;Problem&nbsp;With&nbsp;Single-threaded&nbsp;Shared&nbsp;Mutability</span>](<span style="text-decoration-line: underline;">https://manishearth.github.io/blog/2015/05/17/the-problem-with-shared-mutability/</span>)</div><div>answers&nbsp;this&nbsp;question.</div><br /><div>It&nbsp;gaves&nbsp;2&nbsp;cases&nbsp;that&nbsp;shared&nbsp;mutability&nbsp;causes&nbsp;prolem.</div><div>One&nbsp;is&nbsp;Rust&nbsp;enum&nbsp;variable,&nbsp;which&nbsp;can&nbsp;has&nbsp;different&nbsp;inner&nbsp;type.</div><div>If&nbsp;the&nbsp;inner&nbsp;type&nbsp;changed,&nbsp;the&nbsp;references&nbsp;to&nbsp;the&nbsp;old&nbsp;data&nbsp;would&nbsp;be&nbsp;invalidated.</div><div>Another&nbsp;case&nbsp;is&nbsp;Iterator&nbsp;invalidation&nbsp;that&nbsp;the&nbsp;container's&nbsp;change&nbsp;can&nbsp;invalidate&nbsp;the&nbsp;Iterator.</div><br /><br /></div><img src ="http://www.cppblog.com/jinq0123/aggbug/217773.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2021-08-07 16:05 <a href="http://www.cppblog.com/jinq0123/archive/2021/08/07/217773.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>UE4 Blueprint Multiple Event BeginPlay</title><link>http://www.cppblog.com/jinq0123/archive/2021/07/31/217765.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Sat, 31 Jul 2021 07:19:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2021/07/31/217765.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217765.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2021/07/31/217765.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217765.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217765.html</trackback:ping><description><![CDATA[# Multiple Event BeginPlay<br /><br />(金庆的专栏 2021.7)<br /><br />How to do multiple actions on Event BeginPlay in UE4 Blueprints?<br /><br />Sams-Teach-Yourself-Unreal-Engine-4-Game-Development-in-24-Hours says:<br />```<br />Q. When I try to add a second event node, BeginPlay, the Editor shows me the first one already<br />placed in the Event Graph. Why does this happen?<br /><br />A. Some events, such as Event Tick and the BeginPlay event, can have only one instance per<br />Blueprint. <br />```<br /><br />https://forums.unrealengine.com/t/do-multiple-things-on-event-begin-play/411/10<br />```<br />The Sequence node allows for a single execution pulse to trigger a series of events in order. The node may have any number of outputs, all of which get called as soon as the Sequence node receives an input. They will always get called in order, but without any delay. To a typical user, the outputs will likely appear to have been triggered simultaneously.<br />```<br /><br />Youtube video: [How to do Multiple Actions on Event Begin Play Unreal Engine 4 Blueprints](https://www.youtube.com/watch?v=nqG-ztbs230)<img src ="http://www.cppblog.com/jinq0123/aggbug/217765.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2021-07-31 15:19 <a href="http://www.cppblog.com/jinq0123/archive/2021/07/31/217765.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>第9代游戏主机</title><link>http://www.cppblog.com/jinq0123/archive/2021/05/09/217680.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Sun, 09 May 2021 12:44:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2021/05/09/217680.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217680.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2021/05/09/217680.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217680.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217680.html</trackback:ping><description><![CDATA[# 第9代游戏主机<br /><br />原文：[Ninth generation of video game consoles](https://en.wikipedia.org/wiki/Ninth_generation_of_video_game_consoles)<br /><br />2020.11，微软(MS) Xbox Series X/S 和 Sony PlayStation 5 (PS5) 发布，标志着游戏主机进入第9代。<br /><br />和前代的 Xbox One 和 PS4 相比，新一代主机有可观的性能提升，支持实时光线跟踪，4K分辨率，目标帧率为60。<br />内部都使用了固态硬盘(SSD)。低配版没有光驱，仅支持网络和USB。<br /><br />定位上要胜过任天堂Switch和云游戏服务如 Stadia 和 Amazon Luna.<br /><br />## 背景<br /><br />第8代时间较长。因为摩尔定律，过去几代一般每代为5年时间，但是 MS 和 Sony 出了中间代产品 Xbox One X 和 PS4 Pro.<br /><br />2020.3 开始的 COVID-19 疫情影响也使新一代主机的发布延后了。<br /><br />## 主要主机<br /><br />### PS5<br /><br />有2个机型，基本型和数字型，数字型没有光驱较便宜，其他一样。<br />PS5和PS4的游戏兼容，只有少量游戏不支持，但是可以通过 PS Now 云游戏服务玩 PS4 游戏。<br /><br />### Xbox Series X/S<br /><br />MS 延继了双主机模式：高端的X系列和低端的S系列。S系列没有光驱。<br />两者都支持外部存储和Xbox Live在线分发。向后兼容以前的游戏，但不包括Kinect游戏。<br />MS鼓励开发商使用 Smart Delivery，把Xbox One游戏升级到Xbox Series X/S。<br /><br />## 其他主机<br /><br />* 任天堂 Switch<br />* 云游戏平台：Stadia, Amazon Luna, GeForce Now<img src ="http://www.cppblog.com/jinq0123/aggbug/217680.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2021-05-09 20:44 <a href="http://www.cppblog.com/jinq0123/archive/2021/05/09/217680.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C#异步方法返回void和Task的区别</title><link>http://www.cppblog.com/jinq0123/archive/2021/02/25/217618.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Thu, 25 Feb 2021 02:38:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2021/02/25/217618.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217618.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2021/02/25/217618.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217618.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217618.html</trackback:ping><description><![CDATA[# C#异步方法返回void和Task的区别<br /><br />(金庆的专栏 2021.2)<br /><br />如果异步(async关键字)方法有返回值，返回类型为T时，返回类型必然是 `Task&lt;T&gt;`。<br />但是如果没有返回值，异步方法的返回类型有2种，一个是返回 Task, 一个是返回 void：<br />```<br />&nbsp;&nbsp;&nbsp; public async Task CountDownAsync(int count)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = count; i &gt;= 0; i--)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await Task.Delay(1000); <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public async void CountDown(int count)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = count; i &gt;= 0; i--)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await Task.Delay(1000);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />```<br /><br />调用时，如果返回 Task, 但返回值被忽略时，VS 会用绿色波浪线警告：<br />```<br />&nbsp;&nbsp;&nbsp; CountDownAsync(3);<br />&nbsp;&nbsp;&nbsp; ~~~~~~~~~~~~~~~~~<br />```<br /><br />信息为：<br />```<br />(awaitable) Task AsyncExample.CountDownAsync(int count)<br /><br />Usage:<br />&nbsp;await CountDownAsync(...);<br /><br />Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.<br />```<br /><br />中文为：<br />```<br />CS4014:由于此调用不会等待，因此在此调用完成之前将会继续执行当前方法。请考虑将"await"运算符应用于调用结果。<br />```<br /><br />添加 await 后就正常了：<br />```<br />&nbsp;&nbsp;&nbsp; await CountDownAsync(3);<br />```<br /><br />如果调用者不是一个异步方法，因为只有在异步方法中才可以使用 await,<br />或者并不想在此等待，如想同时执行多个 CountDownAsync(),<br />就不能应用 await 来消除警告。<br /><br />此时可以改用 void 返回值的版本：<br />```<br />void Test()<br />{<br />&nbsp;&nbsp;&nbsp; ...<br />&nbsp;&nbsp;&nbsp; CountDown(3);<br />&nbsp;&nbsp;&nbsp; CountDown(3);<br />&nbsp;&nbsp;&nbsp; ...<br />}<br /><br />async void CountDown(int count)<br />{<br />&nbsp;&nbsp;&nbsp; for (int i = count; i &gt;= 0; i--)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await Task.Delay(1000);<br />&nbsp;&nbsp;&nbsp; }<br />}<br />```<br /><br />&gt; Never call `async Task` methods without also awaiting on the returned Task. If you don&#8217;t want to wait for the async behaviour to complete, you should call an `async void` method instead.<br /><br />摘自：http://www.stevevermeulen.com/index.php/2017/09/using-async-await-in-unity3d-2017/<br /><br />CountDown() 可以直接调用 CountDownAsync() 实现：<br />```<br />async void CountDown(int count)<br />{<br />&nbsp;&nbsp;&nbsp; await CountDownAsync(count);<br />}<br />```<br /><br />使用下划线变量忽略异步方法的返回值也可以消除警告：<br />```<br />void Test()<br />{<br />&nbsp;&nbsp;&nbsp; ...<br />&nbsp;&nbsp;&nbsp; _ = CountDownAsync(3);<br />&nbsp;&nbsp;&nbsp; _ = CountDownAsync(3);<br />&nbsp;&nbsp;&nbsp; ...<br />}<br />```<br /><br />但是这样同时也会忽略 CountDownAsync() 中的异常。如以下异常会被忽略。<br /><br />```<br />void Test()<br />{<br />&nbsp;&nbsp;&nbsp; ...<br />&nbsp;&nbsp;&nbsp; _ = CountDownAsync(3);<br />&nbsp;&nbsp;&nbsp; ...<br />}<br /><br />async Task CountDownAsync(int count)<br />{<br />&nbsp;&nbsp;&nbsp; for (int i = count; i &gt;= 0; i--)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await Task.Delay(1000); <br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; throw new Exception();<br />}<br />```<br /><br />如果是调用返回 void 的异步方法，Unity 会报错：<br />```<br />Exception: Exception of type 'System.Exception' was thrown.<br />```<br /><br />## 对 Async 后缀的说明<br /><br />```<br />You could say that the Async suffix convention is to communicate to the API user that the method is awaitable. For a method to be awaitable, it must return Task for a void, or Task&lt;T&gt; for a value-returning method, which means only the latter can be suffixed with Async.<br />```<br /><br />摘自：https://stackoverflow.com/questions/15951774<br /><br />grpc 生成的代码中，异步请求返回了一个 AsyncCall 对象，AsyncCall 实现了 GetAwaiter() 接口：<br />```<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public virtual grpc::AsyncUnaryCall&lt;global::Routeguide.Feature&gt; GetFeatureAsync(global::Routeguide.Point request, ...)<br />```<br /><br />可以这样调用并等待：<br />```<br />&nbsp;&nbsp;&nbsp; var resp = await client.GetFeatureAsync(req);<br />```<br /><br />虽然返回类型不是`Task&lt;&gt;`, 但是可等待，所以添加了 Async 后缀。<br /><img src ="http://www.cppblog.com/jinq0123/aggbug/217618.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2021-02-25 10:38 <a href="http://www.cppblog.com/jinq0123/archive/2021/02/25/217618.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>避免RPC回调死锁</title><link>http://www.cppblog.com/jinq0123/archive/2020/09/19/217455.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Sat, 19 Sep 2020 05:56:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2020/09/19/217455.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217455.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2020/09/19/217455.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217455.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217455.html</trackback:ping><description><![CDATA[<h1> 避免RPC回调死锁</h1><br />(金庆的专栏 2020.9)<br /><br />今天发现一例 RPC 回调死锁，现象为一串相关的 RPC 调用全部超时失败。<br /><br />A 调用 B 的 RPC, B 再回调 A, 形成一个 RPC 调用环后，<br />如果 A 调用 B 时先加了一个锁，然后 B 回调 A 时又需要这个锁，<br />而此时 A 正在等待 B 的 RPC 返回，之后才会释放锁，<br />这样就形成了死锁。这个 RPC 调用环最终会全部超时失败。<br /><br />这个调用环可能会涉及多个服务，如 A-&gt;B-&gt;C-&gt;...-&gt;A。<br /><br />避免回调死锁有以下方法。<br /><br />## 避免回调，不要有 RPC 调用环<br /><br />一般的服务依赖应该都是无环的。<br />可以画一个服务依赖图，如果没有形成调用环，就可以放心不会有死锁。<br /><br />## RPC 调用时不要加锁<br /><br />一般进入某个 RPC 处理时，会锁住相关的资源，直到处理完成。<br />如果处理过程中需要向外发出 RPC 请求，应该先释放锁，待请求完成后再次获取锁。<br />如果请求过程中需要禁止其他协程操作相关资源，也可以不释放锁，<br />但锁的使用上应该允许加锁失败，不要等待锁。<br /><br />锁应该是能够快速释放的。如果需要加锁去执行一个长时间的操作，<br />这个锁的设计可能需要重新考虑。<br /><br />## 打断调用链<br /><br />有时候 RPC 调用链不必是阻塞等待的。<br />如通知性的，无返回值的RPC, 不需要等待他返回，可以开一个新的协程去执行 RPC.<br />父RPC等待子RPC返回，改为父RPC直接返回，子RPC后台执行，即断开 RPC 的调用依赖。<br />RPC 调用链断开成多段后，调用循环的可能性就大大下降了。<br /><img src ="http://www.cppblog.com/jinq0123/aggbug/217455.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2020-09-19 13:56 <a href="http://www.cppblog.com/jinq0123/archive/2020/09/19/217455.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C# tolua 之间互传 byte[]</title><link>http://www.cppblog.com/jinq0123/archive/2020/08/19/217426.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Wed, 19 Aug 2020 01:03:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2020/08/19/217426.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217426.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2020/08/19/217426.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217426.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217426.html</trackback:ping><description><![CDATA[# C# tolua 之间互传 byte[]<br /><br />(金庆的专栏 2020.8)<br /><br />lua中不区分 string 和 byte[], 而在 C# 中 string 和 byte[] 之间转换涉及编码。<br /><br />C# 中一般这样转：<br /><br />string类型转成byte[]：<br />```<br />byte[] byteArray = System.Text.Encoding.Default.GetBytes(str);<br />```<br />byte[]转成string：<br />```<br />string str = System.Text.Encoding.Default.GetString(byteArray);<br />```<br /><br />Default 编码是本机当前所用编码，还可以用 ASCII, UTF8 等其他编码。<br /><br />测了在 lua 中读入一块2进制数据，调用 tolua 导出的一个方法，如：<br />```<br />public void Print(string s)<br />{<br />&nbsp;&nbsp;&nbsp; byte[] buf = Systen.Text.Encoding.Default.GetBytes(s);<br />&nbsp;&nbsp;&nbsp; Debug.Log(Bitconvert.ToString(buf));<br />}<br />```<br />测了 Default, ASCII, UTF8, ISO-8859-1(Latin-1), Unicode 发现得到的 byte[] 会出错。<br /><br />也试了 [C#中使用Buffer.BlockCopy()方法将string转换为byte array的方法](https://www.cnblogs.com/ChineseMoonGod/p/5689526.html)<br />发现 tolua 传到 C# 的 string 已经是编码过的，直接复制也是错的。<br /><br />xlua 也有相同问题，[Unity xlua 从lua传递byte[]数据到C#](https://www.jianshu.com/p/63987134c1ba)<br />使用 MemoryStream对象来传递byte[]数据，确实有点绕。<br /><br />tolua 中有个 LuaByteBuffer，可以用来传递 byte[].<br />[tolua#中的LuaByteBuffer类](http://bbs.ulua.org/article/ulua/toluazhongdeluabytebufferlei.html)<br /><br />从 lua 传 byte[] 到 C#, 只需要将参数 string 改为 LuaByteBuffer:<br />```<br />public void Print(LuaByteBuffer luaByteBuffer)<br />{<br />&nbsp;&nbsp;&nbsp; byte[] buf = luaByteBuffer.buffer;<br />&nbsp;&nbsp;&nbsp; Debug.Log(Bitconvert.ToString(buf));<br />}<br />```<br /><br />更正确又简单的方法是用 LuaByteBufferAttribute:<br />```<br />[LuaByteBufferAttribute]<br />public void Print(byte[] buf)<br />{<br />&nbsp;&nbsp;&nbsp; Debug.Log(Bitconvert.ToString(buf));<br />}<br />```<br /><br />最终发现不需要 LuaByteBufferAttribute，直接用 byte[] 就行：<br />```<br />public void Print(byte[] buf)<br />{<br />&nbsp;&nbsp;&nbsp; Debug.Log(Bitconvert.ToString(buf));<br />}<br />```<br /><br />C# 传 byte[] 到 lua, 默认为 "System.Byte[]"(userdata)，可以用 tostring() 转为 lua string:<br />```Lua<br />s = tolua.tolstring(result)<br />```<br /><br />如果可以，应该给数据加上标签[LuaByteBufferAttribute]，这样传到 lua 就是 string。<br />或者在C#建一个LuaByteBuffer把byte[]传给lua。<br /><br /><img src ="http://www.cppblog.com/jinq0123/aggbug/217426.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2020-08-19 09:03 <a href="http://www.cppblog.com/jinq0123/archive/2020/08/19/217426.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Unity使用异步grpc</title><link>http://www.cppblog.com/jinq0123/archive/2020/06/22/217366.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Mon, 22 Jun 2020 07:19:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2020/06/22/217366.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/217366.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2020/06/22/217366.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/217366.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/217366.html</trackback:ping><description><![CDATA[# Unity使用异步grpc<br /><br />(金庆的专栏 2020.6)<br /><br />Unity 保证 async 方法运行在主线程中，所以用异步方式调用 grpc 可以大大简化网络通信的代码。<br /><br />以下示例中将 grpc 的 RouteGuide 示例移到 Unity 中运行。<br />https://github.com/grpc/grpc/tree/master/examples/csharp/RouteGuide<br /><br />其中 Main() 中的代码移到 Start() 中运行，阻塞调用改成异步调用, GetFeature() 改成 GetFeatureAsync()。<br /><br />完整代码见：https://gitee.com/jinq0123/unity-grpc-async<br /><br />```<br /><span style="color: #800000; font-family: Courier;">using Grpc.Core;</span><br /><span style="color: #800000; font-family: Courier;">using Routeguide;</span><br /><span style="color: #800000; font-family: Courier;">using System;</span><br /><span style="color: #800000; font-family: Courier;">using System.Collections;</span><br /><span style="color: #800000; font-family: Courier;">using System.Collections.Generic;</span><br /><span style="color: #800000; font-family: Courier;">using UnityEngine;</span><br /><span style="color: #800000; font-family: Courier;">using static Routeguide.Program;</span><br /><br /><span style="color: #800000; font-family: Courier;">public class Test : MonoBehaviour</span><br /><span style="color: #800000; font-family: Courier;">{</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp; // Start is called before the first frame update</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp; async void Start()</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp; {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var channel = new Channel("127.0.0.1:50052", ChannelCredentials.Insecure);</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var client = new RouteGuideClient(new RouteGuide.RouteGuideClient(channel));</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Looking for a valid feature</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await client.GetFeatureAsync(409146138, -746188906);</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Feature missing.</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await client.GetFeatureAsync(0, 0);</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Looking for features between 40, -75 and 42, -73.</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await client.ListFeatures(400000000, -750000000, 420000000, -730000000);</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Record a few randomly selected points from the features file.</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await client.RecordRoute(RouteGuideUtil.LoadFeatures(), 10);</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Send and receive some notes.</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await client.RouteChat();</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await channel.ShutdownAsync();</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Debug.Log("End of test.");</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp;&nbsp; }</span><br /><span style="color: #800000; font-family: Courier;">}</span><br />```<br /><img src ="http://www.cppblog.com/jinq0123/aggbug/217366.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2020-06-22 15:19 <a href="http://www.cppblog.com/jinq0123/archive/2020/06/22/217366.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SVN重命名时不要更改内容</title><link>http://www.cppblog.com/jinq0123/archive/2019/09/20/216848.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Fri, 20 Sep 2019 06:45:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2019/09/20/216848.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/216848.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2019/09/20/216848.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/216848.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/216848.html</trackback:ping><description><![CDATA[SVN重命名时不要更改内容<br /><br />(金庆的专栏 2019.9)<br /><br />svn rename 某个文件并更改内容后提交，历史就会丢失。<br />如果 svn rename 后不改内容，立即提交，就可以延续原有历史。<br />所以改名这样的操作应该独立提交一次。<br /><br />git 改名就强大多了，会比较内容确定其原来的文件。但更改太多也会判断出错。<br /><img src ="http://www.cppblog.com/jinq0123/aggbug/216848.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2019-09-20 14:45 <a href="http://www.cppblog.com/jinq0123/archive/2019/09/20/216848.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>解决vs2017无法安装问题</title><link>http://www.cppblog.com/jinq0123/archive/2018/11/12/216052.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Mon, 12 Nov 2018 02:19:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2018/11/12/216052.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/216052.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2018/11/12/216052.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/216052.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/216052.html</trackback:ping><description><![CDATA[<div>解决vs2017无法安装问题<br /><br />(金庆的专栏 2018.11)<br /><br />从官网下载了 vs_community__1600125377.1541561546.exe，但是运行安装时无法出现产品选择界面。<br /><br />查看 Temp 目录下的日志，没有发现错误。<br /><br />搜索一下，发现有大量的同类错误：<br /><br />* VS : How to fix stuck Visual Studio Community installation problem<br />&nbsp;&nbsp; &nbsp;https://www.howtosolutions.net/2015/08/solving-installation-is-stuck-problem-in-visual-studio-community-edition/<br />* Resolving Installation Issues with Visual Studio 2017<br />&nbsp;&nbsp; &nbsp;http://rion.io/2017/02/17/resolving-installation-issues-with-visual-studio-2017/<br />* Unable to start vs_installer.exe to install VS2017 Community<br />&nbsp;&nbsp; &nbsp;https://social.msdn.microsoft.com/Forums/vstudio/en-US/fc8f5a04-8687-48dd-987e-1cfac67566a1/unable-to-start-vsinstallerexe-to-install-vs2017-community?forum=vssetup<br />* VS 2017 Installer quits before starting<br />&nbsp;&nbsp; &nbsp;https://developercommunity.visualstudio.com/content/problem/8993/vs-2017-installer-quits-before-starting.html<br />* VS2017无法进入安装界面问题的解决方法 - 厚积薄发，持之以恒 - CSDN博客<br />&nbsp;&nbsp; &nbsp;https://blog.csdn.net/qq951127336/article/details/71036868<br />* VS2017安装时自动退出_yanggy_新浪博客<br />&nbsp;&nbsp; &nbsp;http://blog.sina.com.cn/s/blog_702b606a0102y6n3.html<br /><br />但是都没有用。<br /><br />从以上方案中了解到，`C:\Program Files (x86)\Microsoft Visual Studio\Installer`可能存在坏文件，所以无法安装。<br />但是清理之后重新下载，仍然是同样情况。<br /><br />试着运行了其中的 vs_installer.exe，弹出界面说有兼容性错误，可以选择修复。<br />修复之后，vs_installer.exe 就出现产品选择界面了。<br /><br />然后再运行 vs_community__1600125377.1541561546.exe，就可以出现产品选择界面了。<br /><br />问题可能是这个 Installer 安装不对，可能是选择了错误的版本，删除后重新下载还是一样，还好可以手动修复一下。<br /><br />现在正在安装 vs2017.<br /></div><img src ="http://www.cppblog.com/jinq0123/aggbug/216052.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2018-11-12 10:19 <a href="http://www.cppblog.com/jinq0123/archive/2018/11/12/216052.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>docker build 设置代理</title><link>http://www.cppblog.com/jinq0123/archive/2018/09/26/215949.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Wed, 26 Sep 2018 09:37:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2018/09/26/215949.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/215949.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2018/09/26/215949.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/215949.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/215949.html</trackback:ping><description><![CDATA[<div>docker build 设置代理<br /><br />(金庆的专栏 2018.9)<br /><br />Dockerfile.frontendapi 中有 `RUN go get`, 需要设置代理。<br /><br /><span style="color: #0000ff;">docker build . -f Dockerfile.frontendapi \</span><br /><span style="color: #0000ff;">&nbsp;&nbsp; &nbsp;-t registry.cn-shanghai.aliyuncs.com/jinq0123/openmatch-frontendapi:dev \</span><br /><span style="color: #0000ff;">&nbsp;&nbsp; &nbsp;--network host \</span><br /><span style="color: #0000ff;">&nbsp;&nbsp; &nbsp;--build-arg HTTP_PROXY=http://127.0.0.1:1080 \</span><br /><span style="color: #0000ff;">&nbsp;&nbsp; &nbsp;--build-arg HTTPS_PROXY=http://127.0.0.1:1080</span><br /><br />因为 docker build 会在一个容器内执行，所以须指定 network 为 host, 使之可以连接本机的代理。<br /><br />参考：<br />https://stackoverflow.com/questions/22179301/how-do-you-run-apt-get-in-a-dockerfile-behind-a-proxy<br /><br /><span style="color: #0000ff;">[root@pppdc9prda2y java]# docker build </span><br /><span style="color: #0000ff;">&nbsp; --build-arg https_proxy=$HTTP_PROXY --build-arg http_proxy=$HTTP_PROXY </span><br /><span style="color: #0000ff;">&nbsp; --build-arg HTTP_PROXY=$HTTP_PROXY --build-arg HTTPS_PROXY=$HTTP_PROXY </span><br /><span style="color: #0000ff;">&nbsp; --build-arg NO_PROXY=$NO_PROXY&nbsp; --build-arg no_proxy=$NO_PROXY -t java .</span><br /> </div><img src ="http://www.cppblog.com/jinq0123/aggbug/215949.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2018-09-26 17:37 <a href="http://www.cppblog.com/jinq0123/archive/2018/09/26/215949.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>kubernetes导出有状态服务</title><link>http://www.cppblog.com/jinq0123/archive/2018/07/14/215783.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Sat, 14 Jul 2018 03:43:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2018/07/14/215783.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/215783.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2018/07/14/215783.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/215783.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/215783.html</trackback:ping><description><![CDATA[<div>kubernetes导出有状态服务<br /><br />(金庆的专栏 2018.7)<br /><br />网游服务器中的房间服务器是有状态服务器，可以用 kubernetes statefulset 开启多个实例。<br /><br />为了让客户端能够直连房间服务器，除了 statefulset 要求的 headless 服务，<br />还须为每个实例创建 NodePort 类型的服务, 并且选择Pod和禁止转发。<br /><br />下面 bootcamp.yml 先创建了 bootcamp headless 服务(clusterIP: None), <br />又创建了 bootcamp StatefulSet, 实例个数为 2.<br />然后创建 bootcamp-0,1,2 服务，分别对应 bootcamp-0,1,2 pod.<br /><br />服务个数大于实例个数，是想测试下服务没有对应的实例时的表现。<br /><br />网游中的匹配服务器将分配一个房间给客户端，列举 bootcamp-0,1,2 pod 所在节点的外网 IP,<br />连同对应服务的端口，发送给客户端，让客户端直连。<br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ cat bootcamp.yml </span><br /><span style="color: #0000ff; font-family: Courier;">apiVersion: v1</span><br /><span style="color: #0000ff; font-family: Courier;">kind: Service</span><br /><span style="color: #0000ff; font-family: Courier;">metadata:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; name: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; namespace: jq</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; labels:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; name: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">spec:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; ports:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; - port: 8080</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; clusterIP: None&nbsp; # StatefulSet要求Headless服务</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; selector:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; app: bootcamp&nbsp; # 选择 bootcamp 应用</span><br /><br /><span style="color: #0000ff; font-family: Courier;">---</span><br /><span style="color: #0000ff; font-family: Courier;">apiVersion: apps/v1beta1</span><br /><span style="color: #0000ff; font-family: Courier;">kind: StatefulSet</span><br /><span style="color: #0000ff; font-family: Courier;">metadata:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; name: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; namespace: jq</span><br /><span style="color: #0000ff; font-family: Courier;">spec:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; serviceName: bootcamp&nbsp; # 上面的 Headless 服务名</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; replicas: 2</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; template:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; metadata:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; labels:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app: bootcamp&nbsp; # 应用名，与服务中的 selector 对应</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; spec:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; containers:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - name: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; image: docker.io/jocatalin/kubernetes-bootcamp:v1</span><br /><br /><span style="color: #0000ff; font-family: Courier;">---</span><br /><span style="color: #0000ff; font-family: Courier;">kind: Service</span><br /><span style="color: #0000ff; font-family: Courier;">apiVersion: v1</span><br /><span style="color: #0000ff; font-family: Courier;">metadata:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; name: bootcamp-0</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; namespace: jq</span><br /><span style="color: #0000ff; font-family: Courier;">spec:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; type: NodePort&nbsp; # 对外服务</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; externalTrafficPolicy: Local&nbsp; # 不要转发到其他节点</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; selector:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; app: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; statefulset.kubernetes.io/pod-name: bootcamp-0&nbsp; # 选择 pod</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; ports:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; - protocol: TCP</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; nodePort: 30880&nbsp; # 对外端口</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; port: 8080</span><br /><span style="color: #0000ff; font-family: Courier;">---</span><br /><span style="color: #0000ff; font-family: Courier;">kind: Service</span><br /><span style="color: #0000ff; font-family: Courier;">apiVersion: v1</span><br /><span style="color: #0000ff; font-family: Courier;">metadata:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; name: bootcamp-1</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; namespace: jq</span><br /><span style="color: #0000ff; font-family: Courier;">spec:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; type: NodePort</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; externalTrafficPolicy: Local</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; selector:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; app: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; statefulset.kubernetes.io/pod-name: bootcamp-1</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; ports:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; - protocol: TCP</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; nodePort: 30881</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; port: 8080</span><br /><span style="color: #0000ff; font-family: Courier;">---</span><br /><span style="color: #0000ff; font-family: Courier;">kind: Service</span><br /><span style="color: #0000ff; font-family: Courier;">apiVersion: v1</span><br /><span style="color: #0000ff; font-family: Courier;">metadata:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; name: bootcamp-2</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; namespace: jq</span><br /><span style="color: #0000ff; font-family: Courier;">spec:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; type: NodePort</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; externalTrafficPolicy: Local</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; selector:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; app: bootcamp</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; statefulset.kubernetes.io/pod-name: bootcamp-2</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; ports:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; - protocol: TCP</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; nodePort: 30882</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp;&nbsp;&nbsp; port: 8080</span><br /><br />因为 statefulset 的每个实例有不同的标签，所以可以为服务选择一个实例。<br /><br />利用 externalTrafficPolicy: Local 设置来禁止转发。<br />参考 service.spec.externalTrafficPolicy 的说明：<br /><br />https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-type-nodeport<br /><br />Setting service.spec.externalTrafficPolicy to the value Local will only proxy requests to local endpoints, never forwarding traffic to other nodes and thereby preserving the original source IP address. If there are no local endpoints, packets sent to the node are dropped, ...<br /><br />因为有可能多个Pod开在同一节点上，所以对外端口设成了不同的 30880-30882。<br />如果限制每个节点只开一个实例，则对外端口可以设成同一个。<br /><br />创建服务：<br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl apply -f bootcamp.yml </span><br /><span style="color: #0000ff; font-family: Courier;">service "bootcamp" created</span><br /><span style="color: #0000ff; font-family: Courier;">statefulset.apps "bootcamp" created</span><br /><span style="color: #0000ff; font-family: Courier;">service "bootcamp-0" created</span><br /><span style="color: #0000ff; font-family: Courier;">service "bootcamp-1" created</span><br /><span style="color: #0000ff; font-family: Courier;">service "bootcamp-2" created</span><br /><br />服务如下：<br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl get service -n jq</span><br /><span style="color: #0000ff; font-family: Courier;">NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TYPE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CLUSTER-IP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EXTERNAL-IP&nbsp;&nbsp; PORT(S)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AGE</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp&nbsp;&nbsp;&nbsp;&nbsp; ClusterIP&nbsp;&nbsp; None&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;none&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8080/TCP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3m</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-0&nbsp;&nbsp; NodePort&nbsp;&nbsp;&nbsp; 10.96.128.137&nbsp;&nbsp;&nbsp; &lt;none&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8080:30880/TCP&nbsp;&nbsp; 3m</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-1&nbsp;&nbsp; NodePort&nbsp;&nbsp;&nbsp; 10.109.2.56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;none&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8080:30881/TCP&nbsp;&nbsp; 3m</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-2&nbsp;&nbsp; NodePort&nbsp;&nbsp;&nbsp; 10.102.181.193&nbsp;&nbsp; &lt;none&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8080:30882/TCP&nbsp;&nbsp; 3m</span><br /><br />2个实例：<br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl get pod -n jq -o wide</span><br /><span style="color: #0000ff; font-family: Courier;">NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; READY&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp;&nbsp; RESTARTS&nbsp;&nbsp; AGE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NODE</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-0&nbsp;&nbsp; 1/1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4m&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10.244.0.42&nbsp;&nbsp; host-10-240-79-10</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-1&nbsp;&nbsp; 1/1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4m&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10.244.1.63&nbsp;&nbsp; host-10-240-79-11</span><br /><br />访问服务必须指定节点，不会自动转发：<br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.10:30880</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-0 | v=1</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.10:30881</span><br /><span style="color: #0000ff; font-family: Courier;">curl: (7) Failed connect to 10.240.79.10:30881; Connection timed out</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30880</span><br /><span style="color: #0000ff; font-family: Courier;">curl: (7) Failed connect to 10.240.79.11:30880; Connection timed out</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30881</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-1 | v=1</span><br /><br />没有负载均衡，30881端口总是访问 bootcamp-1：<br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30881</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-1 | v=1</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30881</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-1 | v=1</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30881</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-1 | v=1</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30881</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-1 | v=1</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30881</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-1 | v=1</span><br /><br />也可以从外网访问.<br /><br />30882 端口无法连接：<br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.11:30882</span><br /><span style="color: #0000ff; font-family: Courier;">curl: (7) Failed connect to 10.240.79.11:30882; Connection refused</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.10:30882</span><br /><span style="color: #0000ff; font-family: Courier;">curl: (7) Failed connect to 10.240.79.10:30882; Connection refused</span><br /><br />3个端口都有监听：<br /><br /><span style="color: #0000ff; font-family: Courier;">[root@host-10-240-79-11 tmp]# netstat -ntl | grep 3088</span><br /><span style="color: #0000ff; font-family: Courier;">tcp6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 :::30881&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :::*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LISTEN&nbsp;&nbsp;&nbsp; &nbsp;</span><br /><span style="color: #0000ff; font-family: Courier;">tcp6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 :::30882&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :::*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LISTEN&nbsp;&nbsp;&nbsp; &nbsp;</span><br /><span style="color: #0000ff; font-family: Courier;">tcp6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 :::30880&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :::*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LISTEN&nbsp;&nbsp;&nbsp; &nbsp;</span><br /><br />iptables-save 输出如下, 其中 10.244是Pod的网段。<br /><br />没有实例运行的节点上，会丢弃请求：<br /><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -s 127.0.0.0/8 -p tcp -m comment --comment "jq/bootcamp-1:" -m tcp --dport 30881 -j KUBE-MARK-MASQ</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -p tcp -m comment --comment "jq/bootcamp-1:" -m tcp --dport 30881 -j KUBE-XLB-LJXDQ4W47M42IZBH</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -s 127.0.0.0/8 -p tcp -m comment --comment "jq/bootcamp-0:" -m tcp --dport 30880 -j KUBE-MARK-MASQ</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -p tcp -m comment --comment "jq/bootcamp-0:" -m tcp --dport 30880 -j KUBE-XLB-U5NEOQT6R5WSBVOH</span><br /><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-LJXDQ4W47M42IZBH -s 10.244.0.0/16 -m comment --comment "Redirect pods trying to reach external loadbalancer VIP to clusterIP" -j KUBE-SVC-LJXDQ4W47M42IZBH</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-LJXDQ4W47M42IZBH -m comment --comment "jq/bootcamp-1: has no local endpoints" -j KUBE-MARK-DROP</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-U5NEOQT6R5WSBVOH -s 10.244.0.0/16 -m comment --comment "Redirect pods trying to reach external loadbalancer VIP to clusterIP" -j KUBE-SVC-U5NEOQT6R5WSBVOH</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-U5NEOQT6R5WSBVOH -m comment --comment "jq/bootcamp-0: has no local endpoints" -j KUBE-MARK-DROP</span><br /><br />有实例运行的节点上会转发给 Pod 8080：<br /><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -s 127.0.0.0/8 -p tcp -m comment --comment "jq/bootcamp-0:" -m tcp --dport 30880 -j KUBE-MARK-MASQ</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -p tcp -m comment --comment "jq/bootcamp-0:" -m tcp --dport 30880 -j KUBE-XLB-U5NEOQT6R5WSBVOH</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -s 127.0.0.0/8 -p tcp -m comment --comment "jq/bootcamp-1:" -m tcp --dport 30881 -j KUBE-MARK-MASQ</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-NODEPORTS -p tcp -m comment --comment "jq/bootcamp-1:" -m tcp --dport 30881 -j KUBE-XLB-LJXDQ4W47M42IZBH</span><br /><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-LJXDQ4W47M42IZBH -s 10.244.0.0/16 -m comment --comment "Redirect pods trying to reach external loadbalancer VIP to clusterIP" -j KUBE-SVC-LJXDQ4W47M42IZBH</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-LJXDQ4W47M42IZBH -m comment --comment "Balancing rule 0 for jq/bootcamp-1:" -j KUBE-SEP-LJQA4WUIKJUQ5ALU</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-U5NEOQT6R5WSBVOH -s 10.244.0.0/16 -m comment --comment "Redirect pods trying to reach external loadbalancer VIP to clusterIP" -j KUBE-SVC-U5NEOQT6R5WSBVOH</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-XLB-U5NEOQT6R5WSBVOH -m comment --comment "jq/bootcamp-0: has no local endpoints" -j KUBE-MARK-DROP</span><br /><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-SEP-LJQA4WUIKJUQ5ALU -s 10.244.1.63/32 -m comment --comment "jq/bootcamp-1:" -j KUBE-MARK-MASQ</span><br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-SEP-LJQA4WUIKJUQ5ALU -p tcp -m comment --comment "jq/bootcamp-1:" -m tcp -j DNAT --to-destination 10.244.1.63:8080</span><br /><br />30882 端口无法连接<br /><span style="color: #0000ff; font-family: Courier;">-A KUBE-EXTERNAL-SERVICES -p tcp -m comment --comment "jq/bootcamp-2: has no endpoints" -m addrtype --dst-type LOCAL -m tcp --dport 30882 -j REJECT --reject-with icmp-port-unreachable</span><br /><br />测试下扩容：<br /><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl get statefulset -n jq</span><br /><span style="color: #0000ff; font-family: Courier;">NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DESIRED&nbsp;&nbsp; CURRENT&nbsp;&nbsp; AGE</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 45m</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl scale --replicas=3 statefulset/bootcamp -n jq</span><br /><span style="color: #0000ff; font-family: Courier;">statefulset.apps "bootcamp" scaled</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl get statefulset -n jq</span><br /><span style="color: #0000ff; font-family: Courier;">NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DESIRED&nbsp;&nbsp; CURRENT&nbsp;&nbsp; AGE</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 47m</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ kubectl get pod -n jq -o wide</span><br /><span style="color: #0000ff; font-family: Courier;">NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; READY&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp;&nbsp; RESTARTS&nbsp;&nbsp; AGE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NODE</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-0&nbsp;&nbsp; 1/1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 48m&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10.244.0.42&nbsp;&nbsp; host-10-240-79-10</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-1&nbsp;&nbsp; 1/1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 48m&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10.244.1.63&nbsp;&nbsp; host-10-240-79-11</span><br /><span style="color: #0000ff; font-family: Courier;">bootcamp-2&nbsp;&nbsp; 1/1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 45s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10.244.2.60&nbsp;&nbsp; host-10-240-79-12</span><br /><span style="color: #0000ff; font-family: Courier;">[jinqing@host-10-240-79-10 statefulset]$ curl 10.240.79.12:30882</span><br /><span style="color: #0000ff; font-family: Courier;">Hello Kubernetes bootcamp! | Running on: bootcamp-2 | v=1</span><br /></div><img src ="http://www.cppblog.com/jinq0123/aggbug/215783.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2018-07-14 11:43 <a href="http://www.cppblog.com/jinq0123/archive/2018/07/14/215783.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>grpc-go与actor模式</title><link>http://www.cppblog.com/jinq0123/archive/2018/06/12/215720.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Tue, 12 Jun 2018 03:15:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2018/06/12/215720.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/215720.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2018/06/12/215720.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/215720.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/215720.html</trackback:ping><description><![CDATA[<div>grpc-go与actor模式<br /><br />(金庆的专栏 2018.6)<br /><br />grpc-go服务器的每个请求都在一个独立的协程中执行。<br />网游服务器中，一般请求会调用游戏房间的方法，而房间是一个独立的协程。<br />可以将房间实现为actor，grpc请求通过Call()或Post()方法来执行。<br />其中Call()会等待返回，而Post()会异步执行无返回值。<br /><br /><span style="color: #800000; font-family: Courier;">type Room struct {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;// actC 是其他协程向Room协程发送动作的Channel，协程中将依次执行动作。</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;// Action 动作, 是无参数无返回值的函数.</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;actC chan func()</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;...</span><br /><span style="color: #800000; font-family: Courier;">}</span><br /><br /><span style="color: #800000; font-family: Courier;">// Run 运行房间协程.</span><br /><span style="color: #800000; font-family: Courier;">func (r *Room) Run() {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;ticker := time.NewTicker(20 * time.Millisecond)</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;defer ticker.Stop()</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;for r.running {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;select {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;case act := &lt;-r.actC:</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;act()</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;case &lt;-ticker.C:</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;r.tick()</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;}</span><br /><span style="color: #800000; font-family: Courier;">}</span><br /><br /><span style="color: #800000; font-family: Courier;">// Call calls a function f and returns the result.</span><br /><span style="color: #800000; font-family: Courier;">// f runs in the Room's goroutine.</span><br /><span style="color: #800000; font-family: Courier;">func (r *Room) Call(f func() interface{}) interface{} {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;// 结果从ch返回</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;ch := make(chan interface{}, 1)</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;r.actC &lt;- func() {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ch &lt;- f()</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;}</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;// 等待直到返回结果</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;return &lt;-ch</span><br /><span style="color: #800000; font-family: Courier;">}</span><br /><br /><span style="color: #800000; font-family: Courier;">// Post 将一个动作投递到内部协程中执行.</span><br /><span style="color: #800000; font-family: Courier;">func (r *Room) Post(f func()) {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;r.actC &lt;- f</span><br /><span style="color: #800000; font-family: Courier;">}</span><br /><br />grpc服务方法如：<br /><br /><span style="color: #800000; font-family: Courier;">func (m *RoomService) Test(ctx context.Context, req *pb.TestReq) (*pb.TestResp, error) {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;conn := conn_mgr.GetConn(ctx)</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;if conn == nil {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return nil, fmt.Errorf("can not find connection")</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;}</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;room := conn.GetRoom()</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;resp := room.Call(func() interface{} {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return room.Test(req)</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;})</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;return resp.(*pb.TestResp), nil</span><br /><span style="color: #800000; font-family: Courier;">}</span></div><img src ="http://www.cppblog.com/jinq0123/aggbug/215720.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2018-06-12 11:15 <a href="http://www.cppblog.com/jinq0123/archive/2018/06/12/215720.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>推荐Golang的assert库</title><link>http://www.cppblog.com/jinq0123/archive/2018/05/14/215637.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Mon, 14 May 2018 02:42:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2018/05/14/215637.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/215637.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2018/05/14/215637.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/215637.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/215637.html</trackback:ping><description><![CDATA[<div>推荐Golang的assert库<br /><br />(金庆的专栏 2018.5)<br /><br />https://github.com/aurelien-rainone/assertgo<br /><br />Conditionally compiled assertions in Go <br /><br />和C++中的assert()一样，这个是带条件编译的，必须使用 debug 才能启用。<br /><br />如：<br /><br />$ go run -tags debug main.go<br /><br />使用示例：<br /><br />assert.True(true, "never printed")<br /><br /></div><img src ="http://www.cppblog.com/jinq0123/aggbug/215637.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2018-05-14 10:42 <a href="http://www.cppblog.com/jinq0123/archive/2018/05/14/215637.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>consul命令中的几个地址</title><link>http://www.cppblog.com/jinq0123/archive/2018/05/09/215627.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Wed, 09 May 2018 10:32:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2018/05/09/215627.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/215627.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2018/05/09/215627.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/215627.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/215627.html</trackback:ping><description><![CDATA[<div>consul命令中的几个地址<br /><br />(金庆的专栏 2018.5)<br /><br />consul命令行中有以下几个地址参数：<br /><br />* -bind<br />&nbsp;&nbsp; &nbsp;绑定地址，用于集群通信，缺省 0.0.0.0<br />* -clint<br />&nbsp;&nbsp; &nbsp;绑定地址，用于 RPC, DNS, HTTP and HTTPS，缺省 127.0.0.1<br />* -serf-lan-bind<br />&nbsp;&nbsp; &nbsp;绑定地址，用于内网集群通信，缺省使用 -bind 地址<br />* -serf-wan-bind<br />&nbsp;&nbsp; &nbsp;绑定地址，用于跨机房通信，缺省使用 -bind 地址<br />* -advertise<br />&nbsp;&nbsp; &nbsp;通告地址，通告给集群中其他节点，缺省使用 -bind 地址<br />* -advertise-wan<br />&nbsp;&nbsp; &nbsp;通告地址，通告给其他机房的服务节点，缺省使用 -advertise 地址<br />* -join -retry-join<br />&nbsp;&nbsp; &nbsp;加入集群的目标地址<br />* -join-wan -retry-join-wan<br />&nbsp;&nbsp; &nbsp;跨机房邦联的目标地址<br />* -recursor<br />&nbsp;&nbsp; &nbsp;上游DNS地址<br /><br />官方文档： https://www.consul.io/docs/agent/options.html#command-line-options<br /></div><img src ="http://www.cppblog.com/jinq0123/aggbug/215627.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2018-05-09 18:32 <a href="http://www.cppblog.com/jinq0123/archive/2018/05/09/215627.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>服务发现的组件</title><link>http://www.cppblog.com/jinq0123/archive/2018/05/04/215617.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Fri, 04 May 2018 08:07:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2018/05/04/215617.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/215617.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2018/05/04/215617.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/215617.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/215617.html</trackback:ping><description><![CDATA[<div><div>                 <p>服务发现的组件</p>  <p>(金庆的专栏 2018.5)</p>  <p>服务发现有以下组件：</p>  <ul><li><p>Service Registry 服务注册中心</p>  <p>维护服务的列表，提供查询。一般实现为分布式键值存储数据库。</p></li><li><p>Registrator 注册器</p>  <p>监听服务创建和删除事件，并在服务注册中心动态注册或注销服务。</p></li><li><p>Health Checker 健康检查器</p>  <p>监视服务是否健康，并在服务注册中心动态更新服务。</p></li><li><p>Load balancer 负载均衡器</p>  <p>将服务请求分散到各个服务器。</p></li></ul>            </div></div><img src ="http://www.cppblog.com/jinq0123/aggbug/215617.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2018-05-04 16:07 <a href="http://www.cppblog.com/jinq0123/archive/2018/05/04/215617.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>grpc-lua 示例</title><link>http://www.cppblog.com/jinq0123/archive/2018/04/13/215587.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Fri, 13 Apr 2018 07:41:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2018/04/13/215587.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/215587.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2018/04/13/215587.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/215587.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/215587.html</trackback:ping><description><![CDATA[<div>grpc-lua 示例<br /><br />(金庆的专栏 2018.4)<br /><br />grpc-lua (https://github.com/jinq0123/grpc-lua) 是 grpc 的 lua 绑定库，<br />应用了 luapbintf , 不需要生成代码，直接读取 proto 文件。<br /><br />如：<br /><span style="color: #800000;">&nbsp;&nbsp;&nbsp; -- Sync request.</span><br /><span style="color: #800000;">&nbsp;&nbsp;&nbsp; local request = { name = "world" }</span><br /><span style="color: #800000;">&nbsp;&nbsp;&nbsp; local response = assert(stub:sync_request("SayHello", request))</span><br /><span style="color: #800000;">&nbsp;&nbsp;&nbsp; print("Greeter received: " .. response.message)</span><br /><br />完整的示例代码见 examples 目录。<br /><br />服务器和客户端都支持同步和异步调用。<br /><br />将 examples/conan_install.bat.example 去除 .example 后缀，然后运行，<br />将安装所有的依赖库. 须先安装 conan 包管理工具 (http://docs.conan.io/en/latest/installation.html)。<br />还需要设置 git 代理，因为 grpc 的子库需要翻违墙。<br /><br />conan_install.bat 实际上是下载依赖库代码并编译。结果在用户目录 .conan/data/。<br />在其中搜索 lua-cpp.exe, lua-cpp.dll, luapbintf.dll, grpc_lua.dll 并复制到 examples/helloworld/ 目录下。<br /><br />然后在 helloworld 目录下分别运行 run_server.bat 和 run_client.bat 测试。<br /><br /><div>已编译测试可行的完整包可下载：<br />grpc-lua 代码及示例Windows执行程序：https://download.csdn.net/download/jq0123/10346554<br />grpc-lua 示例 CentOS 7.4 执行程序打包：https://download.csdn.net/download/jq0123/10346003<br /><br />CentOS 7.4 实测：<br /><br /><span style="color: #0000ff;">[jinqing@localhost helloworld]$ ls</span><br /><span style="color: #0000ff;">greeter_client.lua&nbsp;&nbsp; grpc_lua.so&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua-cpp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; run_server.bat</span><br /><span style="color: #0000ff;">greeter_server.lua&nbsp;&nbsp; helloworld.proto&nbsp; luapbintf.so</span><br /><span style="color: #0000ff;">greeter_service.lua&nbsp; liblua-cpp.so&nbsp;&nbsp;&nbsp;&nbsp; run_client.bat</span><br /><span style="color: #0000ff;">[jinqing@localhost helloworld]$ ./lua-cpp greeter_server.lua</span><br /><span style="color: #0000ff;">Server listening on 0.0.0.0:50051</span><br /><span style="color: #0000ff;">Got hello from world</span><br /><span style="color: #0000ff;">Got hello from world</span><br /><br /><span style="color: #0000ff;">[jinqing@localhost helloworld]$ ./lua-cpp greeter_client.lua</span><br /><span style="color: #0000ff;">Greeter received: Hello world</span><br /><span style="color: #0000ff;">Async greeter received: Hello world</span><br /><span style="color: #0000ff;">[jinqing@localhost helloworld]$</span><br /><br /><span style="color: #0000ff;">[jinqing@localhost route_guide]$ ./lua-cpp route_guide_server.lua</span><br /><span style="color: #0000ff;">RecordRoute reader end.</span><br /><span style="color: #0000ff;">RouteChat reader end.</span><br /><br /><span style="color: #0000ff;">[jinqing@localhost route_guide]$ ./lua-cpp route_guide_client.lua</span><br /><span style="color: #0000ff;">-------------- Sync get feature --------------</span><br /><span style="color: #0000ff;">Found feature: {</span><br /><span style="color: #0000ff;">...</span><br /></div></div><img src ="http://www.cppblog.com/jinq0123/aggbug/215587.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2018-04-13 15:41 <a href="http://www.cppblog.com/jinq0123/archive/2018/04/13/215587.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用 DocFetcher 全文搜索</title><link>http://www.cppblog.com/jinq0123/archive/2018/04/08/215577.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Sun, 08 Apr 2018 07:17:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2018/04/08/215577.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/215577.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2018/04/08/215577.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/215577.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/215577.html</trackback:ping><description><![CDATA[<div>用 DocFetcher 全文搜索<br /><br />(金庆的专栏 2018.4)<br /><br />以前用 Notepad++ 的全文搜索，没有索引，每次搜索都须等待一会儿。<br /><br />而 DocFetcher 是开源的桌面搜索应用，建好索引后，搜索会立即出结果。<br /><br />先须为某个目录创建索引，选择某些后缀名的文件。然后选中该索引进行搜索。<br /></div><img src="http://www.cppblog.com/images/cppblog_com/jinq0123/DocFetcherUI.png" alt="" width="1340" height="800" border="0" /><img src ="http://www.cppblog.com/jinq0123/aggbug/215577.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2018-04-08 15:17 <a href="http://www.cppblog.com/jinq0123/archive/2018/04/08/215577.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>gRPC-go 连接管理</title><link>http://www.cppblog.com/jinq0123/archive/2017/12/25/215444.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Mon, 25 Dec 2017 11:00:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2017/12/25/215444.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/215444.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2017/12/25/215444.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/215444.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/215444.html</trackback:ping><description><![CDATA[<div>gRPC-go 连接管理<br /><br />(金庆的专栏 2017.12)<br /><br />把 example greeter 改一下，处理 SayHello() 请求时，不仅仅返回本次请求者的名字，<br />还返回上次请求的名字，如：<br />```<br /><span style="color: #0000ff;">&#955; go run greeter_client/main.go</span><br /><span style="color: #0000ff;">2017/12/25 17:59:13 Greeting: Hello 'world' (prev '')</span><br /><span style="color: #0000ff;">2017/12/25 17:59:15 Greeting: Hello 'world2' (prev 'world')</span><br />```<br /><br />先将客户端单次请求改为多次请求：<br /><br />```go<br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;r, err := c.SayHello(context.Background(), &amp;pb.HelloRequest{Name: name})</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;log.Printf("Greeting: %s", r.Message)</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;time.Sleep(2 * time.Second)</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;r, err = c.SayHello(context.Background(), &amp;pb.HelloRequest{Name: name + "2"})</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;log.Printf("Greeting: %s", r.Message)</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;...</span><br />```<br /><br />服务器需要为每个连接保存各自的数据。连接创建时初始化数据，连接断开时清理数据。<br />这里利用了连接统计的接口，不知道是否是最适当的实现方式?<br /><br />服务器创建时添加 StatsHandler 选项，输入一个 stats.Handler 的实现。<br />```<br /><span style="color: #800000; font-family: Courier;">-&nbsp;&nbsp; &nbsp;s := grpc.NewServer()</span><br /><span style="color: #800000; font-family: Courier;">+&nbsp;&nbsp; &nbsp;s := grpc.NewServer(grpc.StatsHandler(&amp;statshandler{}))</span><br />``` <br /><br />statshandler 需实现4个方法，只用到2个连接相关的方法，TagConn() 和 HandleConn(),<br />另外2个 TagRPC() 和 HandleRPC() 用于RPC统计, 实现为空。<br /><br />```go<br /><span style="color: #800000; font-family: Courier;">type statshandler struct{}</span><br /><br /><span style="color: #800000; font-family: Courier;">// TagConn 用来给连接打个标签，以此来标识连接(实在是找不出还有什么办法来标识连接).</span><br /><span style="color: #800000; font-family: Courier;">// 这个标签是个指针，可保证每个连接唯一。</span><br /><span style="color: #800000; font-family: Courier;">// 将该指针添加到上下文中去，键为 connCtxKey{}.</span><br /><span style="color: #800000; font-family: Courier;">func (h *statshandler) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;return context.WithValue(ctx, connCtxKey{}, info)</span><br /><span style="color: #800000; font-family: Courier;">}</span><br /><br /><span style="color: #800000; font-family: Courier;">// TagRPC 为空.</span><br /><span style="color: #800000; font-family: Courier;">func (h *statshandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;return ctx</span><br /><span style="color: #800000; font-family: Courier;">}</span><br /><br /><span style="color: #800000; font-family: Courier;">// HandleConn 会在连接开始和结束时被调用，分别会输入不同的状态.</span><br /><span style="color: #800000; font-family: Courier;">func (h *statshandler) HandleConn(ctx context.Context, s stats.ConnStats) {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;tag, ok := getConnTagFromContext(ctx)</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;if !ok {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;log.Fatal("can not get conn tag")</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;}</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;connsMutex.Lock()</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;defer connsMutex.Unlock()</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;switch s.(type) {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;case *stats.ConnBegin:</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;conns[tag] = ""</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;log.Printf("begin conn, tag = (%p)%#v, now connections = %d\n", tag, tag, len(conns))</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;case *stats.ConnEnd:</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;delete(conns, tag)</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;log.Printf("end conn, tag = (%p)%#v, now connections = %d\n", tag, tag, len(conns))</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;default:</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;log.Printf("illegal ConnStats type\n")</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;}</span><br /><span style="color: #800000; font-family: Courier;">}</span><br /><br /><span style="color: #800000; font-family: Courier;">// HandleRPC 为空.</span><br /><span style="color: #800000; font-family: Courier;">func (h *statshandler) HandleRPC(ctx context.Context, s stats.RPCStats) {</span><br /><span style="color: #800000; font-family: Courier;">}</span><br />```<br /><br />用一个map来管理所有连接，以连接的标签(是个指针)为键，值为上次请求者的名字。<br />因为有多线程访问，所有加个 Mutex 来保护。<br />连接结束时，将从 conns 中删除连接相关的数据。<br /><br />```go<br /><span style="color: #800000; font-family: Courier;">var connsMutex sync.Mutex</span><br /><span style="color: #800000; font-family: Courier;">var conns map[*stats.ConnTagInfo]string = make(map[*stats.ConnTagInfo]string)</span><br />```<br /><br />getConnTagFromContext() 从上下文中取连接标签：<br /><br />```go<br /><span style="color: #800000; font-family: Courier;">type connCtxKey struct{}</span><br /><br /><span style="color: #800000; font-family: Courier;">func getConnTagFromContext(ctx context.Context) (*stats.ConnTagInfo, bool) {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;tag, ok := ctx.Value(connCtxKey{}).(*stats.ConnTagInfo)</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;return tag, ok</span><br /><span style="color: #800000; font-family: Courier;">}</span><br />```<br /><br />最后将 SayHello() 改为记录请求者名字，并返回上次请求者的名字。<br /><br />```go<br /><span style="color: #800000; font-family: Courier;">func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;tag, _ := getConnTagFromContext(ctx)</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;log.Printf("SayHello(), conn tag = (%p)%#v\n", tag, tag)</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;connsMutex.Lock()</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;defer connsMutex.Unlock()</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;prev := conns[tag]</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;conns[tag] = in.Name</span><br /><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;return &amp;pb.HelloReply{Message: fmt.Sprintf("Hello '%s' (prev '%s')", in.Name, prev)}, nil</span><br /><span style="color: #800000; font-family: Courier;">}</span><br />```<br /><br />测试多个客户端连接，可以看到每个客户端有自己的状态，互不影响。<br /><br />```<br /><span style="color: #0000ff;">E:\Git\grpc-go\examples\helloworld (master)</span><br /><span style="color: #0000ff;">&#955; go run greeter_server/main.go</span><br /><span style="color: #0000ff;">2017/12/25 18:39:03 start</span><br /><span style="color: #0000ff;">2017/12/25 18:39:11 begin conn, tag = (0xc042182040)&amp;stats.ConnTagInfo{RemoteAddr:(*net.TCPAddr)(0xc0420818f0), LocalAddr:(*net.TCPAddr)(0xc0420818c0)}, now connections = 1</span><br /><span style="color: #0000ff;">2017/12/25 18:39:11 SayHello(), conn tag = (0xc042182040)&amp;stats.ConnTagInfo{RemoteAddr:(*net.TCPAddr)(0xc0420818f0), LocalAddr:(*net.TCPAddr)(0xc0420818c0)}</span><br /><span style="color: #0000ff;">2017/12/25 18:39:13 SayHello(), conn tag = (0xc042182040)&amp;stats.ConnTagInfo{RemoteAddr:(*net.TCPAddr)(0xc0420818f0), LocalAddr:(*net.TCPAddr)(0xc0420818c0)}</span><br /><span style="color: #0000ff;">2017/12/25 18:39:13 begin conn, tag = (0xc0421ae200)&amp;stats.ConnTagInfo{RemoteAddr:(*net.TCPAddr)(0xc0421de060), LocalAddr:(*net.TCPAddr)(0xc0421de030)}, now connections = 2</span><br /><span style="color: #0000ff;">2017/12/25 18:39:13 SayHello(), conn tag = (0xc0421ae200)&amp;stats.ConnTagInfo{RemoteAddr:(*net.TCPAddr)(0xc0421de060), LocalAddr:(*net.TCPAddr)(0xc0421de030)}</span><br /><span style="color: #0000ff;">2017/12/25 18:39:15 SayHello(), conn tag = (0xc042182040)&amp;stats.ConnTagInfo{RemoteAddr:(*net.TCPAddr)(0xc0420818f0), LocalAddr:(*net.TCPAddr)(0xc0420818c0)}</span><br /><span style="color: #0000ff;">2017/12/25 18:39:15 SayHello(), conn tag = (0xc0421ae200)&amp;stats.ConnTagInfo{RemoteAddr:(*net.TCPAddr)(0xc0421de060), LocalAddr:(*net.TCPAddr)(0xc0421de030)}</span><br /><span style="color: #0000ff;">2017/12/25 18:39:17 SayHello(), conn tag = (0xc042182040)&amp;stats.ConnTagInfo{RemoteAddr:(*net.TCPAddr)(0xc0420818f0), LocalAddr:(*net.TCPAddr)(0xc0420818c0)}</span><br /><span style="color: #0000ff;">2017/12/25 18:39:17 SayHello(), conn tag = (0xc0421ae200)&amp;stats.ConnTagInfo{RemoteAddr:(*net.TCPAddr)(0xc0421de060), LocalAddr:(*net.TCPAddr)(0xc0421de030)}</span><br /><span style="color: #0000ff;">2017/12/25 18:39:19 end conn, tag = (0xc042182040)&amp;stats.ConnTagInfo{RemoteAddr:(*net.TCPAddr)(0xc0420818f0), LocalAddr:(*net.TCPAddr)(0xc0420818c0)}, now connections = 1</span><br /><span style="color: #0000ff;">2017/12/25 18:39:19 SayHello(), conn tag = (0xc0421ae200)&amp;stats.ConnTagInfo{RemoteAddr:(*net.TCPAddr)(0xc0421de060), LocalAddr:(*net.TCPAddr)(0xc0421de030)}</span><br /><span style="color: #0000ff;">2017/12/25 18:39:21 end conn, tag = (0xc0421ae200)&amp;stats.ConnTagInfo{RemoteAddr:(*net.TCPAddr)(0xc0421de060), LocalAddr:(*net.TCPAddr)(0xc0421de030)}, now connections = 0</span><br />```<br /></div><img src ="http://www.cppblog.com/jinq0123/aggbug/215444.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2017-12-25 19:00 <a href="http://www.cppblog.com/jinq0123/archive/2017/12/25/215444.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用 Lile 创建 gRPC-go 服务</title><link>http://www.cppblog.com/jinq0123/archive/2017/11/28/215377.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Tue, 28 Nov 2017 10:47:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2017/11/28/215377.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/215377.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2017/11/28/215377.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/215377.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/215377.html</trackback:ping><description><![CDATA[<div>用 Lile 创建 gRPC-go 服务<br /><br />(金庆的专栏 2017.11)<br /><br />Lile 是一个工具，用于 Go 语言快速创建 gRPC 服务。<br />https://github.com/lileio/lile<br /><br />会自动添加 Prometheus, Zipkin 和 Google PubSub 支持。<br /><br /><span style="color: #0000ff; font-family: Courier;">go get -u github.com/lileio/lile/...</span><br />将安装所有依赖包，并生成 bin/lile.exe, bin/protoc-gen-lile-server.exe.<br />另外还需要安装 protoc.exe.<br /><br />按照示例创建 users 服务：<br /><br /><span style="color: #0000ff; font-family: Courier;">E:\gopath\src\github.com</span><br /><span style="color: #0000ff; font-family: Courier;">&#955; lile new jinq0123/users</span><br /><span style="color: #0000ff; font-family: Courier;">Creating project in E:\gopath\src\github.com\jinq0123\users</span><br /><span style="color: #0000ff; font-family: Courier;">Is this OK? [y]es/[n]o</span><br /><span style="color: #0000ff; font-family: Courier;">y</span><br /><span style="color: #0000ff; font-family: Courier;">.</span><br /><span style="color: #0000ff; font-family: Courier;">&#9500;&#9472;&#9472; server</span><br /><span style="color: #0000ff; font-family: Courier;">&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; server.go</span><br /><span style="color: #0000ff; font-family: Courier;">&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; server_test.go</span><br /><span style="color: #0000ff; font-family: Courier;">&#9500;&#9472;&#9472; subscribers</span><br /><span style="color: #0000ff; font-family: Courier;">&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; subscribers.go</span><br /><span style="color: #0000ff; font-family: Courier;">&#9500;&#9472;&#9472; users</span><br /><span style="color: #0000ff; font-family: Courier;">&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; cmd</span><br /><span style="color: #0000ff; font-family: Courier;">&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; root.go</span><br /><span style="color: #0000ff; font-family: Courier;">&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; serve.go</span><br /><span style="color: #0000ff; font-family: Courier;">&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; subscribe.go</span><br /><span style="color: #0000ff; font-family: Courier;">&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; up.go</span><br /><span style="color: #0000ff; font-family: Courier;">&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; main.go</span><br /><span style="color: #0000ff; font-family: Courier;">&#9500;&#9472;&#9472; users.proto</span><br /><span style="color: #0000ff; font-family: Courier;">&#9500;&#9472;&#9472; Makefile</span><br /><span style="color: #0000ff; font-family: Courier;">&#9500;&#9472;&#9472; Dockerfile</span><br /><span style="color: #0000ff; font-family: Courier;">&#9500;&#9472;&#9472; .travis.yml</span><br /><span style="color: #0000ff; font-family: Courier;">&#9492;&#9472;&#9472; .gitignore</span><br /><br />查看 Makefile, 复制其中 protoc 脚本，将 $$GOPATH 改为 %GOPATH%，运行：<br /><br /><span style="color: #0000ff; font-family: Courier;">E:\gopath\src\github.com\jinq0123\users</span><br /><span style="color: #0000ff; font-family: Courier;">&#955; protoc -I . users.proto --lile-server_out=. --go_out=plugins=grpc:%GOPATH%/src</span><br /><span style="color: #0000ff; font-family: Courier;">2017/11/28 16:59:24 [Creating] server\read.go</span><br /><span style="color: #0000ff; font-family: Courier;">2017/11/28 16:59:24 [Creating test] server\read_test.go</span><br /><br />protoc-gen-lile-server.exe 将生成 server\read.go, 对应 user.proto 中的方法 Users::Read().<br />grpc的插件将生成 users.pb.go，与仅仅用 grpc 生成的代码相同。<br /><br /><span style="color: #0000ff; font-family: Courier;">D:/Go/bin/go.exe install -v [E:/gopath/src/github.com/jinq0123/users/users]</span><br /><span style="color: #0000ff; font-family: Courier;">github.com/jinq0123/users/users</span><br /><span style="color: #0000ff; font-family: Courier;">成功: 进程退出代码 0.</span><br /><br />可直接编译生成 user.exe.<br /><br />无参数运行则显示命令行帮助：<br /><br /><span style="color: #0000ff; font-family: Courier;">E:\gopath\src\github.com\jinq0123\users</span><br /><span style="color: #0000ff; font-family: Courier;">&#955; users</span><br /><span style="color: #0000ff; font-family: Courier;">A gRPC based service</span><br /><br /><span style="color: #0000ff; font-family: Courier;">Usage:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; users [command]</span><br /><br /><span style="color: #0000ff; font-family: Courier;">Available Commands:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; help&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Help about any command</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; serve&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Run the RPC server</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; subscribe&nbsp;&nbsp; Subscribe to and process queue messages</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; up&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; up runs both RPC and pubub subscribers</span><br /><br /><span style="color: #0000ff; font-family: Courier;">Flags:</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; -h, --help&nbsp;&nbsp; help for users</span><br /><br /><span style="color: #0000ff; font-family: Courier;">Use "users [command] --help" for more information about a command.</span><br /><br />用子命令serve启动服务：<br /><br /><span style="color: #0000ff; font-family: Courier;">E:\gopath\src\github.com\jinq0123\users</span><br /><span style="color: #0000ff; font-family: Courier;">&#955; users serve</span><br /><span style="color: #0000ff; font-family: Courier;">INFO[0000] Serving gRPC on :8000</span><br /><span style="color: #0000ff; font-family: Courier;">INFO[0000] Using Zipkin Global tracer</span><br /><span style="color: #0000ff; font-family: Courier;">INFO[0000] Prometheus metrics at :9000/metrics</span><br /><br />http://localhost:9000/metrics 会显示<br /><br /># HELP go_gc_duration_seconds A summary of the GC invocation durations.<br /># TYPE go_gc_duration_seconds summary<br />go_gc_duration_seconds{quantile="0"} 0<br />go_gc_duration_seconds{quantile="0.25"} 0<br />...<br /><br />用 grpc-lua 来测试下：<br /><br /><span style="color: #0000ff; font-family: Courier;">E:\Git\grpc-lua\examples\helloworld (master)</span><br /><span style="color: #0000ff; font-family: Courier;">&#955; lua-cpp.exe</span><br /><span style="color: #0000ff; font-family: Courier;">Lua 5.3.4&nbsp; Copyright (C) 1994-2017 Lua.org, PUC-Rio</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; package.path = "../../src/lua/?.lua;" .. package.path</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; grpc = require("grpc_lua.grpc_lua")</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; grpc.import_proto_file("users.proto")</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; stub = grpc.service_stub("localhost:8000", "users.Users")</span><br /><span style="color: #0000ff; font-family: Courier;">D1128 17:28:13.711000000&nbsp; 4612 dns_resolver.c:301] Using native dns resolver</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; request = {id = "abcd"}</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; response, err, cod = stub:sync_request("Read", request)</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; cod</span><br /><span style="color: #0000ff; font-family: Courier;">2</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; insp = require("inspect")</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; insp(resonse)</span><br /><span style="color: #0000ff; font-family: Courier;">nil</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; insp(err)</span><br /><span style="color: #0000ff; font-family: Courier;">"not yet implemented"</span><br /><br />缺省实现返回 "not yet implemented" 错误。更改实现代如下：<br /><br /><span style="color: #800000; font-family: Courier;">func (s UsersServer) Read(ctx context.Context, r *users.Request) (*users.Response, error) {</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;// return nil, errors.New("not yet implemented")</span><br /><span style="color: #800000; font-family: Courier;">&nbsp;&nbsp; &nbsp;return &amp;users.Response{Id: "Hello, " + r.Id}, nil</span><br /><span style="color: #800000; font-family: Courier;">}</span><br /><br />再次请求：<br /><br /><span style="color: #0000ff; font-family: Courier;">&gt; response, err, cod = stub:sync_request("Read", request)</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; err</span><br /><span style="color: #0000ff; font-family: Courier;">Endpoint read failed</span><br /><span style="color: #0000ff; font-family: Courier;">...</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; response, err, cod = stub:sync_request("Read", request)</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; err</span><br /><span style="color: #0000ff; font-family: Courier;">nil</span><br /><span style="color: #0000ff; font-family: Courier;">&gt; insp(response)</span><br /><span style="color: #0000ff; font-family: Courier;">{</span><br /><span style="color: #0000ff; font-family: Courier;">&nbsp; id = "Hello, abcd"</span><br /><span style="color: #0000ff; font-family: Courier;">}</span><br /></div><img src ="http://www.cppblog.com/jinq0123/aggbug/215377.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2017-11-28 18:47 <a href="http://www.cppblog.com/jinq0123/archive/2017/11/28/215377.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>protobuf中的枚举缺省值应该为ENUN_TYPE_UNSPECIFIED</title><link>http://www.cppblog.com/jinq0123/archive/2017/11/22/215364.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Wed, 22 Nov 2017 02:45:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2017/11/22/215364.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/215364.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2017/11/22/215364.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/215364.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/215364.html</trackback:ping><description><![CDATA[<div>protobuf中的枚举缺省值应该为ENUN_TYPE_UNSPECIFIED<br /><br />(金庆的专栏 2017.11)<br /><br />Googol 公布的 API Design 规范中，*.proto 中的枚举缺省值建议为 ENUN_TYPE_UNSPECIFIED。<br /><br />https://mp.weixin.qq.com/s?__biz=MzA5ODg4Mzk2OQ==&amp;mid=2247483705&amp;idx=1&amp;sn=cc2ffef9ac431510c1791dbe6e774b85<br /><br />The first value should be named ENUM_TYPE_UNSPECIFIED as it is returned when an enum value is not explicitly specified.<br /><br /><span style="font-family: Courier; color: #800000;">enum FooBar {</span><br /><span style="font-family: Courier; color: #800000;">&nbsp; // The first value represents the default and must be == 0.</span><br /><span style="font-family: Courier; color: #800000;">&nbsp; FOO_BAR_UNSPECIFIED = 0;</span><br /><span style="font-family: Courier; color: #800000;">&nbsp; FIRST_VALUE = 1;</span><br /><span style="font-family: Courier; color: #800000;">&nbsp; SECOND_VALUE = 2;</span><br /><span style="font-family: Courier; color: #800000;">}</span><br /><br />参考：protobuf中的枚举缺省值应该为UNKNOWN<br />http://blog.csdn.net/jq0123/article/details/52219597<br /></div><img src ="http://www.cppblog.com/jinq0123/aggbug/215364.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2017-11-22 10:45 <a href="http://www.cppblog.com/jinq0123/archive/2017/11/22/215364.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用 dep 代替 go get 来获取私有库</title><link>http://www.cppblog.com/jinq0123/archive/2017/11/06/215328.html</link><dc:creator>金庆</dc:creator><author>金庆</author><pubDate>Mon, 06 Nov 2017 06:27:00 GMT</pubDate><guid>http://www.cppblog.com/jinq0123/archive/2017/11/06/215328.html</guid><wfw:comment>http://www.cppblog.com/jinq0123/comments/215328.html</wfw:comment><comments>http://www.cppblog.com/jinq0123/archive/2017/11/06/215328.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jinq0123/comments/commentRss/215328.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jinq0123/services/trackbacks/215328.html</trackback:ping><description><![CDATA[<div><p><div><h1>用 dep 代替 go get 来获取私有库</h1></div><br /></p><p>(金庆的专栏 2017.11)</p><p><br /></p>  <p><code>go get</code> 功能比较弱，无法获取分支，标签，特定版本，fork, 而 <code>dep</code> 可以做到。<code>dep</code> 还可以获取私有库。</p>  <p>用 gitee.com 私有库作测试。创建 gogettest 库。</p>  <p>可用：</p>  <pre><code cs=""  has-numbering"=""><span style="font-family: Courier; color: #000080;">go get -u gitee.com/jinq0123/gogettest</span></code></pre>  <p>如果改为私有库则失败：</p>    <pre><div><span style="color: #000080; font-family: Courier;">&#955; go get -v gitee.com/jinq0123/gogettest</span><br style="font-family: Courier;" /><span style="color: #000080; font-family: Courier;">Fetching https://gitee.com/jinq0123/gogettest?go-get=1</span><br style="font-family: Courier;" /><span style="color: #000080; font-family: Courier;">Parsing meta tags from https://gitee.com/jinq0123/gogettest?go-get=1 (status code 403)</span><br style="font-family: Courier;" /><span style="color: #000080; font-family: Courier;">package gitee.com/jinq0123/gogettest: unrecognized import path "gitee.com/jinq0123/gogettest" (parse https://gitee.com/jinq0123/gogettest?go-get=1: no go-import meta tags ())</span></div><br /></pre><h2>用 dep 工具取私有库</h2>  <p><a href="https://github.com/golang/dep" target="_blank">https://github.com/golang/dep</a></p>    <h3><a name="t2"></a>安装dep</h3>    <pre><code cs=""  has-numbering"=""><span style="font-family: Courier; color: #000080;">go get -u github.com/golang/dep/cmd/dep</span></code><span style="font-family: Courier; color: #000080;">1</span><code cs=""  has-numbering"=""></code></pre>    <h3><a name="t3"></a>初始化</h3>  <p>在项目目录下运行：</p>    <pre><code has-numbering"=""><span style="font-family: Courier; color: #000080;">dep init</span></code></pre>  <p>生成 <code>Gopkg.toml</code> 和 <code>Gopkg.lock</code></p>    <h3><a name="t4"></a>添加强制(constraint)</h3>  <p>在 Gopkg.toml 中添加：</p><p>&nbsp;</p><div><span style="color: #800000; font-family: Courier;">[[constraint]]</span><br style="font-family: Courier;" /><span style="color: #800000; font-family: Courier;">&nbsp; branch = "master"</span><br style="font-family: Courier;" /><span style="color: #800000; font-family: Courier;">&nbsp; name = "gitee.com/jinq012345/gogettest"</span><br style="font-family: Courier;" /><span style="color: #800000; font-family: Courier;">&nbsp; source = "https://gitee.com/jinq0123/gogettest.git"</span></div>source 强制使用 https 来获取 gotgettest 库。  <p>&nbsp;</p><p>注意库名改成了 <code>jinq012345</code>, 这样导入： <br /> <code><span style="color: #800000; font-family: Courier;">imort "gitee.com/jinq012345/gogettest"</span></code></p>  <p>name和source的设置可支持从 fork 库获取。</p>    <h3><a name="t5"></a>获取<code>gogettest</code>库</h3>    <pre><code ruby=""  has-numbering"=""><span style="color: #000080;">dep ensure</span></code></pre>  <p>会弹出 https 的登录用户名和密码输入框。</p></div><img src ="http://www.cppblog.com/jinq0123/aggbug/215328.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jinq0123/" target="_blank">金庆</a> 2017-11-06 14:27 <a href="http://www.cppblog.com/jinq0123/archive/2017/11/06/215328.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>