草庐IT

c# - 在没有 Web 应用程序的情况下使用 Amazon Elastic Beanstalk 部署 .NET Windows 服务

coder 2023-07-08 原文

我想创建一个 Elastic Beanstalk 配置,允许我部署 .NET Windows 服务但不部署 Web 应用程序。

我刚读过this blog post其中解释了如何使用 .ebextensions 将 Windows 服务与您的 Web 应用程序一起部署,但是是否存在可以在不为 Web 应用程序部署 Web 部署包的情况下运行 .ebextensions 的场景?

我唯一的选择是创建一个包含 .ebextensions 目录的空 Web 应用程序,然后部署 Web 部署包吗?

Elastic Beanstalk 常见问题解答提到了部署非 Web 应用程序的能力 (here),我在 AWS 开发人员论坛 (here) 上发现了一个类似的(未回答的)问题。

更新

由于在这个问题上缺乏事件并且我无法在互联网上找到任何其他信息,我只是假设这个问题的答案是“否”(至少现在是这样)。

我最终创建了一个空的 Web 应用程序,并使用它通过 .ebextensions YAML 配置部署了我的 Windows 服务。

作为旁注,我想强调 this page from Amazon's documentation我发现这是创建这些特殊配置文件的非常有用的指南。

另一个更新

实现上述方法后,我发现 Elastic Beanstalk 没有为新的 Beanstalk 实例执行我的 .ebextensions 脚本。导致创建新实例时Windows服务安装失败。我不得不跳过几个环节才能最终获得可扩展的解决方案。如果您需要最终解决方案的详细信息,请告诉我。

最终,Elastic Beanstalk 似乎并不适合部署可扩展的 Windows 服务。


基本解决方案

我不愿意发布源代码,因为它不是用于个人项目,但这是我当前部署解决方案的基本结构:

  1. 自定义 EC2 AMI 包含一个在启动时运行的“引导”程序。该程序执行以下操作:
    1.1。 Download a 'zip' archive来自(可配置的)“部署”S3 存储桶
    1.2。将下载的 zip 文件解压缩到临时目录
    1.3。找到/执行了“install.bat”脚本(脚本的名称也是可配置的)。此脚本安装并启动 Windows 服务。
  2. 使用 bootsrap 程序将 Elastic Beanstalk“实例 AMI”设置为自定义 AMI(请参阅:this article)

要部署新代码:将安装 .zip 存档(包含 Windows 服务和 install.bat 文件)上传到 S3 存储桶并终止 Elastic Beanstalk 应用程序的所有 EC2 实例。当重新创建实例时, Bootstrap 将下载/安装新更新的代码。

当然,如果我重新开始,我会跳过使用 Elastic Beanstalk 并使用标准的 AWS 自动缩放以及类似的部署方案。 底线是,如果您没有 Web 应用程序,请不要使用 Elastic Beanstalk;您最好使用标准的 AWS 自动缩放。

新的 AWS 部署工具

亚马逊最近宣布了几个似乎可以解决部署问题的新代码部署/管理服务:http://aws.amazon.com/blogs/aws/code-management-and-deployment/

我还没有使用过这些新服务(我什至不确定它们是否已经发布),但它们看起来很有前途。

最佳答案

由于这个问题已经存在了一段时间,仍然没有答案,但一直引起人们的兴趣,让我分享我对一个非常相似的问题的解决方案——在 EC2 实例上安装 Windows 服务。不过我没有使用 Beanstalk,因为该服务更多地是为快速部署 Web 应用程序而设计的。相反,我直接使用 Beanstalk 在下面使用的 CloudFormation 来部署与 Web 应用程序相关的资源。

堆栈需要现有的 VPC(我们跨越多个可用性区域)、一个存储所有服务构建工件的 S3 存储桶和一个 EC2 key 对。模板使用 Windows AMI 和少数其他资源(如带访问 key 的 IAM 用户)和工作 S3 存储桶创建 EC2 实例,仅用于说明如何创建您的服务可能需要的其他资源。模板还将压缩包的名称作为参数,其中包含已上传到构建工件 S3 存储桶中的所有服务二进制文件和配置文件(我们使用为我们制作的 TeamCity 构建服务器,但您可以手动创建和上传包类(class))。当您构建新版本的服务时,您只需创建新包(例如 service.v2.zip),使用新名称更新堆栈,服务将自动更新。模板包含 4 个不同区域的 AMI 的 ID,但您可以根据需要随时添加其他区域。这是堆栈模板:

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Service stack template.",
    "Parameters": {
        "KeyPair": {
            "Type": "String",
            "Default": "MyDefaultKeys",
            "Description": "Name of EC2 Key Pair."
        },
        "ServicePackageName": {
            "Type": "String",
            "Default": "service.zip",
            "Description": "Name of the zip package of the service files."
        },
        "DeploymentBucketName": {
            "Type": "String",
            "Default": "",
            "Description": "Name of the deployment bucket where all the artifacts are."
        },
        "VPCId": {
            "Type": "String",
            "Default": "",
            "Description": "Identifier of existing VPC."
        },
        "VPCSubnets": {
            "Default": "",
            "Description": "Commaseparated list of existing subnets within the existing VPC. Could be just one.",
            "Type": "CommaDelimitedList"
        },
        "VPCSecurityGroup": {
            "Default": "",
            "Description": "Existing VPC security group. That should be the ID of the VPC's default security group.",
            "Type": "String"
        }
    },
    "Mappings": {
        "Region2WinAMI": {
            "us-east-1": { "64": "ami-40f0d32a" },
            "us-west-1": { "64": "ami-20601740" },
            "us-west-2": { "64": "ami-ff4baf9f" },
            "eu-west-1": { "64": "ami-3367d340" }
        }
    },
    "Resources": {
        "ServiceInstance": {
            "Type": "AWS::EC2::Instance",
            "Metadata": {
                "Comment": "Install Service",
                "AWS::CloudFormation::Init": {
                    "configSets": {
                        "default": [ "ServiceConfig" ]
                    },
                    "ServiceConfig": {
                        "files": {
                            "c:\\service\\settings.config": {
                                "source": { "Fn::Join": [ "/", [ "https://s3.amazonaws.com", { "Ref": "DeploymentBucketName" }, "deployments/stacks", { "Ref": "AWS::StackName" }, "templates/settings.config.mustache" ] ] },
                                "context": {
                                    "region": { "Ref": "AWS::Region" },
                                    "accesskey": { "Ref": "IAMUserAccessKey" },
                                    "secretkey": { "Fn::GetAtt": [ "IAMUserAccessKey", "SecretAccessKey" ] },
                                    "bucket": { "Ref": "BucketName" }
                                }
                            },
                            "c:\\cfn\\cfn-hup.conf": {
                                "content": {
                                    "Fn::Join": [
                                        "",
                                        [
                                            "[main]\n",
                                            "stack=",
                                            { "Ref": "AWS::StackId" },
                                            "\n",
                                            "region=",
                                            { "Ref": "AWS::Region" },
                                            "\n",
                                            "interval=1"
                                        ]
                                    ]
                                }
                            },
                            "c:\\cfn\\hooks.d\\cfn-auto-reloader.conf": {
                                "content": {
                                    "Fn::Join": [
                                        "",
                                        [
                                            "[cfn-auto-reloader-hook]\n",
                                            "triggers=post.update\n",
                                            "path=Resources.ServiceInstance.Metadata.AWS::CloudFormation::Init\n",
                                            "action=cfn-init.exe -v -s ",
                                            { "Ref": "AWS::StackName" },
                                            " -r ServiceInstance --region ",
                                            { "Ref": "AWS::Region" },
                                            "\n"
                                        ]
                                    ]
                                }
                            }
                        },
                        "sources": {
                            "c:\\tmp\\service": { "Fn::Join": [ "/", [ "https://s3.amazonaws.com", { "Ref": "DeploymentBucketName" }, "deployments/stacks", { "Ref": "AWS::StackName" }, "artifacts/Service", { "Ref": "ServicePackageName" } ] ] }
                        },
                        "commands": {
                            "Install Service": {
                                "command": "call c:\\tmp\\service\\install.bat",
                                "ignoreErrors": "false"
                            }
                        },
                        "services": {
                            "windows": {
                                "cfn-hup": {
                                    "enabled": "true",
                                    "ensureRunning": "true",
                                    "files": [ "c:\\cfn\\cfn-hup.conf", "c:\\cfn\\hooks.d\\cfn-auto-reloader.conf" ]
                                }
                            }
                        }
                    }
                }
            },
            "Properties": {
                "ImageId": { "Fn::FindInMap": [ "Region2WinAMI", { "Ref": "AWS::Region" }, "64" ] },
                "InstanceType": "t2.micro",
                "KeyName": { "Ref": "KeyPair" },
                "SecurityGroupIds" : [{ "Ref": "VPCSecurityGroup" }],
                "SubnetId" : { "Fn::Select": [ "0", { "Ref": "VPCSubnets" } ] },
                "UserData": {
                    "Fn::Base64": {
                        "Fn::Join": [
                            "",
                            [
                                "<script>\n",
                                "if not exist \"C:\\logs\" mkdir C:\\logs \n",
                                "cfn-init.exe -v -s ",
                                { "Ref": "AWS::StackName" },
                                " -r ServiceInstance --region ",
                                { "Ref": "AWS::Region" },
                                " -c default \n",
                                "</script>\n"
                            ]
                        ]
                    }
                },
                "BlockDeviceMappings": [
                    {
                        "DeviceName": "/dev/sda1",
                        "Ebs": {
                            "DeleteOnTermination": "true",
                            "VolumeSize": "40",
                            "VolumeType": "gp2"
                        }
                    }
                ],
                "Tags": [
                    { "Key": "Name", "Value": { "Fn::Join": [ ".", [ { "Ref": "AWS::StackName" }, "service" ] ] } }
                ]
            }
        },
        "BucketName": {
            "Type": "AWS::S3::Bucket",
            "Properties": {
                "AccessControl": "PublicRead"
            },
            "DeletionPolicy": "Retain"
        },
        "IAMUser": {
            "Type": "AWS::IAM::User",
            "Properties": {
                "Path": "/",
                "Groups": [ "stack-users" ],
                "Policies": [
                    {
                        "PolicyName": "giveaccesstobuckets",
                        "PolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Effect": "Allow",
                                    "Action": [ "s3:*" ],
                                    "Resource": [ { "Fn::Join": [ "", [ "arn:aws:s3:::", { "Ref": "BucketName" }, "/*" ] ] } ]
                                }
                            ]
                        }
                    }
                ]
            }
        },
        "IAMUserAccessKey": {
            "Type": "AWS::IAM::AccessKey",
            "Properties": {
                "UserName": { "Ref": "IAMUser" }
            }
        }
    }
}

如您所见,复制工件后,我们执行 install.bat 批处理文件(包含在 zip 文件中),它将文件移动到正确的位置并注册服务。这是文件的内容:

@echo off
sc query MyService > NUL
IF ERRORLEVEL 1060 GOTO COPYANDCREATE
sc stop MyService
waitfor /T 20 ServiceStop
echo D | xcopy "c:\tmp\service" "c:\service\" /E /Y /i
GOTO END
:COPYANDCREATE
echo D | xcopy "c:\tmp\service" "c:\service\" /E /Y /i
sc create MyService binpath= "c:\service\MyService.exe" start= "auto"
:END
sc start MyService

模板还创建配置文件(来自也驻留在工件存储桶中的 settings.config.mustache),其中包含有关已创建供服务使用的其他资源的信息。在这里:

<appSettings>
    <add key="AWSAccessKey" value="{{accesskey}}" />
    <add key="AWSSecretKey" value="{{secretkey}}" />
    <add key="AWSRegion" value="{{region}}" />
    <add key="AWSBucket" value="{{bucket}}" />
</appSettings>

您可以从 AWS Web 控制台或 CLI 创建并稍后更新堆栈.

差不多就这些了。您可以访问the AWS CloudFormation website获取有关该服务以及如何使用模板的更多信息。

P.S.:我意识到如果我也共享创建 VPC 的模板会更好。我将它分开,因为我每个区域有一个 VPC。如果您愿意,您可以将它与服务模板集成,但这意味着每次您创建新堆栈时,也会创建一个新的 VPC。这是 VPC 模板:

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "VPC stack template.",
    "Mappings": {
        "Region2AZ": {
            "us-east-1": { "AZ": [ "us-east-1a", "us-east-1b", "us-east-1d" ] },
            "us-west-1": { "AZ": [ "us-west-1b", "us-west-1c" ] },
            "us-west-2": { "AZ": [ "us-west-2a", "us-west-2b", "us-west-2c" ] },
            "eu-west-1": { "AZ": [ "eu-west-1a", "eu-west-1b", "eu-west-1c" ] }
        }
    },
    "Conditions": {
        "RegionHas3Zones": { "Fn::Not" : [ { "Fn::Equals" : [ { "Ref": "AWS::Region" }, "us-west-1" ] } ] }
    },
    "Resources": {
        "VPC": {
            "Type": "AWS::EC2::VPC",
            "Properties": {
                "CidrBlock": "10.0.0.0/16",
                "EnableDnsSupport" : "true",
                "EnableDnsHostnames" : "true"
            }
        },
        "VPCSecurityGroup": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "GroupDescription": "Security group for VPC.",
                "VpcId": { "Ref": "VPC" }
            }
        },
        "Subnet0": {
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": { "Ref": "VPC" },
                "CidrBlock": "10.0.0.0/24",
                "AvailabilityZone": { "Fn::Select": [ "0", { "Fn::FindInMap": [ "Region2AZ", { "Ref": "AWS::Region" }, "AZ" ] } ] }
            }
        },
        "Subnet1": {
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": { "Ref": "VPC" },
                "CidrBlock": "10.0.1.0/24",
                "AvailabilityZone": { "Fn::Select": [ "1", { "Fn::FindInMap": [ "Region2AZ", { "Ref": "AWS::Region" }, "AZ" ] } ] }
            }
        },
        "Subnet2": {
            "Type": "AWS::EC2::Subnet",
            "Condition": "RegionHas3Zones",
            "Properties": {
                "VpcId": { "Ref": "VPC" },
                "CidrBlock": "10.0.2.0/24",
                "AvailabilityZone": { "Fn::Select": [ "2", { "Fn::FindInMap": [ "Region2AZ", { "Ref": "AWS::Region" }, "AZ" ] } ] }
            }
        },
        "InternetGateway": {
            "Type": "AWS::EC2::InternetGateway",
            "Properties": {
            }
        },
        "AttachGateway": {
            "Type": "AWS::EC2::VPCGatewayAttachment",
            "Properties": {
                "VpcId": { "Ref": "VPC" },
                "InternetGatewayId": { "Ref": "InternetGateway" }
            }
        },
        "RouteTable": {
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": { "Ref": "VPC" }
            }
        },
        "Route": {
            "Type": "AWS::EC2::Route",
            "DependsOn": "AttachGateway",
            "Properties": {
                "RouteTableId": { "Ref": "RouteTable" },
                "DestinationCidrBlock": "0.0.0.0/0",
                "GatewayId": { "Ref": "InternetGateway" }
            }
        },
        "SubnetRouteTableAssociation0": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": { "Ref": "Subnet0" },
                "RouteTableId": { "Ref": "RouteTable" }
            }
        },
        "SubnetRouteTableAssociation1": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": { "Ref": "Subnet1" },
                "RouteTableId": { "Ref": "RouteTable" }
            }
        },
        "SubnetRouteTableAssociation2": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Condition": "RegionHas3Zones",
            "Properties": {
                "SubnetId": { "Ref": "Subnet2" },
                "RouteTableId": { "Ref": "RouteTable" }
            }
        },
        "NetworkAcl": {
            "Type": "AWS::EC2::NetworkAcl",
            "Properties": {
                "VpcId": { "Ref": "VPC" }
            }
        },
        "AllowAllInboundTCPAclEntry": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": { "Ref": "NetworkAcl" },
                "RuleNumber": "100",
                "Protocol": "6",
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "0.0.0.0/0",
                "PortRange": { "From": "0", "To": "65535" }
            }
        },
        "AllowAllInboundUDPAclEntry": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": { "Ref": "NetworkAcl" },
                "RuleNumber": "101",
                "Protocol": "17",
                "RuleAction": "allow",
                "Egress": "false",
                "CidrBlock": "0.0.0.0/0",
                "PortRange": { "From": "0", "To": "65535" }
            }
        },
        "AllowAllOutboundTCPAclEntry": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": { "Ref": "NetworkAcl" },
                "RuleNumber": "100",
                "Protocol": "6",
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "0.0.0.0/0",
                "PortRange": { "From": "0", "To": "65535" }
            }
        },
        "AllowAllOutboundUDPAclEntry": {
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "NetworkAclId": { "Ref": "NetworkAcl" },
                "RuleNumber": "101",
                "Protocol": "17",
                "RuleAction": "allow",
                "Egress": "true",
                "CidrBlock": "0.0.0.0/0",
                "PortRange": { "From": "0", "To": "65535" }
            }
        },
        "SubnetNetworkAclAssociation0": {
            "Type": "AWS::EC2::SubnetNetworkAclAssociation",
            "Properties": {
                "SubnetId": { "Ref": "Subnet0" },
                "NetworkAclId": { "Ref": "NetworkAcl" }
            }
        },
        "SubnetNetworkAclAssociation1": {
            "Type": "AWS::EC2::SubnetNetworkAclAssociation",
            "Properties": {
                "SubnetId": { "Ref": "Subnet1" },
                "NetworkAclId": { "Ref": "NetworkAcl" }
            }
        },
        "SubnetNetworkAclAssociation2": {
            "Type": "AWS::EC2::SubnetNetworkAclAssociation",
            "Condition": "RegionHas3Zones",
            "Properties": {
                "SubnetId": { "Ref": "Subnet2" },
                "NetworkAclId": { "Ref": "NetworkAcl" }
            }
        }
    },
    "Outputs": {
        "VPC": {
            "Description": "VPC",
            "Value": { "Ref": "VPC" }
        },
        "VPCSecurityGroup": {
            "Description": "VPC Security Group Id",
            "Value": { "Fn::GetAtt": [ "VPCSecurityGroup", "GroupId" ] }
        },
        "Subnet0": {
            "Description": "Subnet0 Id",
            "Value": { "Ref": "Subnet0" }
        },
        "Subnet1": {
            "Description": "Subnet1 Id",
            "Value": { "Ref": "Subnet1" }
        },
        "Subnet2": {
            "Description": "Subnet2 Id",
            "Condition": "RegionHas3Zones",
            "Value": { "Ref": "Subnet2" }
        }
    }
}

关于c# - 在没有 Web 应用程序的情况下使用 Amazon Elastic Beanstalk 部署 .NET Windows 服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24686305/

有关c# - 在没有 Web 应用程序的情况下使用 Amazon Elastic Beanstalk 部署 .NET Windows 服务的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  10. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

随机推荐