Browse Source

Add concurrency option

renovate/configure
Yota Toyama 8 years ago
parent
commit
7bd8a25639
  1. 35
      arguments.go
  2. 8
      examples/markdown.feature
  3. 4
      file_checker.go
  4. 3
      main.go
  5. 22
      url_checker.go

35
arguments.go

@ -1,35 +1,57 @@
package main package main
import ( import (
"fmt"
"runtime"
"strconv" "strconv"
"time" "time"
"github.com/docopt/docopt-go" "github.com/docopt/docopt-go"
) )
const maxConcurrency = 256
var defaultConcurrency = func() int {
n := 8 * runtime.NumCPU() // 8 is an empirical value.
if n < maxConcurrency {
return n
}
return maxConcurrency
}()
const usage = `Link checker for Markdown and HTML const usage = `Link checker for Markdown and HTML
Usage: Usage:
liche [-t <timeout>] [-v] <filenames>... liche [-c <num-requests>] [-t <timeout>] [-v] <filenames>...
Options: Options:
-v, --verbose Be verbose -c, --concurrency <num-requests> Set max number of concurrent HTTP requests [default: %v]
-t, --timeout <timeout> Set timeout for HTTP requests in seconds [default: 5]` -t, --timeout <timeout> Set timeout for HTTP requests in seconds [default: 5]
-v, --verbose Be verbose`
type arguments struct { type arguments struct {
filenames []string filenames []string
concurrency int
timeout time.Duration timeout time.Duration
verbose bool verbose bool
} }
func getArgs() (arguments, error) { func getArgs() (arguments, error) {
args, err := docopt.Parse(usage, nil, true, "liche", true) args, err := docopt.Parse(fmt.Sprintf(usage, defaultConcurrency), nil, true, "liche", true)
if err != nil {
return arguments{}, err
}
c, err := strconv.ParseInt(args["--concurrency"].(string), 10, 32)
if err != nil { if err != nil {
return arguments{}, err return arguments{}, err
} }
f, err := strconv.ParseFloat(args["--timeout"].(string), 64) t, err := strconv.ParseFloat(args["--timeout"].(string), 64)
if err != nil { if err != nil {
return arguments{}, err return arguments{}, err
@ -37,7 +59,8 @@ func getArgs() (arguments, error) {
return arguments{ return arguments{
args["<filenames>"].([]string), args["<filenames>"].([]string),
time.Duration(f) * time.Second, int(c),
time.Duration(t) * time.Second,
args["--verbose"].(bool), args["--verbose"].(bool),
}, nil }, nil
} }

8
examples/markdown.feature

@ -112,3 +112,11 @@ Feature: Markdown
""" """
When I successfully run `liche --timeout 10 foo.md` When I successfully run `liche --timeout 10 foo.md`
Then the stdout should contain exactly "" Then the stdout should contain exactly ""
Scenario: Set concurrency
Given a file named "foo.md" with:
"""
[Google](https://google.com)
"""
When I successfully run `liche --concurrency 10 foo.md`
Then the stdout should contain exactly ""

4
file_checker.go

@ -15,8 +15,8 @@ type fileChecker struct {
urlChecker urlChecker urlChecker urlChecker
} }
func newFileChecker(timeout time.Duration) fileChecker { func newFileChecker(timeout time.Duration, s semaphore) fileChecker {
return fileChecker{newURLChecker(timeout)} return fileChecker{newURLChecker(timeout, s)}
} }
func (c fileChecker) Check(f string) ([]urlResult, error) { func (c fileChecker) Check(f string) ([]urlResult, error) {

3
main.go

@ -11,7 +11,8 @@ func main() {
} }
rc := make(chan fileResult, len(args.filenames)) rc := make(chan fileResult, len(args.filenames))
c := newFileChecker(args.timeout) s := newSemaphore(args.concurrency)
c := newFileChecker(args.timeout, s)
go c.CheckMany(args.filenames, rc) go c.CheckMany(args.filenames, rc)

22
url_checker.go

@ -2,34 +2,22 @@ package main
import ( import (
"net/http" "net/http"
"runtime"
"sync" "sync"
"time" "time"
) )
const maxOpenFiles = 512
var sem = make(chan bool, func() int {
n := 8 * runtime.NumCPU() // 8 is an empirical value.
if n < maxOpenFiles {
return n
}
return maxOpenFiles
}())
type urlChecker struct { type urlChecker struct {
client http.Client client http.Client
semaphore semaphore
} }
func newURLChecker(timeout time.Duration) urlChecker { func newURLChecker(t time.Duration, s semaphore) urlChecker {
return urlChecker{http.Client{Timeout: timeout}} return urlChecker{http.Client{Timeout: t}, s}
} }
func (c urlChecker) Check(s string) (resultErr error) { func (c urlChecker) Check(s string) (resultErr error) {
sem <- true c.semaphore.Request()
defer func() { <-sem }() defer c.semaphore.Release()
res, err := c.client.Get(s) res, err := c.client.Get(s)

Loading…
Cancel
Save